roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     }
847 });/*
848  * Based on:
849  * Ext JS Library 1.1.1
850  * Copyright(c) 2006-2007, Ext JS, LLC.
851  *
852  * Originally Released Under LGPL - original licence link has changed is not relivant.
853  *
854  * Fork - LGPL
855  * <script type="text/javascript">
856  */
857
858 /**
859  * @class Date
860  *
861  * The date parsing and format syntax is a subset of
862  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
863  * supported will provide results equivalent to their PHP versions.
864  *
865  * Following is the list of all currently supported formats:
866  *<pre>
867 Sample date:
868 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
869
870 Format  Output      Description
871 ------  ----------  --------------------------------------------------------------
872   d      10         Day of the month, 2 digits with leading zeros
873   D      Wed        A textual representation of a day, three letters
874   j      10         Day of the month without leading zeros
875   l      Wednesday  A full textual representation of the day of the week
876   S      th         English ordinal day of month suffix, 2 chars (use with j)
877   w      3          Numeric representation of the day of the week
878   z      9          The julian date, or day of the year (0-365)
879   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
880   F      January    A full textual representation of the month
881   m      01         Numeric representation of a month, with leading zeros
882   M      Jan        Month name abbreviation, three letters
883   n      1          Numeric representation of a month, without leading zeros
884   t      31         Number of days in the given month
885   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
886   Y      2007       A full numeric representation of a year, 4 digits
887   y      07         A two digit representation of a year
888   a      pm         Lowercase Ante meridiem and Post meridiem
889   A      PM         Uppercase Ante meridiem and Post meridiem
890   g      3          12-hour format of an hour without leading zeros
891   G      15         24-hour format of an hour without leading zeros
892   h      03         12-hour format of an hour with leading zeros
893   H      15         24-hour format of an hour with leading zeros
894   i      05         Minutes with leading zeros
895   s      01         Seconds, with leading zeros
896   O      -0600      Difference to Greenwich time (GMT) in hours
897   T      CST        Timezone setting of the machine running the code
898   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
899 </pre>
900  *
901  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
902  * <pre><code>
903 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
904 document.write(dt.format('Y-m-d'));                         //2007-01-10
905 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
906 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
907  </code></pre>
908  *
909  * Here are some standard date/time patterns that you might find helpful.  They
910  * are not part of the source of Date.js, but to use them you can simply copy this
911  * block of code into any script that is included after Date.js and they will also become
912  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
913  * <pre><code>
914 Date.patterns = {
915     ISO8601Long:"Y-m-d H:i:s",
916     ISO8601Short:"Y-m-d",
917     ShortDate: "n/j/Y",
918     LongDate: "l, F d, Y",
919     FullDateTime: "l, F d, Y g:i:s A",
920     MonthDay: "F d",
921     ShortTime: "g:i A",
922     LongTime: "g:i:s A",
923     SortableDateTime: "Y-m-d\\TH:i:s",
924     UniversalSortableDateTime: "Y-m-d H:i:sO",
925     YearMonth: "F, Y"
926 };
927 </code></pre>
928  *
929  * Example usage:
930  * <pre><code>
931 var dt = new Date();
932 document.write(dt.format(Date.patterns.ShortDate));
933  </code></pre>
934  */
935
936 /*
937  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
938  * They generate precompiled functions from date formats instead of parsing and
939  * processing the pattern every time you format a date.  These functions are available
940  * on every Date object (any javascript function).
941  *
942  * The original article and download are here:
943  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
944  *
945  */
946  
947  
948  // was in core
949 /**
950  Returns the number of milliseconds between this date and date
951  @param {Date} date (optional) Defaults to now
952  @return {Number} The diff in milliseconds
953  @member Date getElapsed
954  */
955 Date.prototype.getElapsed = function(date) {
956         return Math.abs((date || new Date()).getTime()-this.getTime());
957 };
958 // was in date file..
959
960
961 // private
962 Date.parseFunctions = {count:0};
963 // private
964 Date.parseRegexes = [];
965 // private
966 Date.formatFunctions = {count:0};
967
968 // private
969 Date.prototype.dateFormat = function(format) {
970     if (Date.formatFunctions[format] == null) {
971         Date.createNewFormat(format);
972     }
973     var func = Date.formatFunctions[format];
974     return this[func]();
975 };
976
977
978 /**
979  * Formats a date given the supplied format string
980  * @param {String} format The format string
981  * @return {String} The formatted date
982  * @method
983  */
984 Date.prototype.format = Date.prototype.dateFormat;
985
986 // private
987 Date.createNewFormat = function(format) {
988     var funcName = "format" + Date.formatFunctions.count++;
989     Date.formatFunctions[format] = funcName;
990     var code = "Date.prototype." + funcName + " = function(){return ";
991     var special = false;
992     var ch = '';
993     for (var i = 0; i < format.length; ++i) {
994         ch = format.charAt(i);
995         if (!special && ch == "\\") {
996             special = true;
997         }
998         else if (special) {
999             special = false;
1000             code += "'" + String.escape(ch) + "' + ";
1001         }
1002         else {
1003             code += Date.getFormatCode(ch);
1004         }
1005     }
1006     /** eval:var:zzzzzzzzzzzzz */
1007     eval(code.substring(0, code.length - 3) + ";}");
1008 };
1009
1010 // private
1011 Date.getFormatCode = function(character) {
1012     switch (character) {
1013     case "d":
1014         return "String.leftPad(this.getDate(), 2, '0') + ";
1015     case "D":
1016         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1017     case "j":
1018         return "this.getDate() + ";
1019     case "l":
1020         return "Date.dayNames[this.getDay()] + ";
1021     case "S":
1022         return "this.getSuffix() + ";
1023     case "w":
1024         return "this.getDay() + ";
1025     case "z":
1026         return "this.getDayOfYear() + ";
1027     case "W":
1028         return "this.getWeekOfYear() + ";
1029     case "F":
1030         return "Date.monthNames[this.getMonth()] + ";
1031     case "m":
1032         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1033     case "M":
1034         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1035     case "n":
1036         return "(this.getMonth() + 1) + ";
1037     case "t":
1038         return "this.getDaysInMonth() + ";
1039     case "L":
1040         return "(this.isLeapYear() ? 1 : 0) + ";
1041     case "Y":
1042         return "this.getFullYear() + ";
1043     case "y":
1044         return "('' + this.getFullYear()).substring(2, 4) + ";
1045     case "a":
1046         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1047     case "A":
1048         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1049     case "g":
1050         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1051     case "G":
1052         return "this.getHours() + ";
1053     case "h":
1054         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1055     case "H":
1056         return "String.leftPad(this.getHours(), 2, '0') + ";
1057     case "i":
1058         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1059     case "s":
1060         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1061     case "O":
1062         return "this.getGMTOffset() + ";
1063     case "T":
1064         return "this.getTimezone() + ";
1065     case "Z":
1066         return "(this.getTimezoneOffset() * -60) + ";
1067     default:
1068         return "'" + String.escape(character) + "' + ";
1069     }
1070 };
1071
1072 /**
1073  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1074  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1075  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1076  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1077  * string or the parse operation will fail.
1078  * Example Usage:
1079 <pre><code>
1080 //dt = Fri May 25 2007 (current date)
1081 var dt = new Date();
1082
1083 //dt = Thu May 25 2006 (today's month/day in 2006)
1084 dt = Date.parseDate("2006", "Y");
1085
1086 //dt = Sun Jan 15 2006 (all date parts specified)
1087 dt = Date.parseDate("2006-1-15", "Y-m-d");
1088
1089 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1090 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1091 </code></pre>
1092  * @param {String} input The unparsed date as a string
1093  * @param {String} format The format the date is in
1094  * @return {Date} The parsed date
1095  * @static
1096  */
1097 Date.parseDate = function(input, format) {
1098     if (Date.parseFunctions[format] == null) {
1099         Date.createParser(format);
1100     }
1101     var func = Date.parseFunctions[format];
1102     return Date[func](input);
1103 };
1104
1105 // private
1106 Date.createParser = function(format) {
1107     var funcName = "parse" + Date.parseFunctions.count++;
1108     var regexNum = Date.parseRegexes.length;
1109     var currentGroup = 1;
1110     Date.parseFunctions[format] = funcName;
1111
1112     var code = "Date." + funcName + " = function(input){\n"
1113         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1114         + "var d = new Date();\n"
1115         + "y = d.getFullYear();\n"
1116         + "m = d.getMonth();\n"
1117         + "d = d.getDate();\n"
1118         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1119         + "if (results && results.length > 0) {";
1120     var regex = "";
1121
1122     var special = false;
1123     var ch = '';
1124     for (var i = 0; i < format.length; ++i) {
1125         ch = format.charAt(i);
1126         if (!special && ch == "\\") {
1127             special = true;
1128         }
1129         else if (special) {
1130             special = false;
1131             regex += String.escape(ch);
1132         }
1133         else {
1134             var obj = Date.formatCodeToRegex(ch, currentGroup);
1135             currentGroup += obj.g;
1136             regex += obj.s;
1137             if (obj.g && obj.c) {
1138                 code += obj.c;
1139             }
1140         }
1141     }
1142
1143     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1144         + "{v = new Date(y, m, d, h, i, s);}\n"
1145         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1146         + "{v = new Date(y, m, d, h, i);}\n"
1147         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1148         + "{v = new Date(y, m, d, h);}\n"
1149         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1150         + "{v = new Date(y, m, d);}\n"
1151         + "else if (y >= 0 && m >= 0)\n"
1152         + "{v = new Date(y, m);}\n"
1153         + "else if (y >= 0)\n"
1154         + "{v = new Date(y);}\n"
1155         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1156         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1157         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1158         + ";}";
1159
1160     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1161      /** eval:var:zzzzzzzzzzzzz */
1162     eval(code);
1163 };
1164
1165 // private
1166 Date.formatCodeToRegex = function(character, currentGroup) {
1167     switch (character) {
1168     case "D":
1169         return {g:0,
1170         c:null,
1171         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1172     case "j":
1173         return {g:1,
1174             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1175             s:"(\\d{1,2})"}; // day of month without leading zeroes
1176     case "d":
1177         return {g:1,
1178             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1179             s:"(\\d{2})"}; // day of month with leading zeroes
1180     case "l":
1181         return {g:0,
1182             c:null,
1183             s:"(?:" + Date.dayNames.join("|") + ")"};
1184     case "S":
1185         return {g:0,
1186             c:null,
1187             s:"(?:st|nd|rd|th)"};
1188     case "w":
1189         return {g:0,
1190             c:null,
1191             s:"\\d"};
1192     case "z":
1193         return {g:0,
1194             c:null,
1195             s:"(?:\\d{1,3})"};
1196     case "W":
1197         return {g:0,
1198             c:null,
1199             s:"(?:\\d{2})"};
1200     case "F":
1201         return {g:1,
1202             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1203             s:"(" + Date.monthNames.join("|") + ")"};
1204     case "M":
1205         return {g:1,
1206             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1207             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1208     case "n":
1209         return {g:1,
1210             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1211             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1212     case "m":
1213         return {g:1,
1214             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1215             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1216     case "t":
1217         return {g:0,
1218             c:null,
1219             s:"\\d{1,2}"};
1220     case "L":
1221         return {g:0,
1222             c:null,
1223             s:"(?:1|0)"};
1224     case "Y":
1225         return {g:1,
1226             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1227             s:"(\\d{4})"};
1228     case "y":
1229         return {g:1,
1230             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1231                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1232             s:"(\\d{1,2})"};
1233     case "a":
1234         return {g:1,
1235             c:"if (results[" + currentGroup + "] == 'am') {\n"
1236                 + "if (h == 12) { h = 0; }\n"
1237                 + "} else { if (h < 12) { h += 12; }}",
1238             s:"(am|pm)"};
1239     case "A":
1240         return {g:1,
1241             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1242                 + "if (h == 12) { h = 0; }\n"
1243                 + "} else { if (h < 12) { h += 12; }}",
1244             s:"(AM|PM)"};
1245     case "g":
1246     case "G":
1247         return {g:1,
1248             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1249             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1250     case "h":
1251     case "H":
1252         return {g:1,
1253             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1254             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1255     case "i":
1256         return {g:1,
1257             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1258             s:"(\\d{2})"};
1259     case "s":
1260         return {g:1,
1261             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1262             s:"(\\d{2})"};
1263     case "O":
1264         return {g:1,
1265             c:[
1266                 "o = results[", currentGroup, "];\n",
1267                 "var sn = o.substring(0,1);\n", // get + / - sign
1268                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1269                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1270                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1271                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1272             ].join(""),
1273             s:"([+\-]\\d{4})"};
1274     case "T":
1275         return {g:0,
1276             c:null,
1277             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1278     case "Z":
1279         return {g:1,
1280             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1281                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1282             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1283     default:
1284         return {g:0,
1285             c:null,
1286             s:String.escape(character)};
1287     }
1288 };
1289
1290 /**
1291  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1292  * @return {String} The abbreviated timezone name (e.g. 'CST')
1293  */
1294 Date.prototype.getTimezone = function() {
1295     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1296 };
1297
1298 /**
1299  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1300  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1301  */
1302 Date.prototype.getGMTOffset = function() {
1303     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1304         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1305         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1306 };
1307
1308 /**
1309  * Get the numeric day number of the year, adjusted for leap year.
1310  * @return {Number} 0 through 364 (365 in leap years)
1311  */
1312 Date.prototype.getDayOfYear = function() {
1313     var num = 0;
1314     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1315     for (var i = 0; i < this.getMonth(); ++i) {
1316         num += Date.daysInMonth[i];
1317     }
1318     return num + this.getDate() - 1;
1319 };
1320
1321 /**
1322  * Get the string representation of the numeric week number of the year
1323  * (equivalent to the format specifier 'W').
1324  * @return {String} '00' through '52'
1325  */
1326 Date.prototype.getWeekOfYear = function() {
1327     // Skip to Thursday of this week
1328     var now = this.getDayOfYear() + (4 - this.getDay());
1329     // Find the first Thursday of the year
1330     var jan1 = new Date(this.getFullYear(), 0, 1);
1331     var then = (7 - jan1.getDay() + 4);
1332     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1333 };
1334
1335 /**
1336  * Whether or not the current date is in a leap year.
1337  * @return {Boolean} True if the current date is in a leap year, else false
1338  */
1339 Date.prototype.isLeapYear = function() {
1340     var year = this.getFullYear();
1341     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1342 };
1343
1344 /**
1345  * Get the first day of the current month, adjusted for leap year.  The returned value
1346  * is the numeric day index within the week (0-6) which can be used in conjunction with
1347  * the {@link #monthNames} array to retrieve the textual day name.
1348  * Example:
1349  *<pre><code>
1350 var dt = new Date('1/10/2007');
1351 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1352 </code></pre>
1353  * @return {Number} The day number (0-6)
1354  */
1355 Date.prototype.getFirstDayOfMonth = function() {
1356     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1357     return (day < 0) ? (day + 7) : day;
1358 };
1359
1360 /**
1361  * Get the last day of the current month, adjusted for leap year.  The returned value
1362  * is the numeric day index within the week (0-6) which can be used in conjunction with
1363  * the {@link #monthNames} array to retrieve the textual day name.
1364  * Example:
1365  *<pre><code>
1366 var dt = new Date('1/10/2007');
1367 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1368 </code></pre>
1369  * @return {Number} The day number (0-6)
1370  */
1371 Date.prototype.getLastDayOfMonth = function() {
1372     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1373     return (day < 0) ? (day + 7) : day;
1374 };
1375
1376
1377 /**
1378  * Get the first date of this date's month
1379  * @return {Date}
1380  */
1381 Date.prototype.getFirstDateOfMonth = function() {
1382     return new Date(this.getFullYear(), this.getMonth(), 1);
1383 };
1384
1385 /**
1386  * Get the last date of this date's month
1387  * @return {Date}
1388  */
1389 Date.prototype.getLastDateOfMonth = function() {
1390     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1391 };
1392 /**
1393  * Get the number of days in the current month, adjusted for leap year.
1394  * @return {Number} The number of days in the month
1395  */
1396 Date.prototype.getDaysInMonth = function() {
1397     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1398     return Date.daysInMonth[this.getMonth()];
1399 };
1400
1401 /**
1402  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1403  * @return {String} 'st, 'nd', 'rd' or 'th'
1404  */
1405 Date.prototype.getSuffix = function() {
1406     switch (this.getDate()) {
1407         case 1:
1408         case 21:
1409         case 31:
1410             return "st";
1411         case 2:
1412         case 22:
1413             return "nd";
1414         case 3:
1415         case 23:
1416             return "rd";
1417         default:
1418             return "th";
1419     }
1420 };
1421
1422 // private
1423 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1424
1425 /**
1426  * An array of textual month names.
1427  * Override these values for international dates, for example...
1428  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1429  * @type Array
1430  * @static
1431  */
1432 Date.monthNames =
1433    ["January",
1434     "February",
1435     "March",
1436     "April",
1437     "May",
1438     "June",
1439     "July",
1440     "August",
1441     "September",
1442     "October",
1443     "November",
1444     "December"];
1445
1446 /**
1447  * An array of textual day names.
1448  * Override these values for international dates, for example...
1449  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1450  * @type Array
1451  * @static
1452  */
1453 Date.dayNames =
1454    ["Sunday",
1455     "Monday",
1456     "Tuesday",
1457     "Wednesday",
1458     "Thursday",
1459     "Friday",
1460     "Saturday"];
1461
1462 // private
1463 Date.y2kYear = 50;
1464 // private
1465 Date.monthNumbers = {
1466     Jan:0,
1467     Feb:1,
1468     Mar:2,
1469     Apr:3,
1470     May:4,
1471     Jun:5,
1472     Jul:6,
1473     Aug:7,
1474     Sep:8,
1475     Oct:9,
1476     Nov:10,
1477     Dec:11};
1478
1479 /**
1480  * Creates and returns a new Date instance with the exact same date value as the called instance.
1481  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1482  * variable will also be changed.  When the intention is to create a new variable that will not
1483  * modify the original instance, you should create a clone.
1484  *
1485  * Example of correctly cloning a date:
1486  * <pre><code>
1487 //wrong way:
1488 var orig = new Date('10/1/2006');
1489 var copy = orig;
1490 copy.setDate(5);
1491 document.write(orig);  //returns 'Thu Oct 05 2006'!
1492
1493 //correct way:
1494 var orig = new Date('10/1/2006');
1495 var copy = orig.clone();
1496 copy.setDate(5);
1497 document.write(orig);  //returns 'Thu Oct 01 2006'
1498 </code></pre>
1499  * @return {Date} The new Date instance
1500  */
1501 Date.prototype.clone = function() {
1502         return new Date(this.getTime());
1503 };
1504
1505 /**
1506  * Clears any time information from this date
1507  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1508  @return {Date} this or the clone
1509  */
1510 Date.prototype.clearTime = function(clone){
1511     if(clone){
1512         return this.clone().clearTime();
1513     }
1514     this.setHours(0);
1515     this.setMinutes(0);
1516     this.setSeconds(0);
1517     this.setMilliseconds(0);
1518     return this;
1519 };
1520
1521 // private
1522 // safari setMonth is broken
1523 if(Roo.isSafari){
1524     Date.brokenSetMonth = Date.prototype.setMonth;
1525         Date.prototype.setMonth = function(num){
1526                 if(num <= -1){
1527                         var n = Math.ceil(-num);
1528                         var back_year = Math.ceil(n/12);
1529                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1530                         this.setFullYear(this.getFullYear() - back_year);
1531                         return Date.brokenSetMonth.call(this, month);
1532                 } else {
1533                         return Date.brokenSetMonth.apply(this, arguments);
1534                 }
1535         };
1536 }
1537
1538 /** Date interval constant 
1539 * @static 
1540 * @type String */
1541 Date.MILLI = "ms";
1542 /** Date interval constant 
1543 * @static 
1544 * @type String */
1545 Date.SECOND = "s";
1546 /** Date interval constant 
1547 * @static 
1548 * @type String */
1549 Date.MINUTE = "mi";
1550 /** Date interval constant 
1551 * @static 
1552 * @type String */
1553 Date.HOUR = "h";
1554 /** Date interval constant 
1555 * @static 
1556 * @type String */
1557 Date.DAY = "d";
1558 /** Date interval constant 
1559 * @static 
1560 * @type String */
1561 Date.MONTH = "mo";
1562 /** Date interval constant 
1563 * @static 
1564 * @type String */
1565 Date.YEAR = "y";
1566
1567 /**
1568  * Provides a convenient method of performing basic date arithmetic.  This method
1569  * does not modify the Date instance being called - it creates and returns
1570  * a new Date instance containing the resulting date value.
1571  *
1572  * Examples:
1573  * <pre><code>
1574 //Basic usage:
1575 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1576 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1577
1578 //Negative values will subtract correctly:
1579 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1580 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1581
1582 //You can even chain several calls together in one line!
1583 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1584 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1585  </code></pre>
1586  *
1587  * @param {String} interval   A valid date interval enum value
1588  * @param {Number} value      The amount to add to the current date
1589  * @return {Date} The new Date instance
1590  */
1591 Date.prototype.add = function(interval, value){
1592   var d = this.clone();
1593   if (!interval || value === 0) return d;
1594   switch(interval.toLowerCase()){
1595     case Date.MILLI:
1596       d.setMilliseconds(this.getMilliseconds() + value);
1597       break;
1598     case Date.SECOND:
1599       d.setSeconds(this.getSeconds() + value);
1600       break;
1601     case Date.MINUTE:
1602       d.setMinutes(this.getMinutes() + value);
1603       break;
1604     case Date.HOUR:
1605       d.setHours(this.getHours() + value);
1606       break;
1607     case Date.DAY:
1608       d.setDate(this.getDate() + value);
1609       break;
1610     case Date.MONTH:
1611       var day = this.getDate();
1612       if(day > 28){
1613           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1614       }
1615       d.setDate(day);
1616       d.setMonth(this.getMonth() + value);
1617       break;
1618     case Date.YEAR:
1619       d.setFullYear(this.getFullYear() + value);
1620       break;
1621   }
1622   return d;
1623 };/*
1624  * Based on:
1625  * Ext JS Library 1.1.1
1626  * Copyright(c) 2006-2007, Ext JS, LLC.
1627  *
1628  * Originally Released Under LGPL - original licence link has changed is not relivant.
1629  *
1630  * Fork - LGPL
1631  * <script type="text/javascript">
1632  */
1633
1634 Roo.lib.Dom = {
1635     getViewWidth : function(full) {
1636         return full ? this.getDocumentWidth() : this.getViewportWidth();
1637     },
1638
1639     getViewHeight : function(full) {
1640         return full ? this.getDocumentHeight() : this.getViewportHeight();
1641     },
1642
1643     getDocumentHeight: function() {
1644         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1645         return Math.max(scrollHeight, this.getViewportHeight());
1646     },
1647
1648     getDocumentWidth: function() {
1649         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1650         return Math.max(scrollWidth, this.getViewportWidth());
1651     },
1652
1653     getViewportHeight: function() {
1654         var height = self.innerHeight;
1655         var mode = document.compatMode;
1656
1657         if ((mode || Roo.isIE) && !Roo.isOpera) {
1658             height = (mode == "CSS1Compat") ?
1659                      document.documentElement.clientHeight :
1660                      document.body.clientHeight;
1661         }
1662
1663         return height;
1664     },
1665
1666     getViewportWidth: function() {
1667         var width = self.innerWidth;
1668         var mode = document.compatMode;
1669
1670         if (mode || Roo.isIE) {
1671             width = (mode == "CSS1Compat") ?
1672                     document.documentElement.clientWidth :
1673                     document.body.clientWidth;
1674         }
1675         return width;
1676     },
1677
1678     isAncestor : function(p, c) {
1679         p = Roo.getDom(p);
1680         c = Roo.getDom(c);
1681         if (!p || !c) {
1682             return false;
1683         }
1684
1685         if (p.contains && !Roo.isSafari) {
1686             return p.contains(c);
1687         } else if (p.compareDocumentPosition) {
1688             return !!(p.compareDocumentPosition(c) & 16);
1689         } else {
1690             var parent = c.parentNode;
1691             while (parent) {
1692                 if (parent == p) {
1693                     return true;
1694                 }
1695                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1696                     return false;
1697                 }
1698                 parent = parent.parentNode;
1699             }
1700             return false;
1701         }
1702     },
1703
1704     getRegion : function(el) {
1705         return Roo.lib.Region.getRegion(el);
1706     },
1707
1708     getY : function(el) {
1709         return this.getXY(el)[1];
1710     },
1711
1712     getX : function(el) {
1713         return this.getXY(el)[0];
1714     },
1715
1716     getXY : function(el) {
1717         var p, pe, b, scroll, bd = document.body;
1718         el = Roo.getDom(el);
1719         var fly = Roo.lib.AnimBase.fly;
1720         if (el.getBoundingClientRect) {
1721             b = el.getBoundingClientRect();
1722             scroll = fly(document).getScroll();
1723             return [b.left + scroll.left, b.top + scroll.top];
1724         }
1725         var x = 0, y = 0;
1726
1727         p = el;
1728
1729         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1730
1731         while (p) {
1732
1733             x += p.offsetLeft;
1734             y += p.offsetTop;
1735
1736             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1737                 hasAbsolute = true;
1738             }
1739
1740             if (Roo.isGecko) {
1741                 pe = fly(p);
1742
1743                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1744                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1745
1746
1747                 x += bl;
1748                 y += bt;
1749
1750
1751                 if (p != el && pe.getStyle('overflow') != 'visible') {
1752                     x += bl;
1753                     y += bt;
1754                 }
1755             }
1756             p = p.offsetParent;
1757         }
1758
1759         if (Roo.isSafari && hasAbsolute) {
1760             x -= bd.offsetLeft;
1761             y -= bd.offsetTop;
1762         }
1763
1764         if (Roo.isGecko && !hasAbsolute) {
1765             var dbd = fly(bd);
1766             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1767             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1768         }
1769
1770         p = el.parentNode;
1771         while (p && p != bd) {
1772             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1773                 x -= p.scrollLeft;
1774                 y -= p.scrollTop;
1775             }
1776             p = p.parentNode;
1777         }
1778         return [x, y];
1779     },
1780  
1781   
1782
1783
1784     setXY : function(el, xy) {
1785         el = Roo.fly(el, '_setXY');
1786         el.position();
1787         var pts = el.translatePoints(xy);
1788         if (xy[0] !== false) {
1789             el.dom.style.left = pts.left + "px";
1790         }
1791         if (xy[1] !== false) {
1792             el.dom.style.top = pts.top + "px";
1793         }
1794     },
1795
1796     setX : function(el, x) {
1797         this.setXY(el, [x, false]);
1798     },
1799
1800     setY : function(el, y) {
1801         this.setXY(el, [false, y]);
1802     }
1803 };
1804 /*
1805  * Portions of this file are based on pieces of Yahoo User Interface Library
1806  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1807  * YUI licensed under the BSD License:
1808  * http://developer.yahoo.net/yui/license.txt
1809  * <script type="text/javascript">
1810  *
1811  */
1812
1813 Roo.lib.Event = function() {
1814     var loadComplete = false;
1815     var listeners = [];
1816     var unloadListeners = [];
1817     var retryCount = 0;
1818     var onAvailStack = [];
1819     var counter = 0;
1820     var lastError = null;
1821
1822     return {
1823         POLL_RETRYS: 200,
1824         POLL_INTERVAL: 20,
1825         EL: 0,
1826         TYPE: 1,
1827         FN: 2,
1828         WFN: 3,
1829         OBJ: 3,
1830         ADJ_SCOPE: 4,
1831         _interval: null,
1832
1833         startInterval: function() {
1834             if (!this._interval) {
1835                 var self = this;
1836                 var callback = function() {
1837                     self._tryPreloadAttach();
1838                 };
1839                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1840
1841             }
1842         },
1843
1844         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1845             onAvailStack.push({ id:         p_id,
1846                 fn:         p_fn,
1847                 obj:        p_obj,
1848                 override:   p_override,
1849                 checkReady: false    });
1850
1851             retryCount = this.POLL_RETRYS;
1852             this.startInterval();
1853         },
1854
1855
1856         addListener: function(el, eventName, fn) {
1857             el = Roo.getDom(el);
1858             if (!el || !fn) {
1859                 return false;
1860             }
1861
1862             if ("unload" == eventName) {
1863                 unloadListeners[unloadListeners.length] =
1864                 [el, eventName, fn];
1865                 return true;
1866             }
1867
1868             var wrappedFn = function(e) {
1869                 return fn(Roo.lib.Event.getEvent(e));
1870             };
1871
1872             var li = [el, eventName, fn, wrappedFn];
1873
1874             var index = listeners.length;
1875             listeners[index] = li;
1876
1877             this.doAdd(el, eventName, wrappedFn, false);
1878             return true;
1879
1880         },
1881
1882
1883         removeListener: function(el, eventName, fn) {
1884             var i, len;
1885
1886             el = Roo.getDom(el);
1887
1888             if(!fn) {
1889                 return this.purgeElement(el, false, eventName);
1890             }
1891
1892
1893             if ("unload" == eventName) {
1894
1895                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1896                     var li = unloadListeners[i];
1897                     if (li &&
1898                         li[0] == el &&
1899                         li[1] == eventName &&
1900                         li[2] == fn) {
1901                         unloadListeners.splice(i, 1);
1902                         return true;
1903                     }
1904                 }
1905
1906                 return false;
1907             }
1908
1909             var cacheItem = null;
1910
1911
1912             var index = arguments[3];
1913
1914             if ("undefined" == typeof index) {
1915                 index = this._getCacheIndex(el, eventName, fn);
1916             }
1917
1918             if (index >= 0) {
1919                 cacheItem = listeners[index];
1920             }
1921
1922             if (!el || !cacheItem) {
1923                 return false;
1924             }
1925
1926             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1927
1928             delete listeners[index][this.WFN];
1929             delete listeners[index][this.FN];
1930             listeners.splice(index, 1);
1931
1932             return true;
1933
1934         },
1935
1936
1937         getTarget: function(ev, resolveTextNode) {
1938             ev = ev.browserEvent || ev;
1939             var t = ev.target || ev.srcElement;
1940             return this.resolveTextNode(t);
1941         },
1942
1943
1944         resolveTextNode: function(node) {
1945             if (Roo.isSafari && node && 3 == node.nodeType) {
1946                 return node.parentNode;
1947             } else {
1948                 return node;
1949             }
1950         },
1951
1952
1953         getPageX: function(ev) {
1954             ev = ev.browserEvent || ev;
1955             var x = ev.pageX;
1956             if (!x && 0 !== x) {
1957                 x = ev.clientX || 0;
1958
1959                 if (Roo.isIE) {
1960                     x += this.getScroll()[1];
1961                 }
1962             }
1963
1964             return x;
1965         },
1966
1967
1968         getPageY: function(ev) {
1969             ev = ev.browserEvent || ev;
1970             var y = ev.pageY;
1971             if (!y && 0 !== y) {
1972                 y = ev.clientY || 0;
1973
1974                 if (Roo.isIE) {
1975                     y += this.getScroll()[0];
1976                 }
1977             }
1978
1979
1980             return y;
1981         },
1982
1983
1984         getXY: function(ev) {
1985             ev = ev.browserEvent || ev;
1986             return [this.getPageX(ev), this.getPageY(ev)];
1987         },
1988
1989
1990         getRelatedTarget: function(ev) {
1991             ev = ev.browserEvent || ev;
1992             var t = ev.relatedTarget;
1993             if (!t) {
1994                 if (ev.type == "mouseout") {
1995                     t = ev.toElement;
1996                 } else if (ev.type == "mouseover") {
1997                     t = ev.fromElement;
1998                 }
1999             }
2000
2001             return this.resolveTextNode(t);
2002         },
2003
2004
2005         getTime: function(ev) {
2006             ev = ev.browserEvent || ev;
2007             if (!ev.time) {
2008                 var t = new Date().getTime();
2009                 try {
2010                     ev.time = t;
2011                 } catch(ex) {
2012                     this.lastError = ex;
2013                     return t;
2014                 }
2015             }
2016
2017             return ev.time;
2018         },
2019
2020
2021         stopEvent: function(ev) {
2022             this.stopPropagation(ev);
2023             this.preventDefault(ev);
2024         },
2025
2026
2027         stopPropagation: function(ev) {
2028             ev = ev.browserEvent || ev;
2029             if (ev.stopPropagation) {
2030                 ev.stopPropagation();
2031             } else {
2032                 ev.cancelBubble = true;
2033             }
2034         },
2035
2036
2037         preventDefault: function(ev) {
2038             ev = ev.browserEvent || ev;
2039             if(ev.preventDefault) {
2040                 ev.preventDefault();
2041             } else {
2042                 ev.returnValue = false;
2043             }
2044         },
2045
2046
2047         getEvent: function(e) {
2048             var ev = e || window.event;
2049             if (!ev) {
2050                 var c = this.getEvent.caller;
2051                 while (c) {
2052                     ev = c.arguments[0];
2053                     if (ev && Event == ev.constructor) {
2054                         break;
2055                     }
2056                     c = c.caller;
2057                 }
2058             }
2059             return ev;
2060         },
2061
2062
2063         getCharCode: function(ev) {
2064             ev = ev.browserEvent || ev;
2065             return ev.charCode || ev.keyCode || 0;
2066         },
2067
2068
2069         _getCacheIndex: function(el, eventName, fn) {
2070             for (var i = 0,len = listeners.length; i < len; ++i) {
2071                 var li = listeners[i];
2072                 if (li &&
2073                     li[this.FN] == fn &&
2074                     li[this.EL] == el &&
2075                     li[this.TYPE] == eventName) {
2076                     return i;
2077                 }
2078             }
2079
2080             return -1;
2081         },
2082
2083
2084         elCache: {},
2085
2086
2087         getEl: function(id) {
2088             return document.getElementById(id);
2089         },
2090
2091
2092         clearCache: function() {
2093         },
2094
2095
2096         _load: function(e) {
2097             loadComplete = true;
2098             var EU = Roo.lib.Event;
2099
2100
2101             if (Roo.isIE) {
2102                 EU.doRemove(window, "load", EU._load);
2103             }
2104         },
2105
2106
2107         _tryPreloadAttach: function() {
2108
2109             if (this.locked) {
2110                 return false;
2111             }
2112
2113             this.locked = true;
2114
2115
2116             var tryAgain = !loadComplete;
2117             if (!tryAgain) {
2118                 tryAgain = (retryCount > 0);
2119             }
2120
2121
2122             var notAvail = [];
2123             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2124                 var item = onAvailStack[i];
2125                 if (item) {
2126                     var el = this.getEl(item.id);
2127
2128                     if (el) {
2129                         if (!item.checkReady ||
2130                             loadComplete ||
2131                             el.nextSibling ||
2132                             (document && document.body)) {
2133
2134                             var scope = el;
2135                             if (item.override) {
2136                                 if (item.override === true) {
2137                                     scope = item.obj;
2138                                 } else {
2139                                     scope = item.override;
2140                                 }
2141                             }
2142                             item.fn.call(scope, item.obj);
2143                             onAvailStack[i] = null;
2144                         }
2145                     } else {
2146                         notAvail.push(item);
2147                     }
2148                 }
2149             }
2150
2151             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2152
2153             if (tryAgain) {
2154
2155                 this.startInterval();
2156             } else {
2157                 clearInterval(this._interval);
2158                 this._interval = null;
2159             }
2160
2161             this.locked = false;
2162
2163             return true;
2164
2165         },
2166
2167
2168         purgeElement: function(el, recurse, eventName) {
2169             var elListeners = this.getListeners(el, eventName);
2170             if (elListeners) {
2171                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2172                     var l = elListeners[i];
2173                     this.removeListener(el, l.type, l.fn);
2174                 }
2175             }
2176
2177             if (recurse && el && el.childNodes) {
2178                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2179                     this.purgeElement(el.childNodes[i], recurse, eventName);
2180                 }
2181             }
2182         },
2183
2184
2185         getListeners: function(el, eventName) {
2186             var results = [], searchLists;
2187             if (!eventName) {
2188                 searchLists = [listeners, unloadListeners];
2189             } else if (eventName == "unload") {
2190                 searchLists = [unloadListeners];
2191             } else {
2192                 searchLists = [listeners];
2193             }
2194
2195             for (var j = 0; j < searchLists.length; ++j) {
2196                 var searchList = searchLists[j];
2197                 if (searchList && searchList.length > 0) {
2198                     for (var i = 0,len = searchList.length; i < len; ++i) {
2199                         var l = searchList[i];
2200                         if (l && l[this.EL] === el &&
2201                             (!eventName || eventName === l[this.TYPE])) {
2202                             results.push({
2203                                 type:   l[this.TYPE],
2204                                 fn:     l[this.FN],
2205                                 obj:    l[this.OBJ],
2206                                 adjust: l[this.ADJ_SCOPE],
2207                                 index:  i
2208                             });
2209                         }
2210                     }
2211                 }
2212             }
2213
2214             return (results.length) ? results : null;
2215         },
2216
2217
2218         _unload: function(e) {
2219
2220             var EU = Roo.lib.Event, i, j, l, len, index;
2221
2222             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2223                 l = unloadListeners[i];
2224                 if (l) {
2225                     var scope = window;
2226                     if (l[EU.ADJ_SCOPE]) {
2227                         if (l[EU.ADJ_SCOPE] === true) {
2228                             scope = l[EU.OBJ];
2229                         } else {
2230                             scope = l[EU.ADJ_SCOPE];
2231                         }
2232                     }
2233                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2234                     unloadListeners[i] = null;
2235                     l = null;
2236                     scope = null;
2237                 }
2238             }
2239
2240             unloadListeners = null;
2241
2242             if (listeners && listeners.length > 0) {
2243                 j = listeners.length;
2244                 while (j) {
2245                     index = j - 1;
2246                     l = listeners[index];
2247                     if (l) {
2248                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2249                                 l[EU.FN], index);
2250                     }
2251                     j = j - 1;
2252                 }
2253                 l = null;
2254
2255                 EU.clearCache();
2256             }
2257
2258             EU.doRemove(window, "unload", EU._unload);
2259
2260         },
2261
2262
2263         getScroll: function() {
2264             var dd = document.documentElement, db = document.body;
2265             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2266                 return [dd.scrollTop, dd.scrollLeft];
2267             } else if (db) {
2268                 return [db.scrollTop, db.scrollLeft];
2269             } else {
2270                 return [0, 0];
2271             }
2272         },
2273
2274
2275         doAdd: function () {
2276             if (window.addEventListener) {
2277                 return function(el, eventName, fn, capture) {
2278                     el.addEventListener(eventName, fn, (capture));
2279                 };
2280             } else if (window.attachEvent) {
2281                 return function(el, eventName, fn, capture) {
2282                     el.attachEvent("on" + eventName, fn);
2283                 };
2284             } else {
2285                 return function() {
2286                 };
2287             }
2288         }(),
2289
2290
2291         doRemove: function() {
2292             if (window.removeEventListener) {
2293                 return function (el, eventName, fn, capture) {
2294                     el.removeEventListener(eventName, fn, (capture));
2295                 };
2296             } else if (window.detachEvent) {
2297                 return function (el, eventName, fn) {
2298                     el.detachEvent("on" + eventName, fn);
2299                 };
2300             } else {
2301                 return function() {
2302                 };
2303             }
2304         }()
2305     };
2306     
2307 }();
2308 (function() {     
2309    
2310     var E = Roo.lib.Event;
2311     E.on = E.addListener;
2312     E.un = E.removeListener;
2313
2314     if (document && document.body) {
2315         E._load();
2316     } else {
2317         E.doAdd(window, "load", E._load);
2318     }
2319     E.doAdd(window, "unload", E._unload);
2320     E._tryPreloadAttach();
2321 })();
2322
2323 /*
2324  * Portions of this file are based on pieces of Yahoo User Interface Library
2325  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2326  * YUI licensed under the BSD License:
2327  * http://developer.yahoo.net/yui/license.txt
2328  * <script type="text/javascript">
2329  *
2330  */
2331
2332 (function() {
2333     
2334     Roo.lib.Ajax = {
2335         request : function(method, uri, cb, data, options) {
2336             if(options){
2337                 var hs = options.headers;
2338                 if(hs){
2339                     for(var h in hs){
2340                         if(hs.hasOwnProperty(h)){
2341                             this.initHeader(h, hs[h], false);
2342                         }
2343                     }
2344                 }
2345                 if(options.xmlData){
2346                     this.initHeader('Content-Type', 'text/xml', false);
2347                     method = 'POST';
2348                     data = options.xmlData;
2349                 }
2350             }
2351
2352             return this.asyncRequest(method, uri, cb, data);
2353         },
2354
2355         serializeForm : function(form) {
2356             if(typeof form == 'string') {
2357                 form = (document.getElementById(form) || document.forms[form]);
2358             }
2359
2360             var el, name, val, disabled, data = '', hasSubmit = false;
2361             for (var i = 0; i < form.elements.length; i++) {
2362                 el = form.elements[i];
2363                 disabled = form.elements[i].disabled;
2364                 name = form.elements[i].name;
2365                 val = form.elements[i].value;
2366
2367                 if (!disabled && name){
2368                     switch (el.type)
2369                             {
2370                         case 'select-one':
2371                         case 'select-multiple':
2372                             for (var j = 0; j < el.options.length; j++) {
2373                                 if (el.options[j].selected) {
2374                                     if (Roo.isIE) {
2375                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2376                                     }
2377                                     else {
2378                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2379                                     }
2380                                 }
2381                             }
2382                             break;
2383                         case 'radio':
2384                         case 'checkbox':
2385                             if (el.checked) {
2386                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2387                             }
2388                             break;
2389                         case 'file':
2390
2391                         case undefined:
2392
2393                         case 'reset':
2394
2395                         case 'button':
2396
2397                             break;
2398                         case 'submit':
2399                             if(hasSubmit == false) {
2400                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2401                                 hasSubmit = true;
2402                             }
2403                             break;
2404                         default:
2405                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2406                             break;
2407                     }
2408                 }
2409             }
2410             data = data.substr(0, data.length - 1);
2411             return data;
2412         },
2413
2414         headers:{},
2415
2416         hasHeaders:false,
2417
2418         useDefaultHeader:true,
2419
2420         defaultPostHeader:'application/x-www-form-urlencoded',
2421
2422         useDefaultXhrHeader:true,
2423
2424         defaultXhrHeader:'XMLHttpRequest',
2425
2426         hasDefaultHeaders:true,
2427
2428         defaultHeaders:{},
2429
2430         poll:{},
2431
2432         timeout:{},
2433
2434         pollInterval:50,
2435
2436         transactionId:0,
2437
2438         setProgId:function(id)
2439         {
2440             this.activeX.unshift(id);
2441         },
2442
2443         setDefaultPostHeader:function(b)
2444         {
2445             this.useDefaultHeader = b;
2446         },
2447
2448         setDefaultXhrHeader:function(b)
2449         {
2450             this.useDefaultXhrHeader = b;
2451         },
2452
2453         setPollingInterval:function(i)
2454         {
2455             if (typeof i == 'number' && isFinite(i)) {
2456                 this.pollInterval = i;
2457             }
2458         },
2459
2460         createXhrObject:function(transactionId)
2461         {
2462             var obj,http;
2463             try
2464             {
2465
2466                 http = new XMLHttpRequest();
2467
2468                 obj = { conn:http, tId:transactionId };
2469             }
2470             catch(e)
2471             {
2472                 for (var i = 0; i < this.activeX.length; ++i) {
2473                     try
2474                     {
2475
2476                         http = new ActiveXObject(this.activeX[i]);
2477
2478                         obj = { conn:http, tId:transactionId };
2479                         break;
2480                     }
2481                     catch(e) {
2482                     }
2483                 }
2484             }
2485             finally
2486             {
2487                 return obj;
2488             }
2489         },
2490
2491         getConnectionObject:function()
2492         {
2493             var o;
2494             var tId = this.transactionId;
2495
2496             try
2497             {
2498                 o = this.createXhrObject(tId);
2499                 if (o) {
2500                     this.transactionId++;
2501                 }
2502             }
2503             catch(e) {
2504             }
2505             finally
2506             {
2507                 return o;
2508             }
2509         },
2510
2511         asyncRequest:function(method, uri, callback, postData)
2512         {
2513             var o = this.getConnectionObject();
2514
2515             if (!o) {
2516                 return null;
2517             }
2518             else {
2519                 o.conn.open(method, uri, true);
2520
2521                 if (this.useDefaultXhrHeader) {
2522                     if (!this.defaultHeaders['X-Requested-With']) {
2523                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2524                     }
2525                 }
2526
2527                 if(postData && this.useDefaultHeader){
2528                     this.initHeader('Content-Type', this.defaultPostHeader);
2529                 }
2530
2531                  if (this.hasDefaultHeaders || this.hasHeaders) {
2532                     this.setHeader(o);
2533                 }
2534
2535                 this.handleReadyState(o, callback);
2536                 o.conn.send(postData || null);
2537
2538                 return o;
2539             }
2540         },
2541
2542         handleReadyState:function(o, callback)
2543         {
2544             var oConn = this;
2545
2546             if (callback && callback.timeout) {
2547                 this.timeout[o.tId] = window.setTimeout(function() {
2548                     oConn.abort(o, callback, true);
2549                 }, callback.timeout);
2550             }
2551
2552             this.poll[o.tId] = window.setInterval(
2553                     function() {
2554                         if (o.conn && o.conn.readyState == 4) {
2555                             window.clearInterval(oConn.poll[o.tId]);
2556                             delete oConn.poll[o.tId];
2557
2558                             if(callback && callback.timeout) {
2559                                 window.clearTimeout(oConn.timeout[o.tId]);
2560                                 delete oConn.timeout[o.tId];
2561                             }
2562
2563                             oConn.handleTransactionResponse(o, callback);
2564                         }
2565                     }
2566                     , this.pollInterval);
2567         },
2568
2569         handleTransactionResponse:function(o, callback, isAbort)
2570         {
2571
2572             if (!callback) {
2573                 this.releaseObject(o);
2574                 return;
2575             }
2576
2577             var httpStatus, responseObject;
2578
2579             try
2580             {
2581                 if (o.conn.status !== undefined && o.conn.status != 0) {
2582                     httpStatus = o.conn.status;
2583                 }
2584                 else {
2585                     httpStatus = 13030;
2586                 }
2587             }
2588             catch(e) {
2589
2590
2591                 httpStatus = 13030;
2592             }
2593
2594             if (httpStatus >= 200 && httpStatus < 300) {
2595                 responseObject = this.createResponseObject(o, callback.argument);
2596                 if (callback.success) {
2597                     if (!callback.scope) {
2598                         callback.success(responseObject);
2599                     }
2600                     else {
2601
2602
2603                         callback.success.apply(callback.scope, [responseObject]);
2604                     }
2605                 }
2606             }
2607             else {
2608                 switch (httpStatus) {
2609
2610                     case 12002:
2611                     case 12029:
2612                     case 12030:
2613                     case 12031:
2614                     case 12152:
2615                     case 13030:
2616                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2617                         if (callback.failure) {
2618                             if (!callback.scope) {
2619                                 callback.failure(responseObject);
2620                             }
2621                             else {
2622                                 callback.failure.apply(callback.scope, [responseObject]);
2623                             }
2624                         }
2625                         break;
2626                     default:
2627                         responseObject = this.createResponseObject(o, callback.argument);
2628                         if (callback.failure) {
2629                             if (!callback.scope) {
2630                                 callback.failure(responseObject);
2631                             }
2632                             else {
2633                                 callback.failure.apply(callback.scope, [responseObject]);
2634                             }
2635                         }
2636                 }
2637             }
2638
2639             this.releaseObject(o);
2640             responseObject = null;
2641         },
2642
2643         createResponseObject:function(o, callbackArg)
2644         {
2645             var obj = {};
2646             var headerObj = {};
2647
2648             try
2649             {
2650                 var headerStr = o.conn.getAllResponseHeaders();
2651                 var header = headerStr.split('\n');
2652                 for (var i = 0; i < header.length; i++) {
2653                     var delimitPos = header[i].indexOf(':');
2654                     if (delimitPos != -1) {
2655                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2656                     }
2657                 }
2658             }
2659             catch(e) {
2660             }
2661
2662             obj.tId = o.tId;
2663             obj.status = o.conn.status;
2664             obj.statusText = o.conn.statusText;
2665             obj.getResponseHeader = headerObj;
2666             obj.getAllResponseHeaders = headerStr;
2667             obj.responseText = o.conn.responseText;
2668             obj.responseXML = o.conn.responseXML;
2669
2670             if (typeof callbackArg !== undefined) {
2671                 obj.argument = callbackArg;
2672             }
2673
2674             return obj;
2675         },
2676
2677         createExceptionObject:function(tId, callbackArg, isAbort)
2678         {
2679             var COMM_CODE = 0;
2680             var COMM_ERROR = 'communication failure';
2681             var ABORT_CODE = -1;
2682             var ABORT_ERROR = 'transaction aborted';
2683
2684             var obj = {};
2685
2686             obj.tId = tId;
2687             if (isAbort) {
2688                 obj.status = ABORT_CODE;
2689                 obj.statusText = ABORT_ERROR;
2690             }
2691             else {
2692                 obj.status = COMM_CODE;
2693                 obj.statusText = COMM_ERROR;
2694             }
2695
2696             if (callbackArg) {
2697                 obj.argument = callbackArg;
2698             }
2699
2700             return obj;
2701         },
2702
2703         initHeader:function(label, value, isDefault)
2704         {
2705             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2706
2707             if (headerObj[label] === undefined) {
2708                 headerObj[label] = value;
2709             }
2710             else {
2711
2712
2713                 headerObj[label] = value + "," + headerObj[label];
2714             }
2715
2716             if (isDefault) {
2717                 this.hasDefaultHeaders = true;
2718             }
2719             else {
2720                 this.hasHeaders = true;
2721             }
2722         },
2723
2724
2725         setHeader:function(o)
2726         {
2727             if (this.hasDefaultHeaders) {
2728                 for (var prop in this.defaultHeaders) {
2729                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2730                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2731                     }
2732                 }
2733             }
2734
2735             if (this.hasHeaders) {
2736                 for (var prop in this.headers) {
2737                     if (this.headers.hasOwnProperty(prop)) {
2738                         o.conn.setRequestHeader(prop, this.headers[prop]);
2739                     }
2740                 }
2741                 this.headers = {};
2742                 this.hasHeaders = false;
2743             }
2744         },
2745
2746         resetDefaultHeaders:function() {
2747             delete this.defaultHeaders;
2748             this.defaultHeaders = {};
2749             this.hasDefaultHeaders = false;
2750         },
2751
2752         abort:function(o, callback, isTimeout)
2753         {
2754             if(this.isCallInProgress(o)) {
2755                 o.conn.abort();
2756                 window.clearInterval(this.poll[o.tId]);
2757                 delete this.poll[o.tId];
2758                 if (isTimeout) {
2759                     delete this.timeout[o.tId];
2760                 }
2761
2762                 this.handleTransactionResponse(o, callback, true);
2763
2764                 return true;
2765             }
2766             else {
2767                 return false;
2768             }
2769         },
2770
2771
2772         isCallInProgress:function(o)
2773         {
2774             if (o && o.conn) {
2775                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2776             }
2777             else {
2778
2779                 return false;
2780             }
2781         },
2782
2783
2784         releaseObject:function(o)
2785         {
2786
2787             o.conn = null;
2788
2789             o = null;
2790         },
2791
2792         activeX:[
2793         'MSXML2.XMLHTTP.3.0',
2794         'MSXML2.XMLHTTP',
2795         'Microsoft.XMLHTTP'
2796         ]
2797
2798
2799     };
2800 })();/*
2801  * Portions of this file are based on pieces of Yahoo User Interface Library
2802  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2803  * YUI licensed under the BSD License:
2804  * http://developer.yahoo.net/yui/license.txt
2805  * <script type="text/javascript">
2806  *
2807  */
2808
2809 Roo.lib.Region = function(t, r, b, l) {
2810     this.top = t;
2811     this[1] = t;
2812     this.right = r;
2813     this.bottom = b;
2814     this.left = l;
2815     this[0] = l;
2816 };
2817
2818
2819 Roo.lib.Region.prototype = {
2820     contains : function(region) {
2821         return ( region.left >= this.left &&
2822                  region.right <= this.right &&
2823                  region.top >= this.top &&
2824                  region.bottom <= this.bottom    );
2825
2826     },
2827
2828     getArea : function() {
2829         return ( (this.bottom - this.top) * (this.right - this.left) );
2830     },
2831
2832     intersect : function(region) {
2833         var t = Math.max(this.top, region.top);
2834         var r = Math.min(this.right, region.right);
2835         var b = Math.min(this.bottom, region.bottom);
2836         var l = Math.max(this.left, region.left);
2837
2838         if (b >= t && r >= l) {
2839             return new Roo.lib.Region(t, r, b, l);
2840         } else {
2841             return null;
2842         }
2843     },
2844     union : function(region) {
2845         var t = Math.min(this.top, region.top);
2846         var r = Math.max(this.right, region.right);
2847         var b = Math.max(this.bottom, region.bottom);
2848         var l = Math.min(this.left, region.left);
2849
2850         return new Roo.lib.Region(t, r, b, l);
2851     },
2852
2853     adjust : function(t, l, b, r) {
2854         this.top += t;
2855         this.left += l;
2856         this.right += r;
2857         this.bottom += b;
2858         return this;
2859     }
2860 };
2861
2862 Roo.lib.Region.getRegion = function(el) {
2863     var p = Roo.lib.Dom.getXY(el);
2864
2865     var t = p[1];
2866     var r = p[0] + el.offsetWidth;
2867     var b = p[1] + el.offsetHeight;
2868     var l = p[0];
2869
2870     return new Roo.lib.Region(t, r, b, l);
2871 };
2872 /*
2873  * Portions of this file are based on pieces of Yahoo User Interface Library
2874  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2875  * YUI licensed under the BSD License:
2876  * http://developer.yahoo.net/yui/license.txt
2877  * <script type="text/javascript">
2878  *
2879  */
2880 //@@dep Roo.lib.Region
2881
2882
2883 Roo.lib.Point = function(x, y) {
2884     if (x instanceof Array) {
2885         y = x[1];
2886         x = x[0];
2887     }
2888     this.x = this.right = this.left = this[0] = x;
2889     this.y = this.top = this.bottom = this[1] = y;
2890 };
2891
2892 Roo.lib.Point.prototype = new Roo.lib.Region();
2893 /*
2894  * Portions of this file are based on pieces of Yahoo User Interface Library
2895  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2896  * YUI licensed under the BSD License:
2897  * http://developer.yahoo.net/yui/license.txt
2898  * <script type="text/javascript">
2899  *
2900  */
2901  
2902 (function() {   
2903
2904     Roo.lib.Anim = {
2905         scroll : function(el, args, duration, easing, cb, scope) {
2906             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2907         },
2908
2909         motion : function(el, args, duration, easing, cb, scope) {
2910             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2911         },
2912
2913         color : function(el, args, duration, easing, cb, scope) {
2914             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2915         },
2916
2917         run : function(el, args, duration, easing, cb, scope, type) {
2918             type = type || Roo.lib.AnimBase;
2919             if (typeof easing == "string") {
2920                 easing = Roo.lib.Easing[easing];
2921             }
2922             var anim = new type(el, args, duration, easing);
2923             anim.animateX(function() {
2924                 Roo.callback(cb, scope);
2925             });
2926             return anim;
2927         }
2928     };
2929 })();/*
2930  * Portions of this file are based on pieces of Yahoo User Interface Library
2931  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2932  * YUI licensed under the BSD License:
2933  * http://developer.yahoo.net/yui/license.txt
2934  * <script type="text/javascript">
2935  *
2936  */
2937
2938 (function() {    
2939     var libFlyweight;
2940     
2941     function fly(el) {
2942         if (!libFlyweight) {
2943             libFlyweight = new Roo.Element.Flyweight();
2944         }
2945         libFlyweight.dom = el;
2946         return libFlyweight;
2947     }
2948
2949     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2950     
2951    
2952     
2953     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2954         if (el) {
2955             this.init(el, attributes, duration, method);
2956         }
2957     };
2958
2959     Roo.lib.AnimBase.fly = fly;
2960     
2961     
2962     
2963     Roo.lib.AnimBase.prototype = {
2964
2965         toString: function() {
2966             var el = this.getEl();
2967             var id = el.id || el.tagName;
2968             return ("Anim " + id);
2969         },
2970
2971         patterns: {
2972             noNegatives:        /width|height|opacity|padding/i,
2973             offsetAttribute:  /^((width|height)|(top|left))$/,
2974             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2975             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2976         },
2977
2978
2979         doMethod: function(attr, start, end) {
2980             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2981         },
2982
2983
2984         setAttribute: function(attr, val, unit) {
2985             if (this.patterns.noNegatives.test(attr)) {
2986                 val = (val > 0) ? val : 0;
2987             }
2988
2989             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2990         },
2991
2992
2993         getAttribute: function(attr) {
2994             var el = this.getEl();
2995             var val = fly(el).getStyle(attr);
2996
2997             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2998                 return parseFloat(val);
2999             }
3000
3001             var a = this.patterns.offsetAttribute.exec(attr) || [];
3002             var pos = !!( a[3] );
3003             var box = !!( a[2] );
3004
3005
3006             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3007                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3008             } else {
3009                 val = 0;
3010             }
3011
3012             return val;
3013         },
3014
3015
3016         getDefaultUnit: function(attr) {
3017             if (this.patterns.defaultUnit.test(attr)) {
3018                 return 'px';
3019             }
3020
3021             return '';
3022         },
3023
3024         animateX : function(callback, scope) {
3025             var f = function() {
3026                 this.onComplete.removeListener(f);
3027                 if (typeof callback == "function") {
3028                     callback.call(scope || this, this);
3029                 }
3030             };
3031             this.onComplete.addListener(f, this);
3032             this.animate();
3033         },
3034
3035
3036         setRuntimeAttribute: function(attr) {
3037             var start;
3038             var end;
3039             var attributes = this.attributes;
3040
3041             this.runtimeAttributes[attr] = {};
3042
3043             var isset = function(prop) {
3044                 return (typeof prop !== 'undefined');
3045             };
3046
3047             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3048                 return false;
3049             }
3050
3051             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3052
3053
3054             if (isset(attributes[attr]['to'])) {
3055                 end = attributes[attr]['to'];
3056             } else if (isset(attributes[attr]['by'])) {
3057                 if (start.constructor == Array) {
3058                     end = [];
3059                     for (var i = 0, len = start.length; i < len; ++i) {
3060                         end[i] = start[i] + attributes[attr]['by'][i];
3061                     }
3062                 } else {
3063                     end = start + attributes[attr]['by'];
3064                 }
3065             }
3066
3067             this.runtimeAttributes[attr].start = start;
3068             this.runtimeAttributes[attr].end = end;
3069
3070
3071             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3072         },
3073
3074
3075         init: function(el, attributes, duration, method) {
3076
3077             var isAnimated = false;
3078
3079
3080             var startTime = null;
3081
3082
3083             var actualFrames = 0;
3084
3085
3086             el = Roo.getDom(el);
3087
3088
3089             this.attributes = attributes || {};
3090
3091
3092             this.duration = duration || 1;
3093
3094
3095             this.method = method || Roo.lib.Easing.easeNone;
3096
3097
3098             this.useSeconds = true;
3099
3100
3101             this.currentFrame = 0;
3102
3103
3104             this.totalFrames = Roo.lib.AnimMgr.fps;
3105
3106
3107             this.getEl = function() {
3108                 return el;
3109             };
3110
3111
3112             this.isAnimated = function() {
3113                 return isAnimated;
3114             };
3115
3116
3117             this.getStartTime = function() {
3118                 return startTime;
3119             };
3120
3121             this.runtimeAttributes = {};
3122
3123
3124             this.animate = function() {
3125                 if (this.isAnimated()) {
3126                     return false;
3127                 }
3128
3129                 this.currentFrame = 0;
3130
3131                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3132
3133                 Roo.lib.AnimMgr.registerElement(this);
3134             };
3135
3136
3137             this.stop = function(finish) {
3138                 if (finish) {
3139                     this.currentFrame = this.totalFrames;
3140                     this._onTween.fire();
3141                 }
3142                 Roo.lib.AnimMgr.stop(this);
3143             };
3144
3145             var onStart = function() {
3146                 this.onStart.fire();
3147
3148                 this.runtimeAttributes = {};
3149                 for (var attr in this.attributes) {
3150                     this.setRuntimeAttribute(attr);
3151                 }
3152
3153                 isAnimated = true;
3154                 actualFrames = 0;
3155                 startTime = new Date();
3156             };
3157
3158
3159             var onTween = function() {
3160                 var data = {
3161                     duration: new Date() - this.getStartTime(),
3162                     currentFrame: this.currentFrame
3163                 };
3164
3165                 data.toString = function() {
3166                     return (
3167                             'duration: ' + data.duration +
3168                             ', currentFrame: ' + data.currentFrame
3169                             );
3170                 };
3171
3172                 this.onTween.fire(data);
3173
3174                 var runtimeAttributes = this.runtimeAttributes;
3175
3176                 for (var attr in runtimeAttributes) {
3177                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3178                 }
3179
3180                 actualFrames += 1;
3181             };
3182
3183             var onComplete = function() {
3184                 var actual_duration = (new Date() - startTime) / 1000 ;
3185
3186                 var data = {
3187                     duration: actual_duration,
3188                     frames: actualFrames,
3189                     fps: actualFrames / actual_duration
3190                 };
3191
3192                 data.toString = function() {
3193                     return (
3194                             'duration: ' + data.duration +
3195                             ', frames: ' + data.frames +
3196                             ', fps: ' + data.fps
3197                             );
3198                 };
3199
3200                 isAnimated = false;
3201                 actualFrames = 0;
3202                 this.onComplete.fire(data);
3203             };
3204
3205
3206             this._onStart = new Roo.util.Event(this);
3207             this.onStart = new Roo.util.Event(this);
3208             this.onTween = new Roo.util.Event(this);
3209             this._onTween = new Roo.util.Event(this);
3210             this.onComplete = new Roo.util.Event(this);
3211             this._onComplete = new Roo.util.Event(this);
3212             this._onStart.addListener(onStart);
3213             this._onTween.addListener(onTween);
3214             this._onComplete.addListener(onComplete);
3215         }
3216     };
3217 })();
3218 /*
3219  * Portions of this file are based on pieces of Yahoo User Interface Library
3220  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3221  * YUI licensed under the BSD License:
3222  * http://developer.yahoo.net/yui/license.txt
3223  * <script type="text/javascript">
3224  *
3225  */
3226
3227 Roo.lib.AnimMgr = new function() {
3228
3229         var thread = null;
3230
3231
3232         var queue = [];
3233
3234
3235         var tweenCount = 0;
3236
3237
3238         this.fps = 1000;
3239
3240
3241         this.delay = 1;
3242
3243
3244         this.registerElement = function(tween) {
3245             queue[queue.length] = tween;
3246             tweenCount += 1;
3247             tween._onStart.fire();
3248             this.start();
3249         };
3250
3251
3252         this.unRegister = function(tween, index) {
3253             tween._onComplete.fire();
3254             index = index || getIndex(tween);
3255             if (index != -1) {
3256                 queue.splice(index, 1);
3257             }
3258
3259             tweenCount -= 1;
3260             if (tweenCount <= 0) {
3261                 this.stop();
3262             }
3263         };
3264
3265
3266         this.start = function() {
3267             if (thread === null) {
3268                 thread = setInterval(this.run, this.delay);
3269             }
3270         };
3271
3272
3273         this.stop = function(tween) {
3274             if (!tween) {
3275                 clearInterval(thread);
3276
3277                 for (var i = 0, len = queue.length; i < len; ++i) {
3278                     if (queue[0].isAnimated()) {
3279                         this.unRegister(queue[0], 0);
3280                     }
3281                 }
3282
3283                 queue = [];
3284                 thread = null;
3285                 tweenCount = 0;
3286             }
3287             else {
3288                 this.unRegister(tween);
3289             }
3290         };
3291
3292
3293         this.run = function() {
3294             for (var i = 0, len = queue.length; i < len; ++i) {
3295                 var tween = queue[i];
3296                 if (!tween || !tween.isAnimated()) {
3297                     continue;
3298                 }
3299
3300                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3301                 {
3302                     tween.currentFrame += 1;
3303
3304                     if (tween.useSeconds) {
3305                         correctFrame(tween);
3306                     }
3307                     tween._onTween.fire();
3308                 }
3309                 else {
3310                     Roo.lib.AnimMgr.stop(tween, i);
3311                 }
3312             }
3313         };
3314
3315         var getIndex = function(anim) {
3316             for (var i = 0, len = queue.length; i < len; ++i) {
3317                 if (queue[i] == anim) {
3318                     return i;
3319                 }
3320             }
3321             return -1;
3322         };
3323
3324
3325         var correctFrame = function(tween) {
3326             var frames = tween.totalFrames;
3327             var frame = tween.currentFrame;
3328             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3329             var elapsed = (new Date() - tween.getStartTime());
3330             var tweak = 0;
3331
3332             if (elapsed < tween.duration * 1000) {
3333                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3334             } else {
3335                 tweak = frames - (frame + 1);
3336             }
3337             if (tweak > 0 && isFinite(tweak)) {
3338                 if (tween.currentFrame + tweak >= frames) {
3339                     tweak = frames - (frame + 1);
3340                 }
3341
3342                 tween.currentFrame += tweak;
3343             }
3344         };
3345     };/*
3346  * Portions of this file are based on pieces of Yahoo User Interface Library
3347  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3348  * YUI licensed under the BSD License:
3349  * http://developer.yahoo.net/yui/license.txt
3350  * <script type="text/javascript">
3351  *
3352  */
3353 Roo.lib.Bezier = new function() {
3354
3355         this.getPosition = function(points, t) {
3356             var n = points.length;
3357             var tmp = [];
3358
3359             for (var i = 0; i < n; ++i) {
3360                 tmp[i] = [points[i][0], points[i][1]];
3361             }
3362
3363             for (var j = 1; j < n; ++j) {
3364                 for (i = 0; i < n - j; ++i) {
3365                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3366                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3367                 }
3368             }
3369
3370             return [ tmp[0][0], tmp[0][1] ];
3371
3372         };
3373     };/*
3374  * Portions of this file are based on pieces of Yahoo User Interface Library
3375  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3376  * YUI licensed under the BSD License:
3377  * http://developer.yahoo.net/yui/license.txt
3378  * <script type="text/javascript">
3379  *
3380  */
3381 (function() {
3382
3383     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3384         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3385     };
3386
3387     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3388
3389     var fly = Roo.lib.AnimBase.fly;
3390     var Y = Roo.lib;
3391     var superclass = Y.ColorAnim.superclass;
3392     var proto = Y.ColorAnim.prototype;
3393
3394     proto.toString = function() {
3395         var el = this.getEl();
3396         var id = el.id || el.tagName;
3397         return ("ColorAnim " + id);
3398     };
3399
3400     proto.patterns.color = /color$/i;
3401     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3402     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3403     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3404     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3405
3406
3407     proto.parseColor = function(s) {
3408         if (s.length == 3) {
3409             return s;
3410         }
3411
3412         var c = this.patterns.hex.exec(s);
3413         if (c && c.length == 4) {
3414             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3415         }
3416
3417         c = this.patterns.rgb.exec(s);
3418         if (c && c.length == 4) {
3419             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3420         }
3421
3422         c = this.patterns.hex3.exec(s);
3423         if (c && c.length == 4) {
3424             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3425         }
3426
3427         return null;
3428     };
3429     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3430     proto.getAttribute = function(attr) {
3431         var el = this.getEl();
3432         if (this.patterns.color.test(attr)) {
3433             var val = fly(el).getStyle(attr);
3434
3435             if (this.patterns.transparent.test(val)) {
3436                 var parent = el.parentNode;
3437                 val = fly(parent).getStyle(attr);
3438
3439                 while (parent && this.patterns.transparent.test(val)) {
3440                     parent = parent.parentNode;
3441                     val = fly(parent).getStyle(attr);
3442                     if (parent.tagName.toUpperCase() == 'HTML') {
3443                         val = '#fff';
3444                     }
3445                 }
3446             }
3447         } else {
3448             val = superclass.getAttribute.call(this, attr);
3449         }
3450
3451         return val;
3452     };
3453     proto.getAttribute = function(attr) {
3454         var el = this.getEl();
3455         if (this.patterns.color.test(attr)) {
3456             var val = fly(el).getStyle(attr);
3457
3458             if (this.patterns.transparent.test(val)) {
3459                 var parent = el.parentNode;
3460                 val = fly(parent).getStyle(attr);
3461
3462                 while (parent && this.patterns.transparent.test(val)) {
3463                     parent = parent.parentNode;
3464                     val = fly(parent).getStyle(attr);
3465                     if (parent.tagName.toUpperCase() == 'HTML') {
3466                         val = '#fff';
3467                     }
3468                 }
3469             }
3470         } else {
3471             val = superclass.getAttribute.call(this, attr);
3472         }
3473
3474         return val;
3475     };
3476
3477     proto.doMethod = function(attr, start, end) {
3478         var val;
3479
3480         if (this.patterns.color.test(attr)) {
3481             val = [];
3482             for (var i = 0, len = start.length; i < len; ++i) {
3483                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3484             }
3485
3486             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3487         }
3488         else {
3489             val = superclass.doMethod.call(this, attr, start, end);
3490         }
3491
3492         return val;
3493     };
3494
3495     proto.setRuntimeAttribute = function(attr) {
3496         superclass.setRuntimeAttribute.call(this, attr);
3497
3498         if (this.patterns.color.test(attr)) {
3499             var attributes = this.attributes;
3500             var start = this.parseColor(this.runtimeAttributes[attr].start);
3501             var end = this.parseColor(this.runtimeAttributes[attr].end);
3502
3503             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3504                 end = this.parseColor(attributes[attr].by);
3505
3506                 for (var i = 0, len = start.length; i < len; ++i) {
3507                     end[i] = start[i] + end[i];
3508                 }
3509             }
3510
3511             this.runtimeAttributes[attr].start = start;
3512             this.runtimeAttributes[attr].end = end;
3513         }
3514     };
3515 })();
3516
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525 Roo.lib.Easing = {
3526
3527
3528     easeNone: function (t, b, c, d) {
3529         return c * t / d + b;
3530     },
3531
3532
3533     easeIn: function (t, b, c, d) {
3534         return c * (t /= d) * t + b;
3535     },
3536
3537
3538     easeOut: function (t, b, c, d) {
3539         return -c * (t /= d) * (t - 2) + b;
3540     },
3541
3542
3543     easeBoth: function (t, b, c, d) {
3544         if ((t /= d / 2) < 1) {
3545             return c / 2 * t * t + b;
3546         }
3547
3548         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3549     },
3550
3551
3552     easeInStrong: function (t, b, c, d) {
3553         return c * (t /= d) * t * t * t + b;
3554     },
3555
3556
3557     easeOutStrong: function (t, b, c, d) {
3558         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3559     },
3560
3561
3562     easeBothStrong: function (t, b, c, d) {
3563         if ((t /= d / 2) < 1) {
3564             return c / 2 * t * t * t * t + b;
3565         }
3566
3567         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3568     },
3569
3570
3571
3572     elasticIn: function (t, b, c, d, a, p) {
3573         if (t == 0) {
3574             return b;
3575         }
3576         if ((t /= d) == 1) {
3577             return b + c;
3578         }
3579         if (!p) {
3580             p = d * .3;
3581         }
3582
3583         if (!a || a < Math.abs(c)) {
3584             a = c;
3585             var s = p / 4;
3586         }
3587         else {
3588             var s = p / (2 * Math.PI) * Math.asin(c / a);
3589         }
3590
3591         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3592     },
3593
3594
3595     elasticOut: function (t, b, c, d, a, p) {
3596         if (t == 0) {
3597             return b;
3598         }
3599         if ((t /= d) == 1) {
3600             return b + c;
3601         }
3602         if (!p) {
3603             p = d * .3;
3604         }
3605
3606         if (!a || a < Math.abs(c)) {
3607             a = c;
3608             var s = p / 4;
3609         }
3610         else {
3611             var s = p / (2 * Math.PI) * Math.asin(c / a);
3612         }
3613
3614         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3615     },
3616
3617
3618     elasticBoth: function (t, b, c, d, a, p) {
3619         if (t == 0) {
3620             return b;
3621         }
3622
3623         if ((t /= d / 2) == 2) {
3624             return b + c;
3625         }
3626
3627         if (!p) {
3628             p = d * (.3 * 1.5);
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         if (t < 1) {
3640             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3641                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3642         }
3643         return a * Math.pow(2, -10 * (t -= 1)) *
3644                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3645     },
3646
3647
3648
3649     backIn: function (t, b, c, d, s) {
3650         if (typeof s == 'undefined') {
3651             s = 1.70158;
3652         }
3653         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3654     },
3655
3656
3657     backOut: function (t, b, c, d, s) {
3658         if (typeof s == 'undefined') {
3659             s = 1.70158;
3660         }
3661         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3662     },
3663
3664
3665     backBoth: function (t, b, c, d, s) {
3666         if (typeof s == 'undefined') {
3667             s = 1.70158;
3668         }
3669
3670         if ((t /= d / 2 ) < 1) {
3671             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3672         }
3673         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3674     },
3675
3676
3677     bounceIn: function (t, b, c, d) {
3678         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3679     },
3680
3681
3682     bounceOut: function (t, b, c, d) {
3683         if ((t /= d) < (1 / 2.75)) {
3684             return c * (7.5625 * t * t) + b;
3685         } else if (t < (2 / 2.75)) {
3686             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3687         } else if (t < (2.5 / 2.75)) {
3688             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3689         }
3690         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3691     },
3692
3693
3694     bounceBoth: function (t, b, c, d) {
3695         if (t < d / 2) {
3696             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3697         }
3698         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3699     }
3700 };/*
3701  * Portions of this file are based on pieces of Yahoo User Interface Library
3702  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3703  * YUI licensed under the BSD License:
3704  * http://developer.yahoo.net/yui/license.txt
3705  * <script type="text/javascript">
3706  *
3707  */
3708     (function() {
3709         Roo.lib.Motion = function(el, attributes, duration, method) {
3710             if (el) {
3711                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3712             }
3713         };
3714
3715         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3716
3717
3718         var Y = Roo.lib;
3719         var superclass = Y.Motion.superclass;
3720         var proto = Y.Motion.prototype;
3721
3722         proto.toString = function() {
3723             var el = this.getEl();
3724             var id = el.id || el.tagName;
3725             return ("Motion " + id);
3726         };
3727
3728         proto.patterns.points = /^points$/i;
3729
3730         proto.setAttribute = function(attr, val, unit) {
3731             if (this.patterns.points.test(attr)) {
3732                 unit = unit || 'px';
3733                 superclass.setAttribute.call(this, 'left', val[0], unit);
3734                 superclass.setAttribute.call(this, 'top', val[1], unit);
3735             } else {
3736                 superclass.setAttribute.call(this, attr, val, unit);
3737             }
3738         };
3739
3740         proto.getAttribute = function(attr) {
3741             if (this.patterns.points.test(attr)) {
3742                 var val = [
3743                         superclass.getAttribute.call(this, 'left'),
3744                         superclass.getAttribute.call(this, 'top')
3745                         ];
3746             } else {
3747                 val = superclass.getAttribute.call(this, attr);
3748             }
3749
3750             return val;
3751         };
3752
3753         proto.doMethod = function(attr, start, end) {
3754             var val = null;
3755
3756             if (this.patterns.points.test(attr)) {
3757                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3758                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3759             } else {
3760                 val = superclass.doMethod.call(this, attr, start, end);
3761             }
3762             return val;
3763         };
3764
3765         proto.setRuntimeAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var el = this.getEl();
3768                 var attributes = this.attributes;
3769                 var start;
3770                 var control = attributes['points']['control'] || [];
3771                 var end;
3772                 var i, len;
3773
3774                 if (control.length > 0 && !(control[0] instanceof Array)) {
3775                     control = [control];
3776                 } else {
3777                     var tmp = [];
3778                     for (i = 0,len = control.length; i < len; ++i) {
3779                         tmp[i] = control[i];
3780                     }
3781                     control = tmp;
3782                 }
3783
3784                 Roo.fly(el).position();
3785
3786                 if (isset(attributes['points']['from'])) {
3787                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3788                 }
3789                 else {
3790                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3791                 }
3792
3793                 start = this.getAttribute('points');
3794
3795
3796                 if (isset(attributes['points']['to'])) {
3797                     end = translateValues.call(this, attributes['points']['to'], start);
3798
3799                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3800                     for (i = 0,len = control.length; i < len; ++i) {
3801                         control[i] = translateValues.call(this, control[i], start);
3802                     }
3803
3804
3805                 } else if (isset(attributes['points']['by'])) {
3806                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3807
3808                     for (i = 0,len = control.length; i < len; ++i) {
3809                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3810                     }
3811                 }
3812
3813                 this.runtimeAttributes[attr] = [start];
3814
3815                 if (control.length > 0) {
3816                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3817                 }
3818
3819                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3820             }
3821             else {
3822                 superclass.setRuntimeAttribute.call(this, attr);
3823             }
3824         };
3825
3826         var translateValues = function(val, start) {
3827             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3828             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3829
3830             return val;
3831         };
3832
3833         var isset = function(prop) {
3834             return (typeof prop !== 'undefined');
3835         };
3836     })();
3837 /*
3838  * Portions of this file are based on pieces of Yahoo User Interface Library
3839  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3840  * YUI licensed under the BSD License:
3841  * http://developer.yahoo.net/yui/license.txt
3842  * <script type="text/javascript">
3843  *
3844  */
3845     (function() {
3846         Roo.lib.Scroll = function(el, attributes, duration, method) {
3847             if (el) {
3848                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3849             }
3850         };
3851
3852         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3853
3854
3855         var Y = Roo.lib;
3856         var superclass = Y.Scroll.superclass;
3857         var proto = Y.Scroll.prototype;
3858
3859         proto.toString = function() {
3860             var el = this.getEl();
3861             var id = el.id || el.tagName;
3862             return ("Scroll " + id);
3863         };
3864
3865         proto.doMethod = function(attr, start, end) {
3866             var val = null;
3867
3868             if (attr == 'scroll') {
3869                 val = [
3870                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3871                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3872                         ];
3873
3874             } else {
3875                 val = superclass.doMethod.call(this, attr, start, end);
3876             }
3877             return val;
3878         };
3879
3880         proto.getAttribute = function(attr) {
3881             var val = null;
3882             var el = this.getEl();
3883
3884             if (attr == 'scroll') {
3885                 val = [ el.scrollLeft, el.scrollTop ];
3886             } else {
3887                 val = superclass.getAttribute.call(this, attr);
3888             }
3889
3890             return val;
3891         };
3892
3893         proto.setAttribute = function(attr, val, unit) {
3894             var el = this.getEl();
3895
3896             if (attr == 'scroll') {
3897                 el.scrollLeft = val[0];
3898                 el.scrollTop = val[1];
3899             } else {
3900                 superclass.setAttribute.call(this, attr, val, unit);
3901             }
3902         };
3903     })();
3904 /*
3905  * Based on:
3906  * Ext JS Library 1.1.1
3907  * Copyright(c) 2006-2007, Ext JS, LLC.
3908  *
3909  * Originally Released Under LGPL - original licence link has changed is not relivant.
3910  *
3911  * Fork - LGPL
3912  * <script type="text/javascript">
3913  */
3914  
3915
3916 /**
3917  * @class Roo.DomHelper
3918  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3919  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3920  * @singleton
3921  */
3922 Roo.DomHelper = function(){
3923     var tempTableEl = null;
3924     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3925     var tableRe = /^table|tbody|tr|td$/i;
3926     var xmlns = {};
3927     // build as innerHTML where available
3928     /** @ignore */
3929     var createHtml = function(o){
3930         if(typeof o == 'string'){
3931             return o;
3932         }
3933         var b = "";
3934         if(!o.tag){
3935             o.tag = "div";
3936         }
3937         b += "<" + o.tag;
3938         for(var attr in o){
3939             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3940             if(attr == "style"){
3941                 var s = o["style"];
3942                 if(typeof s == "function"){
3943                     s = s.call();
3944                 }
3945                 if(typeof s == "string"){
3946                     b += ' style="' + s + '"';
3947                 }else if(typeof s == "object"){
3948                     b += ' style="';
3949                     for(var key in s){
3950                         if(typeof s[key] != "function"){
3951                             b += key + ":" + s[key] + ";";
3952                         }
3953                     }
3954                     b += '"';
3955                 }
3956             }else{
3957                 if(attr == "cls"){
3958                     b += ' class="' + o["cls"] + '"';
3959                 }else if(attr == "htmlFor"){
3960                     b += ' for="' + o["htmlFor"] + '"';
3961                 }else{
3962                     b += " " + attr + '="' + o[attr] + '"';
3963                 }
3964             }
3965         }
3966         if(emptyTags.test(o.tag)){
3967             b += "/>";
3968         }else{
3969             b += ">";
3970             var cn = o.children || o.cn;
3971             if(cn){
3972                 //http://bugs.kde.org/show_bug.cgi?id=71506
3973                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3974                     for(var i = 0, len = cn.length; i < len; i++) {
3975                         b += createHtml(cn[i], b);
3976                     }
3977                 }else{
3978                     b += createHtml(cn, b);
3979                 }
3980             }
3981             if(o.html){
3982                 b += o.html;
3983             }
3984             b += "</" + o.tag + ">";
3985         }
3986         return b;
3987     };
3988
3989     // build as dom
3990     /** @ignore */
3991     var createDom = function(o, parentNode){
3992          
3993         // defininition craeted..
3994         var ns = false;
3995         if (o.ns && o.ns != 'html') {
3996                
3997             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3998                 xmlns[o.ns] = o.xmlns;
3999                 ns = o.xmlns;
4000             }
4001             if (typeof(xmlns[o.ns]) == 'undefined') {
4002                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4003             }
4004             ns = xmlns[o.ns];
4005         }
4006         
4007         
4008         if (typeof(o) == 'string') {
4009             return parentNode.appendChild(document.createTextNode(o));
4010         }
4011         o.tag = o.tag || div;
4012         if (o.ns && Roo.isIE) {
4013             ns = false;
4014             o.tag = o.ns + ':' + o.tag;
4015             
4016         }
4017         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4018         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4019         for(var attr in o){
4020             
4021             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4022                     attr == "style" || typeof o[attr] == "function") continue;
4023                     
4024             if(attr=="cls" && Roo.isIE){
4025                 el.className = o["cls"];
4026             }else{
4027                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4028                 else el[attr] = o[attr];
4029             }
4030         }
4031         Roo.DomHelper.applyStyles(el, o.style);
4032         var cn = o.children || o.cn;
4033         if(cn){
4034             //http://bugs.kde.org/show_bug.cgi?id=71506
4035              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4036                 for(var i = 0, len = cn.length; i < len; i++) {
4037                     createDom(cn[i], el);
4038                 }
4039             }else{
4040                 createDom(cn, el);
4041             }
4042         }
4043         if(o.html){
4044             el.innerHTML = o.html;
4045         }
4046         if(parentNode){
4047            parentNode.appendChild(el);
4048         }
4049         return el;
4050     };
4051
4052     var ieTable = function(depth, s, h, e){
4053         tempTableEl.innerHTML = [s, h, e].join('');
4054         var i = -1, el = tempTableEl;
4055         while(++i < depth){
4056             el = el.firstChild;
4057         }
4058         return el;
4059     };
4060
4061     // kill repeat to save bytes
4062     var ts = '<table>',
4063         te = '</table>',
4064         tbs = ts+'<tbody>',
4065         tbe = '</tbody>'+te,
4066         trs = tbs + '<tr>',
4067         tre = '</tr>'+tbe;
4068
4069     /**
4070      * @ignore
4071      * Nasty code for IE's broken table implementation
4072      */
4073     var insertIntoTable = function(tag, where, el, html){
4074         if(!tempTableEl){
4075             tempTableEl = document.createElement('div');
4076         }
4077         var node;
4078         var before = null;
4079         if(tag == 'td'){
4080             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4081                 return;
4082             }
4083             if(where == 'beforebegin'){
4084                 before = el;
4085                 el = el.parentNode;
4086             } else{
4087                 before = el.nextSibling;
4088                 el = el.parentNode;
4089             }
4090             node = ieTable(4, trs, html, tre);
4091         }
4092         else if(tag == 'tr'){
4093             if(where == 'beforebegin'){
4094                 before = el;
4095                 el = el.parentNode;
4096                 node = ieTable(3, tbs, html, tbe);
4097             } else if(where == 'afterend'){
4098                 before = el.nextSibling;
4099                 el = el.parentNode;
4100                 node = ieTable(3, tbs, html, tbe);
4101             } else{ // INTO a TR
4102                 if(where == 'afterbegin'){
4103                     before = el.firstChild;
4104                 }
4105                 node = ieTable(4, trs, html, tre);
4106             }
4107         } else if(tag == 'tbody'){
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111                 node = ieTable(2, ts, html, te);
4112             } else if(where == 'afterend'){
4113                 before = el.nextSibling;
4114                 el = el.parentNode;
4115                 node = ieTable(2, ts, html, te);
4116             } else{
4117                 if(where == 'afterbegin'){
4118                     before = el.firstChild;
4119                 }
4120                 node = ieTable(3, tbs, html, tbe);
4121             }
4122         } else{ // TABLE
4123             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4124                 return;
4125             }
4126             if(where == 'afterbegin'){
4127                 before = el.firstChild;
4128             }
4129             node = ieTable(2, ts, html, te);
4130         }
4131         el.insertBefore(node, before);
4132         return node;
4133     };
4134
4135     return {
4136     /** True to force the use of DOM instead of html fragments @type Boolean */
4137     useDom : false,
4138
4139     /**
4140      * Returns the markup for the passed Element(s) config
4141      * @param {Object} o The Dom object spec (and children)
4142      * @return {String}
4143      */
4144     markup : function(o){
4145         return createHtml(o);
4146     },
4147
4148     /**
4149      * Applies a style specification to an element
4150      * @param {String/HTMLElement} el The element to apply styles to
4151      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4152      * a function which returns such a specification.
4153      */
4154     applyStyles : function(el, styles){
4155         if(styles){
4156            el = Roo.fly(el);
4157            if(typeof styles == "string"){
4158                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4159                var matches;
4160                while ((matches = re.exec(styles)) != null){
4161                    el.setStyle(matches[1], matches[2]);
4162                }
4163            }else if (typeof styles == "object"){
4164                for (var style in styles){
4165                   el.setStyle(style, styles[style]);
4166                }
4167            }else if (typeof styles == "function"){
4168                 Roo.DomHelper.applyStyles(el, styles.call());
4169            }
4170         }
4171     },
4172
4173     /**
4174      * Inserts an HTML fragment into the Dom
4175      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4176      * @param {HTMLElement} el The context element
4177      * @param {String} html The HTML fragmenet
4178      * @return {HTMLElement} The new node
4179      */
4180     insertHtml : function(where, el, html){
4181         where = where.toLowerCase();
4182         if(el.insertAdjacentHTML){
4183             if(tableRe.test(el.tagName)){
4184                 var rs;
4185                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4186                     return rs;
4187                 }
4188             }
4189             switch(where){
4190                 case "beforebegin":
4191                     el.insertAdjacentHTML('BeforeBegin', html);
4192                     return el.previousSibling;
4193                 case "afterbegin":
4194                     el.insertAdjacentHTML('AfterBegin', html);
4195                     return el.firstChild;
4196                 case "beforeend":
4197                     el.insertAdjacentHTML('BeforeEnd', html);
4198                     return el.lastChild;
4199                 case "afterend":
4200                     el.insertAdjacentHTML('AfterEnd', html);
4201                     return el.nextSibling;
4202             }
4203             throw 'Illegal insertion point -> "' + where + '"';
4204         }
4205         var range = el.ownerDocument.createRange();
4206         var frag;
4207         switch(where){
4208              case "beforebegin":
4209                 range.setStartBefore(el);
4210                 frag = range.createContextualFragment(html);
4211                 el.parentNode.insertBefore(frag, el);
4212                 return el.previousSibling;
4213              case "afterbegin":
4214                 if(el.firstChild){
4215                     range.setStartBefore(el.firstChild);
4216                     frag = range.createContextualFragment(html);
4217                     el.insertBefore(frag, el.firstChild);
4218                     return el.firstChild;
4219                 }else{
4220                     el.innerHTML = html;
4221                     return el.firstChild;
4222                 }
4223             case "beforeend":
4224                 if(el.lastChild){
4225                     range.setStartAfter(el.lastChild);
4226                     frag = range.createContextualFragment(html);
4227                     el.appendChild(frag);
4228                     return el.lastChild;
4229                 }else{
4230                     el.innerHTML = html;
4231                     return el.lastChild;
4232                 }
4233             case "afterend":
4234                 range.setStartAfter(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el.nextSibling);
4237                 return el.nextSibling;
4238             }
4239             throw 'Illegal insertion point -> "' + where + '"';
4240     },
4241
4242     /**
4243      * Creates new Dom element(s) and inserts them before el
4244      * @param {String/HTMLElement/Element} el The context element
4245      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4246      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4247      * @return {HTMLElement/Roo.Element} The new node
4248      */
4249     insertBefore : function(el, o, returnElement){
4250         return this.doInsert(el, o, returnElement, "beforeBegin");
4251     },
4252
4253     /**
4254      * Creates new Dom element(s) and inserts them after el
4255      * @param {String/HTMLElement/Element} el The context element
4256      * @param {Object} o The Dom object spec (and children)
4257      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4258      * @return {HTMLElement/Roo.Element} The new node
4259      */
4260     insertAfter : function(el, o, returnElement){
4261         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4262     },
4263
4264     /**
4265      * Creates new Dom element(s) and inserts them as the first child of el
4266      * @param {String/HTMLElement/Element} el The context element
4267      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4268      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4269      * @return {HTMLElement/Roo.Element} The new node
4270      */
4271     insertFirst : function(el, o, returnElement){
4272         return this.doInsert(el, o, returnElement, "afterBegin");
4273     },
4274
4275     // private
4276     doInsert : function(el, o, returnElement, pos, sibling){
4277         el = Roo.getDom(el);
4278         var newNode;
4279         if(this.useDom || o.ns){
4280             newNode = createDom(o, null);
4281             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4282         }else{
4283             var html = createHtml(o);
4284             newNode = this.insertHtml(pos, el, html);
4285         }
4286         return returnElement ? Roo.get(newNode, true) : newNode;
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and appends them to el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     append : function(el, o, returnElement){
4297         el = Roo.getDom(el);
4298         var newNode;
4299         if(this.useDom || o.ns){
4300             newNode = createDom(o, null);
4301             el.appendChild(newNode);
4302         }else{
4303             var html = createHtml(o);
4304             newNode = this.insertHtml("beforeEnd", el, html);
4305         }
4306         return returnElement ? Roo.get(newNode, true) : newNode;
4307     },
4308
4309     /**
4310      * Creates new Dom element(s) and overwrites the contents of el with them
4311      * @param {String/HTMLElement/Element} el The context element
4312      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4313      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4314      * @return {HTMLElement/Roo.Element} The new node
4315      */
4316     overwrite : function(el, o, returnElement){
4317         el = Roo.getDom(el);
4318         if (o.ns) {
4319           
4320             while (el.childNodes.length) {
4321                 el.removeChild(el.firstChild);
4322             }
4323             createDom(o, el);
4324         } else {
4325             el.innerHTML = createHtml(o);   
4326         }
4327         
4328         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4329     },
4330
4331     /**
4332      * Creates a new Roo.DomHelper.Template from the Dom object spec
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {Roo.DomHelper.Template} The new template
4335      */
4336     createTemplate : function(o){
4337         var html = createHtml(o);
4338         return new Roo.Template(html);
4339     }
4340     };
4341 }();
4342 /*
4343  * Based on:
4344  * Ext JS Library 1.1.1
4345  * Copyright(c) 2006-2007, Ext JS, LLC.
4346  *
4347  * Originally Released Under LGPL - original licence link has changed is not relivant.
4348  *
4349  * Fork - LGPL
4350  * <script type="text/javascript">
4351  */
4352  
4353 /**
4354 * @class Roo.Template
4355 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4356 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4357 * Usage:
4358 <pre><code>
4359 var t = new Roo.Template(
4360     '&lt;div name="{id}"&gt;',
4361         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4362     '&lt;/div&gt;'
4363 );
4364 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4365 </code></pre>
4366 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4367 * @constructor
4368 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4369 */
4370 Roo.Template = function(html){
4371     if(html instanceof Array){
4372         html = html.join("");
4373     }else if(arguments.length > 1){
4374         html = Array.prototype.join.call(arguments, "");
4375     }
4376     /**@private*/
4377     this.html = html;
4378     
4379 };
4380 Roo.Template.prototype = {
4381     /**
4382      * Returns an HTML fragment of this template with the specified values applied.
4383      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4384      * @return {String} The HTML fragment
4385      */
4386     applyTemplate : function(values){
4387         if(this.compiled){
4388             return this.compiled(values);
4389         }
4390         var useF = this.disableFormats !== true;
4391         var fm = Roo.util.Format, tpl = this;
4392         var fn = function(m, name, format, args){
4393             if(format && useF){
4394                 if(format.substr(0, 5) == "this."){
4395                     return tpl.call(format.substr(5), values[name], values);
4396                 }else{
4397                     if(args){
4398                         // quoted values are required for strings in compiled templates, 
4399                         // but for non compiled we need to strip them
4400                         // quoted reversed for jsmin
4401                         var re = /^\s*['"](.*)["']\s*$/;
4402                         args = args.split(',');
4403                         for(var i = 0, len = args.length; i < len; i++){
4404                             args[i] = args[i].replace(re, "$1");
4405                         }
4406                         args = [values[name]].concat(args);
4407                     }else{
4408                         args = [values[name]];
4409                     }
4410                     return fm[format].apply(fm, args);
4411                 }
4412             }else{
4413                 return values[name] !== undefined ? values[name] : "";
4414             }
4415         };
4416         return this.html.replace(this.re, fn);
4417     },
4418     
4419     /**
4420      * Sets the HTML used as the template and optionally compiles it.
4421      * @param {String} html
4422      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4423      * @return {Roo.Template} this
4424      */
4425     set : function(html, compile){
4426         this.html = html;
4427         this.compiled = null;
4428         if(compile){
4429             this.compile();
4430         }
4431         return this;
4432     },
4433     
4434     /**
4435      * True to disable format functions (defaults to false)
4436      * @type Boolean
4437      */
4438     disableFormats : false,
4439     
4440     /**
4441     * The regular expression used to match template variables 
4442     * @type RegExp
4443     * @property 
4444     */
4445     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4446     
4447     /**
4448      * Compiles the template into an internal function, eliminating the RegEx overhead.
4449      * @return {Roo.Template} this
4450      */
4451     compile : function(){
4452         var fm = Roo.util.Format;
4453         var useF = this.disableFormats !== true;
4454         var sep = Roo.isGecko ? "+" : ",";
4455         var fn = function(m, name, format, args){
4456             if(format && useF){
4457                 args = args ? ',' + args : "";
4458                 if(format.substr(0, 5) != "this."){
4459                     format = "fm." + format + '(';
4460                 }else{
4461                     format = 'this.call("'+ format.substr(5) + '", ';
4462                     args = ", values";
4463                 }
4464             }else{
4465                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4466             }
4467             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4468         };
4469         var body;
4470         // branched to use + in gecko and [].join() in others
4471         if(Roo.isGecko){
4472             body = "this.compiled = function(values){ return '" +
4473                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4474                     "';};";
4475         }else{
4476             body = ["this.compiled = function(values){ return ['"];
4477             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4478             body.push("'].join('');};");
4479             body = body.join('');
4480         }
4481         /**
4482          * eval:var:values
4483          * eval:var:fm
4484          */
4485         eval(body);
4486         return this;
4487     },
4488     
4489     // private function used to call members
4490     call : function(fnName, value, allValues){
4491         return this[fnName](value, allValues);
4492     },
4493     
4494     /**
4495      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4496      * @param {String/HTMLElement/Roo.Element} el The context element
4497      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4498      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4499      * @return {HTMLElement/Roo.Element} The new node or Element
4500      */
4501     insertFirst: function(el, values, returnElement){
4502         return this.doInsert('afterBegin', el, values, returnElement);
4503     },
4504
4505     /**
4506      * Applies the supplied values to the template and inserts the new node(s) before el.
4507      * @param {String/HTMLElement/Roo.Element} el The context element
4508      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4510      * @return {HTMLElement/Roo.Element} The new node or Element
4511      */
4512     insertBefore: function(el, values, returnElement){
4513         return this.doInsert('beforeBegin', el, values, returnElement);
4514     },
4515
4516     /**
4517      * Applies the supplied values to the template and inserts the new node(s) after el.
4518      * @param {String/HTMLElement/Roo.Element} el The context element
4519      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4520      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4521      * @return {HTMLElement/Roo.Element} The new node or Element
4522      */
4523     insertAfter : function(el, values, returnElement){
4524         return this.doInsert('afterEnd', el, values, returnElement);
4525     },
4526     
4527     /**
4528      * Applies the supplied values to the template and appends the new node(s) to el.
4529      * @param {String/HTMLElement/Roo.Element} el The context element
4530      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4531      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4532      * @return {HTMLElement/Roo.Element} The new node or Element
4533      */
4534     append : function(el, values, returnElement){
4535         return this.doInsert('beforeEnd', el, values, returnElement);
4536     },
4537
4538     doInsert : function(where, el, values, returnEl){
4539         el = Roo.getDom(el);
4540         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4541         return returnEl ? Roo.get(newNode, true) : newNode;
4542     },
4543
4544     /**
4545      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4546      * @param {String/HTMLElement/Roo.Element} el The context element
4547      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4549      * @return {HTMLElement/Roo.Element} The new node or Element
4550      */
4551     overwrite : function(el, values, returnElement){
4552         el = Roo.getDom(el);
4553         el.innerHTML = this.applyTemplate(values);
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     }
4556 };
4557 /**
4558  * Alias for {@link #applyTemplate}
4559  * @method
4560  */
4561 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4562
4563 // backwards compat
4564 Roo.DomHelper.Template = Roo.Template;
4565
4566 /**
4567  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4568  * @param {String/HTMLElement} el A DOM element or its id
4569  * @returns {Roo.Template} The created template
4570  * @static
4571  */
4572 Roo.Template.from = function(el){
4573     el = Roo.getDom(el);
4574     return new Roo.Template(el.value || el.innerHTML);
4575 };/*
4576  * Based on:
4577  * Ext JS Library 1.1.1
4578  * Copyright(c) 2006-2007, Ext JS, LLC.
4579  *
4580  * Originally Released Under LGPL - original licence link has changed is not relivant.
4581  *
4582  * Fork - LGPL
4583  * <script type="text/javascript">
4584  */
4585  
4586
4587 /*
4588  * This is code is also distributed under MIT license for use
4589  * with jQuery and prototype JavaScript libraries.
4590  */
4591 /**
4592  * @class Roo.DomQuery
4593 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4594 <p>
4595 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4596
4597 <p>
4598 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4599 </p>
4600 <h4>Element Selectors:</h4>
4601 <ul class="list">
4602     <li> <b>*</b> any element</li>
4603     <li> <b>E</b> an element with the tag E</li>
4604     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4605     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4606     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4607     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4608 </ul>
4609 <h4>Attribute Selectors:</h4>
4610 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4611 <ul class="list">
4612     <li> <b>E[foo]</b> has an attribute "foo"</li>
4613     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4614     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4615     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4616     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4617     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4618     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4619 </ul>
4620 <h4>Pseudo Classes:</h4>
4621 <ul class="list">
4622     <li> <b>E:first-child</b> E is the first child of its parent</li>
4623     <li> <b>E:last-child</b> E is the last child of its parent</li>
4624     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4625     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4626     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4627     <li> <b>E:only-child</b> E is the only child of its parent</li>
4628     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4629     <li> <b>E:first</b> the first E in the resultset</li>
4630     <li> <b>E:last</b> the last E in the resultset</li>
4631     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4632     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4633     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4634     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4635     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4636     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4637     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4638     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4639     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4640 </ul>
4641 <h4>CSS Value Selectors:</h4>
4642 <ul class="list">
4643     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4644     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4645     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4646     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4647     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4648     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4649 </ul>
4650  * @singleton
4651  */
4652 Roo.DomQuery = function(){
4653     var cache = {}, simpleCache = {}, valueCache = {};
4654     var nonSpace = /\S/;
4655     var trimRe = /^\s+|\s+$/g;
4656     var tplRe = /\{(\d+)\}/g;
4657     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4658     var tagTokenRe = /^(#)?([\w-\*]+)/;
4659     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4660
4661     function child(p, index){
4662         var i = 0;
4663         var n = p.firstChild;
4664         while(n){
4665             if(n.nodeType == 1){
4666                if(++i == index){
4667                    return n;
4668                }
4669             }
4670             n = n.nextSibling;
4671         }
4672         return null;
4673     };
4674
4675     function next(n){
4676         while((n = n.nextSibling) && n.nodeType != 1);
4677         return n;
4678     };
4679
4680     function prev(n){
4681         while((n = n.previousSibling) && n.nodeType != 1);
4682         return n;
4683     };
4684
4685     function children(d){
4686         var n = d.firstChild, ni = -1;
4687             while(n){
4688                 var nx = n.nextSibling;
4689                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4690                     d.removeChild(n);
4691                 }else{
4692                     n.nodeIndex = ++ni;
4693                 }
4694                 n = nx;
4695             }
4696             return this;
4697         };
4698
4699     function byClassName(c, a, v){
4700         if(!v){
4701             return c;
4702         }
4703         var r = [], ri = -1, cn;
4704         for(var i = 0, ci; ci = c[i]; i++){
4705             if((' '+ci.className+' ').indexOf(v) != -1){
4706                 r[++ri] = ci;
4707             }
4708         }
4709         return r;
4710     };
4711
4712     function attrValue(n, attr){
4713         if(!n.tagName && typeof n.length != "undefined"){
4714             n = n[0];
4715         }
4716         if(!n){
4717             return null;
4718         }
4719         if(attr == "for"){
4720             return n.htmlFor;
4721         }
4722         if(attr == "class" || attr == "className"){
4723             return n.className;
4724         }
4725         return n.getAttribute(attr) || n[attr];
4726
4727     };
4728
4729     function getNodes(ns, mode, tagName){
4730         var result = [], ri = -1, cs;
4731         if(!ns){
4732             return result;
4733         }
4734         tagName = tagName || "*";
4735         if(typeof ns.getElementsByTagName != "undefined"){
4736             ns = [ns];
4737         }
4738         if(!mode){
4739             for(var i = 0, ni; ni = ns[i]; i++){
4740                 cs = ni.getElementsByTagName(tagName);
4741                 for(var j = 0, ci; ci = cs[j]; j++){
4742                     result[++ri] = ci;
4743                 }
4744             }
4745         }else if(mode == "/" || mode == ">"){
4746             var utag = tagName.toUpperCase();
4747             for(var i = 0, ni, cn; ni = ns[i]; i++){
4748                 cn = ni.children || ni.childNodes;
4749                 for(var j = 0, cj; cj = cn[j]; j++){
4750                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4751                         result[++ri] = cj;
4752                     }
4753                 }
4754             }
4755         }else if(mode == "+"){
4756             var utag = tagName.toUpperCase();
4757             for(var i = 0, n; n = ns[i]; i++){
4758                 while((n = n.nextSibling) && n.nodeType != 1);
4759                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4760                     result[++ri] = n;
4761                 }
4762             }
4763         }else if(mode == "~"){
4764             for(var i = 0, n; n = ns[i]; i++){
4765                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4766                 if(n){
4767                     result[++ri] = n;
4768                 }
4769             }
4770         }
4771         return result;
4772     };
4773
4774     function concat(a, b){
4775         if(b.slice){
4776             return a.concat(b);
4777         }
4778         for(var i = 0, l = b.length; i < l; i++){
4779             a[a.length] = b[i];
4780         }
4781         return a;
4782     }
4783
4784     function byTag(cs, tagName){
4785         if(cs.tagName || cs == document){
4786             cs = [cs];
4787         }
4788         if(!tagName){
4789             return cs;
4790         }
4791         var r = [], ri = -1;
4792         tagName = tagName.toLowerCase();
4793         for(var i = 0, ci; ci = cs[i]; i++){
4794             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4795                 r[++ri] = ci;
4796             }
4797         }
4798         return r;
4799     };
4800
4801     function byId(cs, attr, id){
4802         if(cs.tagName || cs == document){
4803             cs = [cs];
4804         }
4805         if(!id){
4806             return cs;
4807         }
4808         var r = [], ri = -1;
4809         for(var i = 0,ci; ci = cs[i]; i++){
4810             if(ci && ci.id == id){
4811                 r[++ri] = ci;
4812                 return r;
4813             }
4814         }
4815         return r;
4816     };
4817
4818     function byAttribute(cs, attr, value, op, custom){
4819         var r = [], ri = -1, st = custom=="{";
4820         var f = Roo.DomQuery.operators[op];
4821         for(var i = 0, ci; ci = cs[i]; i++){
4822             var a;
4823             if(st){
4824                 a = Roo.DomQuery.getStyle(ci, attr);
4825             }
4826             else if(attr == "class" || attr == "className"){
4827                 a = ci.className;
4828             }else if(attr == "for"){
4829                 a = ci.htmlFor;
4830             }else if(attr == "href"){
4831                 a = ci.getAttribute("href", 2);
4832             }else{
4833                 a = ci.getAttribute(attr);
4834             }
4835             if((f && f(a, value)) || (!f && a)){
4836                 r[++ri] = ci;
4837             }
4838         }
4839         return r;
4840     };
4841
4842     function byPseudo(cs, name, value){
4843         return Roo.DomQuery.pseudos[name](cs, value);
4844     };
4845
4846     // This is for IE MSXML which does not support expandos.
4847     // IE runs the same speed using setAttribute, however FF slows way down
4848     // and Safari completely fails so they need to continue to use expandos.
4849     var isIE = window.ActiveXObject ? true : false;
4850
4851     // this eval is stop the compressor from
4852     // renaming the variable to something shorter
4853     
4854     /** eval:var:batch */
4855     var batch = 30803; 
4856
4857     var key = 30803;
4858
4859     function nodupIEXml(cs){
4860         var d = ++key;
4861         cs[0].setAttribute("_nodup", d);
4862         var r = [cs[0]];
4863         for(var i = 1, len = cs.length; i < len; i++){
4864             var c = cs[i];
4865             if(!c.getAttribute("_nodup") != d){
4866                 c.setAttribute("_nodup", d);
4867                 r[r.length] = c;
4868             }
4869         }
4870         for(var i = 0, len = cs.length; i < len; i++){
4871             cs[i].removeAttribute("_nodup");
4872         }
4873         return r;
4874     }
4875
4876     function nodup(cs){
4877         if(!cs){
4878             return [];
4879         }
4880         var len = cs.length, c, i, r = cs, cj, ri = -1;
4881         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4882             return cs;
4883         }
4884         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4885             return nodupIEXml(cs);
4886         }
4887         var d = ++key;
4888         cs[0]._nodup = d;
4889         for(i = 1; c = cs[i]; i++){
4890             if(c._nodup != d){
4891                 c._nodup = d;
4892             }else{
4893                 r = [];
4894                 for(var j = 0; j < i; j++){
4895                     r[++ri] = cs[j];
4896                 }
4897                 for(j = i+1; cj = cs[j]; j++){
4898                     if(cj._nodup != d){
4899                         cj._nodup = d;
4900                         r[++ri] = cj;
4901                     }
4902                 }
4903                 return r;
4904             }
4905         }
4906         return r;
4907     }
4908
4909     function quickDiffIEXml(c1, c2){
4910         var d = ++key;
4911         for(var i = 0, len = c1.length; i < len; i++){
4912             c1[i].setAttribute("_qdiff", d);
4913         }
4914         var r = [];
4915         for(var i = 0, len = c2.length; i < len; i++){
4916             if(c2[i].getAttribute("_qdiff") != d){
4917                 r[r.length] = c2[i];
4918             }
4919         }
4920         for(var i = 0, len = c1.length; i < len; i++){
4921            c1[i].removeAttribute("_qdiff");
4922         }
4923         return r;
4924     }
4925
4926     function quickDiff(c1, c2){
4927         var len1 = c1.length;
4928         if(!len1){
4929             return c2;
4930         }
4931         if(isIE && c1[0].selectSingleNode){
4932             return quickDiffIEXml(c1, c2);
4933         }
4934         var d = ++key;
4935         for(var i = 0; i < len1; i++){
4936             c1[i]._qdiff = d;
4937         }
4938         var r = [];
4939         for(var i = 0, len = c2.length; i < len; i++){
4940             if(c2[i]._qdiff != d){
4941                 r[r.length] = c2[i];
4942             }
4943         }
4944         return r;
4945     }
4946
4947     function quickId(ns, mode, root, id){
4948         if(ns == root){
4949            var d = root.ownerDocument || root;
4950            return d.getElementById(id);
4951         }
4952         ns = getNodes(ns, mode, "*");
4953         return byId(ns, null, id);
4954     }
4955
4956     return {
4957         getStyle : function(el, name){
4958             return Roo.fly(el).getStyle(name);
4959         },
4960         /**
4961          * Compiles a selector/xpath query into a reusable function. The returned function
4962          * takes one parameter "root" (optional), which is the context node from where the query should start.
4963          * @param {String} selector The selector/xpath query
4964          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4965          * @return {Function}
4966          */
4967         compile : function(path, type){
4968             type = type || "select";
4969             
4970             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4971             var q = path, mode, lq;
4972             var tk = Roo.DomQuery.matchers;
4973             var tklen = tk.length;
4974             var mm;
4975
4976             // accept leading mode switch
4977             var lmode = q.match(modeRe);
4978             if(lmode && lmode[1]){
4979                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4980                 q = q.replace(lmode[1], "");
4981             }
4982             // strip leading slashes
4983             while(path.substr(0, 1)=="/"){
4984                 path = path.substr(1);
4985             }
4986
4987             while(q && lq != q){
4988                 lq = q;
4989                 var tm = q.match(tagTokenRe);
4990                 if(type == "select"){
4991                     if(tm){
4992                         if(tm[1] == "#"){
4993                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4994                         }else{
4995                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4996                         }
4997                         q = q.replace(tm[0], "");
4998                     }else if(q.substr(0, 1) != '@'){
4999                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5000                     }
5001                 }else{
5002                     if(tm){
5003                         if(tm[1] == "#"){
5004                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5005                         }else{
5006                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5007                         }
5008                         q = q.replace(tm[0], "");
5009                     }
5010                 }
5011                 while(!(mm = q.match(modeRe))){
5012                     var matched = false;
5013                     for(var j = 0; j < tklen; j++){
5014                         var t = tk[j];
5015                         var m = q.match(t.re);
5016                         if(m){
5017                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5018                                                     return m[i];
5019                                                 });
5020                             q = q.replace(m[0], "");
5021                             matched = true;
5022                             break;
5023                         }
5024                     }
5025                     // prevent infinite loop on bad selector
5026                     if(!matched){
5027                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5028                     }
5029                 }
5030                 if(mm[1]){
5031                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5032                     q = q.replace(mm[1], "");
5033                 }
5034             }
5035             fn[fn.length] = "return nodup(n);\n}";
5036             
5037              /** 
5038               * list of variables that need from compression as they are used by eval.
5039              *  eval:var:batch 
5040              *  eval:var:nodup
5041              *  eval:var:byTag
5042              *  eval:var:ById
5043              *  eval:var:getNodes
5044              *  eval:var:quickId
5045              *  eval:var:mode
5046              *  eval:var:root
5047              *  eval:var:n
5048              *  eval:var:byClassName
5049              *  eval:var:byPseudo
5050              *  eval:var:byAttribute
5051              *  eval:var:attrValue
5052              * 
5053              **/ 
5054             eval(fn.join(""));
5055             return f;
5056         },
5057
5058         /**
5059          * Selects a group of elements.
5060          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5061          * @param {Node} root (optional) The start of the query (defaults to document).
5062          * @return {Array}
5063          */
5064         select : function(path, root, type){
5065             if(!root || root == document){
5066                 root = document;
5067             }
5068             if(typeof root == "string"){
5069                 root = document.getElementById(root);
5070             }
5071             var paths = path.split(",");
5072             var results = [];
5073             for(var i = 0, len = paths.length; i < len; i++){
5074                 var p = paths[i].replace(trimRe, "");
5075                 if(!cache[p]){
5076                     cache[p] = Roo.DomQuery.compile(p);
5077                     if(!cache[p]){
5078                         throw p + " is not a valid selector";
5079                     }
5080                 }
5081                 var result = cache[p](root);
5082                 if(result && result != document){
5083                     results = results.concat(result);
5084                 }
5085             }
5086             if(paths.length > 1){
5087                 return nodup(results);
5088             }
5089             return results;
5090         },
5091
5092         /**
5093          * Selects a single element.
5094          * @param {String} selector The selector/xpath query
5095          * @param {Node} root (optional) The start of the query (defaults to document).
5096          * @return {Element}
5097          */
5098         selectNode : function(path, root){
5099             return Roo.DomQuery.select(path, root)[0];
5100         },
5101
5102         /**
5103          * Selects the value of a node, optionally replacing null with the defaultValue.
5104          * @param {String} selector The selector/xpath query
5105          * @param {Node} root (optional) The start of the query (defaults to document).
5106          * @param {String} defaultValue
5107          */
5108         selectValue : function(path, root, defaultValue){
5109             path = path.replace(trimRe, "");
5110             if(!valueCache[path]){
5111                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5112             }
5113             var n = valueCache[path](root);
5114             n = n[0] ? n[0] : n;
5115             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5116             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5117         },
5118
5119         /**
5120          * Selects the value of a node, parsing integers and floats.
5121          * @param {String} selector The selector/xpath query
5122          * @param {Node} root (optional) The start of the query (defaults to document).
5123          * @param {Number} defaultValue
5124          * @return {Number}
5125          */
5126         selectNumber : function(path, root, defaultValue){
5127             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5128             return parseFloat(v);
5129         },
5130
5131         /**
5132          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5133          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5134          * @param {String} selector The simple selector to test
5135          * @return {Boolean}
5136          */
5137         is : function(el, ss){
5138             if(typeof el == "string"){
5139                 el = document.getElementById(el);
5140             }
5141             var isArray = (el instanceof Array);
5142             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5143             return isArray ? (result.length == el.length) : (result.length > 0);
5144         },
5145
5146         /**
5147          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5148          * @param {Array} el An array of elements to filter
5149          * @param {String} selector The simple selector to test
5150          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5151          * the selector instead of the ones that match
5152          * @return {Array}
5153          */
5154         filter : function(els, ss, nonMatches){
5155             ss = ss.replace(trimRe, "");
5156             if(!simpleCache[ss]){
5157                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5158             }
5159             var result = simpleCache[ss](els);
5160             return nonMatches ? quickDiff(result, els) : result;
5161         },
5162
5163         /**
5164          * Collection of matching regular expressions and code snippets.
5165          */
5166         matchers : [{
5167                 re: /^\.([\w-]+)/,
5168                 select: 'n = byClassName(n, null, " {1} ");'
5169             }, {
5170                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5171                 select: 'n = byPseudo(n, "{1}", "{2}");'
5172             },{
5173                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5174                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5175             }, {
5176                 re: /^#([\w-]+)/,
5177                 select: 'n = byId(n, null, "{1}");'
5178             },{
5179                 re: /^@([\w-]+)/,
5180                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5181             }
5182         ],
5183
5184         /**
5185          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5186          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5187          */
5188         operators : {
5189             "=" : function(a, v){
5190                 return a == v;
5191             },
5192             "!=" : function(a, v){
5193                 return a != v;
5194             },
5195             "^=" : function(a, v){
5196                 return a && a.substr(0, v.length) == v;
5197             },
5198             "$=" : function(a, v){
5199                 return a && a.substr(a.length-v.length) == v;
5200             },
5201             "*=" : function(a, v){
5202                 return a && a.indexOf(v) !== -1;
5203             },
5204             "%=" : function(a, v){
5205                 return (a % v) == 0;
5206             },
5207             "|=" : function(a, v){
5208                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5209             },
5210             "~=" : function(a, v){
5211                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5212             }
5213         },
5214
5215         /**
5216          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5217          * and the argument (if any) supplied in the selector.
5218          */
5219         pseudos : {
5220             "first-child" : function(c){
5221                 var r = [], ri = -1, n;
5222                 for(var i = 0, ci; ci = n = c[i]; i++){
5223                     while((n = n.previousSibling) && n.nodeType != 1);
5224                     if(!n){
5225                         r[++ri] = ci;
5226                     }
5227                 }
5228                 return r;
5229             },
5230
5231             "last-child" : function(c){
5232                 var r = [], ri = -1, n;
5233                 for(var i = 0, ci; ci = n = c[i]; i++){
5234                     while((n = n.nextSibling) && n.nodeType != 1);
5235                     if(!n){
5236                         r[++ri] = ci;
5237                     }
5238                 }
5239                 return r;
5240             },
5241
5242             "nth-child" : function(c, a) {
5243                 var r = [], ri = -1;
5244                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5245                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5246                 for(var i = 0, n; n = c[i]; i++){
5247                     var pn = n.parentNode;
5248                     if (batch != pn._batch) {
5249                         var j = 0;
5250                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5251                             if(cn.nodeType == 1){
5252                                cn.nodeIndex = ++j;
5253                             }
5254                         }
5255                         pn._batch = batch;
5256                     }
5257                     if (f == 1) {
5258                         if (l == 0 || n.nodeIndex == l){
5259                             r[++ri] = n;
5260                         }
5261                     } else if ((n.nodeIndex + l) % f == 0){
5262                         r[++ri] = n;
5263                     }
5264                 }
5265
5266                 return r;
5267             },
5268
5269             "only-child" : function(c){
5270                 var r = [], ri = -1;;
5271                 for(var i = 0, ci; ci = c[i]; i++){
5272                     if(!prev(ci) && !next(ci)){
5273                         r[++ri] = ci;
5274                     }
5275                 }
5276                 return r;
5277             },
5278
5279             "empty" : function(c){
5280                 var r = [], ri = -1;
5281                 for(var i = 0, ci; ci = c[i]; i++){
5282                     var cns = ci.childNodes, j = 0, cn, empty = true;
5283                     while(cn = cns[j]){
5284                         ++j;
5285                         if(cn.nodeType == 1 || cn.nodeType == 3){
5286                             empty = false;
5287                             break;
5288                         }
5289                     }
5290                     if(empty){
5291                         r[++ri] = ci;
5292                     }
5293                 }
5294                 return r;
5295             },
5296
5297             "contains" : function(c, v){
5298                 var r = [], ri = -1;
5299                 for(var i = 0, ci; ci = c[i]; i++){
5300                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5301                         r[++ri] = ci;
5302                     }
5303                 }
5304                 return r;
5305             },
5306
5307             "nodeValue" : function(c, v){
5308                 var r = [], ri = -1;
5309                 for(var i = 0, ci; ci = c[i]; i++){
5310                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5311                         r[++ri] = ci;
5312                     }
5313                 }
5314                 return r;
5315             },
5316
5317             "checked" : function(c){
5318                 var r = [], ri = -1;
5319                 for(var i = 0, ci; ci = c[i]; i++){
5320                     if(ci.checked == true){
5321                         r[++ri] = ci;
5322                     }
5323                 }
5324                 return r;
5325             },
5326
5327             "not" : function(c, ss){
5328                 return Roo.DomQuery.filter(c, ss, true);
5329             },
5330
5331             "odd" : function(c){
5332                 return this["nth-child"](c, "odd");
5333             },
5334
5335             "even" : function(c){
5336                 return this["nth-child"](c, "even");
5337             },
5338
5339             "nth" : function(c, a){
5340                 return c[a-1] || [];
5341             },
5342
5343             "first" : function(c){
5344                 return c[0] || [];
5345             },
5346
5347             "last" : function(c){
5348                 return c[c.length-1] || [];
5349             },
5350
5351             "has" : function(c, ss){
5352                 var s = Roo.DomQuery.select;
5353                 var r = [], ri = -1;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(s(ss, ci).length > 0){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "next" : function(c, ss){
5363                 var is = Roo.DomQuery.is;
5364                 var r = [], ri = -1;
5365                 for(var i = 0, ci; ci = c[i]; i++){
5366                     var n = next(ci);
5367                     if(n && is(n, ss)){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "prev" : function(c, ss){
5375                 var is = Roo.DomQuery.is;
5376                 var r = [], ri = -1;
5377                 for(var i = 0, ci; ci = c[i]; i++){
5378                     var n = prev(ci);
5379                     if(n && is(n, ss)){
5380                         r[++ri] = ci;
5381                     }
5382                 }
5383                 return r;
5384             }
5385         }
5386     };
5387 }();
5388
5389 /**
5390  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5391  * @param {String} path The selector/xpath query
5392  * @param {Node} root (optional) The start of the query (defaults to document).
5393  * @return {Array}
5394  * @member Roo
5395  * @method query
5396  */
5397 Roo.query = Roo.DomQuery.select;
5398 /*
5399  * Based on:
5400  * Ext JS Library 1.1.1
5401  * Copyright(c) 2006-2007, Ext JS, LLC.
5402  *
5403  * Originally Released Under LGPL - original licence link has changed is not relivant.
5404  *
5405  * Fork - LGPL
5406  * <script type="text/javascript">
5407  */
5408
5409 /**
5410  * @class Roo.util.Observable
5411  * Base class that provides a common interface for publishing events. Subclasses are expected to
5412  * to have a property "events" with all the events defined.<br>
5413  * For example:
5414  * <pre><code>
5415  Employee = function(name){
5416     this.name = name;
5417     this.addEvents({
5418         "fired" : true,
5419         "quit" : true
5420     });
5421  }
5422  Roo.extend(Employee, Roo.util.Observable);
5423 </code></pre>
5424  * @param {Object} config properties to use (incuding events / listeners)
5425  */
5426
5427 Roo.util.Observable = function(cfg){
5428     
5429     cfg = cfg|| {};
5430     this.addEvents(cfg.events || {});
5431     if (cfg.events) {
5432         delete cfg.events; // make sure
5433     }
5434      
5435     Roo.apply(this, cfg);
5436     
5437     if(this.listeners){
5438         this.on(this.listeners);
5439         delete this.listeners;
5440     }
5441 };
5442 Roo.util.Observable.prototype = {
5443     /** 
5444  * @cfg {Object} listeners  list of events and functions to call for this object, 
5445  * For example :
5446  * <pre><code>
5447     listeners :  { 
5448        'click' : function(e) {
5449            ..... 
5450         } ,
5451         .... 
5452     } 
5453   </code></pre>
5454  */
5455     
5456     
5457     /**
5458      * Fires the specified event with the passed parameters (minus the event name).
5459      * @param {String} eventName
5460      * @param {Object...} args Variable number of parameters are passed to handlers
5461      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5462      */
5463     fireEvent : function(){
5464         var ce = this.events[arguments[0].toLowerCase()];
5465         if(typeof ce == "object"){
5466             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5467         }else{
5468             return true;
5469         }
5470     },
5471
5472     // private
5473     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5474
5475     /**
5476      * Appends an event handler to this component
5477      * @param {String}   eventName The type of event to listen for
5478      * @param {Function} handler The method the event invokes
5479      * @param {Object}   scope (optional) The scope in which to execute the handler
5480      * function. The handler function's "this" context.
5481      * @param {Object}   options (optional) An object containing handler configuration
5482      * properties. This may contain any of the following properties:<ul>
5483      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5484      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5485      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5486      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5487      * by the specified number of milliseconds. If the event fires again within that time, the original
5488      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5489      * </ul><br>
5490      * <p>
5491      * <b>Combining Options</b><br>
5492      * Using the options argument, it is possible to combine different types of listeners:<br>
5493      * <br>
5494      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5495                 <pre><code>
5496                 el.on('click', this.onClick, this, {
5497                         single: true,
5498                 delay: 100,
5499                 forumId: 4
5500                 });
5501                 </code></pre>
5502      * <p>
5503      * <b>Attaching multiple handlers in 1 call</b><br>
5504      * The method also allows for a single argument to be passed which is a config object containing properties
5505      * which specify multiple handlers.
5506      * <pre><code>
5507                 el.on({
5508                         'click': {
5509                         fn: this.onClick,
5510                         scope: this,
5511                         delay: 100
5512                 }, 
5513                 'mouseover': {
5514                         fn: this.onMouseOver,
5515                         scope: this
5516                 },
5517                 'mouseout': {
5518                         fn: this.onMouseOut,
5519                         scope: this
5520                 }
5521                 });
5522                 </code></pre>
5523      * <p>
5524      * Or a shorthand syntax which passes the same scope object to all handlers:
5525         <pre><code>
5526                 el.on({
5527                         'click': this.onClick,
5528                 'mouseover': this.onMouseOver,
5529                 'mouseout': this.onMouseOut,
5530                 scope: this
5531                 });
5532                 </code></pre>
5533      */
5534     addListener : function(eventName, fn, scope, o){
5535         if(typeof eventName == "object"){
5536             o = eventName;
5537             for(var e in o){
5538                 if(this.filterOptRe.test(e)){
5539                     continue;
5540                 }
5541                 if(typeof o[e] == "function"){
5542                     // shared options
5543                     this.addListener(e, o[e], o.scope,  o);
5544                 }else{
5545                     // individual options
5546                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5547                 }
5548             }
5549             return;
5550         }
5551         o = (!o || typeof o == "boolean") ? {} : o;
5552         eventName = eventName.toLowerCase();
5553         var ce = this.events[eventName] || true;
5554         if(typeof ce == "boolean"){
5555             ce = new Roo.util.Event(this, eventName);
5556             this.events[eventName] = ce;
5557         }
5558         ce.addListener(fn, scope, o);
5559     },
5560
5561     /**
5562      * Removes a listener
5563      * @param {String}   eventName     The type of event to listen for
5564      * @param {Function} handler        The handler to remove
5565      * @param {Object}   scope  (optional) The scope (this object) for the handler
5566      */
5567     removeListener : function(eventName, fn, scope){
5568         var ce = this.events[eventName.toLowerCase()];
5569         if(typeof ce == "object"){
5570             ce.removeListener(fn, scope);
5571         }
5572     },
5573
5574     /**
5575      * Removes all listeners for this object
5576      */
5577     purgeListeners : function(){
5578         for(var evt in this.events){
5579             if(typeof this.events[evt] == "object"){
5580                  this.events[evt].clearListeners();
5581             }
5582         }
5583     },
5584
5585     relayEvents : function(o, events){
5586         var createHandler = function(ename){
5587             return function(){
5588                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5589             };
5590         };
5591         for(var i = 0, len = events.length; i < len; i++){
5592             var ename = events[i];
5593             if(!this.events[ename]){ this.events[ename] = true; };
5594             o.on(ename, createHandler(ename), this);
5595         }
5596     },
5597
5598     /**
5599      * Used to define events on this Observable
5600      * @param {Object} object The object with the events defined
5601      */
5602     addEvents : function(o){
5603         if(!this.events){
5604             this.events = {};
5605         }
5606         Roo.applyIf(this.events, o);
5607     },
5608
5609     /**
5610      * Checks to see if this object has any listeners for a specified event
5611      * @param {String} eventName The name of the event to check for
5612      * @return {Boolean} True if the event is being listened for, else false
5613      */
5614     hasListener : function(eventName){
5615         var e = this.events[eventName];
5616         return typeof e == "object" && e.listeners.length > 0;
5617     }
5618 };
5619 /**
5620  * Appends an event handler to this element (shorthand for addListener)
5621  * @param {String}   eventName     The type of event to listen for
5622  * @param {Function} handler        The method the event invokes
5623  * @param {Object}   scope (optional) The scope in which to execute the handler
5624  * function. The handler function's "this" context.
5625  * @param {Object}   options  (optional)
5626  * @method
5627  */
5628 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5629 /**
5630  * Removes a listener (shorthand for removeListener)
5631  * @param {String}   eventName     The type of event to listen for
5632  * @param {Function} handler        The handler to remove
5633  * @param {Object}   scope  (optional) The scope (this object) for the handler
5634  * @method
5635  */
5636 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5637
5638 /**
5639  * Starts capture on the specified Observable. All events will be passed
5640  * to the supplied function with the event name + standard signature of the event
5641  * <b>before</b> the event is fired. If the supplied function returns false,
5642  * the event will not fire.
5643  * @param {Observable} o The Observable to capture
5644  * @param {Function} fn The function to call
5645  * @param {Object} scope (optional) The scope (this object) for the fn
5646  * @static
5647  */
5648 Roo.util.Observable.capture = function(o, fn, scope){
5649     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5650 };
5651
5652 /**
5653  * Removes <b>all</b> added captures from the Observable.
5654  * @param {Observable} o The Observable to release
5655  * @static
5656  */
5657 Roo.util.Observable.releaseCapture = function(o){
5658     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5659 };
5660
5661 (function(){
5662
5663     var createBuffered = function(h, o, scope){
5664         var task = new Roo.util.DelayedTask();
5665         return function(){
5666             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5667         };
5668     };
5669
5670     var createSingle = function(h, e, fn, scope){
5671         return function(){
5672             e.removeListener(fn, scope);
5673             return h.apply(scope, arguments);
5674         };
5675     };
5676
5677     var createDelayed = function(h, o, scope){
5678         return function(){
5679             var args = Array.prototype.slice.call(arguments, 0);
5680             setTimeout(function(){
5681                 h.apply(scope, args);
5682             }, o.delay || 10);
5683         };
5684     };
5685
5686     Roo.util.Event = function(obj, name){
5687         this.name = name;
5688         this.obj = obj;
5689         this.listeners = [];
5690     };
5691
5692     Roo.util.Event.prototype = {
5693         addListener : function(fn, scope, options){
5694             var o = options || {};
5695             scope = scope || this.obj;
5696             if(!this.isListening(fn, scope)){
5697                 var l = {fn: fn, scope: scope, options: o};
5698                 var h = fn;
5699                 if(o.delay){
5700                     h = createDelayed(h, o, scope);
5701                 }
5702                 if(o.single){
5703                     h = createSingle(h, this, fn, scope);
5704                 }
5705                 if(o.buffer){
5706                     h = createBuffered(h, o, scope);
5707                 }
5708                 l.fireFn = h;
5709                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5710                     this.listeners.push(l);
5711                 }else{
5712                     this.listeners = this.listeners.slice(0);
5713                     this.listeners.push(l);
5714                 }
5715             }
5716         },
5717
5718         findListener : function(fn, scope){
5719             scope = scope || this.obj;
5720             var ls = this.listeners;
5721             for(var i = 0, len = ls.length; i < len; i++){
5722                 var l = ls[i];
5723                 if(l.fn == fn && l.scope == scope){
5724                     return i;
5725                 }
5726             }
5727             return -1;
5728         },
5729
5730         isListening : function(fn, scope){
5731             return this.findListener(fn, scope) != -1;
5732         },
5733
5734         removeListener : function(fn, scope){
5735             var index;
5736             if((index = this.findListener(fn, scope)) != -1){
5737                 if(!this.firing){
5738                     this.listeners.splice(index, 1);
5739                 }else{
5740                     this.listeners = this.listeners.slice(0);
5741                     this.listeners.splice(index, 1);
5742                 }
5743                 return true;
5744             }
5745             return false;
5746         },
5747
5748         clearListeners : function(){
5749             this.listeners = [];
5750         },
5751
5752         fire : function(){
5753             var ls = this.listeners, scope, len = ls.length;
5754             if(len > 0){
5755                 this.firing = true;
5756                 var args = Array.prototype.slice.call(arguments, 0);
5757                 for(var i = 0; i < len; i++){
5758                     var l = ls[i];
5759                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5760                         this.firing = false;
5761                         return false;
5762                     }
5763                 }
5764                 this.firing = false;
5765             }
5766             return true;
5767         }
5768     };
5769 })();/*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779
5780 /**
5781  * @class Roo.EventManager
5782  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5783  * several useful events directly.
5784  * See {@link Roo.EventObject} for more details on normalized event objects.
5785  * @singleton
5786  */
5787 Roo.EventManager = function(){
5788     var docReadyEvent, docReadyProcId, docReadyState = false;
5789     var resizeEvent, resizeTask, textEvent, textSize;
5790     var E = Roo.lib.Event;
5791     var D = Roo.lib.Dom;
5792
5793
5794     var fireDocReady = function(){
5795         if(!docReadyState){
5796             docReadyState = true;
5797             Roo.isReady = true;
5798             if(docReadyProcId){
5799                 clearInterval(docReadyProcId);
5800             }
5801             if(Roo.isGecko || Roo.isOpera) {
5802                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5803             }
5804             if(Roo.isIE){
5805                 var defer = document.getElementById("ie-deferred-loader");
5806                 if(defer){
5807                     defer.onreadystatechange = null;
5808                     defer.parentNode.removeChild(defer);
5809                 }
5810             }
5811             if(docReadyEvent){
5812                 docReadyEvent.fire();
5813                 docReadyEvent.clearListeners();
5814             }
5815         }
5816     };
5817     
5818     var initDocReady = function(){
5819         docReadyEvent = new Roo.util.Event();
5820         if(Roo.isGecko || Roo.isOpera) {
5821             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5822         }else if(Roo.isIE){
5823             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5824             var defer = document.getElementById("ie-deferred-loader");
5825             defer.onreadystatechange = function(){
5826                 if(this.readyState == "complete"){
5827                     fireDocReady();
5828                 }
5829             };
5830         }else if(Roo.isSafari){ 
5831             docReadyProcId = setInterval(function(){
5832                 var rs = document.readyState;
5833                 if(rs == "complete") {
5834                     fireDocReady();     
5835                  }
5836             }, 10);
5837         }
5838         // no matter what, make sure it fires on load
5839         E.on(window, "load", fireDocReady);
5840     };
5841
5842     var createBuffered = function(h, o){
5843         var task = new Roo.util.DelayedTask(h);
5844         return function(e){
5845             // create new event object impl so new events don't wipe out properties
5846             e = new Roo.EventObjectImpl(e);
5847             task.delay(o.buffer, h, null, [e]);
5848         };
5849     };
5850
5851     var createSingle = function(h, el, ename, fn){
5852         return function(e){
5853             Roo.EventManager.removeListener(el, ename, fn);
5854             h(e);
5855         };
5856     };
5857
5858     var createDelayed = function(h, o){
5859         return function(e){
5860             // create new event object impl so new events don't wipe out properties
5861             e = new Roo.EventObjectImpl(e);
5862             setTimeout(function(){
5863                 h(e);
5864             }, o.delay || 10);
5865         };
5866     };
5867
5868     var listen = function(element, ename, opt, fn, scope){
5869         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5870         fn = fn || o.fn; scope = scope || o.scope;
5871         var el = Roo.getDom(element);
5872         if(!el){
5873             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5874         }
5875         var h = function(e){
5876             e = Roo.EventObject.setEvent(e);
5877             var t;
5878             if(o.delegate){
5879                 t = e.getTarget(o.delegate, el);
5880                 if(!t){
5881                     return;
5882                 }
5883             }else{
5884                 t = e.target;
5885             }
5886             if(o.stopEvent === true){
5887                 e.stopEvent();
5888             }
5889             if(o.preventDefault === true){
5890                e.preventDefault();
5891             }
5892             if(o.stopPropagation === true){
5893                 e.stopPropagation();
5894             }
5895
5896             if(o.normalized === false){
5897                 e = e.browserEvent;
5898             }
5899
5900             fn.call(scope || el, e, t, o);
5901         };
5902         if(o.delay){
5903             h = createDelayed(h, o);
5904         }
5905         if(o.single){
5906             h = createSingle(h, el, ename, fn);
5907         }
5908         if(o.buffer){
5909             h = createBuffered(h, o);
5910         }
5911         fn._handlers = fn._handlers || [];
5912         fn._handlers.push([Roo.id(el), ename, h]);
5913
5914         E.on(el, ename, h);
5915         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5916             el.addEventListener("DOMMouseScroll", h, false);
5917             E.on(window, 'unload', function(){
5918                 el.removeEventListener("DOMMouseScroll", h, false);
5919             });
5920         }
5921         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5922             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5923         }
5924         return h;
5925     };
5926
5927     var stopListening = function(el, ename, fn){
5928         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5929         if(hds){
5930             for(var i = 0, len = hds.length; i < len; i++){
5931                 var h = hds[i];
5932                 if(h[0] == id && h[1] == ename){
5933                     hd = h[2];
5934                     hds.splice(i, 1);
5935                     break;
5936                 }
5937             }
5938         }
5939         E.un(el, ename, hd);
5940         el = Roo.getDom(el);
5941         if(ename == "mousewheel" && el.addEventListener){
5942             el.removeEventListener("DOMMouseScroll", hd, false);
5943         }
5944         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5945             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5946         }
5947     };
5948
5949     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5950     
5951     var pub = {
5952         
5953         
5954         /** 
5955          * Fix for doc tools
5956          * @scope Roo.EventManager
5957          */
5958         
5959         
5960         /** 
5961          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5962          * object with a Roo.EventObject
5963          * @param {Function} fn        The method the event invokes
5964          * @param {Object}   scope    An object that becomes the scope of the handler
5965          * @param {boolean}  override If true, the obj passed in becomes
5966          *                             the execution scope of the listener
5967          * @return {Function} The wrapped function
5968          * @deprecated
5969          */
5970         wrap : function(fn, scope, override){
5971             return function(e){
5972                 Roo.EventObject.setEvent(e);
5973                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5974             };
5975         },
5976         
5977         /**
5978      * Appends an event handler to an element (shorthand for addListener)
5979      * @param {String/HTMLElement}   element        The html element or id to assign the
5980      * @param {String}   eventName The type of event to listen for
5981      * @param {Function} handler The method the event invokes
5982      * @param {Object}   scope (optional) The scope in which to execute the handler
5983      * function. The handler function's "this" context.
5984      * @param {Object}   options (optional) An object containing handler configuration
5985      * properties. This may contain any of the following properties:<ul>
5986      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5987      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5988      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5989      * <li>preventDefault {Boolean} True to prevent the default action</li>
5990      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5991      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5992      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5993      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5994      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5995      * by the specified number of milliseconds. If the event fires again within that time, the original
5996      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5997      * </ul><br>
5998      * <p>
5999      * <b>Combining Options</b><br>
6000      * Using the options argument, it is possible to combine different types of listeners:<br>
6001      * <br>
6002      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6003      * Code:<pre><code>
6004 el.on('click', this.onClick, this, {
6005     single: true,
6006     delay: 100,
6007     stopEvent : true,
6008     forumId: 4
6009 });</code></pre>
6010      * <p>
6011      * <b>Attaching multiple handlers in 1 call</b><br>
6012       * The method also allows for a single argument to be passed which is a config object containing properties
6013      * which specify multiple handlers.
6014      * <p>
6015      * Code:<pre><code>
6016 el.on({
6017     'click' : {
6018         fn: this.onClick
6019         scope: this,
6020         delay: 100
6021     },
6022     'mouseover' : {
6023         fn: this.onMouseOver
6024         scope: this
6025     },
6026     'mouseout' : {
6027         fn: this.onMouseOut
6028         scope: this
6029     }
6030 });</code></pre>
6031      * <p>
6032      * Or a shorthand syntax:<br>
6033      * Code:<pre><code>
6034 el.on({
6035     'click' : this.onClick,
6036     'mouseover' : this.onMouseOver,
6037     'mouseout' : this.onMouseOut
6038     scope: this
6039 });</code></pre>
6040      */
6041         addListener : function(element, eventName, fn, scope, options){
6042             if(typeof eventName == "object"){
6043                 var o = eventName;
6044                 for(var e in o){
6045                     if(propRe.test(e)){
6046                         continue;
6047                     }
6048                     if(typeof o[e] == "function"){
6049                         // shared options
6050                         listen(element, e, o, o[e], o.scope);
6051                     }else{
6052                         // individual options
6053                         listen(element, e, o[e]);
6054                     }
6055                 }
6056                 return;
6057             }
6058             return listen(element, eventName, options, fn, scope);
6059         },
6060         
6061         /**
6062          * Removes an event handler
6063          *
6064          * @param {String/HTMLElement}   element        The id or html element to remove the 
6065          *                             event from
6066          * @param {String}   eventName     The type of event
6067          * @param {Function} fn
6068          * @return {Boolean} True if a listener was actually removed
6069          */
6070         removeListener : function(element, eventName, fn){
6071             return stopListening(element, eventName, fn);
6072         },
6073         
6074         /**
6075          * Fires when the document is ready (before onload and before images are loaded). Can be 
6076          * accessed shorthanded Roo.onReady().
6077          * @param {Function} fn        The method the event invokes
6078          * @param {Object}   scope    An  object that becomes the scope of the handler
6079          * @param {boolean}  options
6080          */
6081         onDocumentReady : function(fn, scope, options){
6082             if(docReadyState){ // if it already fired
6083                 docReadyEvent.addListener(fn, scope, options);
6084                 docReadyEvent.fire();
6085                 docReadyEvent.clearListeners();
6086                 return;
6087             }
6088             if(!docReadyEvent){
6089                 initDocReady();
6090             }
6091             docReadyEvent.addListener(fn, scope, options);
6092         },
6093         
6094         /**
6095          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6096          * @param {Function} fn        The method the event invokes
6097          * @param {Object}   scope    An object that becomes the scope of the handler
6098          * @param {boolean}  options
6099          */
6100         onWindowResize : function(fn, scope, options){
6101             if(!resizeEvent){
6102                 resizeEvent = new Roo.util.Event();
6103                 resizeTask = new Roo.util.DelayedTask(function(){
6104                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6105                 });
6106                 E.on(window, "resize", function(){
6107                     if(Roo.isIE){
6108                         resizeTask.delay(50);
6109                     }else{
6110                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6111                     }
6112                 });
6113             }
6114             resizeEvent.addListener(fn, scope, options);
6115         },
6116
6117         /**
6118          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6119          * @param {Function} fn        The method the event invokes
6120          * @param {Object}   scope    An object that becomes the scope of the handler
6121          * @param {boolean}  options
6122          */
6123         onTextResize : function(fn, scope, options){
6124             if(!textEvent){
6125                 textEvent = new Roo.util.Event();
6126                 var textEl = new Roo.Element(document.createElement('div'));
6127                 textEl.dom.className = 'x-text-resize';
6128                 textEl.dom.innerHTML = 'X';
6129                 textEl.appendTo(document.body);
6130                 textSize = textEl.dom.offsetHeight;
6131                 setInterval(function(){
6132                     if(textEl.dom.offsetHeight != textSize){
6133                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6134                     }
6135                 }, this.textResizeInterval);
6136             }
6137             textEvent.addListener(fn, scope, options);
6138         },
6139
6140         /**
6141          * Removes the passed window resize listener.
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    The scope of handler
6144          */
6145         removeResizeListener : function(fn, scope){
6146             if(resizeEvent){
6147                 resizeEvent.removeListener(fn, scope);
6148             }
6149         },
6150
6151         // private
6152         fireResize : function(){
6153             if(resizeEvent){
6154                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6155             }   
6156         },
6157         /**
6158          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6159          */
6160         ieDeferSrc : false,
6161         /**
6162          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6163          */
6164         textResizeInterval : 50
6165     };
6166     
6167     /**
6168      * Fix for doc tools
6169      * @scopeAlias pub=Roo.EventManager
6170      */
6171     
6172      /**
6173      * Appends an event handler to an element (shorthand for addListener)
6174      * @param {String/HTMLElement}   element        The html element or id to assign the
6175      * @param {String}   eventName The type of event to listen for
6176      * @param {Function} handler The method the event invokes
6177      * @param {Object}   scope (optional) The scope in which to execute the handler
6178      * function. The handler function's "this" context.
6179      * @param {Object}   options (optional) An object containing handler configuration
6180      * properties. This may contain any of the following properties:<ul>
6181      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6182      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6183      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6184      * <li>preventDefault {Boolean} True to prevent the default action</li>
6185      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6186      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6187      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6188      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6189      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6190      * by the specified number of milliseconds. If the event fires again within that time, the original
6191      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6192      * </ul><br>
6193      * <p>
6194      * <b>Combining Options</b><br>
6195      * Using the options argument, it is possible to combine different types of listeners:<br>
6196      * <br>
6197      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6198      * Code:<pre><code>
6199 el.on('click', this.onClick, this, {
6200     single: true,
6201     delay: 100,
6202     stopEvent : true,
6203     forumId: 4
6204 });</code></pre>
6205      * <p>
6206      * <b>Attaching multiple handlers in 1 call</b><br>
6207       * The method also allows for a single argument to be passed which is a config object containing properties
6208      * which specify multiple handlers.
6209      * <p>
6210      * Code:<pre><code>
6211 el.on({
6212     'click' : {
6213         fn: this.onClick
6214         scope: this,
6215         delay: 100
6216     },
6217     'mouseover' : {
6218         fn: this.onMouseOver
6219         scope: this
6220     },
6221     'mouseout' : {
6222         fn: this.onMouseOut
6223         scope: this
6224     }
6225 });</code></pre>
6226      * <p>
6227      * Or a shorthand syntax:<br>
6228      * Code:<pre><code>
6229 el.on({
6230     'click' : this.onClick,
6231     'mouseover' : this.onMouseOver,
6232     'mouseout' : this.onMouseOut
6233     scope: this
6234 });</code></pre>
6235      */
6236     pub.on = pub.addListener;
6237     pub.un = pub.removeListener;
6238
6239     pub.stoppedMouseDownEvent = new Roo.util.Event();
6240     return pub;
6241 }();
6242 /**
6243   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6244   * @param {Function} fn        The method the event invokes
6245   * @param {Object}   scope    An  object that becomes the scope of the handler
6246   * @param {boolean}  override If true, the obj passed in becomes
6247   *                             the execution scope of the listener
6248   * @member Roo
6249   * @method onReady
6250  */
6251 Roo.onReady = Roo.EventManager.onDocumentReady;
6252
6253 Roo.onReady(function(){
6254     var bd = Roo.get(document.body);
6255     if(!bd){ return; }
6256
6257     var cls = [
6258             Roo.isIE ? "roo-ie"
6259             : Roo.isGecko ? "roo-gecko"
6260             : Roo.isOpera ? "roo-opera"
6261             : Roo.isSafari ? "roo-safari" : ""];
6262
6263     if(Roo.isMac){
6264         cls.push("roo-mac");
6265     }
6266     if(Roo.isLinux){
6267         cls.push("roo-linux");
6268     }
6269     if(Roo.isBorderBox){
6270         cls.push('roo-border-box');
6271     }
6272     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6273         var p = bd.dom.parentNode;
6274         if(p){
6275             p.className += ' roo-strict';
6276         }
6277     }
6278     bd.addClass(cls.join(' '));
6279 });
6280
6281 /**
6282  * @class Roo.EventObject
6283  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6284  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6285  * Example:
6286  * <pre><code>
6287  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6288     e.preventDefault();
6289     var target = e.getTarget();
6290     ...
6291  }
6292  var myDiv = Roo.get("myDiv");
6293  myDiv.on("click", handleClick);
6294  //or
6295  Roo.EventManager.on("myDiv", 'click', handleClick);
6296  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6297  </code></pre>
6298  * @singleton
6299  */
6300 Roo.EventObject = function(){
6301     
6302     var E = Roo.lib.Event;
6303     
6304     // safari keypress events for special keys return bad keycodes
6305     var safariKeys = {
6306         63234 : 37, // left
6307         63235 : 39, // right
6308         63232 : 38, // up
6309         63233 : 40, // down
6310         63276 : 33, // page up
6311         63277 : 34, // page down
6312         63272 : 46, // delete
6313         63273 : 36, // home
6314         63275 : 35  // end
6315     };
6316
6317     // normalize button clicks
6318     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6319                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6320
6321     Roo.EventObjectImpl = function(e){
6322         if(e){
6323             this.setEvent(e.browserEvent || e);
6324         }
6325     };
6326     Roo.EventObjectImpl.prototype = {
6327         /**
6328          * Used to fix doc tools.
6329          * @scope Roo.EventObject.prototype
6330          */
6331             
6332
6333         
6334         
6335         /** The normal browser event */
6336         browserEvent : null,
6337         /** The button pressed in a mouse event */
6338         button : -1,
6339         /** True if the shift key was down during the event */
6340         shiftKey : false,
6341         /** True if the control key was down during the event */
6342         ctrlKey : false,
6343         /** True if the alt key was down during the event */
6344         altKey : false,
6345
6346         /** Key constant 
6347         * @type Number */
6348         BACKSPACE : 8,
6349         /** Key constant 
6350         * @type Number */
6351         TAB : 9,
6352         /** Key constant 
6353         * @type Number */
6354         RETURN : 13,
6355         /** Key constant 
6356         * @type Number */
6357         ENTER : 13,
6358         /** Key constant 
6359         * @type Number */
6360         SHIFT : 16,
6361         /** Key constant 
6362         * @type Number */
6363         CONTROL : 17,
6364         /** Key constant 
6365         * @type Number */
6366         ESC : 27,
6367         /** Key constant 
6368         * @type Number */
6369         SPACE : 32,
6370         /** Key constant 
6371         * @type Number */
6372         PAGEUP : 33,
6373         /** Key constant 
6374         * @type Number */
6375         PAGEDOWN : 34,
6376         /** Key constant 
6377         * @type Number */
6378         END : 35,
6379         /** Key constant 
6380         * @type Number */
6381         HOME : 36,
6382         /** Key constant 
6383         * @type Number */
6384         LEFT : 37,
6385         /** Key constant 
6386         * @type Number */
6387         UP : 38,
6388         /** Key constant 
6389         * @type Number */
6390         RIGHT : 39,
6391         /** Key constant 
6392         * @type Number */
6393         DOWN : 40,
6394         /** Key constant 
6395         * @type Number */
6396         DELETE : 46,
6397         /** Key constant 
6398         * @type Number */
6399         F5 : 116,
6400
6401            /** @private */
6402         setEvent : function(e){
6403             if(e == this || (e && e.browserEvent)){ // already wrapped
6404                 return e;
6405             }
6406             this.browserEvent = e;
6407             if(e){
6408                 // normalize buttons
6409                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6410                 if(e.type == 'click' && this.button == -1){
6411                     this.button = 0;
6412                 }
6413                 this.type = e.type;
6414                 this.shiftKey = e.shiftKey;
6415                 // mac metaKey behaves like ctrlKey
6416                 this.ctrlKey = e.ctrlKey || e.metaKey;
6417                 this.altKey = e.altKey;
6418                 // in getKey these will be normalized for the mac
6419                 this.keyCode = e.keyCode;
6420                 // keyup warnings on firefox.
6421                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6422                 // cache the target for the delayed and or buffered events
6423                 this.target = E.getTarget(e);
6424                 // same for XY
6425                 this.xy = E.getXY(e);
6426             }else{
6427                 this.button = -1;
6428                 this.shiftKey = false;
6429                 this.ctrlKey = false;
6430                 this.altKey = false;
6431                 this.keyCode = 0;
6432                 this.charCode =0;
6433                 this.target = null;
6434                 this.xy = [0, 0];
6435             }
6436             return this;
6437         },
6438
6439         /**
6440          * Stop the event (preventDefault and stopPropagation)
6441          */
6442         stopEvent : function(){
6443             if(this.browserEvent){
6444                 if(this.browserEvent.type == 'mousedown'){
6445                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6446                 }
6447                 E.stopEvent(this.browserEvent);
6448             }
6449         },
6450
6451         /**
6452          * Prevents the browsers default handling of the event.
6453          */
6454         preventDefault : function(){
6455             if(this.browserEvent){
6456                 E.preventDefault(this.browserEvent);
6457             }
6458         },
6459
6460         /** @private */
6461         isNavKeyPress : function(){
6462             var k = this.keyCode;
6463             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6464             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6465         },
6466
6467         isSpecialKey : function(){
6468             var k = this.keyCode;
6469             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6470             (k == 16) || (k == 17) ||
6471             (k >= 18 && k <= 20) ||
6472             (k >= 33 && k <= 35) ||
6473             (k >= 36 && k <= 39) ||
6474             (k >= 44 && k <= 45);
6475         },
6476         /**
6477          * Cancels bubbling of the event.
6478          */
6479         stopPropagation : function(){
6480             if(this.browserEvent){
6481                 if(this.type == 'mousedown'){
6482                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6483                 }
6484                 E.stopPropagation(this.browserEvent);
6485             }
6486         },
6487
6488         /**
6489          * Gets the key code for the event.
6490          * @return {Number}
6491          */
6492         getCharCode : function(){
6493             return this.charCode || this.keyCode;
6494         },
6495
6496         /**
6497          * Returns a normalized keyCode for the event.
6498          * @return {Number} The key code
6499          */
6500         getKey : function(){
6501             var k = this.keyCode || this.charCode;
6502             return Roo.isSafari ? (safariKeys[k] || k) : k;
6503         },
6504
6505         /**
6506          * Gets the x coordinate of the event.
6507          * @return {Number}
6508          */
6509         getPageX : function(){
6510             return this.xy[0];
6511         },
6512
6513         /**
6514          * Gets the y coordinate of the event.
6515          * @return {Number}
6516          */
6517         getPageY : function(){
6518             return this.xy[1];
6519         },
6520
6521         /**
6522          * Gets the time of the event.
6523          * @return {Number}
6524          */
6525         getTime : function(){
6526             if(this.browserEvent){
6527                 return E.getTime(this.browserEvent);
6528             }
6529             return null;
6530         },
6531
6532         /**
6533          * Gets the page coordinates of the event.
6534          * @return {Array} The xy values like [x, y]
6535          */
6536         getXY : function(){
6537             return this.xy;
6538         },
6539
6540         /**
6541          * Gets the target for the event.
6542          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6543          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6544                 search as a number or element (defaults to 10 || document.body)
6545          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6546          * @return {HTMLelement}
6547          */
6548         getTarget : function(selector, maxDepth, returnEl){
6549             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6550         },
6551         /**
6552          * Gets the related target.
6553          * @return {HTMLElement}
6554          */
6555         getRelatedTarget : function(){
6556             if(this.browserEvent){
6557                 return E.getRelatedTarget(this.browserEvent);
6558             }
6559             return null;
6560         },
6561
6562         /**
6563          * Normalizes mouse wheel delta across browsers
6564          * @return {Number} The delta
6565          */
6566         getWheelDelta : function(){
6567             var e = this.browserEvent;
6568             var delta = 0;
6569             if(e.wheelDelta){ /* IE/Opera. */
6570                 delta = e.wheelDelta/120;
6571             }else if(e.detail){ /* Mozilla case. */
6572                 delta = -e.detail/3;
6573             }
6574             return delta;
6575         },
6576
6577         /**
6578          * Returns true if the control, meta, shift or alt key was pressed during this event.
6579          * @return {Boolean}
6580          */
6581         hasModifier : function(){
6582             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6583         },
6584
6585         /**
6586          * Returns true if the target of this event equals el or is a child of el
6587          * @param {String/HTMLElement/Element} el
6588          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6589          * @return {Boolean}
6590          */
6591         within : function(el, related){
6592             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6593             return t && Roo.fly(el).contains(t);
6594         },
6595
6596         getPoint : function(){
6597             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6598         }
6599     };
6600
6601     return new Roo.EventObjectImpl();
6602 }();
6603             
6604     /*
6605  * Based on:
6606  * Ext JS Library 1.1.1
6607  * Copyright(c) 2006-2007, Ext JS, LLC.
6608  *
6609  * Originally Released Under LGPL - original licence link has changed is not relivant.
6610  *
6611  * Fork - LGPL
6612  * <script type="text/javascript">
6613  */
6614
6615  
6616 // was in Composite Element!??!?!
6617  
6618 (function(){
6619     var D = Roo.lib.Dom;
6620     var E = Roo.lib.Event;
6621     var A = Roo.lib.Anim;
6622
6623     // local style camelizing for speed
6624     var propCache = {};
6625     var camelRe = /(-[a-z])/gi;
6626     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6627     var view = document.defaultView;
6628
6629 /**
6630  * @class Roo.Element
6631  * Represents an Element in the DOM.<br><br>
6632  * Usage:<br>
6633 <pre><code>
6634 var el = Roo.get("my-div");
6635
6636 // or with getEl
6637 var el = getEl("my-div");
6638
6639 // or with a DOM element
6640 var el = Roo.get(myDivElement);
6641 </code></pre>
6642  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6643  * each call instead of constructing a new one.<br><br>
6644  * <b>Animations</b><br />
6645  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6646  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6647 <pre>
6648 Option    Default   Description
6649 --------- --------  ---------------------------------------------
6650 duration  .35       The duration of the animation in seconds
6651 easing    easeOut   The YUI easing method
6652 callback  none      A function to execute when the anim completes
6653 scope     this      The scope (this) of the callback function
6654 </pre>
6655 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6656 * manipulate the animation. Here's an example:
6657 <pre><code>
6658 var el = Roo.get("my-div");
6659
6660 // no animation
6661 el.setWidth(100);
6662
6663 // default animation
6664 el.setWidth(100, true);
6665
6666 // animation with some options set
6667 el.setWidth(100, {
6668     duration: 1,
6669     callback: this.foo,
6670     scope: this
6671 });
6672
6673 // using the "anim" property to get the Anim object
6674 var opt = {
6675     duration: 1,
6676     callback: this.foo,
6677     scope: this
6678 };
6679 el.setWidth(100, opt);
6680 ...
6681 if(opt.anim.isAnimated()){
6682     opt.anim.stop();
6683 }
6684 </code></pre>
6685 * <b> Composite (Collections of) Elements</b><br />
6686  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6687  * @constructor Create a new Element directly.
6688  * @param {String/HTMLElement} element
6689  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6690  */
6691     Roo.Element = function(element, forceNew){
6692         var dom = typeof element == "string" ?
6693                 document.getElementById(element) : element;
6694         if(!dom){ // invalid id/element
6695             return null;
6696         }
6697         var id = dom.id;
6698         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6699             return Roo.Element.cache[id];
6700         }
6701
6702         /**
6703          * The DOM element
6704          * @type HTMLElement
6705          */
6706         this.dom = dom;
6707
6708         /**
6709          * The DOM element ID
6710          * @type String
6711          */
6712         this.id = id || Roo.id(dom);
6713     };
6714
6715     var El = Roo.Element;
6716
6717     El.prototype = {
6718         /**
6719          * The element's default display mode  (defaults to "")
6720          * @type String
6721          */
6722         originalDisplay : "",
6723
6724         visibilityMode : 1,
6725         /**
6726          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6727          * @type String
6728          */
6729         defaultUnit : "px",
6730         /**
6731          * Sets the element's visibility mode. When setVisible() is called it
6732          * will use this to determine whether to set the visibility or the display property.
6733          * @param visMode Element.VISIBILITY or Element.DISPLAY
6734          * @return {Roo.Element} this
6735          */
6736         setVisibilityMode : function(visMode){
6737             this.visibilityMode = visMode;
6738             return this;
6739         },
6740         /**
6741          * Convenience method for setVisibilityMode(Element.DISPLAY)
6742          * @param {String} display (optional) What to set display to when visible
6743          * @return {Roo.Element} this
6744          */
6745         enableDisplayMode : function(display){
6746             this.setVisibilityMode(El.DISPLAY);
6747             if(typeof display != "undefined") this.originalDisplay = display;
6748             return this;
6749         },
6750
6751         /**
6752          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6753          * @param {String} selector The simple selector to test
6754          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6755                 search as a number or element (defaults to 10 || document.body)
6756          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6757          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6758          */
6759         findParent : function(simpleSelector, maxDepth, returnEl){
6760             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6761             maxDepth = maxDepth || 50;
6762             if(typeof maxDepth != "number"){
6763                 stopEl = Roo.getDom(maxDepth);
6764                 maxDepth = 10;
6765             }
6766             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6767                 if(dq.is(p, simpleSelector)){
6768                     return returnEl ? Roo.get(p) : p;
6769                 }
6770                 depth++;
6771                 p = p.parentNode;
6772             }
6773             return null;
6774         },
6775
6776
6777         /**
6778          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6779          * @param {String} selector The simple selector to test
6780          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6781                 search as a number or element (defaults to 10 || document.body)
6782          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6783          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6784          */
6785         findParentNode : function(simpleSelector, maxDepth, returnEl){
6786             var p = Roo.fly(this.dom.parentNode, '_internal');
6787             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6788         },
6789
6790         /**
6791          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6792          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6793          * @param {String} selector The simple selector to test
6794          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6795                 search as a number or element (defaults to 10 || document.body)
6796          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6797          */
6798         up : function(simpleSelector, maxDepth){
6799             return this.findParentNode(simpleSelector, maxDepth, true);
6800         },
6801
6802
6803
6804         /**
6805          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6806          * @param {String} selector The simple selector to test
6807          * @return {Boolean} True if this element matches the selector, else false
6808          */
6809         is : function(simpleSelector){
6810             return Roo.DomQuery.is(this.dom, simpleSelector);
6811         },
6812
6813         /**
6814          * Perform animation on this element.
6815          * @param {Object} args The YUI animation control args
6816          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6817          * @param {Function} onComplete (optional) Function to call when animation completes
6818          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6819          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6820          * @return {Roo.Element} this
6821          */
6822         animate : function(args, duration, onComplete, easing, animType){
6823             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6824             return this;
6825         },
6826
6827         /*
6828          * @private Internal animation call
6829          */
6830         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6831             animType = animType || 'run';
6832             opt = opt || {};
6833             var anim = Roo.lib.Anim[animType](
6834                 this.dom, args,
6835                 (opt.duration || defaultDur) || .35,
6836                 (opt.easing || defaultEase) || 'easeOut',
6837                 function(){
6838                     Roo.callback(cb, this);
6839                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6840                 },
6841                 this
6842             );
6843             opt.anim = anim;
6844             return anim;
6845         },
6846
6847         // private legacy anim prep
6848         preanim : function(a, i){
6849             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6850         },
6851
6852         /**
6853          * Removes worthless text nodes
6854          * @param {Boolean} forceReclean (optional) By default the element
6855          * keeps track if it has been cleaned already so
6856          * you can call this over and over. However, if you update the element and
6857          * need to force a reclean, you can pass true.
6858          */
6859         clean : function(forceReclean){
6860             if(this.isCleaned && forceReclean !== true){
6861                 return this;
6862             }
6863             var ns = /\S/;
6864             var d = this.dom, n = d.firstChild, ni = -1;
6865             while(n){
6866                 var nx = n.nextSibling;
6867                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6868                     d.removeChild(n);
6869                 }else{
6870                     n.nodeIndex = ++ni;
6871                 }
6872                 n = nx;
6873             }
6874             this.isCleaned = true;
6875             return this;
6876         },
6877
6878         // private
6879         calcOffsetsTo : function(el){
6880             el = Roo.get(el);
6881             var d = el.dom;
6882             var restorePos = false;
6883             if(el.getStyle('position') == 'static'){
6884                 el.position('relative');
6885                 restorePos = true;
6886             }
6887             var x = 0, y =0;
6888             var op = this.dom;
6889             while(op && op != d && op.tagName != 'HTML'){
6890                 x+= op.offsetLeft;
6891                 y+= op.offsetTop;
6892                 op = op.offsetParent;
6893             }
6894             if(restorePos){
6895                 el.position('static');
6896             }
6897             return [x, y];
6898         },
6899
6900         /**
6901          * Scrolls this element into view within the passed container.
6902          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6903          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6904          * @return {Roo.Element} this
6905          */
6906         scrollIntoView : function(container, hscroll){
6907             var c = Roo.getDom(container) || document.body;
6908             var el = this.dom;
6909
6910             var o = this.calcOffsetsTo(c),
6911                 l = o[0],
6912                 t = o[1],
6913                 b = t+el.offsetHeight,
6914                 r = l+el.offsetWidth;
6915
6916             var ch = c.clientHeight;
6917             var ct = parseInt(c.scrollTop, 10);
6918             var cl = parseInt(c.scrollLeft, 10);
6919             var cb = ct + ch;
6920             var cr = cl + c.clientWidth;
6921
6922             if(t < ct){
6923                 c.scrollTop = t;
6924             }else if(b > cb){
6925                 c.scrollTop = b-ch;
6926             }
6927
6928             if(hscroll !== false){
6929                 if(l < cl){
6930                     c.scrollLeft = l;
6931                 }else if(r > cr){
6932                     c.scrollLeft = r-c.clientWidth;
6933                 }
6934             }
6935             return this;
6936         },
6937
6938         // private
6939         scrollChildIntoView : function(child, hscroll){
6940             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6941         },
6942
6943         /**
6944          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6945          * the new height may not be available immediately.
6946          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6947          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6948          * @param {Function} onComplete (optional) Function to call when animation completes
6949          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6950          * @return {Roo.Element} this
6951          */
6952         autoHeight : function(animate, duration, onComplete, easing){
6953             var oldHeight = this.getHeight();
6954             this.clip();
6955             this.setHeight(1); // force clipping
6956             setTimeout(function(){
6957                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6958                 if(!animate){
6959                     this.setHeight(height);
6960                     this.unclip();
6961                     if(typeof onComplete == "function"){
6962                         onComplete();
6963                     }
6964                 }else{
6965                     this.setHeight(oldHeight); // restore original height
6966                     this.setHeight(height, animate, duration, function(){
6967                         this.unclip();
6968                         if(typeof onComplete == "function") onComplete();
6969                     }.createDelegate(this), easing);
6970                 }
6971             }.createDelegate(this), 0);
6972             return this;
6973         },
6974
6975         /**
6976          * Returns true if this element is an ancestor of the passed element
6977          * @param {HTMLElement/String} el The element to check
6978          * @return {Boolean} True if this element is an ancestor of el, else false
6979          */
6980         contains : function(el){
6981             if(!el){return false;}
6982             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6983         },
6984
6985         /**
6986          * Checks whether the element is currently visible using both visibility and display properties.
6987          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6988          * @return {Boolean} True if the element is currently visible, else false
6989          */
6990         isVisible : function(deep) {
6991             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6992             if(deep !== true || !vis){
6993                 return vis;
6994             }
6995             var p = this.dom.parentNode;
6996             while(p && p.tagName.toLowerCase() != "body"){
6997                 if(!Roo.fly(p, '_isVisible').isVisible()){
6998                     return false;
6999                 }
7000                 p = p.parentNode;
7001             }
7002             return true;
7003         },
7004
7005         /**
7006          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7007          * @param {String} selector The CSS selector
7008          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7009          * @return {CompositeElement/CompositeElementLite} The composite element
7010          */
7011         select : function(selector, unique){
7012             return El.select(selector, unique, this.dom);
7013         },
7014
7015         /**
7016          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7017          * @param {String} selector The CSS selector
7018          * @return {Array} An array of the matched nodes
7019          */
7020         query : function(selector, unique){
7021             return Roo.DomQuery.select(selector, this.dom);
7022         },
7023
7024         /**
7025          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7026          * @param {String} selector The CSS selector
7027          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7028          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7029          */
7030         child : function(selector, returnDom){
7031             var n = Roo.DomQuery.selectNode(selector, this.dom);
7032             return returnDom ? n : Roo.get(n);
7033         },
7034
7035         /**
7036          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7037          * @param {String} selector The CSS selector
7038          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7039          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7040          */
7041         down : function(selector, returnDom){
7042             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7043             return returnDom ? n : Roo.get(n);
7044         },
7045
7046         /**
7047          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7048          * @param {String} group The group the DD object is member of
7049          * @param {Object} config The DD config object
7050          * @param {Object} overrides An object containing methods to override/implement on the DD object
7051          * @return {Roo.dd.DD} The DD object
7052          */
7053         initDD : function(group, config, overrides){
7054             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7055             return Roo.apply(dd, overrides);
7056         },
7057
7058         /**
7059          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7060          * @param {String} group The group the DDProxy object is member of
7061          * @param {Object} config The DDProxy config object
7062          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7063          * @return {Roo.dd.DDProxy} The DDProxy object
7064          */
7065         initDDProxy : function(group, config, overrides){
7066             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7067             return Roo.apply(dd, overrides);
7068         },
7069
7070         /**
7071          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7072          * @param {String} group The group the DDTarget object is member of
7073          * @param {Object} config The DDTarget config object
7074          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7075          * @return {Roo.dd.DDTarget} The DDTarget object
7076          */
7077         initDDTarget : function(group, config, overrides){
7078             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7079             return Roo.apply(dd, overrides);
7080         },
7081
7082         /**
7083          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7084          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7085          * @param {Boolean} visible Whether the element is visible
7086          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7087          * @return {Roo.Element} this
7088          */
7089          setVisible : function(visible, animate){
7090             if(!animate || !A){
7091                 if(this.visibilityMode == El.DISPLAY){
7092                     this.setDisplayed(visible);
7093                 }else{
7094                     this.fixDisplay();
7095                     this.dom.style.visibility = visible ? "visible" : "hidden";
7096                 }
7097             }else{
7098                 // closure for composites
7099                 var dom = this.dom;
7100                 var visMode = this.visibilityMode;
7101                 if(visible){
7102                     this.setOpacity(.01);
7103                     this.setVisible(true);
7104                 }
7105                 this.anim({opacity: { to: (visible?1:0) }},
7106                       this.preanim(arguments, 1),
7107                       null, .35, 'easeIn', function(){
7108                          if(!visible){
7109                              if(visMode == El.DISPLAY){
7110                                  dom.style.display = "none";
7111                              }else{
7112                                  dom.style.visibility = "hidden";
7113                              }
7114                              Roo.get(dom).setOpacity(1);
7115                          }
7116                      });
7117             }
7118             return this;
7119         },
7120
7121         /**
7122          * Returns true if display is not "none"
7123          * @return {Boolean}
7124          */
7125         isDisplayed : function() {
7126             return this.getStyle("display") != "none";
7127         },
7128
7129         /**
7130          * Toggles the element's visibility or display, depending on visibility mode.
7131          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7132          * @return {Roo.Element} this
7133          */
7134         toggle : function(animate){
7135             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7136             return this;
7137         },
7138
7139         /**
7140          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7141          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7142          * @return {Roo.Element} this
7143          */
7144         setDisplayed : function(value) {
7145             if(typeof value == "boolean"){
7146                value = value ? this.originalDisplay : "none";
7147             }
7148             this.setStyle("display", value);
7149             return this;
7150         },
7151
7152         /**
7153          * Tries to focus the element. Any exceptions are caught and ignored.
7154          * @return {Roo.Element} this
7155          */
7156         focus : function() {
7157             try{
7158                 this.dom.focus();
7159             }catch(e){}
7160             return this;
7161         },
7162
7163         /**
7164          * Tries to blur the element. Any exceptions are caught and ignored.
7165          * @return {Roo.Element} this
7166          */
7167         blur : function() {
7168             try{
7169                 this.dom.blur();
7170             }catch(e){}
7171             return this;
7172         },
7173
7174         /**
7175          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7176          * @param {String/Array} className The CSS class to add, or an array of classes
7177          * @return {Roo.Element} this
7178          */
7179         addClass : function(className){
7180             if(className instanceof Array){
7181                 for(var i = 0, len = className.length; i < len; i++) {
7182                     this.addClass(className[i]);
7183                 }
7184             }else{
7185                 if(className && !this.hasClass(className)){
7186                     this.dom.className = this.dom.className + " " + className;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         /**
7193          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7194          * @param {String/Array} className The CSS class to add, or an array of classes
7195          * @return {Roo.Element} this
7196          */
7197         radioClass : function(className){
7198             var siblings = this.dom.parentNode.childNodes;
7199             for(var i = 0; i < siblings.length; i++) {
7200                 var s = siblings[i];
7201                 if(s.nodeType == 1){
7202                     Roo.get(s).removeClass(className);
7203                 }
7204             }
7205             this.addClass(className);
7206             return this;
7207         },
7208
7209         /**
7210          * Removes one or more CSS classes from the element.
7211          * @param {String/Array} className The CSS class to remove, or an array of classes
7212          * @return {Roo.Element} this
7213          */
7214         removeClass : function(className){
7215             if(!className || !this.dom.className){
7216                 return this;
7217             }
7218             if(className instanceof Array){
7219                 for(var i = 0, len = className.length; i < len; i++) {
7220                     this.removeClass(className[i]);
7221                 }
7222             }else{
7223                 if(this.hasClass(className)){
7224                     var re = this.classReCache[className];
7225                     if (!re) {
7226                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7227                        this.classReCache[className] = re;
7228                     }
7229                     this.dom.className =
7230                         this.dom.className.replace(re, " ");
7231                 }
7232             }
7233             return this;
7234         },
7235
7236         // private
7237         classReCache: {},
7238
7239         /**
7240          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7241          * @param {String} className The CSS class to toggle
7242          * @return {Roo.Element} this
7243          */
7244         toggleClass : function(className){
7245             if(this.hasClass(className)){
7246                 this.removeClass(className);
7247             }else{
7248                 this.addClass(className);
7249             }
7250             return this;
7251         },
7252
7253         /**
7254          * Checks if the specified CSS class exists on this element's DOM node.
7255          * @param {String} className The CSS class to check for
7256          * @return {Boolean} True if the class exists, else false
7257          */
7258         hasClass : function(className){
7259             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7260         },
7261
7262         /**
7263          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7264          * @param {String} oldClassName The CSS class to replace
7265          * @param {String} newClassName The replacement CSS class
7266          * @return {Roo.Element} this
7267          */
7268         replaceClass : function(oldClassName, newClassName){
7269             this.removeClass(oldClassName);
7270             this.addClass(newClassName);
7271             return this;
7272         },
7273
7274         /**
7275          * Returns an object with properties matching the styles requested.
7276          * For example, el.getStyles('color', 'font-size', 'width') might return
7277          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7278          * @param {String} style1 A style name
7279          * @param {String} style2 A style name
7280          * @param {String} etc.
7281          * @return {Object} The style object
7282          */
7283         getStyles : function(){
7284             var a = arguments, len = a.length, r = {};
7285             for(var i = 0; i < len; i++){
7286                 r[a[i]] = this.getStyle(a[i]);
7287             }
7288             return r;
7289         },
7290
7291         /**
7292          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7293          * @param {String} property The style property whose value is returned.
7294          * @return {String} The current value of the style property for this element.
7295          */
7296         getStyle : function(){
7297             return view && view.getComputedStyle ?
7298                 function(prop){
7299                     var el = this.dom, v, cs, camel;
7300                     if(prop == 'float'){
7301                         prop = "cssFloat";
7302                     }
7303                     if(el.style && (v = el.style[prop])){
7304                         return v;
7305                     }
7306                     if(cs = view.getComputedStyle(el, "")){
7307                         if(!(camel = propCache[prop])){
7308                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7309                         }
7310                         return cs[camel];
7311                     }
7312                     return null;
7313                 } :
7314                 function(prop){
7315                     var el = this.dom, v, cs, camel;
7316                     if(prop == 'opacity'){
7317                         if(typeof el.style.filter == 'string'){
7318                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7319                             if(m){
7320                                 var fv = parseFloat(m[1]);
7321                                 if(!isNaN(fv)){
7322                                     return fv ? fv / 100 : 0;
7323                                 }
7324                             }
7325                         }
7326                         return 1;
7327                     }else if(prop == 'float'){
7328                         prop = "styleFloat";
7329                     }
7330                     if(!(camel = propCache[prop])){
7331                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7332                     }
7333                     if(v = el.style[camel]){
7334                         return v;
7335                     }
7336                     if(cs = el.currentStyle){
7337                         return cs[camel];
7338                     }
7339                     return null;
7340                 };
7341         }(),
7342
7343         /**
7344          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7345          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7346          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7347          * @return {Roo.Element} this
7348          */
7349         setStyle : function(prop, value){
7350             if(typeof prop == "string"){
7351                 var camel;
7352                 if(!(camel = propCache[prop])){
7353                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7354                 }
7355                 if(camel == 'opacity') {
7356                     this.setOpacity(value);
7357                 }else{
7358                     this.dom.style[camel] = value;
7359                 }
7360             }else{
7361                 for(var style in prop){
7362                     if(typeof prop[style] != "function"){
7363                        this.setStyle(style, prop[style]);
7364                     }
7365                 }
7366             }
7367             return this;
7368         },
7369
7370         /**
7371          * More flexible version of {@link #setStyle} for setting style properties.
7372          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7373          * a function which returns such a specification.
7374          * @return {Roo.Element} this
7375          */
7376         applyStyles : function(style){
7377             Roo.DomHelper.applyStyles(this.dom, style);
7378             return this;
7379         },
7380
7381         /**
7382           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7383           * @return {Number} The X position of the element
7384           */
7385         getX : function(){
7386             return D.getX(this.dom);
7387         },
7388
7389         /**
7390           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7391           * @return {Number} The Y position of the element
7392           */
7393         getY : function(){
7394             return D.getY(this.dom);
7395         },
7396
7397         /**
7398           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7399           * @return {Array} The XY position of the element
7400           */
7401         getXY : function(){
7402             return D.getXY(this.dom);
7403         },
7404
7405         /**
7406          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7407          * @param {Number} The X position of the element
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411         setX : function(x, animate){
7412             if(!animate || !A){
7413                 D.setX(this.dom, x);
7414             }else{
7415                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7416             }
7417             return this;
7418         },
7419
7420         /**
7421          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7422          * @param {Number} The Y position of the element
7423          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7424          * @return {Roo.Element} this
7425          */
7426         setY : function(y, animate){
7427             if(!animate || !A){
7428                 D.setY(this.dom, y);
7429             }else{
7430                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7437          * @param {String} left The left CSS property value
7438          * @return {Roo.Element} this
7439          */
7440         setLeft : function(left){
7441             this.setStyle("left", this.addUnits(left));
7442             return this;
7443         },
7444
7445         /**
7446          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7447          * @param {String} top The top CSS property value
7448          * @return {Roo.Element} this
7449          */
7450         setTop : function(top){
7451             this.setStyle("top", this.addUnits(top));
7452             return this;
7453         },
7454
7455         /**
7456          * Sets the element's CSS right style.
7457          * @param {String} right The right CSS property value
7458          * @return {Roo.Element} this
7459          */
7460         setRight : function(right){
7461             this.setStyle("right", this.addUnits(right));
7462             return this;
7463         },
7464
7465         /**
7466          * Sets the element's CSS bottom style.
7467          * @param {String} bottom The bottom CSS property value
7468          * @return {Roo.Element} this
7469          */
7470         setBottom : function(bottom){
7471             this.setStyle("bottom", this.addUnits(bottom));
7472             return this;
7473         },
7474
7475         /**
7476          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7477          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7478          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7479          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7480          * @return {Roo.Element} this
7481          */
7482         setXY : function(pos, animate){
7483             if(!animate || !A){
7484                 D.setXY(this.dom, pos);
7485             }else{
7486                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7487             }
7488             return this;
7489         },
7490
7491         /**
7492          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7493          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7494          * @param {Number} x X value for new position (coordinates are page-based)
7495          * @param {Number} y Y value for new position (coordinates are page-based)
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Roo.Element} this
7498          */
7499         setLocation : function(x, y, animate){
7500             this.setXY([x, y], this.preanim(arguments, 2));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Number} x X value for new position (coordinates are page-based)
7508          * @param {Number} y Y value for new position (coordinates are page-based)
7509          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7510          * @return {Roo.Element} this
7511          */
7512         moveTo : function(x, y, animate){
7513             this.setXY([x, y], this.preanim(arguments, 2));
7514             return this;
7515         },
7516
7517         /**
7518          * Returns the region of the given element.
7519          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7520          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7521          */
7522         getRegion : function(){
7523             return D.getRegion(this.dom);
7524         },
7525
7526         /**
7527          * Returns the offset height of the element
7528          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7529          * @return {Number} The element's height
7530          */
7531         getHeight : function(contentHeight){
7532             var h = this.dom.offsetHeight || 0;
7533             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7534         },
7535
7536         /**
7537          * Returns the offset width of the element
7538          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7539          * @return {Number} The element's width
7540          */
7541         getWidth : function(contentWidth){
7542             var w = this.dom.offsetWidth || 0;
7543             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7544         },
7545
7546         /**
7547          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7548          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7549          * if a height has not been set using CSS.
7550          * @return {Number}
7551          */
7552         getComputedHeight : function(){
7553             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7554             if(!h){
7555                 h = parseInt(this.getStyle('height'), 10) || 0;
7556                 if(!this.isBorderBox()){
7557                     h += this.getFrameWidth('tb');
7558                 }
7559             }
7560             return h;
7561         },
7562
7563         /**
7564          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7565          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7566          * if a width has not been set using CSS.
7567          * @return {Number}
7568          */
7569         getComputedWidth : function(){
7570             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7571             if(!w){
7572                 w = parseInt(this.getStyle('width'), 10) || 0;
7573                 if(!this.isBorderBox()){
7574                     w += this.getFrameWidth('lr');
7575                 }
7576             }
7577             return w;
7578         },
7579
7580         /**
7581          * Returns the size of the element.
7582          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7583          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7584          */
7585         getSize : function(contentSize){
7586             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7587         },
7588
7589         /**
7590          * Returns the width and height of the viewport.
7591          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7592          */
7593         getViewSize : function(){
7594             var d = this.dom, doc = document, aw = 0, ah = 0;
7595             if(d == doc || d == doc.body){
7596                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7597             }else{
7598                 return {
7599                     width : d.clientWidth,
7600                     height: d.clientHeight
7601                 };
7602             }
7603         },
7604
7605         /**
7606          * Returns the value of the "value" attribute
7607          * @param {Boolean} asNumber true to parse the value as a number
7608          * @return {String/Number}
7609          */
7610         getValue : function(asNumber){
7611             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7612         },
7613
7614         // private
7615         adjustWidth : function(width){
7616             if(typeof width == "number"){
7617                 if(this.autoBoxAdjust && !this.isBorderBox()){
7618                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7619                 }
7620                 if(width < 0){
7621                     width = 0;
7622                 }
7623             }
7624             return width;
7625         },
7626
7627         // private
7628         adjustHeight : function(height){
7629             if(typeof height == "number"){
7630                if(this.autoBoxAdjust && !this.isBorderBox()){
7631                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7632                }
7633                if(height < 0){
7634                    height = 0;
7635                }
7636             }
7637             return height;
7638         },
7639
7640         /**
7641          * Set the width of the element
7642          * @param {Number} width The new width
7643          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7644          * @return {Roo.Element} this
7645          */
7646         setWidth : function(width, animate){
7647             width = this.adjustWidth(width);
7648             if(!animate || !A){
7649                 this.dom.style.width = this.addUnits(width);
7650             }else{
7651                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7652             }
7653             return this;
7654         },
7655
7656         /**
7657          * Set the height of the element
7658          * @param {Number} height The new height
7659          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662          setHeight : function(height, animate){
7663             height = this.adjustHeight(height);
7664             if(!animate || !A){
7665                 this.dom.style.height = this.addUnits(height);
7666             }else{
7667                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7668             }
7669             return this;
7670         },
7671
7672         /**
7673          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7674          * @param {Number} width The new width
7675          * @param {Number} height The new height
7676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7677          * @return {Roo.Element} this
7678          */
7679          setSize : function(width, height, animate){
7680             if(typeof width == "object"){ // in case of object from getSize()
7681                 height = width.height; width = width.width;
7682             }
7683             width = this.adjustWidth(width); height = this.adjustHeight(height);
7684             if(!animate || !A){
7685                 this.dom.style.width = this.addUnits(width);
7686                 this.dom.style.height = this.addUnits(height);
7687             }else{
7688                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7689             }
7690             return this;
7691         },
7692
7693         /**
7694          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7695          * @param {Number} x X value for new position (coordinates are page-based)
7696          * @param {Number} y Y value for new position (coordinates are page-based)
7697          * @param {Number} width The new width
7698          * @param {Number} height The new height
7699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7700          * @return {Roo.Element} this
7701          */
7702         setBounds : function(x, y, width, height, animate){
7703             if(!animate || !A){
7704                 this.setSize(width, height);
7705                 this.setLocation(x, y);
7706             }else{
7707                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7708                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7709                               this.preanim(arguments, 4), 'motion');
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7716          * @param {Roo.lib.Region} region The region to fill
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setRegion : function(region, animate){
7721             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7722             return this;
7723         },
7724
7725         /**
7726          * Appends an event handler
7727          *
7728          * @param {String}   eventName     The type of event to append
7729          * @param {Function} fn        The method the event invokes
7730          * @param {Object} scope       (optional) The scope (this object) of the fn
7731          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7732          */
7733         addListener : function(eventName, fn, scope, options){
7734             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7735         },
7736
7737         /**
7738          * Removes an event handler from this element
7739          * @param {String} eventName the type of event to remove
7740          * @param {Function} fn the method the event invokes
7741          * @return {Roo.Element} this
7742          */
7743         removeListener : function(eventName, fn){
7744             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7745             return this;
7746         },
7747
7748         /**
7749          * Removes all previous added listeners from this element
7750          * @return {Roo.Element} this
7751          */
7752         removeAllListeners : function(){
7753             E.purgeElement(this.dom);
7754             return this;
7755         },
7756
7757         relayEvent : function(eventName, observable){
7758             this.on(eventName, function(e){
7759                 observable.fireEvent(eventName, e);
7760             });
7761         },
7762
7763         /**
7764          * Set the opacity of the element
7765          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setOpacity : function(opacity, animate){
7770             if(!animate || !A){
7771                 var s = this.dom.style;
7772                 if(Roo.isIE){
7773                     s.zoom = 1;
7774                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7775                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7776                 }else{
7777                     s.opacity = opacity;
7778                 }
7779             }else{
7780                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7781             }
7782             return this;
7783         },
7784
7785         /**
7786          * Gets the left X coordinate
7787          * @param {Boolean} local True to get the local css position instead of page coordinate
7788          * @return {Number}
7789          */
7790         getLeft : function(local){
7791             if(!local){
7792                 return this.getX();
7793             }else{
7794                 return parseInt(this.getStyle("left"), 10) || 0;
7795             }
7796         },
7797
7798         /**
7799          * Gets the right X coordinate of the element (element X position + element width)
7800          * @param {Boolean} local True to get the local css position instead of page coordinate
7801          * @return {Number}
7802          */
7803         getRight : function(local){
7804             if(!local){
7805                 return this.getX() + this.getWidth();
7806             }else{
7807                 return (this.getLeft(true) + this.getWidth()) || 0;
7808             }
7809         },
7810
7811         /**
7812          * Gets the top Y coordinate
7813          * @param {Boolean} local True to get the local css position instead of page coordinate
7814          * @return {Number}
7815          */
7816         getTop : function(local) {
7817             if(!local){
7818                 return this.getY();
7819             }else{
7820                 return parseInt(this.getStyle("top"), 10) || 0;
7821             }
7822         },
7823
7824         /**
7825          * Gets the bottom Y coordinate of the element (element Y position + element height)
7826          * @param {Boolean} local True to get the local css position instead of page coordinate
7827          * @return {Number}
7828          */
7829         getBottom : function(local){
7830             if(!local){
7831                 return this.getY() + this.getHeight();
7832             }else{
7833                 return (this.getTop(true) + this.getHeight()) || 0;
7834             }
7835         },
7836
7837         /**
7838         * Initializes positioning on this element. If a desired position is not passed, it will make the
7839         * the element positioned relative IF it is not already positioned.
7840         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7841         * @param {Number} zIndex (optional) The zIndex to apply
7842         * @param {Number} x (optional) Set the page X position
7843         * @param {Number} y (optional) Set the page Y position
7844         */
7845         position : function(pos, zIndex, x, y){
7846             if(!pos){
7847                if(this.getStyle('position') == 'static'){
7848                    this.setStyle('position', 'relative');
7849                }
7850             }else{
7851                 this.setStyle("position", pos);
7852             }
7853             if(zIndex){
7854                 this.setStyle("z-index", zIndex);
7855             }
7856             if(x !== undefined && y !== undefined){
7857                 this.setXY([x, y]);
7858             }else if(x !== undefined){
7859                 this.setX(x);
7860             }else if(y !== undefined){
7861                 this.setY(y);
7862             }
7863         },
7864
7865         /**
7866         * Clear positioning back to the default when the document was loaded
7867         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7868         * @return {Roo.Element} this
7869          */
7870         clearPositioning : function(value){
7871             value = value ||'';
7872             this.setStyle({
7873                 "left": value,
7874                 "right": value,
7875                 "top": value,
7876                 "bottom": value,
7877                 "z-index": "",
7878                 "position" : "static"
7879             });
7880             return this;
7881         },
7882
7883         /**
7884         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7885         * snapshot before performing an update and then restoring the element.
7886         * @return {Object}
7887         */
7888         getPositioning : function(){
7889             var l = this.getStyle("left");
7890             var t = this.getStyle("top");
7891             return {
7892                 "position" : this.getStyle("position"),
7893                 "left" : l,
7894                 "right" : l ? "" : this.getStyle("right"),
7895                 "top" : t,
7896                 "bottom" : t ? "" : this.getStyle("bottom"),
7897                 "z-index" : this.getStyle("z-index")
7898             };
7899         },
7900
7901         /**
7902          * Gets the width of the border(s) for the specified side(s)
7903          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7904          * passing lr would get the border (l)eft width + the border (r)ight width.
7905          * @return {Number} The width of the sides passed added together
7906          */
7907         getBorderWidth : function(side){
7908             return this.addStyles(side, El.borders);
7909         },
7910
7911         /**
7912          * Gets the width of the padding(s) for the specified side(s)
7913          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7914          * passing lr would get the padding (l)eft + the padding (r)ight.
7915          * @return {Number} The padding of the sides passed added together
7916          */
7917         getPadding : function(side){
7918             return this.addStyles(side, El.paddings);
7919         },
7920
7921         /**
7922         * Set positioning with an object returned by getPositioning().
7923         * @param {Object} posCfg
7924         * @return {Roo.Element} this
7925          */
7926         setPositioning : function(pc){
7927             this.applyStyles(pc);
7928             if(pc.right == "auto"){
7929                 this.dom.style.right = "";
7930             }
7931             if(pc.bottom == "auto"){
7932                 this.dom.style.bottom = "";
7933             }
7934             return this;
7935         },
7936
7937         // private
7938         fixDisplay : function(){
7939             if(this.getStyle("display") == "none"){
7940                 this.setStyle("visibility", "hidden");
7941                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7942                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7943                     this.setStyle("display", "block");
7944                 }
7945             }
7946         },
7947
7948         /**
7949          * Quick set left and top adding default units
7950          * @param {String} left The left CSS property value
7951          * @param {String} top The top CSS property value
7952          * @return {Roo.Element} this
7953          */
7954          setLeftTop : function(left, top){
7955             this.dom.style.left = this.addUnits(left);
7956             this.dom.style.top = this.addUnits(top);
7957             return this;
7958         },
7959
7960         /**
7961          * Move this element relative to its current position.
7962          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7963          * @param {Number} distance How far to move the element in pixels
7964          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7965          * @return {Roo.Element} this
7966          */
7967          move : function(direction, distance, animate){
7968             var xy = this.getXY();
7969             direction = direction.toLowerCase();
7970             switch(direction){
7971                 case "l":
7972                 case "left":
7973                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7974                     break;
7975                case "r":
7976                case "right":
7977                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7978                     break;
7979                case "t":
7980                case "top":
7981                case "up":
7982                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7983                     break;
7984                case "b":
7985                case "bottom":
7986                case "down":
7987                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7988                     break;
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7995          * @return {Roo.Element} this
7996          */
7997         clip : function(){
7998             if(!this.isClipped){
7999                this.isClipped = true;
8000                this.originalClip = {
8001                    "o": this.getStyle("overflow"),
8002                    "x": this.getStyle("overflow-x"),
8003                    "y": this.getStyle("overflow-y")
8004                };
8005                this.setStyle("overflow", "hidden");
8006                this.setStyle("overflow-x", "hidden");
8007                this.setStyle("overflow-y", "hidden");
8008             }
8009             return this;
8010         },
8011
8012         /**
8013          *  Return clipping (overflow) to original clipping before clip() was called
8014          * @return {Roo.Element} this
8015          */
8016         unclip : function(){
8017             if(this.isClipped){
8018                 this.isClipped = false;
8019                 var o = this.originalClip;
8020                 if(o.o){this.setStyle("overflow", o.o);}
8021                 if(o.x){this.setStyle("overflow-x", o.x);}
8022                 if(o.y){this.setStyle("overflow-y", o.y);}
8023             }
8024             return this;
8025         },
8026
8027
8028         /**
8029          * Gets the x,y coordinates specified by the anchor position on the element.
8030          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8031          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8032          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8033          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8034          * @return {Array} [x, y] An array containing the element's x and y coordinates
8035          */
8036         getAnchorXY : function(anchor, local, s){
8037             //Passing a different size is useful for pre-calculating anchors,
8038             //especially for anchored animations that change the el size.
8039
8040             var w, h, vp = false;
8041             if(!s){
8042                 var d = this.dom;
8043                 if(d == document.body || d == document){
8044                     vp = true;
8045                     w = D.getViewWidth(); h = D.getViewHeight();
8046                 }else{
8047                     w = this.getWidth(); h = this.getHeight();
8048                 }
8049             }else{
8050                 w = s.width;  h = s.height;
8051             }
8052             var x = 0, y = 0, r = Math.round;
8053             switch((anchor || "tl").toLowerCase()){
8054                 case "c":
8055                     x = r(w*.5);
8056                     y = r(h*.5);
8057                 break;
8058                 case "t":
8059                     x = r(w*.5);
8060                     y = 0;
8061                 break;
8062                 case "l":
8063                     x = 0;
8064                     y = r(h*.5);
8065                 break;
8066                 case "r":
8067                     x = w;
8068                     y = r(h*.5);
8069                 break;
8070                 case "b":
8071                     x = r(w*.5);
8072                     y = h;
8073                 break;
8074                 case "tl":
8075                     x = 0;
8076                     y = 0;
8077                 break;
8078                 case "bl":
8079                     x = 0;
8080                     y = h;
8081                 break;
8082                 case "br":
8083                     x = w;
8084                     y = h;
8085                 break;
8086                 case "tr":
8087                     x = w;
8088                     y = 0;
8089                 break;
8090             }
8091             if(local === true){
8092                 return [x, y];
8093             }
8094             if(vp){
8095                 var sc = this.getScroll();
8096                 return [x + sc.left, y + sc.top];
8097             }
8098             //Add the element's offset xy
8099             var o = this.getXY();
8100             return [x+o[0], y+o[1]];
8101         },
8102
8103         /**
8104          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8105          * supported position values.
8106          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8107          * @param {String} position The position to align to.
8108          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8109          * @return {Array} [x, y]
8110          */
8111         getAlignToXY : function(el, p, o){
8112             el = Roo.get(el);
8113             var d = this.dom;
8114             if(!el.dom){
8115                 throw "Element.alignTo with an element that doesn't exist";
8116             }
8117             var c = false; //constrain to viewport
8118             var p1 = "", p2 = "";
8119             o = o || [0,0];
8120
8121             if(!p){
8122                 p = "tl-bl";
8123             }else if(p == "?"){
8124                 p = "tl-bl?";
8125             }else if(p.indexOf("-") == -1){
8126                 p = "tl-" + p;
8127             }
8128             p = p.toLowerCase();
8129             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8130             if(!m){
8131                throw "Element.alignTo with an invalid alignment " + p;
8132             }
8133             p1 = m[1]; p2 = m[2]; c = !!m[3];
8134
8135             //Subtract the aligned el's internal xy from the target's offset xy
8136             //plus custom offset to get the aligned el's new offset xy
8137             var a1 = this.getAnchorXY(p1, true);
8138             var a2 = el.getAnchorXY(p2, false);
8139             var x = a2[0] - a1[0] + o[0];
8140             var y = a2[1] - a1[1] + o[1];
8141             if(c){
8142                 //constrain the aligned el to viewport if necessary
8143                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8144                 // 5px of margin for ie
8145                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8146
8147                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8148                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8149                 //otherwise swap the aligned el to the opposite border of the target.
8150                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8151                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8152                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8153                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8154
8155                var doc = document;
8156                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8157                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8158
8159                if((x+w) > dw + scrollX){
8160                     x = swapX ? r.left-w : dw+scrollX-w;
8161                 }
8162                if(x < scrollX){
8163                    x = swapX ? r.right : scrollX;
8164                }
8165                if((y+h) > dh + scrollY){
8166                     y = swapY ? r.top-h : dh+scrollY-h;
8167                 }
8168                if (y < scrollY){
8169                    y = swapY ? r.bottom : scrollY;
8170                }
8171             }
8172             return [x,y];
8173         },
8174
8175         // private
8176         getConstrainToXY : function(){
8177             var os = {top:0, left:0, bottom:0, right: 0};
8178
8179             return function(el, local, offsets, proposedXY){
8180                 el = Roo.get(el);
8181                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8182
8183                 var vw, vh, vx = 0, vy = 0;
8184                 if(el.dom == document.body || el.dom == document){
8185                     vw = Roo.lib.Dom.getViewWidth();
8186                     vh = Roo.lib.Dom.getViewHeight();
8187                 }else{
8188                     vw = el.dom.clientWidth;
8189                     vh = el.dom.clientHeight;
8190                     if(!local){
8191                         var vxy = el.getXY();
8192                         vx = vxy[0];
8193                         vy = vxy[1];
8194                     }
8195                 }
8196
8197                 var s = el.getScroll();
8198
8199                 vx += offsets.left + s.left;
8200                 vy += offsets.top + s.top;
8201
8202                 vw -= offsets.right;
8203                 vh -= offsets.bottom;
8204
8205                 var vr = vx+vw;
8206                 var vb = vy+vh;
8207
8208                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8209                 var x = xy[0], y = xy[1];
8210                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8211
8212                 // only move it if it needs it
8213                 var moved = false;
8214
8215                 // first validate right/bottom
8216                 if((x + w) > vr){
8217                     x = vr - w;
8218                     moved = true;
8219                 }
8220                 if((y + h) > vb){
8221                     y = vb - h;
8222                     moved = true;
8223                 }
8224                 // then make sure top/left isn't negative
8225                 if(x < vx){
8226                     x = vx;
8227                     moved = true;
8228                 }
8229                 if(y < vy){
8230                     y = vy;
8231                     moved = true;
8232                 }
8233                 return moved ? [x, y] : false;
8234             };
8235         }(),
8236
8237         // private
8238         adjustForConstraints : function(xy, parent, offsets){
8239             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8240         },
8241
8242         /**
8243          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8244          * document it aligns it to the viewport.
8245          * The position parameter is optional, and can be specified in any one of the following formats:
8246          * <ul>
8247          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8248          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8249          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8250          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8251          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8252          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8253          * </ul>
8254          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8255          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8256          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8257          * that specified in order to enforce the viewport constraints.
8258          * Following are all of the supported anchor positions:
8259     <pre>
8260     Value  Description
8261     -----  -----------------------------
8262     tl     The top left corner (default)
8263     t      The center of the top edge
8264     tr     The top right corner
8265     l      The center of the left edge
8266     c      In the center of the element
8267     r      The center of the right edge
8268     bl     The bottom left corner
8269     b      The center of the bottom edge
8270     br     The bottom right corner
8271     </pre>
8272     Example Usage:
8273     <pre><code>
8274     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8275     el.alignTo("other-el");
8276
8277     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8278     el.alignTo("other-el", "tr?");
8279
8280     // align the bottom right corner of el with the center left edge of other-el
8281     el.alignTo("other-el", "br-l?");
8282
8283     // align the center of el with the bottom left corner of other-el and
8284     // adjust the x position by -6 pixels (and the y position by 0)
8285     el.alignTo("other-el", "c-bl", [-6, 0]);
8286     </code></pre>
8287          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8288          * @param {String} position The position to align to.
8289          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8290          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8291          * @return {Roo.Element} this
8292          */
8293         alignTo : function(element, position, offsets, animate){
8294             var xy = this.getAlignToXY(element, position, offsets);
8295             this.setXY(xy, this.preanim(arguments, 3));
8296             return this;
8297         },
8298
8299         /**
8300          * Anchors an element to another element and realigns it when the window is resized.
8301          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8302          * @param {String} position The position to align to.
8303          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8304          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8305          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8306          * is a number, it is used as the buffer delay (defaults to 50ms).
8307          * @param {Function} callback The function to call after the animation finishes
8308          * @return {Roo.Element} this
8309          */
8310         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8311             var action = function(){
8312                 this.alignTo(el, alignment, offsets, animate);
8313                 Roo.callback(callback, this);
8314             };
8315             Roo.EventManager.onWindowResize(action, this);
8316             var tm = typeof monitorScroll;
8317             if(tm != 'undefined'){
8318                 Roo.EventManager.on(window, 'scroll', action, this,
8319                     {buffer: tm == 'number' ? monitorScroll : 50});
8320             }
8321             action.call(this); // align immediately
8322             return this;
8323         },
8324         /**
8325          * Clears any opacity settings from this element. Required in some cases for IE.
8326          * @return {Roo.Element} this
8327          */
8328         clearOpacity : function(){
8329             if (window.ActiveXObject) {
8330                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8331                     this.dom.style.filter = "";
8332                 }
8333             } else {
8334                 this.dom.style.opacity = "";
8335                 this.dom.style["-moz-opacity"] = "";
8336                 this.dom.style["-khtml-opacity"] = "";
8337             }
8338             return this;
8339         },
8340
8341         /**
8342          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8343          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8344          * @return {Roo.Element} this
8345          */
8346         hide : function(animate){
8347             this.setVisible(false, this.preanim(arguments, 0));
8348             return this;
8349         },
8350
8351         /**
8352         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8353         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8354          * @return {Roo.Element} this
8355          */
8356         show : function(animate){
8357             this.setVisible(true, this.preanim(arguments, 0));
8358             return this;
8359         },
8360
8361         /**
8362          * @private Test if size has a unit, otherwise appends the default
8363          */
8364         addUnits : function(size){
8365             return Roo.Element.addUnits(size, this.defaultUnit);
8366         },
8367
8368         /**
8369          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8370          * @return {Roo.Element} this
8371          */
8372         beginMeasure : function(){
8373             var el = this.dom;
8374             if(el.offsetWidth || el.offsetHeight){
8375                 return this; // offsets work already
8376             }
8377             var changed = [];
8378             var p = this.dom, b = document.body; // start with this element
8379             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8380                 var pe = Roo.get(p);
8381                 if(pe.getStyle('display') == 'none'){
8382                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8383                     p.style.visibility = "hidden";
8384                     p.style.display = "block";
8385                 }
8386                 p = p.parentNode;
8387             }
8388             this._measureChanged = changed;
8389             return this;
8390
8391         },
8392
8393         /**
8394          * Restores displays to before beginMeasure was called
8395          * @return {Roo.Element} this
8396          */
8397         endMeasure : function(){
8398             var changed = this._measureChanged;
8399             if(changed){
8400                 for(var i = 0, len = changed.length; i < len; i++) {
8401                     var r = changed[i];
8402                     r.el.style.visibility = r.visibility;
8403                     r.el.style.display = "none";
8404                 }
8405                 this._measureChanged = null;
8406             }
8407             return this;
8408         },
8409
8410         /**
8411         * Update the innerHTML of this element, optionally searching for and processing scripts
8412         * @param {String} html The new HTML
8413         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8414         * @param {Function} callback For async script loading you can be noticed when the update completes
8415         * @return {Roo.Element} this
8416          */
8417         update : function(html, loadScripts, callback){
8418             if(typeof html == "undefined"){
8419                 html = "";
8420             }
8421             if(loadScripts !== true){
8422                 this.dom.innerHTML = html;
8423                 if(typeof callback == "function"){
8424                     callback();
8425                 }
8426                 return this;
8427             }
8428             var id = Roo.id();
8429             var dom = this.dom;
8430
8431             html += '<span id="' + id + '"></span>';
8432
8433             E.onAvailable(id, function(){
8434                 var hd = document.getElementsByTagName("head")[0];
8435                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8436                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8437                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8438
8439                 var match;
8440                 while(match = re.exec(html)){
8441                     var attrs = match[1];
8442                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8443                     if(srcMatch && srcMatch[2]){
8444                        var s = document.createElement("script");
8445                        s.src = srcMatch[2];
8446                        var typeMatch = attrs.match(typeRe);
8447                        if(typeMatch && typeMatch[2]){
8448                            s.type = typeMatch[2];
8449                        }
8450                        hd.appendChild(s);
8451                     }else if(match[2] && match[2].length > 0){
8452                         if(window.execScript) {
8453                            window.execScript(match[2]);
8454                         } else {
8455                             /**
8456                              * eval:var:id
8457                              * eval:var:dom
8458                              * eval:var:html
8459                              * 
8460                              */
8461                            window.eval(match[2]);
8462                         }
8463                     }
8464                 }
8465                 var el = document.getElementById(id);
8466                 if(el){el.parentNode.removeChild(el);}
8467                 if(typeof callback == "function"){
8468                     callback();
8469                 }
8470             });
8471             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8472             return this;
8473         },
8474
8475         /**
8476          * Direct access to the UpdateManager update() method (takes the same parameters).
8477          * @param {String/Function} url The url for this request or a function to call to get the url
8478          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8479          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8480          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8481          * @return {Roo.Element} this
8482          */
8483         load : function(){
8484             var um = this.getUpdateManager();
8485             um.update.apply(um, arguments);
8486             return this;
8487         },
8488
8489         /**
8490         * Gets this element's UpdateManager
8491         * @return {Roo.UpdateManager} The UpdateManager
8492         */
8493         getUpdateManager : function(){
8494             if(!this.updateManager){
8495                 this.updateManager = new Roo.UpdateManager(this);
8496             }
8497             return this.updateManager;
8498         },
8499
8500         /**
8501          * Disables text selection for this element (normalized across browsers)
8502          * @return {Roo.Element} this
8503          */
8504         unselectable : function(){
8505             this.dom.unselectable = "on";
8506             this.swallowEvent("selectstart", true);
8507             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8508             this.addClass("x-unselectable");
8509             return this;
8510         },
8511
8512         /**
8513         * Calculates the x, y to center this element on the screen
8514         * @return {Array} The x, y values [x, y]
8515         */
8516         getCenterXY : function(){
8517             return this.getAlignToXY(document, 'c-c');
8518         },
8519
8520         /**
8521         * Centers the Element in either the viewport, or another Element.
8522         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8523         */
8524         center : function(centerIn){
8525             this.alignTo(centerIn || document, 'c-c');
8526             return this;
8527         },
8528
8529         /**
8530          * Tests various css rules/browsers to determine if this element uses a border box
8531          * @return {Boolean}
8532          */
8533         isBorderBox : function(){
8534             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8535         },
8536
8537         /**
8538          * Return a box {x, y, width, height} that can be used to set another elements
8539          * size/location to match this element.
8540          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8541          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8542          * @return {Object} box An object in the format {x, y, width, height}
8543          */
8544         getBox : function(contentBox, local){
8545             var xy;
8546             if(!local){
8547                 xy = this.getXY();
8548             }else{
8549                 var left = parseInt(this.getStyle("left"), 10) || 0;
8550                 var top = parseInt(this.getStyle("top"), 10) || 0;
8551                 xy = [left, top];
8552             }
8553             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8554             if(!contentBox){
8555                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8556             }else{
8557                 var l = this.getBorderWidth("l")+this.getPadding("l");
8558                 var r = this.getBorderWidth("r")+this.getPadding("r");
8559                 var t = this.getBorderWidth("t")+this.getPadding("t");
8560                 var b = this.getBorderWidth("b")+this.getPadding("b");
8561                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8562             }
8563             bx.right = bx.x + bx.width;
8564             bx.bottom = bx.y + bx.height;
8565             return bx;
8566         },
8567
8568         /**
8569          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8570          for more information about the sides.
8571          * @param {String} sides
8572          * @return {Number}
8573          */
8574         getFrameWidth : function(sides, onlyContentBox){
8575             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8576         },
8577
8578         /**
8579          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8580          * @param {Object} box The box to fill {x, y, width, height}
8581          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8582          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8583          * @return {Roo.Element} this
8584          */
8585         setBox : function(box, adjust, animate){
8586             var w = box.width, h = box.height;
8587             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8588                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8589                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8590             }
8591             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8592             return this;
8593         },
8594
8595         /**
8596          * Forces the browser to repaint this element
8597          * @return {Roo.Element} this
8598          */
8599          repaint : function(){
8600             var dom = this.dom;
8601             this.addClass("x-repaint");
8602             setTimeout(function(){
8603                 Roo.get(dom).removeClass("x-repaint");
8604             }, 1);
8605             return this;
8606         },
8607
8608         /**
8609          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8610          * then it returns the calculated width of the sides (see getPadding)
8611          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8612          * @return {Object/Number}
8613          */
8614         getMargins : function(side){
8615             if(!side){
8616                 return {
8617                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8618                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8619                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8620                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8621                 };
8622             }else{
8623                 return this.addStyles(side, El.margins);
8624              }
8625         },
8626
8627         // private
8628         addStyles : function(sides, styles){
8629             var val = 0, v, w;
8630             for(var i = 0, len = sides.length; i < len; i++){
8631                 v = this.getStyle(styles[sides.charAt(i)]);
8632                 if(v){
8633                      w = parseInt(v, 10);
8634                      if(w){ val += w; }
8635                 }
8636             }
8637             return val;
8638         },
8639
8640         /**
8641          * Creates a proxy element of this element
8642          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8643          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8644          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8645          * @return {Roo.Element} The new proxy element
8646          */
8647         createProxy : function(config, renderTo, matchBox){
8648             if(renderTo){
8649                 renderTo = Roo.getDom(renderTo);
8650             }else{
8651                 renderTo = document.body;
8652             }
8653             config = typeof config == "object" ?
8654                 config : {tag : "div", cls: config};
8655             var proxy = Roo.DomHelper.append(renderTo, config, true);
8656             if(matchBox){
8657                proxy.setBox(this.getBox());
8658             }
8659             return proxy;
8660         },
8661
8662         /**
8663          * Puts a mask over this element to disable user interaction. Requires core.css.
8664          * This method can only be applied to elements which accept child nodes.
8665          * @param {String} msg (optional) A message to display in the mask
8666          * @param {String} msgCls (optional) A css class to apply to the msg element
8667          * @return {Element} The mask  element
8668          */
8669         mask : function(msg, msgCls){
8670             if(this.getStyle("position") == "static"){
8671                 this.setStyle("position", "relative");
8672             }
8673             if(!this._mask){
8674                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8675             }
8676             this.addClass("x-masked");
8677             this._mask.setDisplayed(true);
8678             if(typeof msg == 'string'){
8679                 if(!this._maskMsg){
8680                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8681                 }
8682                 var mm = this._maskMsg;
8683                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8684                 mm.dom.firstChild.innerHTML = msg;
8685                 mm.setDisplayed(true);
8686                 mm.center(this);
8687             }
8688             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8689                 this._mask.setHeight(this.getHeight());
8690             }
8691             return this._mask;
8692         },
8693
8694         /**
8695          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8696          * it is cached for reuse.
8697          */
8698         unmask : function(removeEl){
8699             if(this._mask){
8700                 if(removeEl === true){
8701                     this._mask.remove();
8702                     delete this._mask;
8703                     if(this._maskMsg){
8704                         this._maskMsg.remove();
8705                         delete this._maskMsg;
8706                     }
8707                 }else{
8708                     this._mask.setDisplayed(false);
8709                     if(this._maskMsg){
8710                         this._maskMsg.setDisplayed(false);
8711                     }
8712                 }
8713             }
8714             this.removeClass("x-masked");
8715         },
8716
8717         /**
8718          * Returns true if this element is masked
8719          * @return {Boolean}
8720          */
8721         isMasked : function(){
8722             return this._mask && this._mask.isVisible();
8723         },
8724
8725         /**
8726          * Creates an iframe shim for this element to keep selects and other windowed objects from
8727          * showing through.
8728          * @return {Roo.Element} The new shim element
8729          */
8730         createShim : function(){
8731             var el = document.createElement('iframe');
8732             el.frameBorder = 'no';
8733             el.className = 'roo-shim';
8734             if(Roo.isIE && Roo.isSecure){
8735                 el.src = Roo.SSL_SECURE_URL;
8736             }
8737             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8738             shim.autoBoxAdjust = false;
8739             return shim;
8740         },
8741
8742         /**
8743          * Removes this element from the DOM and deletes it from the cache
8744          */
8745         remove : function(){
8746             if(this.dom.parentNode){
8747                 this.dom.parentNode.removeChild(this.dom);
8748             }
8749             delete El.cache[this.dom.id];
8750         },
8751
8752         /**
8753          * Sets up event handlers to add and remove a css class when the mouse is over this element
8754          * @param {String} className
8755          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8756          * mouseout events for children elements
8757          * @return {Roo.Element} this
8758          */
8759         addClassOnOver : function(className, preventFlicker){
8760             this.on("mouseover", function(){
8761                 Roo.fly(this, '_internal').addClass(className);
8762             }, this.dom);
8763             var removeFn = function(e){
8764                 if(preventFlicker !== true || !e.within(this, true)){
8765                     Roo.fly(this, '_internal').removeClass(className);
8766                 }
8767             };
8768             this.on("mouseout", removeFn, this.dom);
8769             return this;
8770         },
8771
8772         /**
8773          * Sets up event handlers to add and remove a css class when this element has the focus
8774          * @param {String} className
8775          * @return {Roo.Element} this
8776          */
8777         addClassOnFocus : function(className){
8778             this.on("focus", function(){
8779                 Roo.fly(this, '_internal').addClass(className);
8780             }, this.dom);
8781             this.on("blur", function(){
8782                 Roo.fly(this, '_internal').removeClass(className);
8783             }, this.dom);
8784             return this;
8785         },
8786         /**
8787          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8788          * @param {String} className
8789          * @return {Roo.Element} this
8790          */
8791         addClassOnClick : function(className){
8792             var dom = this.dom;
8793             this.on("mousedown", function(){
8794                 Roo.fly(dom, '_internal').addClass(className);
8795                 var d = Roo.get(document);
8796                 var fn = function(){
8797                     Roo.fly(dom, '_internal').removeClass(className);
8798                     d.removeListener("mouseup", fn);
8799                 };
8800                 d.on("mouseup", fn);
8801             });
8802             return this;
8803         },
8804
8805         /**
8806          * Stops the specified event from bubbling and optionally prevents the default action
8807          * @param {String} eventName
8808          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8809          * @return {Roo.Element} this
8810          */
8811         swallowEvent : function(eventName, preventDefault){
8812             var fn = function(e){
8813                 e.stopPropagation();
8814                 if(preventDefault){
8815                     e.preventDefault();
8816                 }
8817             };
8818             if(eventName instanceof Array){
8819                 for(var i = 0, len = eventName.length; i < len; i++){
8820                      this.on(eventName[i], fn);
8821                 }
8822                 return this;
8823             }
8824             this.on(eventName, fn);
8825             return this;
8826         },
8827
8828         /**
8829          * @private
8830          */
8831       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8832
8833         /**
8834          * Sizes this element to its parent element's dimensions performing
8835          * neccessary box adjustments.
8836          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8837          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8838          * @return {Roo.Element} this
8839          */
8840         fitToParent : function(monitorResize, targetParent) {
8841           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8842           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8843           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8844             return;
8845           }
8846           var p = Roo.get(targetParent || this.dom.parentNode);
8847           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8848           if (monitorResize === true) {
8849             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8850             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8851           }
8852           return this;
8853         },
8854
8855         /**
8856          * Gets the next sibling, skipping text nodes
8857          * @return {HTMLElement} The next sibling or null
8858          */
8859         getNextSibling : function(){
8860             var n = this.dom.nextSibling;
8861             while(n && n.nodeType != 1){
8862                 n = n.nextSibling;
8863             }
8864             return n;
8865         },
8866
8867         /**
8868          * Gets the previous sibling, skipping text nodes
8869          * @return {HTMLElement} The previous sibling or null
8870          */
8871         getPrevSibling : function(){
8872             var n = this.dom.previousSibling;
8873             while(n && n.nodeType != 1){
8874                 n = n.previousSibling;
8875             }
8876             return n;
8877         },
8878
8879
8880         /**
8881          * Appends the passed element(s) to this element
8882          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8883          * @return {Roo.Element} this
8884          */
8885         appendChild: function(el){
8886             el = Roo.get(el);
8887             el.appendTo(this);
8888             return this;
8889         },
8890
8891         /**
8892          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8893          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8894          * automatically generated with the specified attributes.
8895          * @param {HTMLElement} insertBefore (optional) a child element of this element
8896          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8897          * @return {Roo.Element} The new child element
8898          */
8899         createChild: function(config, insertBefore, returnDom){
8900             config = config || {tag:'div'};
8901             if(insertBefore){
8902                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8903             }
8904             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8905         },
8906
8907         /**
8908          * Appends this element to the passed element
8909          * @param {String/HTMLElement/Element} el The new parent element
8910          * @return {Roo.Element} this
8911          */
8912         appendTo: function(el){
8913             el = Roo.getDom(el);
8914             el.appendChild(this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Inserts this element before the passed element in the DOM
8920          * @param {String/HTMLElement/Element} el The element to insert before
8921          * @return {Roo.Element} this
8922          */
8923         insertBefore: function(el){
8924             el = Roo.getDom(el);
8925             el.parentNode.insertBefore(this.dom, el);
8926             return this;
8927         },
8928
8929         /**
8930          * Inserts this element after the passed element in the DOM
8931          * @param {String/HTMLElement/Element} el The element to insert after
8932          * @return {Roo.Element} this
8933          */
8934         insertAfter: function(el){
8935             el = Roo.getDom(el);
8936             el.parentNode.insertBefore(this.dom, el.nextSibling);
8937             return this;
8938         },
8939
8940         /**
8941          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8942          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8943          * @return {Roo.Element} The new child
8944          */
8945         insertFirst: function(el, returnDom){
8946             el = el || {};
8947             if(typeof el == 'object' && !el.nodeType){ // dh config
8948                 return this.createChild(el, this.dom.firstChild, returnDom);
8949             }else{
8950                 el = Roo.getDom(el);
8951                 this.dom.insertBefore(el, this.dom.firstChild);
8952                 return !returnDom ? Roo.get(el) : el;
8953             }
8954         },
8955
8956         /**
8957          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8958          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8959          * @param {String} where (optional) 'before' or 'after' defaults to before
8960          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8961          * @return {Roo.Element} the inserted Element
8962          */
8963         insertSibling: function(el, where, returnDom){
8964             where = where ? where.toLowerCase() : 'before';
8965             el = el || {};
8966             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8967
8968             if(typeof el == 'object' && !el.nodeType){ // dh config
8969                 if(where == 'after' && !this.dom.nextSibling){
8970                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8971                 }else{
8972                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8973                 }
8974
8975             }else{
8976                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8977                             where == 'before' ? this.dom : this.dom.nextSibling);
8978                 if(!returnDom){
8979                     rt = Roo.get(rt);
8980                 }
8981             }
8982             return rt;
8983         },
8984
8985         /**
8986          * Creates and wraps this element with another element
8987          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8988          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8989          * @return {HTMLElement/Element} The newly created wrapper element
8990          */
8991         wrap: function(config, returnDom){
8992             if(!config){
8993                 config = {tag: "div"};
8994             }
8995             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8996             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8997             return newEl;
8998         },
8999
9000         /**
9001          * Replaces the passed element with this element
9002          * @param {String/HTMLElement/Element} el The element to replace
9003          * @return {Roo.Element} this
9004          */
9005         replace: function(el){
9006             el = Roo.get(el);
9007             this.insertBefore(el);
9008             el.remove();
9009             return this;
9010         },
9011
9012         /**
9013          * Inserts an html fragment into this element
9014          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9015          * @param {String} html The HTML fragment
9016          * @param {Boolean} returnEl True to return an Roo.Element
9017          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9018          */
9019         insertHtml : function(where, html, returnEl){
9020             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9021             return returnEl ? Roo.get(el) : el;
9022         },
9023
9024         /**
9025          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9026          * @param {Object} o The object with the attributes
9027          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9028          * @return {Roo.Element} this
9029          */
9030         set : function(o, useSet){
9031             var el = this.dom;
9032             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9033             for(var attr in o){
9034                 if(attr == "style" || typeof o[attr] == "function") continue;
9035                 if(attr=="cls"){
9036                     el.className = o["cls"];
9037                 }else{
9038                     if(useSet) el.setAttribute(attr, o[attr]);
9039                     else el[attr] = o[attr];
9040                 }
9041             }
9042             if(o.style){
9043                 Roo.DomHelper.applyStyles(el, o.style);
9044             }
9045             return this;
9046         },
9047
9048         /**
9049          * Convenience method for constructing a KeyMap
9050          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9051          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9052          * @param {Function} fn The function to call
9053          * @param {Object} scope (optional) The scope of the function
9054          * @return {Roo.KeyMap} The KeyMap created
9055          */
9056         addKeyListener : function(key, fn, scope){
9057             var config;
9058             if(typeof key != "object" || key instanceof Array){
9059                 config = {
9060                     key: key,
9061                     fn: fn,
9062                     scope: scope
9063                 };
9064             }else{
9065                 config = {
9066                     key : key.key,
9067                     shift : key.shift,
9068                     ctrl : key.ctrl,
9069                     alt : key.alt,
9070                     fn: fn,
9071                     scope: scope
9072                 };
9073             }
9074             return new Roo.KeyMap(this, config);
9075         },
9076
9077         /**
9078          * Creates a KeyMap for this element
9079          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9080          * @return {Roo.KeyMap} The KeyMap created
9081          */
9082         addKeyMap : function(config){
9083             return new Roo.KeyMap(this, config);
9084         },
9085
9086         /**
9087          * Returns true if this element is scrollable.
9088          * @return {Boolean}
9089          */
9090          isScrollable : function(){
9091             var dom = this.dom;
9092             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9093         },
9094
9095         /**
9096          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9097          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9098          * @param {Number} value The new scroll value
9099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9100          * @return {Element} this
9101          */
9102
9103         scrollTo : function(side, value, animate){
9104             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9105             if(!animate || !A){
9106                 this.dom[prop] = value;
9107             }else{
9108                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9109                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9110             }
9111             return this;
9112         },
9113
9114         /**
9115          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9116          * within this element's scrollable range.
9117          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9118          * @param {Number} distance How far to scroll the element in pixels
9119          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9120          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9121          * was scrolled as far as it could go.
9122          */
9123          scroll : function(direction, distance, animate){
9124              if(!this.isScrollable()){
9125                  return;
9126              }
9127              var el = this.dom;
9128              var l = el.scrollLeft, t = el.scrollTop;
9129              var w = el.scrollWidth, h = el.scrollHeight;
9130              var cw = el.clientWidth, ch = el.clientHeight;
9131              direction = direction.toLowerCase();
9132              var scrolled = false;
9133              var a = this.preanim(arguments, 2);
9134              switch(direction){
9135                  case "l":
9136                  case "left":
9137                      if(w - l > cw){
9138                          var v = Math.min(l + distance, w-cw);
9139                          this.scrollTo("left", v, a);
9140                          scrolled = true;
9141                      }
9142                      break;
9143                 case "r":
9144                 case "right":
9145                      if(l > 0){
9146                          var v = Math.max(l - distance, 0);
9147                          this.scrollTo("left", v, a);
9148                          scrolled = true;
9149                      }
9150                      break;
9151                 case "t":
9152                 case "top":
9153                 case "up":
9154                      if(t > 0){
9155                          var v = Math.max(t - distance, 0);
9156                          this.scrollTo("top", v, a);
9157                          scrolled = true;
9158                      }
9159                      break;
9160                 case "b":
9161                 case "bottom":
9162                 case "down":
9163                      if(h - t > ch){
9164                          var v = Math.min(t + distance, h-ch);
9165                          this.scrollTo("top", v, a);
9166                          scrolled = true;
9167                      }
9168                      break;
9169              }
9170              return scrolled;
9171         },
9172
9173         /**
9174          * Translates the passed page coordinates into left/top css values for this element
9175          * @param {Number/Array} x The page x or an array containing [x, y]
9176          * @param {Number} y The page y
9177          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9178          */
9179         translatePoints : function(x, y){
9180             if(typeof x == 'object' || x instanceof Array){
9181                 y = x[1]; x = x[0];
9182             }
9183             var p = this.getStyle('position');
9184             var o = this.getXY();
9185
9186             var l = parseInt(this.getStyle('left'), 10);
9187             var t = parseInt(this.getStyle('top'), 10);
9188
9189             if(isNaN(l)){
9190                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9191             }
9192             if(isNaN(t)){
9193                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9194             }
9195
9196             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9197         },
9198
9199         /**
9200          * Returns the current scroll position of the element.
9201          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9202          */
9203         getScroll : function(){
9204             var d = this.dom, doc = document;
9205             if(d == doc || d == doc.body){
9206                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9207                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9208                 return {left: l, top: t};
9209             }else{
9210                 return {left: d.scrollLeft, top: d.scrollTop};
9211             }
9212         },
9213
9214         /**
9215          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9216          * are convert to standard 6 digit hex color.
9217          * @param {String} attr The css attribute
9218          * @param {String} defaultValue The default value to use when a valid color isn't found
9219          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9220          * YUI color anims.
9221          */
9222         getColor : function(attr, defaultValue, prefix){
9223             var v = this.getStyle(attr);
9224             if(!v || v == "transparent" || v == "inherit") {
9225                 return defaultValue;
9226             }
9227             var color = typeof prefix == "undefined" ? "#" : prefix;
9228             if(v.substr(0, 4) == "rgb("){
9229                 var rvs = v.slice(4, v.length -1).split(",");
9230                 for(var i = 0; i < 3; i++){
9231                     var h = parseInt(rvs[i]).toString(16);
9232                     if(h < 16){
9233                         h = "0" + h;
9234                     }
9235                     color += h;
9236                 }
9237             } else {
9238                 if(v.substr(0, 1) == "#"){
9239                     if(v.length == 4) {
9240                         for(var i = 1; i < 4; i++){
9241                             var c = v.charAt(i);
9242                             color +=  c + c;
9243                         }
9244                     }else if(v.length == 7){
9245                         color += v.substr(1);
9246                     }
9247                 }
9248             }
9249             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9250         },
9251
9252         /**
9253          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9254          * gradient background, rounded corners and a 4-way shadow.
9255          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9256          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9257          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9258          * @return {Roo.Element} this
9259          */
9260         boxWrap : function(cls){
9261             cls = cls || 'x-box';
9262             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9263             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9264             return el;
9265         },
9266
9267         /**
9268          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9269          * @param {String} namespace The namespace in which to look for the attribute
9270          * @param {String} name The attribute name
9271          * @return {String} The attribute value
9272          */
9273         getAttributeNS : Roo.isIE ? function(ns, name){
9274             var d = this.dom;
9275             var type = typeof d[ns+":"+name];
9276             if(type != 'undefined' && type != 'unknown'){
9277                 return d[ns+":"+name];
9278             }
9279             return d[name];
9280         } : function(ns, name){
9281             var d = this.dom;
9282             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9283         }
9284     };
9285
9286     var ep = El.prototype;
9287
9288     /**
9289      * Appends an event handler (Shorthand for addListener)
9290      * @param {String}   eventName     The type of event to append
9291      * @param {Function} fn        The method the event invokes
9292      * @param {Object} scope       (optional) The scope (this object) of the fn
9293      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9294      * @method
9295      */
9296     ep.on = ep.addListener;
9297         // backwards compat
9298     ep.mon = ep.addListener;
9299
9300     /**
9301      * Removes an event handler from this element (shorthand for removeListener)
9302      * @param {String} eventName the type of event to remove
9303      * @param {Function} fn the method the event invokes
9304      * @return {Roo.Element} this
9305      * @method
9306      */
9307     ep.un = ep.removeListener;
9308
9309     /**
9310      * true to automatically adjust width and height settings for box-model issues (default to true)
9311      */
9312     ep.autoBoxAdjust = true;
9313
9314     // private
9315     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9316
9317     // private
9318     El.addUnits = function(v, defaultUnit){
9319         if(v === "" || v == "auto"){
9320             return v;
9321         }
9322         if(v === undefined){
9323             return '';
9324         }
9325         if(typeof v == "number" || !El.unitPattern.test(v)){
9326             return v + (defaultUnit || 'px');
9327         }
9328         return v;
9329     };
9330
9331     // special markup used throughout Roo when box wrapping elements
9332     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9333     /**
9334      * Visibility mode constant - Use visibility to hide element
9335      * @static
9336      * @type Number
9337      */
9338     El.VISIBILITY = 1;
9339     /**
9340      * Visibility mode constant - Use display to hide element
9341      * @static
9342      * @type Number
9343      */
9344     El.DISPLAY = 2;
9345
9346     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9347     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9348     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9349
9350
9351
9352     /**
9353      * @private
9354      */
9355     El.cache = {};
9356
9357     var docEl;
9358
9359     /**
9360      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9361      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9362      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9363      * @return {Element} The Element object
9364      * @static
9365      */
9366     El.get = function(el){
9367         var ex, elm, id;
9368         if(!el){ return null; }
9369         if(typeof el == "string"){ // element id
9370             if(!(elm = document.getElementById(el))){
9371                 return null;
9372             }
9373             if(ex = El.cache[el]){
9374                 ex.dom = elm;
9375             }else{
9376                 ex = El.cache[el] = new El(elm);
9377             }
9378             return ex;
9379         }else if(el.tagName){ // dom element
9380             if(!(id = el.id)){
9381                 id = Roo.id(el);
9382             }
9383             if(ex = El.cache[id]){
9384                 ex.dom = el;
9385             }else{
9386                 ex = El.cache[id] = new El(el);
9387             }
9388             return ex;
9389         }else if(el instanceof El){
9390             if(el != docEl){
9391                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9392                                                               // catch case where it hasn't been appended
9393                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9394             }
9395             return el;
9396         }else if(el.isComposite){
9397             return el;
9398         }else if(el instanceof Array){
9399             return El.select(el);
9400         }else if(el == document){
9401             // create a bogus element object representing the document object
9402             if(!docEl){
9403                 var f = function(){};
9404                 f.prototype = El.prototype;
9405                 docEl = new f();
9406                 docEl.dom = document;
9407             }
9408             return docEl;
9409         }
9410         return null;
9411     };
9412
9413     // private
9414     El.uncache = function(el){
9415         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9416             if(a[i]){
9417                 delete El.cache[a[i].id || a[i]];
9418             }
9419         }
9420     };
9421
9422     // private
9423     // Garbage collection - uncache elements/purge listeners on orphaned elements
9424     // so we don't hold a reference and cause the browser to retain them
9425     El.garbageCollect = function(){
9426         if(!Roo.enableGarbageCollector){
9427             clearInterval(El.collectorThread);
9428             return;
9429         }
9430         for(var eid in El.cache){
9431             var el = El.cache[eid], d = el.dom;
9432             // -------------------------------------------------------
9433             // Determining what is garbage:
9434             // -------------------------------------------------------
9435             // !d
9436             // dom node is null, definitely garbage
9437             // -------------------------------------------------------
9438             // !d.parentNode
9439             // no parentNode == direct orphan, definitely garbage
9440             // -------------------------------------------------------
9441             // !d.offsetParent && !document.getElementById(eid)
9442             // display none elements have no offsetParent so we will
9443             // also try to look it up by it's id. However, check
9444             // offsetParent first so we don't do unneeded lookups.
9445             // This enables collection of elements that are not orphans
9446             // directly, but somewhere up the line they have an orphan
9447             // parent.
9448             // -------------------------------------------------------
9449             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9450                 delete El.cache[eid];
9451                 if(d && Roo.enableListenerCollection){
9452                     E.purgeElement(d);
9453                 }
9454             }
9455         }
9456     }
9457     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9458
9459
9460     // dom is optional
9461     El.Flyweight = function(dom){
9462         this.dom = dom;
9463     };
9464     El.Flyweight.prototype = El.prototype;
9465
9466     El._flyweights = {};
9467     /**
9468      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9469      * the dom node can be overwritten by other code.
9470      * @param {String/HTMLElement} el The dom node or id
9471      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9472      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9473      * @static
9474      * @return {Element} The shared Element object
9475      */
9476     El.fly = function(el, named){
9477         named = named || '_global';
9478         el = Roo.getDom(el);
9479         if(!el){
9480             return null;
9481         }
9482         if(!El._flyweights[named]){
9483             El._flyweights[named] = new El.Flyweight();
9484         }
9485         El._flyweights[named].dom = el;
9486         return El._flyweights[named];
9487     };
9488
9489     /**
9490      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9491      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9492      * Shorthand of {@link Roo.Element#get}
9493      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9494      * @return {Element} The Element object
9495      * @member Roo
9496      * @method get
9497      */
9498     Roo.get = El.get;
9499     /**
9500      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9501      * the dom node can be overwritten by other code.
9502      * Shorthand of {@link Roo.Element#fly}
9503      * @param {String/HTMLElement} el The dom node or id
9504      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9505      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9506      * @static
9507      * @return {Element} The shared Element object
9508      * @member Roo
9509      * @method fly
9510      */
9511     Roo.fly = El.fly;
9512
9513     // speedy lookup for elements never to box adjust
9514     var noBoxAdjust = Roo.isStrict ? {
9515         select:1
9516     } : {
9517         input:1, select:1, textarea:1
9518     };
9519     if(Roo.isIE || Roo.isGecko){
9520         noBoxAdjust['button'] = 1;
9521     }
9522
9523
9524     Roo.EventManager.on(window, 'unload', function(){
9525         delete El.cache;
9526         delete El._flyweights;
9527     });
9528 })();
9529
9530
9531
9532
9533 if(Roo.DomQuery){
9534     Roo.Element.selectorFunction = Roo.DomQuery.select;
9535 }
9536
9537 Roo.Element.select = function(selector, unique, root){
9538     var els;
9539     if(typeof selector == "string"){
9540         els = Roo.Element.selectorFunction(selector, root);
9541     }else if(selector.length !== undefined){
9542         els = selector;
9543     }else{
9544         throw "Invalid selector";
9545     }
9546     if(unique === true){
9547         return new Roo.CompositeElement(els);
9548     }else{
9549         return new Roo.CompositeElementLite(els);
9550     }
9551 };
9552 /**
9553  * Selects elements based on the passed CSS selector to enable working on them as 1.
9554  * @param {String/Array} selector The CSS selector or an array of elements
9555  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9556  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9557  * @return {CompositeElementLite/CompositeElement}
9558  * @member Roo
9559  * @method select
9560  */
9561 Roo.select = Roo.Element.select;
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576 /*
9577  * Based on:
9578  * Ext JS Library 1.1.1
9579  * Copyright(c) 2006-2007, Ext JS, LLC.
9580  *
9581  * Originally Released Under LGPL - original licence link has changed is not relivant.
9582  *
9583  * Fork - LGPL
9584  * <script type="text/javascript">
9585  */
9586
9587
9588
9589 //Notifies Element that fx methods are available
9590 Roo.enableFx = true;
9591
9592 /**
9593  * @class Roo.Fx
9594  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9595  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9596  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9597  * Element effects to work.</p><br/>
9598  *
9599  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9600  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9601  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9602  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9603  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9604  * expected results and should be done with care.</p><br/>
9605  *
9606  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9607  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9608 <pre>
9609 Value  Description
9610 -----  -----------------------------
9611 tl     The top left corner
9612 t      The center of the top edge
9613 tr     The top right corner
9614 l      The center of the left edge
9615 r      The center of the right edge
9616 bl     The bottom left corner
9617 b      The center of the bottom edge
9618 br     The bottom right corner
9619 </pre>
9620  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9621  * below are common options that can be passed to any Fx method.</b>
9622  * @cfg {Function} callback A function called when the effect is finished
9623  * @cfg {Object} scope The scope of the effect function
9624  * @cfg {String} easing A valid Easing value for the effect
9625  * @cfg {String} afterCls A css class to apply after the effect
9626  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9627  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9628  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9629  * effects that end with the element being visually hidden, ignored otherwise)
9630  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9631  * a function which returns such a specification that will be applied to the Element after the effect finishes
9632  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9633  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9634  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9635  */
9636 Roo.Fx = {
9637         /**
9638          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9639          * origin for the slide effect.  This function automatically handles wrapping the element with
9640          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9641          * Usage:
9642          *<pre><code>
9643 // default: slide the element in from the top
9644 el.slideIn();
9645
9646 // custom: slide the element in from the right with a 2-second duration
9647 el.slideIn('r', { duration: 2 });
9648
9649 // common config options shown with default values
9650 el.slideIn('t', {
9651     easing: 'easeOut',
9652     duration: .5
9653 });
9654 </code></pre>
9655          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9656          * @param {Object} options (optional) Object literal with any of the Fx config options
9657          * @return {Roo.Element} The Element
9658          */
9659     slideIn : function(anchor, o){
9660         var el = this.getFxEl();
9661         o = o || {};
9662
9663         el.queueFx(o, function(){
9664
9665             anchor = anchor || "t";
9666
9667             // fix display to visibility
9668             this.fixDisplay();
9669
9670             // restore values after effect
9671             var r = this.getFxRestore();
9672             var b = this.getBox();
9673             // fixed size for slide
9674             this.setSize(b);
9675
9676             // wrap if needed
9677             var wrap = this.fxWrap(r.pos, o, "hidden");
9678
9679             var st = this.dom.style;
9680             st.visibility = "visible";
9681             st.position = "absolute";
9682
9683             // clear out temp styles after slide and unwrap
9684             var after = function(){
9685                 el.fxUnwrap(wrap, r.pos, o);
9686                 st.width = r.width;
9687                 st.height = r.height;
9688                 el.afterFx(o);
9689             };
9690             // time to calc the positions
9691             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9692
9693             switch(anchor.toLowerCase()){
9694                 case "t":
9695                     wrap.setSize(b.width, 0);
9696                     st.left = st.bottom = "0";
9697                     a = {height: bh};
9698                 break;
9699                 case "l":
9700                     wrap.setSize(0, b.height);
9701                     st.right = st.top = "0";
9702                     a = {width: bw};
9703                 break;
9704                 case "r":
9705                     wrap.setSize(0, b.height);
9706                     wrap.setX(b.right);
9707                     st.left = st.top = "0";
9708                     a = {width: bw, points: pt};
9709                 break;
9710                 case "b":
9711                     wrap.setSize(b.width, 0);
9712                     wrap.setY(b.bottom);
9713                     st.left = st.top = "0";
9714                     a = {height: bh, points: pt};
9715                 break;
9716                 case "tl":
9717                     wrap.setSize(0, 0);
9718                     st.right = st.bottom = "0";
9719                     a = {width: bw, height: bh};
9720                 break;
9721                 case "bl":
9722                     wrap.setSize(0, 0);
9723                     wrap.setY(b.y+b.height);
9724                     st.right = st.top = "0";
9725                     a = {width: bw, height: bh, points: pt};
9726                 break;
9727                 case "br":
9728                     wrap.setSize(0, 0);
9729                     wrap.setXY([b.right, b.bottom]);
9730                     st.left = st.top = "0";
9731                     a = {width: bw, height: bh, points: pt};
9732                 break;
9733                 case "tr":
9734                     wrap.setSize(0, 0);
9735                     wrap.setX(b.x+b.width);
9736                     st.left = st.bottom = "0";
9737                     a = {width: bw, height: bh, points: pt};
9738                 break;
9739             }
9740             this.dom.style.visibility = "visible";
9741             wrap.show();
9742
9743             arguments.callee.anim = wrap.fxanim(a,
9744                 o,
9745                 'motion',
9746                 .5,
9747                 'easeOut', after);
9748         });
9749         return this;
9750     },
9751     
9752         /**
9753          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9754          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9755          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9756          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9757          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9758          * Usage:
9759          *<pre><code>
9760 // default: slide the element out to the top
9761 el.slideOut();
9762
9763 // custom: slide the element out to the right with a 2-second duration
9764 el.slideOut('r', { duration: 2 });
9765
9766 // common config options shown with default values
9767 el.slideOut('t', {
9768     easing: 'easeOut',
9769     duration: .5,
9770     remove: false,
9771     useDisplay: false
9772 });
9773 </code></pre>
9774          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9775          * @param {Object} options (optional) Object literal with any of the Fx config options
9776          * @return {Roo.Element} The Element
9777          */
9778     slideOut : function(anchor, o){
9779         var el = this.getFxEl();
9780         o = o || {};
9781
9782         el.queueFx(o, function(){
9783
9784             anchor = anchor || "t";
9785
9786             // restore values after effect
9787             var r = this.getFxRestore();
9788             
9789             var b = this.getBox();
9790             // fixed size for slide
9791             this.setSize(b);
9792
9793             // wrap if needed
9794             var wrap = this.fxWrap(r.pos, o, "visible");
9795
9796             var st = this.dom.style;
9797             st.visibility = "visible";
9798             st.position = "absolute";
9799
9800             wrap.setSize(b);
9801
9802             var after = function(){
9803                 if(o.useDisplay){
9804                     el.setDisplayed(false);
9805                 }else{
9806                     el.hide();
9807                 }
9808
9809                 el.fxUnwrap(wrap, r.pos, o);
9810
9811                 st.width = r.width;
9812                 st.height = r.height;
9813
9814                 el.afterFx(o);
9815             };
9816
9817             var a, zero = {to: 0};
9818             switch(anchor.toLowerCase()){
9819                 case "t":
9820                     st.left = st.bottom = "0";
9821                     a = {height: zero};
9822                 break;
9823                 case "l":
9824                     st.right = st.top = "0";
9825                     a = {width: zero};
9826                 break;
9827                 case "r":
9828                     st.left = st.top = "0";
9829                     a = {width: zero, points: {to:[b.right, b.y]}};
9830                 break;
9831                 case "b":
9832                     st.left = st.top = "0";
9833                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9834                 break;
9835                 case "tl":
9836                     st.right = st.bottom = "0";
9837                     a = {width: zero, height: zero};
9838                 break;
9839                 case "bl":
9840                     st.right = st.top = "0";
9841                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9842                 break;
9843                 case "br":
9844                     st.left = st.top = "0";
9845                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9846                 break;
9847                 case "tr":
9848                     st.left = st.bottom = "0";
9849                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9850                 break;
9851             }
9852
9853             arguments.callee.anim = wrap.fxanim(a,
9854                 o,
9855                 'motion',
9856                 .5,
9857                 "easeOut", after);
9858         });
9859         return this;
9860     },
9861
9862         /**
9863          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9864          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9865          * The element must be removed from the DOM using the 'remove' config option if desired.
9866          * Usage:
9867          *<pre><code>
9868 // default
9869 el.puff();
9870
9871 // common config options shown with default values
9872 el.puff({
9873     easing: 'easeOut',
9874     duration: .5,
9875     remove: false,
9876     useDisplay: false
9877 });
9878 </code></pre>
9879          * @param {Object} options (optional) Object literal with any of the Fx config options
9880          * @return {Roo.Element} The Element
9881          */
9882     puff : function(o){
9883         var el = this.getFxEl();
9884         o = o || {};
9885
9886         el.queueFx(o, function(){
9887             this.clearOpacity();
9888             this.show();
9889
9890             // restore values after effect
9891             var r = this.getFxRestore();
9892             var st = this.dom.style;
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.clearOpacity();
9902
9903                 el.setPositioning(r.pos);
9904                 st.width = r.width;
9905                 st.height = r.height;
9906                 st.fontSize = '';
9907                 el.afterFx(o);
9908             };
9909
9910             var width = this.getWidth();
9911             var height = this.getHeight();
9912
9913             arguments.callee.anim = this.fxanim({
9914                     width : {to: this.adjustWidth(width * 2)},
9915                     height : {to: this.adjustHeight(height * 2)},
9916                     points : {by: [-(width * .5), -(height * .5)]},
9917                     opacity : {to: 0},
9918                     fontSize: {to:200, unit: "%"}
9919                 },
9920                 o,
9921                 'motion',
9922                 .5,
9923                 "easeOut", after);
9924         });
9925         return this;
9926     },
9927
9928         /**
9929          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9930          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9931          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9932          * Usage:
9933          *<pre><code>
9934 // default
9935 el.switchOff();
9936
9937 // all config options shown with default values
9938 el.switchOff({
9939     easing: 'easeIn',
9940     duration: .3,
9941     remove: false,
9942     useDisplay: false
9943 });
9944 </code></pre>
9945          * @param {Object} options (optional) Object literal with any of the Fx config options
9946          * @return {Roo.Element} The Element
9947          */
9948     switchOff : function(o){
9949         var el = this.getFxEl();
9950         o = o || {};
9951
9952         el.queueFx(o, function(){
9953             this.clearOpacity();
9954             this.clip();
9955
9956             // restore values after effect
9957             var r = this.getFxRestore();
9958             var st = this.dom.style;
9959
9960             var after = function(){
9961                 if(o.useDisplay){
9962                     el.setDisplayed(false);
9963                 }else{
9964                     el.hide();
9965                 }
9966
9967                 el.clearOpacity();
9968                 el.setPositioning(r.pos);
9969                 st.width = r.width;
9970                 st.height = r.height;
9971
9972                 el.afterFx(o);
9973             };
9974
9975             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9976                 this.clearOpacity();
9977                 (function(){
9978                     this.fxanim({
9979                         height:{to:1},
9980                         points:{by:[0, this.getHeight() * .5]}
9981                     }, o, 'motion', 0.3, 'easeIn', after);
9982                 }).defer(100, this);
9983             });
9984         });
9985         return this;
9986     },
9987
9988     /**
9989      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9990      * changed using the "attr" config option) and then fading back to the original color. If no original
9991      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9992      * Usage:
9993 <pre><code>
9994 // default: highlight background to yellow
9995 el.highlight();
9996
9997 // custom: highlight foreground text to blue for 2 seconds
9998 el.highlight("0000ff", { attr: 'color', duration: 2 });
9999
10000 // common config options shown with default values
10001 el.highlight("ffff9c", {
10002     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10003     endColor: (current color) or "ffffff",
10004     easing: 'easeIn',
10005     duration: 1
10006 });
10007 </code></pre>
10008      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10009      * @param {Object} options (optional) Object literal with any of the Fx config options
10010      * @return {Roo.Element} The Element
10011      */ 
10012     highlight : function(color, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017             color = color || "ffff9c";
10018             attr = o.attr || "backgroundColor";
10019
10020             this.clearOpacity();
10021             this.show();
10022
10023             var origColor = this.getColor(attr);
10024             var restoreColor = this.dom.style[attr];
10025             endColor = (o.endColor || origColor) || "ffffff";
10026
10027             var after = function(){
10028                 el.dom.style[attr] = restoreColor;
10029                 el.afterFx(o);
10030             };
10031
10032             var a = {};
10033             a[attr] = {from: color, to: endColor};
10034             arguments.callee.anim = this.fxanim(a,
10035                 o,
10036                 'color',
10037                 1,
10038                 'easeIn', after);
10039         });
10040         return this;
10041     },
10042
10043    /**
10044     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10045     * Usage:
10046 <pre><code>
10047 // default: a single light blue ripple
10048 el.frame();
10049
10050 // custom: 3 red ripples lasting 3 seconds total
10051 el.frame("ff0000", 3, { duration: 3 });
10052
10053 // common config options shown with default values
10054 el.frame("C3DAF9", 1, {
10055     duration: 1 //duration of entire animation (not each individual ripple)
10056     // Note: Easing is not configurable and will be ignored if included
10057 });
10058 </code></pre>
10059     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10060     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10061     * @param {Object} options (optional) Object literal with any of the Fx config options
10062     * @return {Roo.Element} The Element
10063     */
10064     frame : function(color, count, o){
10065         var el = this.getFxEl();
10066         o = o || {};
10067
10068         el.queueFx(o, function(){
10069             color = color || "#C3DAF9";
10070             if(color.length == 6){
10071                 color = "#" + color;
10072             }
10073             count = count || 1;
10074             duration = o.duration || 1;
10075             this.show();
10076
10077             var b = this.getBox();
10078             var animFn = function(){
10079                 var proxy = this.createProxy({
10080
10081                      style:{
10082                         visbility:"hidden",
10083                         position:"absolute",
10084                         "z-index":"35000", // yee haw
10085                         border:"0px solid " + color
10086                      }
10087                   });
10088                 var scale = Roo.isBorderBox ? 2 : 1;
10089                 proxy.animate({
10090                     top:{from:b.y, to:b.y - 20},
10091                     left:{from:b.x, to:b.x - 20},
10092                     borderWidth:{from:0, to:10},
10093                     opacity:{from:1, to:0},
10094                     height:{from:b.height, to:(b.height + (20*scale))},
10095                     width:{from:b.width, to:(b.width + (20*scale))}
10096                 }, duration, function(){
10097                     proxy.remove();
10098                 });
10099                 if(--count > 0){
10100                      animFn.defer((duration/2)*1000, this);
10101                 }else{
10102                     el.afterFx(o);
10103                 }
10104             };
10105             animFn.call(this);
10106         });
10107         return this;
10108     },
10109
10110    /**
10111     * Creates a pause before any subsequent queued effects begin.  If there are
10112     * no effects queued after the pause it will have no effect.
10113     * Usage:
10114 <pre><code>
10115 el.pause(1);
10116 </code></pre>
10117     * @param {Number} seconds The length of time to pause (in seconds)
10118     * @return {Roo.Element} The Element
10119     */
10120     pause : function(seconds){
10121         var el = this.getFxEl();
10122         var o = {};
10123
10124         el.queueFx(o, function(){
10125             setTimeout(function(){
10126                 el.afterFx(o);
10127             }, seconds * 1000);
10128         });
10129         return this;
10130     },
10131
10132    /**
10133     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10134     * using the "endOpacity" config option.
10135     * Usage:
10136 <pre><code>
10137 // default: fade in from opacity 0 to 100%
10138 el.fadeIn();
10139
10140 // custom: fade in from opacity 0 to 75% over 2 seconds
10141 el.fadeIn({ endOpacity: .75, duration: 2});
10142
10143 // common config options shown with default values
10144 el.fadeIn({
10145     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10146     easing: 'easeOut',
10147     duration: .5
10148 });
10149 </code></pre>
10150     * @param {Object} options (optional) Object literal with any of the Fx config options
10151     * @return {Roo.Element} The Element
10152     */
10153     fadeIn : function(o){
10154         var el = this.getFxEl();
10155         o = o || {};
10156         el.queueFx(o, function(){
10157             this.setOpacity(0);
10158             this.fixDisplay();
10159             this.dom.style.visibility = 'visible';
10160             var to = o.endOpacity || 1;
10161             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10162                 o, null, .5, "easeOut", function(){
10163                 if(to == 1){
10164                     this.clearOpacity();
10165                 }
10166                 el.afterFx(o);
10167             });
10168         });
10169         return this;
10170     },
10171
10172    /**
10173     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10174     * using the "endOpacity" config option.
10175     * Usage:
10176 <pre><code>
10177 // default: fade out from the element's current opacity to 0
10178 el.fadeOut();
10179
10180 // custom: fade out from the element's current opacity to 25% over 2 seconds
10181 el.fadeOut({ endOpacity: .25, duration: 2});
10182
10183 // common config options shown with default values
10184 el.fadeOut({
10185     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10186     easing: 'easeOut',
10187     duration: .5
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192     * @param {Object} options (optional) Object literal with any of the Fx config options
10193     * @return {Roo.Element} The Element
10194     */
10195     fadeOut : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198         el.queueFx(o, function(){
10199             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10200                 o, null, .5, "easeOut", function(){
10201                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10202                      this.dom.style.display = "none";
10203                 }else{
10204                      this.dom.style.visibility = "hidden";
10205                 }
10206                 this.clearOpacity();
10207                 el.afterFx(o);
10208             });
10209         });
10210         return this;
10211     },
10212
10213    /**
10214     * Animates the transition of an element's dimensions from a starting height/width
10215     * to an ending height/width.
10216     * Usage:
10217 <pre><code>
10218 // change height and width to 100x100 pixels
10219 el.scale(100, 100);
10220
10221 // common config options shown with default values.  The height and width will default to
10222 // the element's existing values if passed as null.
10223 el.scale(
10224     [element's width],
10225     [element's height], {
10226     easing: 'easeOut',
10227     duration: .35
10228 });
10229 </code></pre>
10230     * @param {Number} width  The new width (pass undefined to keep the original width)
10231     * @param {Number} height  The new height (pass undefined to keep the original height)
10232     * @param {Object} options (optional) Object literal with any of the Fx config options
10233     * @return {Roo.Element} The Element
10234     */
10235     scale : function(w, h, o){
10236         this.shift(Roo.apply({}, o, {
10237             width: w,
10238             height: h
10239         }));
10240         return this;
10241     },
10242
10243    /**
10244     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10245     * Any of these properties not specified in the config object will not be changed.  This effect 
10246     * requires that at least one new dimension, position or opacity setting must be passed in on
10247     * the config object in order for the function to have any effect.
10248     * Usage:
10249 <pre><code>
10250 // slide the element horizontally to x position 200 while changing the height and opacity
10251 el.shift({ x: 200, height: 50, opacity: .8 });
10252
10253 // common config options shown with default values.
10254 el.shift({
10255     width: [element's width],
10256     height: [element's height],
10257     x: [element's x position],
10258     y: [element's y position],
10259     opacity: [element's opacity],
10260     easing: 'easeOut',
10261     duration: .35
10262 });
10263 </code></pre>
10264     * @param {Object} options  Object literal with any of the Fx config options
10265     * @return {Roo.Element} The Element
10266     */
10267     shift : function(o){
10268         var el = this.getFxEl();
10269         o = o || {};
10270         el.queueFx(o, function(){
10271             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10272             if(w !== undefined){
10273                 a.width = {to: this.adjustWidth(w)};
10274             }
10275             if(h !== undefined){
10276                 a.height = {to: this.adjustHeight(h)};
10277             }
10278             if(x !== undefined || y !== undefined){
10279                 a.points = {to: [
10280                     x !== undefined ? x : this.getX(),
10281                     y !== undefined ? y : this.getY()
10282                 ]};
10283             }
10284             if(op !== undefined){
10285                 a.opacity = {to: op};
10286             }
10287             if(o.xy !== undefined){
10288                 a.points = {to: o.xy};
10289             }
10290             arguments.callee.anim = this.fxanim(a,
10291                 o, 'motion', .35, "easeOut", function(){
10292                 el.afterFx(o);
10293             });
10294         });
10295         return this;
10296     },
10297
10298         /**
10299          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10300          * ending point of the effect.
10301          * Usage:
10302          *<pre><code>
10303 // default: slide the element downward while fading out
10304 el.ghost();
10305
10306 // custom: slide the element out to the right with a 2-second duration
10307 el.ghost('r', { duration: 2 });
10308
10309 // common config options shown with default values
10310 el.ghost('b', {
10311     easing: 'easeOut',
10312     duration: .5
10313     remove: false,
10314     useDisplay: false
10315 });
10316 </code></pre>
10317          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10318          * @param {Object} options (optional) Object literal with any of the Fx config options
10319          * @return {Roo.Element} The Element
10320          */
10321     ghost : function(anchor, o){
10322         var el = this.getFxEl();
10323         o = o || {};
10324
10325         el.queueFx(o, function(){
10326             anchor = anchor || "b";
10327
10328             // restore values after effect
10329             var r = this.getFxRestore();
10330             var w = this.getWidth(),
10331                 h = this.getHeight();
10332
10333             var st = this.dom.style;
10334
10335             var after = function(){
10336                 if(o.useDisplay){
10337                     el.setDisplayed(false);
10338                 }else{
10339                     el.hide();
10340                 }
10341
10342                 el.clearOpacity();
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346
10347                 el.afterFx(o);
10348             };
10349
10350             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10351             switch(anchor.toLowerCase()){
10352                 case "t":
10353                     pt.by = [0, -h];
10354                 break;
10355                 case "l":
10356                     pt.by = [-w, 0];
10357                 break;
10358                 case "r":
10359                     pt.by = [w, 0];
10360                 break;
10361                 case "b":
10362                     pt.by = [0, h];
10363                 break;
10364                 case "tl":
10365                     pt.by = [-w, -h];
10366                 break;
10367                 case "bl":
10368                     pt.by = [-w, h];
10369                 break;
10370                 case "br":
10371                     pt.by = [w, h];
10372                 break;
10373                 case "tr":
10374                     pt.by = [w, -h];
10375                 break;
10376             }
10377
10378             arguments.callee.anim = this.fxanim(a,
10379                 o,
10380                 'motion',
10381                 .5,
10382                 "easeOut", after);
10383         });
10384         return this;
10385     },
10386
10387         /**
10388          * Ensures that all effects queued after syncFx is called on the element are
10389          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10390          * @return {Roo.Element} The Element
10391          */
10392     syncFx : function(){
10393         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10394             block : false,
10395             concurrent : true,
10396             stopFx : false
10397         });
10398         return this;
10399     },
10400
10401         /**
10402          * Ensures that all effects queued after sequenceFx is called on the element are
10403          * run in sequence.  This is the opposite of {@link #syncFx}.
10404          * @return {Roo.Element} The Element
10405          */
10406     sequenceFx : function(){
10407         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10408             block : false,
10409             concurrent : false,
10410             stopFx : false
10411         });
10412         return this;
10413     },
10414
10415         /* @private */
10416     nextFx : function(){
10417         var ef = this.fxQueue[0];
10418         if(ef){
10419             ef.call(this);
10420         }
10421     },
10422
10423         /**
10424          * Returns true if the element has any effects actively running or queued, else returns false.
10425          * @return {Boolean} True if element has active effects, else false
10426          */
10427     hasActiveFx : function(){
10428         return this.fxQueue && this.fxQueue[0];
10429     },
10430
10431         /**
10432          * Stops any running effects and clears the element's internal effects queue if it contains
10433          * any additional effects that haven't started yet.
10434          * @return {Roo.Element} The Element
10435          */
10436     stopFx : function(){
10437         if(this.hasActiveFx()){
10438             var cur = this.fxQueue[0];
10439             if(cur && cur.anim && cur.anim.isAnimated()){
10440                 this.fxQueue = [cur]; // clear out others
10441                 cur.anim.stop(true);
10442             }
10443         }
10444         return this;
10445     },
10446
10447         /* @private */
10448     beforeFx : function(o){
10449         if(this.hasActiveFx() && !o.concurrent){
10450            if(o.stopFx){
10451                this.stopFx();
10452                return true;
10453            }
10454            return false;
10455         }
10456         return true;
10457     },
10458
10459         /**
10460          * Returns true if the element is currently blocking so that no other effect can be queued
10461          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10462          * used to ensure that an effect initiated by a user action runs to completion prior to the
10463          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10464          * @return {Boolean} True if blocking, else false
10465          */
10466     hasFxBlock : function(){
10467         var q = this.fxQueue;
10468         return q && q[0] && q[0].block;
10469     },
10470
10471         /* @private */
10472     queueFx : function(o, fn){
10473         if(!this.fxQueue){
10474             this.fxQueue = [];
10475         }
10476         if(!this.hasFxBlock()){
10477             Roo.applyIf(o, this.fxDefaults);
10478             if(!o.concurrent){
10479                 var run = this.beforeFx(o);
10480                 fn.block = o.block;
10481                 this.fxQueue.push(fn);
10482                 if(run){
10483                     this.nextFx();
10484                 }
10485             }else{
10486                 fn.call(this);
10487             }
10488         }
10489         return this;
10490     },
10491
10492         /* @private */
10493     fxWrap : function(pos, o, vis){
10494         var wrap;
10495         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10496             var wrapXY;
10497             if(o.fixPosition){
10498                 wrapXY = this.getXY();
10499             }
10500             var div = document.createElement("div");
10501             div.style.visibility = vis;
10502             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10503             wrap.setPositioning(pos);
10504             if(wrap.getStyle("position") == "static"){
10505                 wrap.position("relative");
10506             }
10507             this.clearPositioning('auto');
10508             wrap.clip();
10509             wrap.dom.appendChild(this.dom);
10510             if(wrapXY){
10511                 wrap.setXY(wrapXY);
10512             }
10513         }
10514         return wrap;
10515     },
10516
10517         /* @private */
10518     fxUnwrap : function(wrap, pos, o){
10519         this.clearPositioning();
10520         this.setPositioning(pos);
10521         if(!o.wrap){
10522             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10523             wrap.remove();
10524         }
10525     },
10526
10527         /* @private */
10528     getFxRestore : function(){
10529         var st = this.dom.style;
10530         return {pos: this.getPositioning(), width: st.width, height : st.height};
10531     },
10532
10533         /* @private */
10534     afterFx : function(o){
10535         if(o.afterStyle){
10536             this.applyStyles(o.afterStyle);
10537         }
10538         if(o.afterCls){
10539             this.addClass(o.afterCls);
10540         }
10541         if(o.remove === true){
10542             this.remove();
10543         }
10544         Roo.callback(o.callback, o.scope, [this]);
10545         if(!o.concurrent){
10546             this.fxQueue.shift();
10547             this.nextFx();
10548         }
10549     },
10550
10551         /* @private */
10552     getFxEl : function(){ // support for composite element fx
10553         return Roo.get(this.dom);
10554     },
10555
10556         /* @private */
10557     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10558         animType = animType || 'run';
10559         opt = opt || {};
10560         var anim = Roo.lib.Anim[animType](
10561             this.dom, args,
10562             (opt.duration || defaultDur) || .35,
10563             (opt.easing || defaultEase) || 'easeOut',
10564             function(){
10565                 Roo.callback(cb, this);
10566             },
10567             this
10568         );
10569         opt.anim = anim;
10570         return anim;
10571     }
10572 };
10573
10574 // backwords compat
10575 Roo.Fx.resize = Roo.Fx.scale;
10576
10577 //When included, Roo.Fx is automatically applied to Element so that all basic
10578 //effects are available directly via the Element API
10579 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590
10591 /**
10592  * @class Roo.CompositeElement
10593  * Standard composite class. Creates a Roo.Element for every element in the collection.
10594  * <br><br>
10595  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10596  * actions will be performed on all the elements in this collection.</b>
10597  * <br><br>
10598  * All methods return <i>this</i> and can be chained.
10599  <pre><code>
10600  var els = Roo.select("#some-el div.some-class", true);
10601  // or select directly from an existing element
10602  var el = Roo.get('some-el');
10603  el.select('div.some-class', true);
10604
10605  els.setWidth(100); // all elements become 100 width
10606  els.hide(true); // all elements fade out and hide
10607  // or
10608  els.setWidth(100).hide(true);
10609  </code></pre>
10610  */
10611 Roo.CompositeElement = function(els){
10612     this.elements = [];
10613     this.addElements(els);
10614 };
10615 Roo.CompositeElement.prototype = {
10616     isComposite: true,
10617     addElements : function(els){
10618         if(!els) return this;
10619         if(typeof els == "string"){
10620             els = Roo.Element.selectorFunction(els);
10621         }
10622         var yels = this.elements;
10623         var index = yels.length-1;
10624         for(var i = 0, len = els.length; i < len; i++) {
10625                 yels[++index] = Roo.get(els[i]);
10626         }
10627         return this;
10628     },
10629
10630     /**
10631     * Clears this composite and adds the elements returned by the passed selector.
10632     * @param {String/Array} els A string CSS selector, an array of elements or an element
10633     * @return {CompositeElement} this
10634     */
10635     fill : function(els){
10636         this.elements = [];
10637         this.add(els);
10638         return this;
10639     },
10640
10641     /**
10642     * Filters this composite to only elements that match the passed selector.
10643     * @param {String} selector A string CSS selector
10644     * @return {CompositeElement} this
10645     */
10646     filter : function(selector){
10647         var els = [];
10648         this.each(function(el){
10649             if(el.is(selector)){
10650                 els[els.length] = el.dom;
10651             }
10652         });
10653         this.fill(els);
10654         return this;
10655     },
10656
10657     invoke : function(fn, args){
10658         var els = this.elements;
10659         for(var i = 0, len = els.length; i < len; i++) {
10660                 Roo.Element.prototype[fn].apply(els[i], args);
10661         }
10662         return this;
10663     },
10664     /**
10665     * Adds elements to this composite.
10666     * @param {String/Array} els A string CSS selector, an array of elements or an element
10667     * @return {CompositeElement} this
10668     */
10669     add : function(els){
10670         if(typeof els == "string"){
10671             this.addElements(Roo.Element.selectorFunction(els));
10672         }else if(els.length !== undefined){
10673             this.addElements(els);
10674         }else{
10675             this.addElements([els]);
10676         }
10677         return this;
10678     },
10679     /**
10680     * Calls the passed function passing (el, this, index) for each element in this composite.
10681     * @param {Function} fn The function to call
10682     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10683     * @return {CompositeElement} this
10684     */
10685     each : function(fn, scope){
10686         var els = this.elements;
10687         for(var i = 0, len = els.length; i < len; i++){
10688             if(fn.call(scope || els[i], els[i], this, i) === false) {
10689                 break;
10690             }
10691         }
10692         return this;
10693     },
10694
10695     /**
10696      * Returns the Element object at the specified index
10697      * @param {Number} index
10698      * @return {Roo.Element}
10699      */
10700     item : function(index){
10701         return this.elements[index] || null;
10702     },
10703
10704     /**
10705      * Returns the first Element
10706      * @return {Roo.Element}
10707      */
10708     first : function(){
10709         return this.item(0);
10710     },
10711
10712     /**
10713      * Returns the last Element
10714      * @return {Roo.Element}
10715      */
10716     last : function(){
10717         return this.item(this.elements.length-1);
10718     },
10719
10720     /**
10721      * Returns the number of elements in this composite
10722      * @return Number
10723      */
10724     getCount : function(){
10725         return this.elements.length;
10726     },
10727
10728     /**
10729      * Returns true if this composite contains the passed element
10730      * @return Boolean
10731      */
10732     contains : function(el){
10733         return this.indexOf(el) !== -1;
10734     },
10735
10736     /**
10737      * Returns true if this composite contains the passed element
10738      * @return Boolean
10739      */
10740     indexOf : function(el){
10741         return this.elements.indexOf(Roo.get(el));
10742     },
10743
10744
10745     /**
10746     * Removes the specified element(s).
10747     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10748     * or an array of any of those.
10749     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10750     * @return {CompositeElement} this
10751     */
10752     removeElement : function(el, removeDom){
10753         if(el instanceof Array){
10754             for(var i = 0, len = el.length; i < len; i++){
10755                 this.removeElement(el[i]);
10756             }
10757             return this;
10758         }
10759         var index = typeof el == 'number' ? el : this.indexOf(el);
10760         if(index !== -1){
10761             if(removeDom){
10762                 var d = this.elements[index];
10763                 if(d.dom){
10764                     d.remove();
10765                 }else{
10766                     d.parentNode.removeChild(d);
10767                 }
10768             }
10769             this.elements.splice(index, 1);
10770         }
10771         return this;
10772     },
10773
10774     /**
10775     * Replaces the specified element with the passed element.
10776     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10777     * to replace.
10778     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10779     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10780     * @return {CompositeElement} this
10781     */
10782     replaceElement : function(el, replacement, domReplace){
10783         var index = typeof el == 'number' ? el : this.indexOf(el);
10784         if(index !== -1){
10785             if(domReplace){
10786                 this.elements[index].replaceWith(replacement);
10787             }else{
10788                 this.elements.splice(index, 1, Roo.get(replacement))
10789             }
10790         }
10791         return this;
10792     },
10793
10794     /**
10795      * Removes all elements.
10796      */
10797     clear : function(){
10798         this.elements = [];
10799     }
10800 };
10801 (function(){
10802     Roo.CompositeElement.createCall = function(proto, fnName){
10803         if(!proto[fnName]){
10804             proto[fnName] = function(){
10805                 return this.invoke(fnName, arguments);
10806             };
10807         }
10808     };
10809     for(var fnName in Roo.Element.prototype){
10810         if(typeof Roo.Element.prototype[fnName] == "function"){
10811             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10812         }
10813     };
10814 })();
10815 /*
10816  * Based on:
10817  * Ext JS Library 1.1.1
10818  * Copyright(c) 2006-2007, Ext JS, LLC.
10819  *
10820  * Originally Released Under LGPL - original licence link has changed is not relivant.
10821  *
10822  * Fork - LGPL
10823  * <script type="text/javascript">
10824  */
10825
10826 /**
10827  * @class Roo.CompositeElementLite
10828  * @extends Roo.CompositeElement
10829  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10830  <pre><code>
10831  var els = Roo.select("#some-el div.some-class");
10832  // or select directly from an existing element
10833  var el = Roo.get('some-el');
10834  el.select('div.some-class');
10835
10836  els.setWidth(100); // all elements become 100 width
10837  els.hide(true); // all elements fade out and hide
10838  // or
10839  els.setWidth(100).hide(true);
10840  </code></pre><br><br>
10841  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10842  * actions will be performed on all the elements in this collection.</b>
10843  */
10844 Roo.CompositeElementLite = function(els){
10845     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10846     this.el = new Roo.Element.Flyweight();
10847 };
10848 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10849     addElements : function(els){
10850         if(els){
10851             if(els instanceof Array){
10852                 this.elements = this.elements.concat(els);
10853             }else{
10854                 var yels = this.elements;
10855                 var index = yels.length-1;
10856                 for(var i = 0, len = els.length; i < len; i++) {
10857                     yels[++index] = els[i];
10858                 }
10859             }
10860         }
10861         return this;
10862     },
10863     invoke : function(fn, args){
10864         var els = this.elements;
10865         var el = this.el;
10866         for(var i = 0, len = els.length; i < len; i++) {
10867             el.dom = els[i];
10868                 Roo.Element.prototype[fn].apply(el, args);
10869         }
10870         return this;
10871     },
10872     /**
10873      * Returns a flyweight Element of the dom element object at the specified index
10874      * @param {Number} index
10875      * @return {Roo.Element}
10876      */
10877     item : function(index){
10878         if(!this.elements[index]){
10879             return null;
10880         }
10881         this.el.dom = this.elements[index];
10882         return this.el;
10883     },
10884
10885     // fixes scope with flyweight
10886     addListener : function(eventName, handler, scope, opt){
10887         var els = this.elements;
10888         for(var i = 0, len = els.length; i < len; i++) {
10889             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10890         }
10891         return this;
10892     },
10893
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10896     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10897     * a reference to the dom node, use el.dom.</b>
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         var el = this.el;
10905         for(var i = 0, len = els.length; i < len; i++){
10906             el.dom = els[i];
10907                 if(fn.call(scope || el, el, this, i) === false){
10908                 break;
10909             }
10910         }
10911         return this;
10912     },
10913
10914     indexOf : function(el){
10915         return this.elements.indexOf(Roo.getDom(el));
10916     },
10917
10918     replaceElement : function(el, replacement, domReplace){
10919         var index = typeof el == 'number' ? el : this.indexOf(el);
10920         if(index !== -1){
10921             replacement = Roo.getDom(replacement);
10922             if(domReplace){
10923                 var d = this.elements[index];
10924                 d.parentNode.insertBefore(replacement, d);
10925                 d.parentNode.removeChild(d);
10926             }
10927             this.elements.splice(index, 1, replacement);
10928         }
10929         return this;
10930     }
10931 });
10932 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10933
10934 /*
10935  * Based on:
10936  * Ext JS Library 1.1.1
10937  * Copyright(c) 2006-2007, Ext JS, LLC.
10938  *
10939  * Originally Released Under LGPL - original licence link has changed is not relivant.
10940  *
10941  * Fork - LGPL
10942  * <script type="text/javascript">
10943  */
10944
10945  
10946
10947 /**
10948  * @class Roo.data.Connection
10949  * @extends Roo.util.Observable
10950  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10951  * either to a configured URL, or to a URL specified at request time.<br><br>
10952  * <p>
10953  * Requests made by this class are asynchronous, and will return immediately. No data from
10954  * the server will be available to the statement immediately following the {@link #request} call.
10955  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10956  * <p>
10957  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10958  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10959  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10960  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10961  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10962  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10963  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10964  * standard DOM methods.
10965  * @constructor
10966  * @param {Object} config a configuration object.
10967  */
10968 Roo.data.Connection = function(config){
10969     Roo.apply(this, config);
10970     this.addEvents({
10971         /**
10972          * @event beforerequest
10973          * Fires before a network request is made to retrieve a data object.
10974          * @param {Connection} conn This Connection object.
10975          * @param {Object} options The options config object passed to the {@link #request} method.
10976          */
10977         "beforerequest" : true,
10978         /**
10979          * @event requestcomplete
10980          * Fires if the request was successfully completed.
10981          * @param {Connection} conn This Connection object.
10982          * @param {Object} response The XHR object containing the response data.
10983          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10984          * @param {Object} options The options config object passed to the {@link #request} method.
10985          */
10986         "requestcomplete" : true,
10987         /**
10988          * @event requestexception
10989          * Fires if an error HTTP status was returned from the server.
10990          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10991          * @param {Connection} conn This Connection object.
10992          * @param {Object} response The XHR object containing the response data.
10993          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10994          * @param {Object} options The options config object passed to the {@link #request} method.
10995          */
10996         "requestexception" : true
10997     });
10998     Roo.data.Connection.superclass.constructor.call(this);
10999 };
11000
11001 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11002     /**
11003      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11004      */
11005     /**
11006      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11007      * extra parameters to each request made by this object. (defaults to undefined)
11008      */
11009     /**
11010      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11011      *  to each request made by this object. (defaults to undefined)
11012      */
11013     /**
11014      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11015      */
11016     /**
11017      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11018      */
11019     timeout : 30000,
11020     /**
11021      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11022      * @type Boolean
11023      */
11024     autoAbort:false,
11025
11026     /**
11027      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11028      * @type Boolean
11029      */
11030     disableCaching: true,
11031
11032     /**
11033      * Sends an HTTP request to a remote server.
11034      * @param {Object} options An object which may contain the following properties:<ul>
11035      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11036      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11037      * request, a url encoded string or a function to call to get either.</li>
11038      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11039      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11040      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11041      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11042      * <li>options {Object} The parameter to the request call.</li>
11043      * <li>success {Boolean} True if the request succeeded.</li>
11044      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11045      * </ul></li>
11046      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11047      * The callback is passed the following parameters:<ul>
11048      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11049      * <li>options {Object} The parameter to the request call.</li>
11050      * </ul></li>
11051      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11052      * The callback is passed the following parameters:<ul>
11053      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11054      * <li>options {Object} The parameter to the request call.</li>
11055      * </ul></li>
11056      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11057      * for the callback function. Defaults to the browser window.</li>
11058      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11059      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11060      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11061      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11062      * params for the post data. Any params will be appended to the URL.</li>
11063      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11064      * </ul>
11065      * @return {Number} transactionId
11066      */
11067     request : function(o){
11068         if(this.fireEvent("beforerequest", this, o) !== false){
11069             var p = o.params;
11070
11071             if(typeof p == "function"){
11072                 p = p.call(o.scope||window, o);
11073             }
11074             if(typeof p == "object"){
11075                 p = Roo.urlEncode(o.params);
11076             }
11077             if(this.extraParams){
11078                 var extras = Roo.urlEncode(this.extraParams);
11079                 p = p ? (p + '&' + extras) : extras;
11080             }
11081
11082             var url = o.url || this.url;
11083             if(typeof url == 'function'){
11084                 url = url.call(o.scope||window, o);
11085             }
11086
11087             if(o.form){
11088                 var form = Roo.getDom(o.form);
11089                 url = url || form.action;
11090
11091                 var enctype = form.getAttribute("enctype");
11092                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11093                     return this.doFormUpload(o, p, url);
11094                 }
11095                 var f = Roo.lib.Ajax.serializeForm(form);
11096                 p = p ? (p + '&' + f) : f;
11097             }
11098
11099             var hs = o.headers;
11100             if(this.defaultHeaders){
11101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11102                 if(!o.headers){
11103                     o.headers = hs;
11104                 }
11105             }
11106
11107             var cb = {
11108                 success: this.handleResponse,
11109                 failure: this.handleFailure,
11110                 scope: this,
11111                 argument: {options: o},
11112                 timeout : this.timeout
11113             };
11114
11115             var method = o.method||this.method||(p ? "POST" : "GET");
11116
11117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11119             }
11120
11121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11122                 if(o.autoAbort){
11123                     this.abort();
11124                 }
11125             }else if(this.autoAbort !== false){
11126                 this.abort();
11127             }
11128
11129             if((method == 'GET' && p) || o.xmlData){
11130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11131                 p = '';
11132             }
11133             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11134             return this.transId;
11135         }else{
11136             Roo.callback(o.callback, o.scope, [o, null, null]);
11137             return null;
11138         }
11139     },
11140
11141     /**
11142      * Determine whether this object has a request outstanding.
11143      * @param {Number} transactionId (Optional) defaults to the last transaction
11144      * @return {Boolean} True if there is an outstanding request.
11145      */
11146     isLoading : function(transId){
11147         if(transId){
11148             return Roo.lib.Ajax.isCallInProgress(transId);
11149         }else{
11150             return this.transId ? true : false;
11151         }
11152     },
11153
11154     /**
11155      * Aborts any outstanding request.
11156      * @param {Number} transactionId (Optional) defaults to the last transaction
11157      */
11158     abort : function(transId){
11159         if(transId || this.isLoading()){
11160             Roo.lib.Ajax.abort(transId || this.transId);
11161         }
11162     },
11163
11164     // private
11165     handleResponse : function(response){
11166         this.transId = false;
11167         var options = response.argument.options;
11168         response.argument = options ? options.argument : null;
11169         this.fireEvent("requestcomplete", this, response, options);
11170         Roo.callback(options.success, options.scope, [response, options]);
11171         Roo.callback(options.callback, options.scope, [options, true, response]);
11172     },
11173
11174     // private
11175     handleFailure : function(response, e){
11176         this.transId = false;
11177         var options = response.argument.options;
11178         response.argument = options ? options.argument : null;
11179         this.fireEvent("requestexception", this, response, options, e);
11180         Roo.callback(options.failure, options.scope, [response, options]);
11181         Roo.callback(options.callback, options.scope, [options, false, response]);
11182     },
11183
11184     // private
11185     doFormUpload : function(o, ps, url){
11186         var id = Roo.id();
11187         var frame = document.createElement('iframe');
11188         frame.id = id;
11189         frame.name = id;
11190         frame.className = 'x-hidden';
11191         if(Roo.isIE){
11192             frame.src = Roo.SSL_SECURE_URL;
11193         }
11194         document.body.appendChild(frame);
11195
11196         if(Roo.isIE){
11197            document.frames[id].name = id;
11198         }
11199
11200         var form = Roo.getDom(o.form);
11201         form.target = id;
11202         form.method = 'POST';
11203         form.enctype = form.encoding = 'multipart/form-data';
11204         if(url){
11205             form.action = url;
11206         }
11207
11208         var hiddens, hd;
11209         if(ps){ // add dynamic params
11210             hiddens = [];
11211             ps = Roo.urlDecode(ps, false);
11212             for(var k in ps){
11213                 if(ps.hasOwnProperty(k)){
11214                     hd = document.createElement('input');
11215                     hd.type = 'hidden';
11216                     hd.name = k;
11217                     hd.value = ps[k];
11218                     form.appendChild(hd);
11219                     hiddens.push(hd);
11220                 }
11221             }
11222         }
11223
11224         function cb(){
11225             var r = {  // bogus response object
11226                 responseText : '',
11227                 responseXML : null
11228             };
11229
11230             r.argument = o ? o.argument : null;
11231
11232             try { //
11233                 var doc;
11234                 if(Roo.isIE){
11235                     doc = frame.contentWindow.document;
11236                 }else {
11237                     doc = (frame.contentDocument || window.frames[id].document);
11238                 }
11239                 if(doc && doc.body){
11240                     r.responseText = doc.body.innerHTML;
11241                 }
11242                 if(doc && doc.XMLDocument){
11243                     r.responseXML = doc.XMLDocument;
11244                 }else {
11245                     r.responseXML = doc;
11246                 }
11247             }
11248             catch(e) {
11249                 // ignore
11250             }
11251
11252             Roo.EventManager.removeListener(frame, 'load', cb, this);
11253
11254             this.fireEvent("requestcomplete", this, r, o);
11255             Roo.callback(o.success, o.scope, [r, o]);
11256             Roo.callback(o.callback, o.scope, [o, true, r]);
11257
11258             setTimeout(function(){document.body.removeChild(frame);}, 100);
11259         }
11260
11261         Roo.EventManager.on(frame, 'load', cb, this);
11262         form.submit();
11263
11264         if(hiddens){ // remove dynamic params
11265             for(var i = 0, len = hiddens.length; i < len; i++){
11266                 form.removeChild(hiddens[i]);
11267             }
11268         }
11269     }
11270 });
11271
11272 /**
11273  * @class Roo.Ajax
11274  * @extends Roo.data.Connection
11275  * Global Ajax request class.
11276  *
11277  * @singleton
11278  */
11279 Roo.Ajax = new Roo.data.Connection({
11280     // fix up the docs
11281    /**
11282      * @cfg {String} url @hide
11283      */
11284     /**
11285      * @cfg {Object} extraParams @hide
11286      */
11287     /**
11288      * @cfg {Object} defaultHeaders @hide
11289      */
11290     /**
11291      * @cfg {String} method (Optional) @hide
11292      */
11293     /**
11294      * @cfg {Number} timeout (Optional) @hide
11295      */
11296     /**
11297      * @cfg {Boolean} autoAbort (Optional) @hide
11298      */
11299
11300     /**
11301      * @cfg {Boolean} disableCaching (Optional) @hide
11302      */
11303
11304     /**
11305      * @property  disableCaching
11306      * True to add a unique cache-buster param to GET requests. (defaults to true)
11307      * @type Boolean
11308      */
11309     /**
11310      * @property  url
11311      * The default URL to be used for requests to the server. (defaults to undefined)
11312      * @type String
11313      */
11314     /**
11315      * @property  extraParams
11316      * An object containing properties which are used as
11317      * extra parameters to each request made by this object. (defaults to undefined)
11318      * @type Object
11319      */
11320     /**
11321      * @property  defaultHeaders
11322      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11323      * @type Object
11324      */
11325     /**
11326      * @property  method
11327      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11328      * @type String
11329      */
11330     /**
11331      * @property  timeout
11332      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11333      * @type Number
11334      */
11335
11336     /**
11337      * @property  autoAbort
11338      * Whether a new request should abort any pending requests. (defaults to false)
11339      * @type Boolean
11340      */
11341     autoAbort : false,
11342
11343     /**
11344      * Serialize the passed form into a url encoded string
11345      * @param {String/HTMLElement} form
11346      * @return {String}
11347      */
11348     serializeForm : function(form){
11349         return Roo.lib.Ajax.serializeForm(form);
11350     }
11351 });/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361  
11362 /**
11363  * @class Roo.Ajax
11364  * @extends Roo.data.Connection
11365  * Global Ajax request class.
11366  *
11367  * @instanceOf  Roo.data.Connection
11368  */
11369 Roo.Ajax = new Roo.data.Connection({
11370     // fix up the docs
11371     
11372     /**
11373      * fix up scoping
11374      * @scope Roo.Ajax
11375      */
11376     
11377    /**
11378      * @cfg {String} url @hide
11379      */
11380     /**
11381      * @cfg {Object} extraParams @hide
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders @hide
11385      */
11386     /**
11387      * @cfg {String} method (Optional) @hide
11388      */
11389     /**
11390      * @cfg {Number} timeout (Optional) @hide
11391      */
11392     /**
11393      * @cfg {Boolean} autoAbort (Optional) @hide
11394      */
11395
11396     /**
11397      * @cfg {Boolean} disableCaching (Optional) @hide
11398      */
11399
11400     /**
11401      * @property  disableCaching
11402      * True to add a unique cache-buster param to GET requests. (defaults to true)
11403      * @type Boolean
11404      */
11405     /**
11406      * @property  url
11407      * The default URL to be used for requests to the server. (defaults to undefined)
11408      * @type String
11409      */
11410     /**
11411      * @property  extraParams
11412      * An object containing properties which are used as
11413      * extra parameters to each request made by this object. (defaults to undefined)
11414      * @type Object
11415      */
11416     /**
11417      * @property  defaultHeaders
11418      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11419      * @type Object
11420      */
11421     /**
11422      * @property  method
11423      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11424      * @type String
11425      */
11426     /**
11427      * @property  timeout
11428      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11429      * @type Number
11430      */
11431
11432     /**
11433      * @property  autoAbort
11434      * Whether a new request should abort any pending requests. (defaults to false)
11435      * @type Boolean
11436      */
11437     autoAbort : false,
11438
11439     /**
11440      * Serialize the passed form into a url encoded string
11441      * @param {String/HTMLElement} form
11442      * @return {String}
11443      */
11444     serializeForm : function(form){
11445         return Roo.lib.Ajax.serializeForm(form);
11446     }
11447 });/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458  
11459 /**
11460  * @class Roo.UpdateManager
11461  * @extends Roo.util.Observable
11462  * Provides AJAX-style update for Element object.<br><br>
11463  * Usage:<br>
11464  * <pre><code>
11465  * // Get it from a Roo.Element object
11466  * var el = Roo.get("foo");
11467  * var mgr = el.getUpdateManager();
11468  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11469  * ...
11470  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11471  * <br>
11472  * // or directly (returns the same UpdateManager instance)
11473  * var mgr = new Roo.UpdateManager("myElementId");
11474  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11475  * mgr.on("update", myFcnNeedsToKnow);
11476  * <br>
11477    // short handed call directly from the element object
11478    Roo.get("foo").load({
11479         url: "bar.php",
11480         scripts:true,
11481         params: "for=bar",
11482         text: "Loading Foo..."
11483    });
11484  * </code></pre>
11485  * @constructor
11486  * Create new UpdateManager directly.
11487  * @param {String/HTMLElement/Roo.Element} el The element to update
11488  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11489  */
11490 Roo.UpdateManager = function(el, forceNew){
11491     el = Roo.get(el);
11492     if(!forceNew && el.updateManager){
11493         return el.updateManager;
11494     }
11495     /**
11496      * The Element object
11497      * @type Roo.Element
11498      */
11499     this.el = el;
11500     /**
11501      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11502      * @type String
11503      */
11504     this.defaultUrl = null;
11505
11506     this.addEvents({
11507         /**
11508          * @event beforeupdate
11509          * Fired before an update is made, return false from your handler and the update is cancelled.
11510          * @param {Roo.Element} el
11511          * @param {String/Object/Function} url
11512          * @param {String/Object} params
11513          */
11514         "beforeupdate": true,
11515         /**
11516          * @event update
11517          * Fired after successful update is made.
11518          * @param {Roo.Element} el
11519          * @param {Object} oResponseObject The response Object
11520          */
11521         "update": true,
11522         /**
11523          * @event failure
11524          * Fired on update failure.
11525          * @param {Roo.Element} el
11526          * @param {Object} oResponseObject The response Object
11527          */
11528         "failure": true
11529     });
11530     var d = Roo.UpdateManager.defaults;
11531     /**
11532      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11533      * @type String
11534      */
11535     this.sslBlankUrl = d.sslBlankUrl;
11536     /**
11537      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11538      * @type Boolean
11539      */
11540     this.disableCaching = d.disableCaching;
11541     /**
11542      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11543      * @type String
11544      */
11545     this.indicatorText = d.indicatorText;
11546     /**
11547      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11548      * @type String
11549      */
11550     this.showLoadIndicator = d.showLoadIndicator;
11551     /**
11552      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11553      * @type Number
11554      */
11555     this.timeout = d.timeout;
11556
11557     /**
11558      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11559      * @type Boolean
11560      */
11561     this.loadScripts = d.loadScripts;
11562
11563     /**
11564      * Transaction object of current executing transaction
11565      */
11566     this.transaction = null;
11567
11568     /**
11569      * @private
11570      */
11571     this.autoRefreshProcId = null;
11572     /**
11573      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11574      * @type Function
11575      */
11576     this.refreshDelegate = this.refresh.createDelegate(this);
11577     /**
11578      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11579      * @type Function
11580      */
11581     this.updateDelegate = this.update.createDelegate(this);
11582     /**
11583      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11584      * @type Function
11585      */
11586     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11587     /**
11588      * @private
11589      */
11590     this.successDelegate = this.processSuccess.createDelegate(this);
11591     /**
11592      * @private
11593      */
11594     this.failureDelegate = this.processFailure.createDelegate(this);
11595
11596     if(!this.renderer){
11597      /**
11598       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11599       */
11600     this.renderer = new Roo.UpdateManager.BasicRenderer();
11601     }
11602     
11603     Roo.UpdateManager.superclass.constructor.call(this);
11604 };
11605
11606 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11607     /**
11608      * Get the Element this UpdateManager is bound to
11609      * @return {Roo.Element} The element
11610      */
11611     getEl : function(){
11612         return this.el;
11613     },
11614     /**
11615      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11616      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11617 <pre><code>
11618 um.update({<br/>
11619     url: "your-url.php",<br/>
11620     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11621     callback: yourFunction,<br/>
11622     scope: yourObject, //(optional scope)  <br/>
11623     discardUrl: false, <br/>
11624     nocache: false,<br/>
11625     text: "Loading...",<br/>
11626     timeout: 30,<br/>
11627     scripts: false<br/>
11628 });
11629 </code></pre>
11630      * The only required property is url. The optional properties nocache, text and scripts
11631      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11632      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11634      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11635      */
11636     update : function(url, params, callback, discardUrl){
11637         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11638             var method = this.method, cfg;
11639             if(typeof url == "object"){ // must be config object
11640                 cfg = url;
11641                 url = cfg.url;
11642                 params = params || cfg.params;
11643                 callback = callback || cfg.callback;
11644                 discardUrl = discardUrl || cfg.discardUrl;
11645                 if(callback && cfg.scope){
11646                     callback = callback.createDelegate(cfg.scope);
11647                 }
11648                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11649                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11650                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11651                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11652                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11653             }
11654             this.showLoading();
11655             if(!discardUrl){
11656                 this.defaultUrl = url;
11657             }
11658             if(typeof url == "function"){
11659                 url = url.call(this);
11660             }
11661
11662             method = method || (params ? "POST" : "GET");
11663             if(method == "GET"){
11664                 url = this.prepareUrl(url);
11665             }
11666
11667             var o = Roo.apply(cfg ||{}, {
11668                 url : url,
11669                 params: params,
11670                 success: this.successDelegate,
11671                 failure: this.failureDelegate,
11672                 callback: undefined,
11673                 timeout: (this.timeout*1000),
11674                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11675             });
11676
11677             this.transaction = Roo.Ajax.request(o);
11678         }
11679     },
11680
11681     /**
11682      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11683      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11684      * @param {String/HTMLElement} form The form Id or form element
11685      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11686      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11687      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11688      */
11689     formUpdate : function(form, url, reset, callback){
11690         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11691             if(typeof url == "function"){
11692                 url = url.call(this);
11693             }
11694             form = Roo.getDom(form);
11695             this.transaction = Roo.Ajax.request({
11696                 form: form,
11697                 url:url,
11698                 success: this.successDelegate,
11699                 failure: this.failureDelegate,
11700                 timeout: (this.timeout*1000),
11701                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11702             });
11703             this.showLoading.defer(1, this);
11704         }
11705     },
11706
11707     /**
11708      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11709      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11710      */
11711     refresh : function(callback){
11712         if(this.defaultUrl == null){
11713             return;
11714         }
11715         this.update(this.defaultUrl, null, callback, true);
11716     },
11717
11718     /**
11719      * Set this element to auto refresh.
11720      * @param {Number} interval How often to update (in seconds).
11721      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11722      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11723      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11724      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11725      */
11726     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11727         if(refreshNow){
11728             this.update(url || this.defaultUrl, params, callback, true);
11729         }
11730         if(this.autoRefreshProcId){
11731             clearInterval(this.autoRefreshProcId);
11732         }
11733         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11734     },
11735
11736     /**
11737      * Stop auto refresh on this element.
11738      */
11739      stopAutoRefresh : function(){
11740         if(this.autoRefreshProcId){
11741             clearInterval(this.autoRefreshProcId);
11742             delete this.autoRefreshProcId;
11743         }
11744     },
11745
11746     isAutoRefreshing : function(){
11747        return this.autoRefreshProcId ? true : false;
11748     },
11749     /**
11750      * Called to update the element to "Loading" state. Override to perform custom action.
11751      */
11752     showLoading : function(){
11753         if(this.showLoadIndicator){
11754             this.el.update(this.indicatorText);
11755         }
11756     },
11757
11758     /**
11759      * Adds unique parameter to query string if disableCaching = true
11760      * @private
11761      */
11762     prepareUrl : function(url){
11763         if(this.disableCaching){
11764             var append = "_dc=" + (new Date().getTime());
11765             if(url.indexOf("?") !== -1){
11766                 url += "&" + append;
11767             }else{
11768                 url += "?" + append;
11769             }
11770         }
11771         return url;
11772     },
11773
11774     /**
11775      * @private
11776      */
11777     processSuccess : function(response){
11778         this.transaction = null;
11779         if(response.argument.form && response.argument.reset){
11780             try{ // put in try/catch since some older FF releases had problems with this
11781                 response.argument.form.reset();
11782             }catch(e){}
11783         }
11784         if(this.loadScripts){
11785             this.renderer.render(this.el, response, this,
11786                 this.updateComplete.createDelegate(this, [response]));
11787         }else{
11788             this.renderer.render(this.el, response, this);
11789             this.updateComplete(response);
11790         }
11791     },
11792
11793     updateComplete : function(response){
11794         this.fireEvent("update", this.el, response);
11795         if(typeof response.argument.callback == "function"){
11796             response.argument.callback(this.el, true, response);
11797         }
11798     },
11799
11800     /**
11801      * @private
11802      */
11803     processFailure : function(response){
11804         this.transaction = null;
11805         this.fireEvent("failure", this.el, response);
11806         if(typeof response.argument.callback == "function"){
11807             response.argument.callback(this.el, false, response);
11808         }
11809     },
11810
11811     /**
11812      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11813      * @param {Object} renderer The object implementing the render() method
11814      */
11815     setRenderer : function(renderer){
11816         this.renderer = renderer;
11817     },
11818
11819     getRenderer : function(){
11820        return this.renderer;
11821     },
11822
11823     /**
11824      * Set the defaultUrl used for updates
11825      * @param {String/Function} defaultUrl The url or a function to call to get the url
11826      */
11827     setDefaultUrl : function(defaultUrl){
11828         this.defaultUrl = defaultUrl;
11829     },
11830
11831     /**
11832      * Aborts the executing transaction
11833      */
11834     abort : function(){
11835         if(this.transaction){
11836             Roo.Ajax.abort(this.transaction);
11837         }
11838     },
11839
11840     /**
11841      * Returns true if an update is in progress
11842      * @return {Boolean}
11843      */
11844     isUpdating : function(){
11845         if(this.transaction){
11846             return Roo.Ajax.isLoading(this.transaction);
11847         }
11848         return false;
11849     }
11850 });
11851
11852 /**
11853  * @class Roo.UpdateManager.defaults
11854  * @static (not really - but it helps the doc tool)
11855  * The defaults collection enables customizing the default properties of UpdateManager
11856  */
11857    Roo.UpdateManager.defaults = {
11858        /**
11859          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11860          * @type Number
11861          */
11862          timeout : 30,
11863
11864          /**
11865          * True to process scripts by default (Defaults to false).
11866          * @type Boolean
11867          */
11868         loadScripts : false,
11869
11870         /**
11871         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11872         * @type String
11873         */
11874         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11875         /**
11876          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11877          * @type Boolean
11878          */
11879         disableCaching : false,
11880         /**
11881          * Whether to show indicatorText when loading (Defaults to true).
11882          * @type Boolean
11883          */
11884         showLoadIndicator : true,
11885         /**
11886          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11887          * @type String
11888          */
11889         indicatorText : '<div class="loading-indicator">Loading...</div>'
11890    };
11891
11892 /**
11893  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11894  *Usage:
11895  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11896  * @param {String/HTMLElement/Roo.Element} el The element to update
11897  * @param {String} url The url
11898  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11899  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11900  * @static
11901  * @deprecated
11902  * @member Roo.UpdateManager
11903  */
11904 Roo.UpdateManager.updateElement = function(el, url, params, options){
11905     var um = Roo.get(el, true).getUpdateManager();
11906     Roo.apply(um, options);
11907     um.update(url, params, options ? options.callback : null);
11908 };
11909 // alias for backwards compat
11910 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11911 /**
11912  * @class Roo.UpdateManager.BasicRenderer
11913  * Default Content renderer. Updates the elements innerHTML with the responseText.
11914  */
11915 Roo.UpdateManager.BasicRenderer = function(){};
11916
11917 Roo.UpdateManager.BasicRenderer.prototype = {
11918     /**
11919      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11920      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11921      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11922      * @param {Roo.Element} el The element being rendered
11923      * @param {Object} response The YUI Connect response object
11924      * @param {UpdateManager} updateManager The calling update manager
11925      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11926      */
11927      render : function(el, response, updateManager, callback){
11928         el.update(response.responseText, updateManager.loadScripts, callback);
11929     }
11930 };
11931 /*
11932  * Based on:
11933  * Ext JS Library 1.1.1
11934  * Copyright(c) 2006-2007, Ext JS, LLC.
11935  *
11936  * Originally Released Under LGPL - original licence link has changed is not relivant.
11937  *
11938  * Fork - LGPL
11939  * <script type="text/javascript">
11940  */
11941
11942 /**
11943  * @class Roo.util.DelayedTask
11944  * Provides a convenient method of performing setTimeout where a new
11945  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11946  * You can use this class to buffer
11947  * the keypress events for a certain number of milliseconds, and perform only if they stop
11948  * for that amount of time.
11949  * @constructor The parameters to this constructor serve as defaults and are not required.
11950  * @param {Function} fn (optional) The default function to timeout
11951  * @param {Object} scope (optional) The default scope of that timeout
11952  * @param {Array} args (optional) The default Array of arguments
11953  */
11954 Roo.util.DelayedTask = function(fn, scope, args){
11955     var id = null, d, t;
11956
11957     var call = function(){
11958         var now = new Date().getTime();
11959         if(now - t >= d){
11960             clearInterval(id);
11961             id = null;
11962             fn.apply(scope, args || []);
11963         }
11964     };
11965     /**
11966      * Cancels any pending timeout and queues a new one
11967      * @param {Number} delay The milliseconds to delay
11968      * @param {Function} newFn (optional) Overrides function passed to constructor
11969      * @param {Object} newScope (optional) Overrides scope passed to constructor
11970      * @param {Array} newArgs (optional) Overrides args passed to constructor
11971      */
11972     this.delay = function(delay, newFn, newScope, newArgs){
11973         if(id && delay != d){
11974             this.cancel();
11975         }
11976         d = delay;
11977         t = new Date().getTime();
11978         fn = newFn || fn;
11979         scope = newScope || scope;
11980         args = newArgs || args;
11981         if(!id){
11982             id = setInterval(call, d);
11983         }
11984     };
11985
11986     /**
11987      * Cancel the last queued timeout
11988      */
11989     this.cancel = function(){
11990         if(id){
11991             clearInterval(id);
11992             id = null;
11993         }
11994     };
11995 };/*
11996  * Based on:
11997  * Ext JS Library 1.1.1
11998  * Copyright(c) 2006-2007, Ext JS, LLC.
11999  *
12000  * Originally Released Under LGPL - original licence link has changed is not relivant.
12001  *
12002  * Fork - LGPL
12003  * <script type="text/javascript">
12004  */
12005  
12006  
12007 Roo.util.TaskRunner = function(interval){
12008     interval = interval || 10;
12009     var tasks = [], removeQueue = [];
12010     var id = 0;
12011     var running = false;
12012
12013     var stopThread = function(){
12014         running = false;
12015         clearInterval(id);
12016         id = 0;
12017     };
12018
12019     var startThread = function(){
12020         if(!running){
12021             running = true;
12022             id = setInterval(runTasks, interval);
12023         }
12024     };
12025
12026     var removeTask = function(task){
12027         removeQueue.push(task);
12028         if(task.onStop){
12029             task.onStop();
12030         }
12031     };
12032
12033     var runTasks = function(){
12034         if(removeQueue.length > 0){
12035             for(var i = 0, len = removeQueue.length; i < len; i++){
12036                 tasks.remove(removeQueue[i]);
12037             }
12038             removeQueue = [];
12039             if(tasks.length < 1){
12040                 stopThread();
12041                 return;
12042             }
12043         }
12044         var now = new Date().getTime();
12045         for(var i = 0, len = tasks.length; i < len; ++i){
12046             var t = tasks[i];
12047             var itime = now - t.taskRunTime;
12048             if(t.interval <= itime){
12049                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12050                 t.taskRunTime = now;
12051                 if(rt === false || t.taskRunCount === t.repeat){
12052                     removeTask(t);
12053                     return;
12054                 }
12055             }
12056             if(t.duration && t.duration <= (now - t.taskStartTime)){
12057                 removeTask(t);
12058             }
12059         }
12060     };
12061
12062     /**
12063      * Queues a new task.
12064      * @param {Object} task
12065      */
12066     this.start = function(task){
12067         tasks.push(task);
12068         task.taskStartTime = new Date().getTime();
12069         task.taskRunTime = 0;
12070         task.taskRunCount = 0;
12071         startThread();
12072         return task;
12073     };
12074
12075     this.stop = function(task){
12076         removeTask(task);
12077         return task;
12078     };
12079
12080     this.stopAll = function(){
12081         stopThread();
12082         for(var i = 0, len = tasks.length; i < len; i++){
12083             if(tasks[i].onStop){
12084                 tasks[i].onStop();
12085             }
12086         }
12087         tasks = [];
12088         removeQueue = [];
12089     };
12090 };
12091
12092 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103  
12104 /**
12105  * @class Roo.util.MixedCollection
12106  * @extends Roo.util.Observable
12107  * A Collection class that maintains both numeric indexes and keys and exposes events.
12108  * @constructor
12109  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12110  * collection (defaults to false)
12111  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12112  * and return the key value for that item.  This is used when available to look up the key on items that
12113  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12114  * equivalent to providing an implementation for the {@link #getKey} method.
12115  */
12116 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12117     this.items = [];
12118     this.map = {};
12119     this.keys = [];
12120     this.length = 0;
12121     this.addEvents({
12122         /**
12123          * @event clear
12124          * Fires when the collection is cleared.
12125          */
12126         "clear" : true,
12127         /**
12128          * @event add
12129          * Fires when an item is added to the collection.
12130          * @param {Number} index The index at which the item was added.
12131          * @param {Object} o The item added.
12132          * @param {String} key The key associated with the added item.
12133          */
12134         "add" : true,
12135         /**
12136          * @event replace
12137          * Fires when an item is replaced in the collection.
12138          * @param {String} key he key associated with the new added.
12139          * @param {Object} old The item being replaced.
12140          * @param {Object} new The new item.
12141          */
12142         "replace" : true,
12143         /**
12144          * @event remove
12145          * Fires when an item is removed from the collection.
12146          * @param {Object} o The item being removed.
12147          * @param {String} key (optional) The key associated with the removed item.
12148          */
12149         "remove" : true,
12150         "sort" : true
12151     });
12152     this.allowFunctions = allowFunctions === true;
12153     if(keyFn){
12154         this.getKey = keyFn;
12155     }
12156     Roo.util.MixedCollection.superclass.constructor.call(this);
12157 };
12158
12159 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12160     allowFunctions : false,
12161     
12162 /**
12163  * Adds an item to the collection.
12164  * @param {String} key The key to associate with the item
12165  * @param {Object} o The item to add.
12166  * @return {Object} The item added.
12167  */
12168     add : function(key, o){
12169         if(arguments.length == 1){
12170             o = arguments[0];
12171             key = this.getKey(o);
12172         }
12173         if(typeof key == "undefined" || key === null){
12174             this.length++;
12175             this.items.push(o);
12176             this.keys.push(null);
12177         }else{
12178             var old = this.map[key];
12179             if(old){
12180                 return this.replace(key, o);
12181             }
12182             this.length++;
12183             this.items.push(o);
12184             this.map[key] = o;
12185             this.keys.push(key);
12186         }
12187         this.fireEvent("add", this.length-1, o, key);
12188         return o;
12189     },
12190    
12191 /**
12192   * MixedCollection has a generic way to fetch keys if you implement getKey.
12193 <pre><code>
12194 // normal way
12195 var mc = new Roo.util.MixedCollection();
12196 mc.add(someEl.dom.id, someEl);
12197 mc.add(otherEl.dom.id, otherEl);
12198 //and so on
12199
12200 // using getKey
12201 var mc = new Roo.util.MixedCollection();
12202 mc.getKey = function(el){
12203    return el.dom.id;
12204 };
12205 mc.add(someEl);
12206 mc.add(otherEl);
12207
12208 // or via the constructor
12209 var mc = new Roo.util.MixedCollection(false, function(el){
12210    return el.dom.id;
12211 });
12212 mc.add(someEl);
12213 mc.add(otherEl);
12214 </code></pre>
12215  * @param o {Object} The item for which to find the key.
12216  * @return {Object} The key for the passed item.
12217  */
12218     getKey : function(o){
12219          return o.id; 
12220     },
12221    
12222 /**
12223  * Replaces an item in the collection.
12224  * @param {String} key The key associated with the item to replace, or the item to replace.
12225  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12226  * @return {Object}  The new item.
12227  */
12228     replace : function(key, o){
12229         if(arguments.length == 1){
12230             o = arguments[0];
12231             key = this.getKey(o);
12232         }
12233         var old = this.item(key);
12234         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12235              return this.add(key, o);
12236         }
12237         var index = this.indexOfKey(key);
12238         this.items[index] = o;
12239         this.map[key] = o;
12240         this.fireEvent("replace", key, old, o);
12241         return o;
12242     },
12243    
12244 /**
12245  * Adds all elements of an Array or an Object to the collection.
12246  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12247  * an Array of values, each of which are added to the collection.
12248  */
12249     addAll : function(objs){
12250         if(arguments.length > 1 || objs instanceof Array){
12251             var args = arguments.length > 1 ? arguments : objs;
12252             for(var i = 0, len = args.length; i < len; i++){
12253                 this.add(args[i]);
12254             }
12255         }else{
12256             for(var key in objs){
12257                 if(this.allowFunctions || typeof objs[key] != "function"){
12258                     this.add(key, objs[key]);
12259                 }
12260             }
12261         }
12262     },
12263    
12264 /**
12265  * Executes the specified function once for every item in the collection, passing each
12266  * item as the first and only parameter. returning false from the function will stop the iteration.
12267  * @param {Function} fn The function to execute for each item.
12268  * @param {Object} scope (optional) The scope in which to execute the function.
12269  */
12270     each : function(fn, scope){
12271         var items = [].concat(this.items); // each safe for removal
12272         for(var i = 0, len = items.length; i < len; i++){
12273             if(fn.call(scope || items[i], items[i], i, len) === false){
12274                 break;
12275             }
12276         }
12277     },
12278    
12279 /**
12280  * Executes the specified function once for every key in the collection, passing each
12281  * key, and its associated item as the first two parameters.
12282  * @param {Function} fn The function to execute for each item.
12283  * @param {Object} scope (optional) The scope in which to execute the function.
12284  */
12285     eachKey : function(fn, scope){
12286         for(var i = 0, len = this.keys.length; i < len; i++){
12287             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12288         }
12289     },
12290    
12291 /**
12292  * Returns the first item in the collection which elicits a true return value from the
12293  * passed selection function.
12294  * @param {Function} fn The selection function to execute for each item.
12295  * @param {Object} scope (optional) The scope in which to execute the function.
12296  * @return {Object} The first item in the collection which returned true from the selection function.
12297  */
12298     find : function(fn, scope){
12299         for(var i = 0, len = this.items.length; i < len; i++){
12300             if(fn.call(scope || window, this.items[i], this.keys[i])){
12301                 return this.items[i];
12302             }
12303         }
12304         return null;
12305     },
12306    
12307 /**
12308  * Inserts an item at the specified index in the collection.
12309  * @param {Number} index The index to insert the item at.
12310  * @param {String} key The key to associate with the new item, or the item itself.
12311  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12312  * @return {Object} The item inserted.
12313  */
12314     insert : function(index, key, o){
12315         if(arguments.length == 2){
12316             o = arguments[1];
12317             key = this.getKey(o);
12318         }
12319         if(index >= this.length){
12320             return this.add(key, o);
12321         }
12322         this.length++;
12323         this.items.splice(index, 0, o);
12324         if(typeof key != "undefined" && key != null){
12325             this.map[key] = o;
12326         }
12327         this.keys.splice(index, 0, key);
12328         this.fireEvent("add", index, o, key);
12329         return o;
12330     },
12331    
12332 /**
12333  * Removed an item from the collection.
12334  * @param {Object} o The item to remove.
12335  * @return {Object} The item removed.
12336  */
12337     remove : function(o){
12338         return this.removeAt(this.indexOf(o));
12339     },
12340    
12341 /**
12342  * Remove an item from a specified index in the collection.
12343  * @param {Number} index The index within the collection of the item to remove.
12344  */
12345     removeAt : function(index){
12346         if(index < this.length && index >= 0){
12347             this.length--;
12348             var o = this.items[index];
12349             this.items.splice(index, 1);
12350             var key = this.keys[index];
12351             if(typeof key != "undefined"){
12352                 delete this.map[key];
12353             }
12354             this.keys.splice(index, 1);
12355             this.fireEvent("remove", o, key);
12356         }
12357     },
12358    
12359 /**
12360  * Removed an item associated with the passed key fom the collection.
12361  * @param {String} key The key of the item to remove.
12362  */
12363     removeKey : function(key){
12364         return this.removeAt(this.indexOfKey(key));
12365     },
12366    
12367 /**
12368  * Returns the number of items in the collection.
12369  * @return {Number} the number of items in the collection.
12370  */
12371     getCount : function(){
12372         return this.length; 
12373     },
12374    
12375 /**
12376  * Returns index within the collection of the passed Object.
12377  * @param {Object} o The item to find the index of.
12378  * @return {Number} index of the item.
12379  */
12380     indexOf : function(o){
12381         if(!this.items.indexOf){
12382             for(var i = 0, len = this.items.length; i < len; i++){
12383                 if(this.items[i] == o) return i;
12384             }
12385             return -1;
12386         }else{
12387             return this.items.indexOf(o);
12388         }
12389     },
12390    
12391 /**
12392  * Returns index within the collection of the passed key.
12393  * @param {String} key The key to find the index of.
12394  * @return {Number} index of the key.
12395  */
12396     indexOfKey : function(key){
12397         if(!this.keys.indexOf){
12398             for(var i = 0, len = this.keys.length; i < len; i++){
12399                 if(this.keys[i] == key) return i;
12400             }
12401             return -1;
12402         }else{
12403             return this.keys.indexOf(key);
12404         }
12405     },
12406    
12407 /**
12408  * Returns the item associated with the passed key OR index. Key has priority over index.
12409  * @param {String/Number} key The key or index of the item.
12410  * @return {Object} The item associated with the passed key.
12411  */
12412     item : function(key){
12413         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12414         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12415     },
12416     
12417 /**
12418  * Returns the item at the specified index.
12419  * @param {Number} index The index of the item.
12420  * @return {Object}
12421  */
12422     itemAt : function(index){
12423         return this.items[index];
12424     },
12425     
12426 /**
12427  * Returns the item associated with the passed key.
12428  * @param {String/Number} key The key of the item.
12429  * @return {Object} The item associated with the passed key.
12430  */
12431     key : function(key){
12432         return this.map[key];
12433     },
12434    
12435 /**
12436  * Returns true if the collection contains the passed Object as an item.
12437  * @param {Object} o  The Object to look for in the collection.
12438  * @return {Boolean} True if the collection contains the Object as an item.
12439  */
12440     contains : function(o){
12441         return this.indexOf(o) != -1;
12442     },
12443    
12444 /**
12445  * Returns true if the collection contains the passed Object as a key.
12446  * @param {String} key The key to look for in the collection.
12447  * @return {Boolean} True if the collection contains the Object as a key.
12448  */
12449     containsKey : function(key){
12450         return typeof this.map[key] != "undefined";
12451     },
12452    
12453 /**
12454  * Removes all items from the collection.
12455  */
12456     clear : function(){
12457         this.length = 0;
12458         this.items = [];
12459         this.keys = [];
12460         this.map = {};
12461         this.fireEvent("clear");
12462     },
12463    
12464 /**
12465  * Returns the first item in the collection.
12466  * @return {Object} the first item in the collection..
12467  */
12468     first : function(){
12469         return this.items[0]; 
12470     },
12471    
12472 /**
12473  * Returns the last item in the collection.
12474  * @return {Object} the last item in the collection..
12475  */
12476     last : function(){
12477         return this.items[this.length-1];   
12478     },
12479     
12480     _sort : function(property, dir, fn){
12481         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12482         fn = fn || function(a, b){
12483             return a-b;
12484         };
12485         var c = [], k = this.keys, items = this.items;
12486         for(var i = 0, len = items.length; i < len; i++){
12487             c[c.length] = {key: k[i], value: items[i], index: i};
12488         }
12489         c.sort(function(a, b){
12490             var v = fn(a[property], b[property]) * dsc;
12491             if(v == 0){
12492                 v = (a.index < b.index ? -1 : 1);
12493             }
12494             return v;
12495         });
12496         for(var i = 0, len = c.length; i < len; i++){
12497             items[i] = c[i].value;
12498             k[i] = c[i].key;
12499         }
12500         this.fireEvent("sort", this);
12501     },
12502     
12503     /**
12504      * Sorts this collection with the passed comparison function
12505      * @param {String} direction (optional) "ASC" or "DESC"
12506      * @param {Function} fn (optional) comparison function
12507      */
12508     sort : function(dir, fn){
12509         this._sort("value", dir, fn);
12510     },
12511     
12512     /**
12513      * Sorts this collection by keys
12514      * @param {String} direction (optional) "ASC" or "DESC"
12515      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12516      */
12517     keySort : function(dir, fn){
12518         this._sort("key", dir, fn || function(a, b){
12519             return String(a).toUpperCase()-String(b).toUpperCase();
12520         });
12521     },
12522     
12523     /**
12524      * Returns a range of items in this collection
12525      * @param {Number} startIndex (optional) defaults to 0
12526      * @param {Number} endIndex (optional) default to the last item
12527      * @return {Array} An array of items
12528      */
12529     getRange : function(start, end){
12530         var items = this.items;
12531         if(items.length < 1){
12532             return [];
12533         }
12534         start = start || 0;
12535         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12536         var r = [];
12537         if(start <= end){
12538             for(var i = start; i <= end; i++) {
12539                     r[r.length] = items[i];
12540             }
12541         }else{
12542             for(var i = start; i >= end; i--) {
12543                     r[r.length] = items[i];
12544             }
12545         }
12546         return r;
12547     },
12548         
12549     /**
12550      * Filter the <i>objects</i> in this collection by a specific property. 
12551      * Returns a new collection that has been filtered.
12552      * @param {String} property A property on your objects
12553      * @param {String/RegExp} value Either string that the property values 
12554      * should start with or a RegExp to test against the property
12555      * @return {MixedCollection} The new filtered collection
12556      */
12557     filter : function(property, value){
12558         if(!value.exec){ // not a regex
12559             value = String(value);
12560             if(value.length == 0){
12561                 return this.clone();
12562             }
12563             value = new RegExp("^" + Roo.escapeRe(value), "i");
12564         }
12565         return this.filterBy(function(o){
12566             return o && value.test(o[property]);
12567         });
12568         },
12569     
12570     /**
12571      * Filter by a function. * Returns a new collection that has been filtered.
12572      * The passed function will be called with each 
12573      * object in the collection. If the function returns true, the value is included 
12574      * otherwise it is filtered.
12575      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12576      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12577      * @return {MixedCollection} The new filtered collection
12578      */
12579     filterBy : function(fn, scope){
12580         var r = new Roo.util.MixedCollection();
12581         r.getKey = this.getKey;
12582         var k = this.keys, it = this.items;
12583         for(var i = 0, len = it.length; i < len; i++){
12584             if(fn.call(scope||this, it[i], k[i])){
12585                                 r.add(k[i], it[i]);
12586                         }
12587         }
12588         return r;
12589     },
12590     
12591     /**
12592      * Creates a duplicate of this collection
12593      * @return {MixedCollection}
12594      */
12595     clone : function(){
12596         var r = new Roo.util.MixedCollection();
12597         var k = this.keys, it = this.items;
12598         for(var i = 0, len = it.length; i < len; i++){
12599             r.add(k[i], it[i]);
12600         }
12601         r.getKey = this.getKey;
12602         return r;
12603     }
12604 });
12605 /**
12606  * Returns the item associated with the passed key or index.
12607  * @method
12608  * @param {String/Number} key The key or index of the item.
12609  * @return {Object} The item associated with the passed key.
12610  */
12611 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12612  * Based on:
12613  * Ext JS Library 1.1.1
12614  * Copyright(c) 2006-2007, Ext JS, LLC.
12615  *
12616  * Originally Released Under LGPL - original licence link has changed is not relivant.
12617  *
12618  * Fork - LGPL
12619  * <script type="text/javascript">
12620  */
12621 /**
12622  * @class Roo.util.JSON
12623  * Modified version of Douglas Crockford"s json.js that doesn"t
12624  * mess with the Object prototype 
12625  * http://www.json.org/js.html
12626  * @singleton
12627  */
12628 Roo.util.JSON = new (function(){
12629     var useHasOwn = {}.hasOwnProperty ? true : false;
12630     
12631     // crashes Safari in some instances
12632     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12633     
12634     var pad = function(n) {
12635         return n < 10 ? "0" + n : n;
12636     };
12637     
12638     var m = {
12639         "\b": '\\b',
12640         "\t": '\\t',
12641         "\n": '\\n',
12642         "\f": '\\f',
12643         "\r": '\\r',
12644         '"' : '\\"',
12645         "\\": '\\\\'
12646     };
12647
12648     var encodeString = function(s){
12649         if (/["\\\x00-\x1f]/.test(s)) {
12650             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12651                 var c = m[b];
12652                 if(c){
12653                     return c;
12654                 }
12655                 c = b.charCodeAt();
12656                 return "\\u00" +
12657                     Math.floor(c / 16).toString(16) +
12658                     (c % 16).toString(16);
12659             }) + '"';
12660         }
12661         return '"' + s + '"';
12662     };
12663     
12664     var encodeArray = function(o){
12665         var a = ["["], b, i, l = o.length, v;
12666             for (i = 0; i < l; i += 1) {
12667                 v = o[i];
12668                 switch (typeof v) {
12669                     case "undefined":
12670                     case "function":
12671                     case "unknown":
12672                         break;
12673                     default:
12674                         if (b) {
12675                             a.push(',');
12676                         }
12677                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12678                         b = true;
12679                 }
12680             }
12681             a.push("]");
12682             return a.join("");
12683     };
12684     
12685     var encodeDate = function(o){
12686         return '"' + o.getFullYear() + "-" +
12687                 pad(o.getMonth() + 1) + "-" +
12688                 pad(o.getDate()) + "T" +
12689                 pad(o.getHours()) + ":" +
12690                 pad(o.getMinutes()) + ":" +
12691                 pad(o.getSeconds()) + '"';
12692     };
12693     
12694     /**
12695      * Encodes an Object, Array or other value
12696      * @param {Mixed} o The variable to encode
12697      * @return {String} The JSON string
12698      */
12699     this.encode = function(o){
12700         if(typeof o == "undefined" || o === null){
12701             return "null";
12702         }else if(o instanceof Array){
12703             return encodeArray(o);
12704         }else if(o instanceof Date){
12705             return encodeDate(o);
12706         }else if(typeof o == "string"){
12707             return encodeString(o);
12708         }else if(typeof o == "number"){
12709             return isFinite(o) ? String(o) : "null";
12710         }else if(typeof o == "boolean"){
12711             return String(o);
12712         }else {
12713             var a = ["{"], b, i, v;
12714             for (i in o) {
12715                 if(!useHasOwn || o.hasOwnProperty(i)) {
12716                     v = o[i];
12717                     switch (typeof v) {
12718                     case "undefined":
12719                     case "function":
12720                     case "unknown":
12721                         break;
12722                     default:
12723                         if(b){
12724                             a.push(',');
12725                         }
12726                         a.push(this.encode(i), ":",
12727                                 v === null ? "null" : this.encode(v));
12728                         b = true;
12729                     }
12730                 }
12731             }
12732             a.push("}");
12733             return a.join("");
12734         }
12735     };
12736     
12737     /**
12738      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12739      * @param {String} json The JSON string
12740      * @return {Object} The resulting object
12741      */
12742     this.decode = function(json){
12743         /**
12744          * eval:var:json
12745          */
12746         return eval("(" + json + ')');
12747     };
12748 })();
12749 /** 
12750  * Shorthand for {@link Roo.util.JSON#encode}
12751  * @member Roo encode 
12752  * @method */
12753 Roo.encode = Roo.util.JSON.encode;
12754 /** 
12755  * Shorthand for {@link Roo.util.JSON#decode}
12756  * @member Roo decode 
12757  * @method */
12758 Roo.decode = Roo.util.JSON.decode;
12759 /*
12760  * Based on:
12761  * Ext JS Library 1.1.1
12762  * Copyright(c) 2006-2007, Ext JS, LLC.
12763  *
12764  * Originally Released Under LGPL - original licence link has changed is not relivant.
12765  *
12766  * Fork - LGPL
12767  * <script type="text/javascript">
12768  */
12769  
12770 /**
12771  * @class Roo.util.Format
12772  * Reusable data formatting functions
12773  * @singleton
12774  */
12775 Roo.util.Format = function(){
12776     var trimRe = /^\s+|\s+$/g;
12777     return {
12778         /**
12779          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12780          * @param {String} value The string to truncate
12781          * @param {Number} length The maximum length to allow before truncating
12782          * @return {String} The converted text
12783          */
12784         ellipsis : function(value, len){
12785             if(value && value.length > len){
12786                 return value.substr(0, len-3)+"...";
12787             }
12788             return value;
12789         },
12790
12791         /**
12792          * Checks a reference and converts it to empty string if it is undefined
12793          * @param {Mixed} value Reference to check
12794          * @return {Mixed} Empty string if converted, otherwise the original value
12795          */
12796         undef : function(value){
12797             return typeof value != "undefined" ? value : "";
12798         },
12799
12800         /**
12801          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12802          * @param {String} value The string to encode
12803          * @return {String} The encoded text
12804          */
12805         htmlEncode : function(value){
12806             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12807         },
12808
12809         /**
12810          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12811          * @param {String} value The string to decode
12812          * @return {String} The decoded text
12813          */
12814         htmlDecode : function(value){
12815             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12816         },
12817
12818         /**
12819          * Trims any whitespace from either side of a string
12820          * @param {String} value The text to trim
12821          * @return {String} The trimmed text
12822          */
12823         trim : function(value){
12824             return String(value).replace(trimRe, "");
12825         },
12826
12827         /**
12828          * Returns a substring from within an original string
12829          * @param {String} value The original text
12830          * @param {Number} start The start index of the substring
12831          * @param {Number} length The length of the substring
12832          * @return {String} The substring
12833          */
12834         substr : function(value, start, length){
12835             return String(value).substr(start, length);
12836         },
12837
12838         /**
12839          * Converts a string to all lower case letters
12840          * @param {String} value The text to convert
12841          * @return {String} The converted text
12842          */
12843         lowercase : function(value){
12844             return String(value).toLowerCase();
12845         },
12846
12847         /**
12848          * Converts a string to all upper case letters
12849          * @param {String} value The text to convert
12850          * @return {String} The converted text
12851          */
12852         uppercase : function(value){
12853             return String(value).toUpperCase();
12854         },
12855
12856         /**
12857          * Converts the first character only of a string to upper case
12858          * @param {String} value The text to convert
12859          * @return {String} The converted text
12860          */
12861         capitalize : function(value){
12862             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12863         },
12864
12865         // private
12866         call : function(value, fn){
12867             if(arguments.length > 2){
12868                 var args = Array.prototype.slice.call(arguments, 2);
12869                 args.unshift(value);
12870                  
12871                 return /** eval:var:value */  eval(fn).apply(window, args);
12872             }else{
12873                 /** eval:var:value */
12874                 return /** eval:var:value */ eval(fn).call(window, value);
12875             }
12876         },
12877
12878         /**
12879          * Format a number as US currency
12880          * @param {Number/String} value The numeric value to format
12881          * @return {String} The formatted currency string
12882          */
12883         usMoney : function(v){
12884             v = (Math.round((v-0)*100))/100;
12885             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12886             v = String(v);
12887             var ps = v.split('.');
12888             var whole = ps[0];
12889             var sub = ps[1] ? '.'+ ps[1] : '.00';
12890             var r = /(\d+)(\d{3})/;
12891             while (r.test(whole)) {
12892                 whole = whole.replace(r, '$1' + ',' + '$2');
12893             }
12894             return "$" + whole + sub ;
12895         },
12896
12897         /**
12898          * Parse a value into a formatted date using the specified format pattern.
12899          * @param {Mixed} value The value to format
12900          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12901          * @return {String} The formatted date string
12902          */
12903         date : function(v, format){
12904             if(!v){
12905                 return "";
12906             }
12907             if(!(v instanceof Date)){
12908                 v = new Date(Date.parse(v));
12909             }
12910             return v.dateFormat(format || "m/d/Y");
12911         },
12912
12913         /**
12914          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12915          * @param {String} format Any valid date format string
12916          * @return {Function} The date formatting function
12917          */
12918         dateRenderer : function(format){
12919             return function(v){
12920                 return Roo.util.Format.date(v, format);  
12921             };
12922         },
12923
12924         // private
12925         stripTagsRE : /<\/?[^>]+>/gi,
12926         
12927         /**
12928          * Strips all HTML tags
12929          * @param {Mixed} value The text from which to strip tags
12930          * @return {String} The stripped text
12931          */
12932         stripTags : function(v){
12933             return !v ? v : String(v).replace(this.stripTagsRE, "");
12934         }
12935     };
12936 }();/*
12937  * Based on:
12938  * Ext JS Library 1.1.1
12939  * Copyright(c) 2006-2007, Ext JS, LLC.
12940  *
12941  * Originally Released Under LGPL - original licence link has changed is not relivant.
12942  *
12943  * Fork - LGPL
12944  * <script type="text/javascript">
12945  */
12946
12947
12948  
12949
12950 /**
12951  * @class Roo.MasterTemplate
12952  * @extends Roo.Template
12953  * Provides a template that can have child templates. The syntax is:
12954 <pre><code>
12955 var t = new Roo.MasterTemplate(
12956         '&lt;select name="{name}"&gt;',
12957                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12958         '&lt;/select&gt;'
12959 );
12960 t.add('options', {value: 'foo', text: 'bar'});
12961 // or you can add multiple child elements in one shot
12962 t.addAll('options', [
12963     {value: 'foo', text: 'bar'},
12964     {value: 'foo2', text: 'bar2'},
12965     {value: 'foo3', text: 'bar3'}
12966 ]);
12967 // then append, applying the master template values
12968 t.append('my-form', {name: 'my-select'});
12969 </code></pre>
12970 * A name attribute for the child template is not required if you have only one child
12971 * template or you want to refer to them by index.
12972  */
12973 Roo.MasterTemplate = function(){
12974     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12975     this.originalHtml = this.html;
12976     var st = {};
12977     var m, re = this.subTemplateRe;
12978     re.lastIndex = 0;
12979     var subIndex = 0;
12980     while(m = re.exec(this.html)){
12981         var name = m[1], content = m[2];
12982         st[subIndex] = {
12983             name: name,
12984             index: subIndex,
12985             buffer: [],
12986             tpl : new Roo.Template(content)
12987         };
12988         if(name){
12989             st[name] = st[subIndex];
12990         }
12991         st[subIndex].tpl.compile();
12992         st[subIndex].tpl.call = this.call.createDelegate(this);
12993         subIndex++;
12994     }
12995     this.subCount = subIndex;
12996     this.subs = st;
12997 };
12998 Roo.extend(Roo.MasterTemplate, Roo.Template, {
12999     /**
13000     * The regular expression used to match sub templates
13001     * @type RegExp
13002     * @property
13003     */
13004     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13005
13006     /**
13007      * Applies the passed values to a child template.
13008      * @param {String/Number} name (optional) The name or index of the child template
13009      * @param {Array/Object} values The values to be applied to the template
13010      * @return {MasterTemplate} this
13011      */
13012      add : function(name, values){
13013         if(arguments.length == 1){
13014             values = arguments[0];
13015             name = 0;
13016         }
13017         var s = this.subs[name];
13018         s.buffer[s.buffer.length] = s.tpl.apply(values);
13019         return this;
13020     },
13021
13022     /**
13023      * Applies all the passed values to a child template.
13024      * @param {String/Number} name (optional) The name or index of the child template
13025      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13026      * @param {Boolean} reset (optional) True to reset the template first
13027      * @return {MasterTemplate} this
13028      */
13029     fill : function(name, values, reset){
13030         var a = arguments;
13031         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13032             values = a[0];
13033             name = 0;
13034             reset = a[1];
13035         }
13036         if(reset){
13037             this.reset();
13038         }
13039         for(var i = 0, len = values.length; i < len; i++){
13040             this.add(name, values[i]);
13041         }
13042         return this;
13043     },
13044
13045     /**
13046      * Resets the template for reuse
13047      * @return {MasterTemplate} this
13048      */
13049      reset : function(){
13050         var s = this.subs;
13051         for(var i = 0; i < this.subCount; i++){
13052             s[i].buffer = [];
13053         }
13054         return this;
13055     },
13056
13057     applyTemplate : function(values){
13058         var s = this.subs;
13059         var replaceIndex = -1;
13060         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13061             return s[++replaceIndex].buffer.join("");
13062         });
13063         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13064     },
13065
13066     apply : function(){
13067         return this.applyTemplate.apply(this, arguments);
13068     },
13069
13070     compile : function(){return this;}
13071 });
13072
13073 /**
13074  * Alias for fill().
13075  * @method
13076  */
13077 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13078  /**
13079  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13080  * var tpl = Roo.MasterTemplate.from('element-id');
13081  * @param {String/HTMLElement} el
13082  * @param {Object} config
13083  * @static
13084  */
13085 Roo.MasterTemplate.from = function(el, config){
13086     el = Roo.getDom(el);
13087     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13088 };/*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098
13099  
13100 /**
13101  * @class Roo.util.CSS
13102  * Utility class for manipulating CSS rules
13103  * @singleton
13104  */
13105 Roo.util.CSS = function(){
13106         var rules = null;
13107         var doc = document;
13108
13109     var camelRe = /(-[a-z])/gi;
13110     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13111
13112    return {
13113    /**
13114     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13115     * tag and appended to the HEAD of the document.
13116     * @param {String} cssText The text containing the css rules
13117     * @param {String} id An id to add to the stylesheet for later removal
13118     * @return {StyleSheet}
13119     */
13120    createStyleSheet : function(cssText, id){
13121        var ss;
13122        var head = doc.getElementsByTagName("head")[0];
13123        var rules = doc.createElement("style");
13124        rules.setAttribute("type", "text/css");
13125        if(id){
13126            rules.setAttribute("id", id);
13127        }
13128        if(Roo.isIE){
13129            head.appendChild(rules);
13130            ss = rules.styleSheet;
13131            ss.cssText = cssText;
13132        }else{
13133            try{
13134                 rules.appendChild(doc.createTextNode(cssText));
13135            }catch(e){
13136                rules.cssText = cssText; 
13137            }
13138            head.appendChild(rules);
13139            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13140        }
13141        this.cacheStyleSheet(ss);
13142        return ss;
13143    },
13144
13145    /**
13146     * Removes a style or link tag by id
13147     * @param {String} id The id of the tag
13148     */
13149    removeStyleSheet : function(id){
13150        var existing = doc.getElementById(id);
13151        if(existing){
13152            existing.parentNode.removeChild(existing);
13153        }
13154    },
13155
13156    /**
13157     * Dynamically swaps an existing stylesheet reference for a new one
13158     * @param {String} id The id of an existing link tag to remove
13159     * @param {String} url The href of the new stylesheet to include
13160     */
13161    swapStyleSheet : function(id, url){
13162        this.removeStyleSheet(id);
13163        var ss = doc.createElement("link");
13164        ss.setAttribute("rel", "stylesheet");
13165        ss.setAttribute("type", "text/css");
13166        ss.setAttribute("id", id);
13167        ss.setAttribute("href", url);
13168        doc.getElementsByTagName("head")[0].appendChild(ss);
13169    },
13170    
13171    /**
13172     * Refresh the rule cache if you have dynamically added stylesheets
13173     * @return {Object} An object (hash) of rules indexed by selector
13174     */
13175    refreshCache : function(){
13176        return this.getRules(true);
13177    },
13178
13179    // private
13180    cacheStyleSheet : function(ss){
13181        if(!rules){
13182            rules = {};
13183        }
13184        try{// try catch for cross domain access issue
13185            var ssRules = ss.cssRules || ss.rules;
13186            for(var j = ssRules.length-1; j >= 0; --j){
13187                rules[ssRules[j].selectorText] = ssRules[j];
13188            }
13189        }catch(e){}
13190    },
13191    
13192    /**
13193     * Gets all css rules for the document
13194     * @param {Boolean} refreshCache true to refresh the internal cache
13195     * @return {Object} An object (hash) of rules indexed by selector
13196     */
13197    getRules : function(refreshCache){
13198                 if(rules == null || refreshCache){
13199                         rules = {};
13200                         var ds = doc.styleSheets;
13201                         for(var i =0, len = ds.length; i < len; i++){
13202                             try{
13203                         this.cacheStyleSheet(ds[i]);
13204                     }catch(e){} 
13205                 }
13206                 }
13207                 return rules;
13208         },
13209         
13210         /**
13211     * Gets an an individual CSS rule by selector(s)
13212     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13213     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13214     * @return {CSSRule} The CSS rule or null if one is not found
13215     */
13216    getRule : function(selector, refreshCache){
13217                 var rs = this.getRules(refreshCache);
13218                 if(!(selector instanceof Array)){
13219                     return rs[selector];
13220                 }
13221                 for(var i = 0; i < selector.length; i++){
13222                         if(rs[selector[i]]){
13223                                 return rs[selector[i]];
13224                         }
13225                 }
13226                 return null;
13227         },
13228         
13229         
13230         /**
13231     * Updates a rule property
13232     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13233     * @param {String} property The css property
13234     * @param {String} value The new value for the property
13235     * @return {Boolean} true If a rule was found and updated
13236     */
13237    updateRule : function(selector, property, value){
13238                 if(!(selector instanceof Array)){
13239                         var rule = this.getRule(selector);
13240                         if(rule){
13241                                 rule.style[property.replace(camelRe, camelFn)] = value;
13242                                 return true;
13243                         }
13244                 }else{
13245                         for(var i = 0; i < selector.length; i++){
13246                                 if(this.updateRule(selector[i], property, value)){
13247                                         return true;
13248                                 }
13249                         }
13250                 }
13251                 return false;
13252         }
13253    };   
13254 }();/*
13255  * Based on:
13256  * Ext JS Library 1.1.1
13257  * Copyright(c) 2006-2007, Ext JS, LLC.
13258  *
13259  * Originally Released Under LGPL - original licence link has changed is not relivant.
13260  *
13261  * Fork - LGPL
13262  * <script type="text/javascript">
13263  */
13264
13265  
13266
13267 /**
13268  * @class Roo.util.ClickRepeater
13269  * @extends Roo.util.Observable
13270  * 
13271  * A wrapper class which can be applied to any element. Fires a "click" event while the
13272  * mouse is pressed. The interval between firings may be specified in the config but
13273  * defaults to 10 milliseconds.
13274  * 
13275  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13276  * 
13277  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13278  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13279  * Similar to an autorepeat key delay.
13280  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13281  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13282  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13283  *           "interval" and "delay" are ignored. "immediate" is honored.
13284  * @cfg {Boolean} preventDefault True to prevent the default click event
13285  * @cfg {Boolean} stopDefault True to stop the default click event
13286  * 
13287  * @history
13288  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13289  *     2007-02-02 jvs Renamed to ClickRepeater
13290  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13291  *
13292  *  @constructor
13293  * @param {String/HTMLElement/Element} el The element to listen on
13294  * @param {Object} config
13295  **/
13296 Roo.util.ClickRepeater = function(el, config)
13297 {
13298     this.el = Roo.get(el);
13299     this.el.unselectable();
13300
13301     Roo.apply(this, config);
13302
13303     this.addEvents({
13304     /**
13305      * @event mousedown
13306      * Fires when the mouse button is depressed.
13307      * @param {Roo.util.ClickRepeater} this
13308      */
13309         "mousedown" : true,
13310     /**
13311      * @event click
13312      * Fires on a specified interval during the time the element is pressed.
13313      * @param {Roo.util.ClickRepeater} this
13314      */
13315         "click" : true,
13316     /**
13317      * @event mouseup
13318      * Fires when the mouse key is released.
13319      * @param {Roo.util.ClickRepeater} this
13320      */
13321         "mouseup" : true
13322     });
13323
13324     this.el.on("mousedown", this.handleMouseDown, this);
13325     if(this.preventDefault || this.stopDefault){
13326         this.el.on("click", function(e){
13327             if(this.preventDefault){
13328                 e.preventDefault();
13329             }
13330             if(this.stopDefault){
13331                 e.stopEvent();
13332             }
13333         }, this);
13334     }
13335
13336     // allow inline handler
13337     if(this.handler){
13338         this.on("click", this.handler,  this.scope || this);
13339     }
13340
13341     Roo.util.ClickRepeater.superclass.constructor.call(this);
13342 };
13343
13344 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13345     interval : 20,
13346     delay: 250,
13347     preventDefault : true,
13348     stopDefault : false,
13349     timer : 0,
13350
13351     // private
13352     handleMouseDown : function(){
13353         clearTimeout(this.timer);
13354         this.el.blur();
13355         if(this.pressClass){
13356             this.el.addClass(this.pressClass);
13357         }
13358         this.mousedownTime = new Date();
13359
13360         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13361         this.el.on("mouseout", this.handleMouseOut, this);
13362
13363         this.fireEvent("mousedown", this);
13364         this.fireEvent("click", this);
13365         
13366         this.timer = this.click.defer(this.delay || this.interval, this);
13367     },
13368
13369     // private
13370     click : function(){
13371         this.fireEvent("click", this);
13372         this.timer = this.click.defer(this.getInterval(), this);
13373     },
13374
13375     // private
13376     getInterval: function(){
13377         if(!this.accelerate){
13378             return this.interval;
13379         }
13380         var pressTime = this.mousedownTime.getElapsed();
13381         if(pressTime < 500){
13382             return 400;
13383         }else if(pressTime < 1700){
13384             return 320;
13385         }else if(pressTime < 2600){
13386             return 250;
13387         }else if(pressTime < 3500){
13388             return 180;
13389         }else if(pressTime < 4400){
13390             return 140;
13391         }else if(pressTime < 5300){
13392             return 80;
13393         }else if(pressTime < 6200){
13394             return 50;
13395         }else{
13396             return 10;
13397         }
13398     },
13399
13400     // private
13401     handleMouseOut : function(){
13402         clearTimeout(this.timer);
13403         if(this.pressClass){
13404             this.el.removeClass(this.pressClass);
13405         }
13406         this.el.on("mouseover", this.handleMouseReturn, this);
13407     },
13408
13409     // private
13410     handleMouseReturn : function(){
13411         this.el.un("mouseover", this.handleMouseReturn);
13412         if(this.pressClass){
13413             this.el.addClass(this.pressClass);
13414         }
13415         this.click();
13416     },
13417
13418     // private
13419     handleMouseUp : function(){
13420         clearTimeout(this.timer);
13421         this.el.un("mouseover", this.handleMouseReturn);
13422         this.el.un("mouseout", this.handleMouseOut);
13423         Roo.get(document).un("mouseup", this.handleMouseUp);
13424         this.el.removeClass(this.pressClass);
13425         this.fireEvent("mouseup", this);
13426     }
13427 });/*
13428  * Based on:
13429  * Ext JS Library 1.1.1
13430  * Copyright(c) 2006-2007, Ext JS, LLC.
13431  *
13432  * Originally Released Under LGPL - original licence link has changed is not relivant.
13433  *
13434  * Fork - LGPL
13435  * <script type="text/javascript">
13436  */
13437
13438  
13439 /**
13440  * @class Roo.KeyNav
13441  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13442  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13443  * way to implement custom navigation schemes for any UI component.</p>
13444  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13445  * pageUp, pageDown, del, home, end.  Usage:</p>
13446  <pre><code>
13447 var nav = new Roo.KeyNav("my-element", {
13448     "left" : function(e){
13449         this.moveLeft(e.ctrlKey);
13450     },
13451     "right" : function(e){
13452         this.moveRight(e.ctrlKey);
13453     },
13454     "enter" : function(e){
13455         this.save();
13456     },
13457     scope : this
13458 });
13459 </code></pre>
13460  * @constructor
13461  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13462  * @param {Object} config The config
13463  */
13464 Roo.KeyNav = function(el, config){
13465     this.el = Roo.get(el);
13466     Roo.apply(this, config);
13467     if(!this.disabled){
13468         this.disabled = true;
13469         this.enable();
13470     }
13471 };
13472
13473 Roo.KeyNav.prototype = {
13474     /**
13475      * @cfg {Boolean} disabled
13476      * True to disable this KeyNav instance (defaults to false)
13477      */
13478     disabled : false,
13479     /**
13480      * @cfg {String} defaultEventAction
13481      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13482      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13483      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13484      */
13485     defaultEventAction: "stopEvent",
13486     /**
13487      * @cfg {Boolean} forceKeyDown
13488      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13489      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13490      * handle keydown instead of keypress.
13491      */
13492     forceKeyDown : false,
13493
13494     // private
13495     prepareEvent : function(e){
13496         var k = e.getKey();
13497         var h = this.keyToHandler[k];
13498         //if(h && this[h]){
13499         //    e.stopPropagation();
13500         //}
13501         if(Roo.isSafari && h && k >= 37 && k <= 40){
13502             e.stopEvent();
13503         }
13504     },
13505
13506     // private
13507     relay : function(e){
13508         var k = e.getKey();
13509         var h = this.keyToHandler[k];
13510         if(h && this[h]){
13511             if(this.doRelay(e, this[h], h) !== true){
13512                 e[this.defaultEventAction]();
13513             }
13514         }
13515     },
13516
13517     // private
13518     doRelay : function(e, h, hname){
13519         return h.call(this.scope || this, e);
13520     },
13521
13522     // possible handlers
13523     enter : false,
13524     left : false,
13525     right : false,
13526     up : false,
13527     down : false,
13528     tab : false,
13529     esc : false,
13530     pageUp : false,
13531     pageDown : false,
13532     del : false,
13533     home : false,
13534     end : false,
13535
13536     // quick lookup hash
13537     keyToHandler : {
13538         37 : "left",
13539         39 : "right",
13540         38 : "up",
13541         40 : "down",
13542         33 : "pageUp",
13543         34 : "pageDown",
13544         46 : "del",
13545         36 : "home",
13546         35 : "end",
13547         13 : "enter",
13548         27 : "esc",
13549         9  : "tab"
13550     },
13551
13552         /**
13553          * Enable this KeyNav
13554          */
13555         enable: function(){
13556                 if(this.disabled){
13557             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13558             // the EventObject will normalize Safari automatically
13559             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13560                 this.el.on("keydown", this.relay,  this);
13561             }else{
13562                 this.el.on("keydown", this.prepareEvent,  this);
13563                 this.el.on("keypress", this.relay,  this);
13564             }
13565                     this.disabled = false;
13566                 }
13567         },
13568
13569         /**
13570          * Disable this KeyNav
13571          */
13572         disable: function(){
13573                 if(!this.disabled){
13574                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13575                 this.el.un("keydown", this.relay);
13576             }else{
13577                 this.el.un("keydown", this.prepareEvent);
13578                 this.el.un("keypress", this.relay);
13579             }
13580                     this.disabled = true;
13581                 }
13582         }
13583 };/*
13584  * Based on:
13585  * Ext JS Library 1.1.1
13586  * Copyright(c) 2006-2007, Ext JS, LLC.
13587  *
13588  * Originally Released Under LGPL - original licence link has changed is not relivant.
13589  *
13590  * Fork - LGPL
13591  * <script type="text/javascript">
13592  */
13593
13594  
13595 /**
13596  * @class Roo.KeyMap
13597  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13598  * The constructor accepts the same config object as defined by {@link #addBinding}.
13599  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13600  * combination it will call the function with this signature (if the match is a multi-key
13601  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13602  * A KeyMap can also handle a string representation of keys.<br />
13603  * Usage:
13604  <pre><code>
13605 // map one key by key code
13606 var map = new Roo.KeyMap("my-element", {
13607     key: 13, // or Roo.EventObject.ENTER
13608     fn: myHandler,
13609     scope: myObject
13610 });
13611
13612 // map multiple keys to one action by string
13613 var map = new Roo.KeyMap("my-element", {
13614     key: "a\r\n\t",
13615     fn: myHandler,
13616     scope: myObject
13617 });
13618
13619 // map multiple keys to multiple actions by strings and array of codes
13620 var map = new Roo.KeyMap("my-element", [
13621     {
13622         key: [10,13],
13623         fn: function(){ alert("Return was pressed"); }
13624     }, {
13625         key: "abc",
13626         fn: function(){ alert('a, b or c was pressed'); }
13627     }, {
13628         key: "\t",
13629         ctrl:true,
13630         shift:true,
13631         fn: function(){ alert('Control + shift + tab was pressed.'); }
13632     }
13633 ]);
13634 </code></pre>
13635  * <b>Note: A KeyMap starts enabled</b>
13636  * @constructor
13637  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13638  * @param {Object} config The config (see {@link #addBinding})
13639  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13640  */
13641 Roo.KeyMap = function(el, config, eventName){
13642     this.el  = Roo.get(el);
13643     this.eventName = eventName || "keydown";
13644     this.bindings = [];
13645     if(config){
13646         this.addBinding(config);
13647     }
13648     this.enable();
13649 };
13650
13651 Roo.KeyMap.prototype = {
13652     /**
13653      * True to stop the event from bubbling and prevent the default browser action if the
13654      * key was handled by the KeyMap (defaults to false)
13655      * @type Boolean
13656      */
13657     stopEvent : false,
13658
13659     /**
13660      * Add a new binding to this KeyMap. The following config object properties are supported:
13661      * <pre>
13662 Property    Type             Description
13663 ----------  ---------------  ----------------------------------------------------------------------
13664 key         String/Array     A single keycode or an array of keycodes to handle
13665 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13666 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13667 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13668 fn          Function         The function to call when KeyMap finds the expected key combination
13669 scope       Object           The scope of the callback function
13670 </pre>
13671      *
13672      * Usage:
13673      * <pre><code>
13674 // Create a KeyMap
13675 var map = new Roo.KeyMap(document, {
13676     key: Roo.EventObject.ENTER,
13677     fn: handleKey,
13678     scope: this
13679 });
13680
13681 //Add a new binding to the existing KeyMap later
13682 map.addBinding({
13683     key: 'abc',
13684     shift: true,
13685     fn: handleKey,
13686     scope: this
13687 });
13688 </code></pre>
13689      * @param {Object/Array} config A single KeyMap config or an array of configs
13690      */
13691         addBinding : function(config){
13692         if(config instanceof Array){
13693             for(var i = 0, len = config.length; i < len; i++){
13694                 this.addBinding(config[i]);
13695             }
13696             return;
13697         }
13698         var keyCode = config.key,
13699             shift = config.shift, 
13700             ctrl = config.ctrl, 
13701             alt = config.alt,
13702             fn = config.fn,
13703             scope = config.scope;
13704         if(typeof keyCode == "string"){
13705             var ks = [];
13706             var keyString = keyCode.toUpperCase();
13707             for(var j = 0, len = keyString.length; j < len; j++){
13708                 ks.push(keyString.charCodeAt(j));
13709             }
13710             keyCode = ks;
13711         }
13712         var keyArray = keyCode instanceof Array;
13713         var handler = function(e){
13714             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13715                 var k = e.getKey();
13716                 if(keyArray){
13717                     for(var i = 0, len = keyCode.length; i < len; i++){
13718                         if(keyCode[i] == k){
13719                           if(this.stopEvent){
13720                               e.stopEvent();
13721                           }
13722                           fn.call(scope || window, k, e);
13723                           return;
13724                         }
13725                     }
13726                 }else{
13727                     if(k == keyCode){
13728                         if(this.stopEvent){
13729                            e.stopEvent();
13730                         }
13731                         fn.call(scope || window, k, e);
13732                     }
13733                 }
13734             }
13735         };
13736         this.bindings.push(handler);  
13737         },
13738
13739     /**
13740      * Shorthand for adding a single key listener
13741      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13742      * following options:
13743      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13744      * @param {Function} fn The function to call
13745      * @param {Object} scope (optional) The scope of the function
13746      */
13747     on : function(key, fn, scope){
13748         var keyCode, shift, ctrl, alt;
13749         if(typeof key == "object" && !(key instanceof Array)){
13750             keyCode = key.key;
13751             shift = key.shift;
13752             ctrl = key.ctrl;
13753             alt = key.alt;
13754         }else{
13755             keyCode = key;
13756         }
13757         this.addBinding({
13758             key: keyCode,
13759             shift: shift,
13760             ctrl: ctrl,
13761             alt: alt,
13762             fn: fn,
13763             scope: scope
13764         })
13765     },
13766
13767     // private
13768     handleKeyDown : function(e){
13769             if(this.enabled){ //just in case
13770             var b = this.bindings;
13771             for(var i = 0, len = b.length; i < len; i++){
13772                 b[i].call(this, e);
13773             }
13774             }
13775         },
13776         
13777         /**
13778          * Returns true if this KeyMap is enabled
13779          * @return {Boolean} 
13780          */
13781         isEnabled : function(){
13782             return this.enabled;  
13783         },
13784         
13785         /**
13786          * Enables this KeyMap
13787          */
13788         enable: function(){
13789                 if(!this.enabled){
13790                     this.el.on(this.eventName, this.handleKeyDown, this);
13791                     this.enabled = true;
13792                 }
13793         },
13794
13795         /**
13796          * Disable this KeyMap
13797          */
13798         disable: function(){
13799                 if(this.enabled){
13800                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13801                     this.enabled = false;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.util.TextMetrics
13818  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13819  * wide, in pixels, a given block of text will be.
13820  * @singleton
13821  */
13822 Roo.util.TextMetrics = function(){
13823     var shared;
13824     return {
13825         /**
13826          * Measures the size of the specified text
13827          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13828          * that can affect the size of the rendered text
13829          * @param {String} text The text to measure
13830          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13831          * in order to accurately measure the text height
13832          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13833          */
13834         measure : function(el, text, fixedWidth){
13835             if(!shared){
13836                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13837             }
13838             shared.bind(el);
13839             shared.setFixedWidth(fixedWidth || 'auto');
13840             return shared.getSize(text);
13841         },
13842
13843         /**
13844          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13845          * the overhead of multiple calls to initialize the style properties on each measurement.
13846          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13847          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13848          * in order to accurately measure the text height
13849          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13850          */
13851         createInstance : function(el, fixedWidth){
13852             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13853         }
13854     };
13855 }();
13856
13857 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13858     var ml = new Roo.Element(document.createElement('div'));
13859     document.body.appendChild(ml.dom);
13860     ml.position('absolute');
13861     ml.setLeftTop(-1000, -1000);
13862     ml.hide();
13863
13864     if(fixedWidth){
13865         ml.setWidth(fixedWidth);
13866     }
13867
13868     var instance = {
13869         /**
13870          * Returns the size of the specified text based on the internal element's style and width properties
13871          * @param {String} text The text to measure
13872          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13873          */
13874         getSize : function(text){
13875             ml.update(text);
13876             var s = ml.getSize();
13877             ml.update('');
13878             return s;
13879         },
13880
13881         /**
13882          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13883          * that can affect the size of the rendered text
13884          * @param {String/HTMLElement} el The element, dom node or id
13885          */
13886         bind : function(el){
13887             ml.setStyle(
13888                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13889             );
13890         },
13891
13892         /**
13893          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13894          * to set a fixed width in order to accurately measure the text height.
13895          * @param {Number} width The width to set on the element
13896          */
13897         setFixedWidth : function(width){
13898             ml.setWidth(width);
13899         },
13900
13901         /**
13902          * Returns the measured width of the specified text
13903          * @param {String} text The text to measure
13904          * @return {Number} width The width in pixels
13905          */
13906         getWidth : function(text){
13907             ml.dom.style.width = 'auto';
13908             return this.getSize(text).width;
13909         },
13910
13911         /**
13912          * Returns the measured height of the specified text.  For multiline text, be sure to call
13913          * {@link #setFixedWidth} if necessary.
13914          * @param {String} text The text to measure
13915          * @return {Number} height The height in pixels
13916          */
13917         getHeight : function(text){
13918             return this.getSize(text).height;
13919         }
13920     };
13921
13922     instance.bind(bindTo);
13923
13924     return instance;
13925 };
13926
13927 // backwards compat
13928 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13929  * Based on:
13930  * Ext JS Library 1.1.1
13931  * Copyright(c) 2006-2007, Ext JS, LLC.
13932  *
13933  * Originally Released Under LGPL - original licence link has changed is not relivant.
13934  *
13935  * Fork - LGPL
13936  * <script type="text/javascript">
13937  */
13938
13939 /**
13940  * @class Roo.state.Provider
13941  * Abstract base class for state provider implementations. This class provides methods
13942  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13943  * Provider interface.
13944  */
13945 Roo.state.Provider = function(){
13946     /**
13947      * @event statechange
13948      * Fires when a state change occurs.
13949      * @param {Provider} this This state provider
13950      * @param {String} key The state key which was changed
13951      * @param {String} value The encoded value for the state
13952      */
13953     this.addEvents({
13954         "statechange": true
13955     });
13956     this.state = {};
13957     Roo.state.Provider.superclass.constructor.call(this);
13958 };
13959 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13960     /**
13961      * Returns the current value for a key
13962      * @param {String} name The key name
13963      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13964      * @return {Mixed} The state data
13965      */
13966     get : function(name, defaultValue){
13967         return typeof this.state[name] == "undefined" ?
13968             defaultValue : this.state[name];
13969     },
13970     
13971     /**
13972      * Clears a value from the state
13973      * @param {String} name The key name
13974      */
13975     clear : function(name){
13976         delete this.state[name];
13977         this.fireEvent("statechange", this, name, null);
13978     },
13979     
13980     /**
13981      * Sets the value for a key
13982      * @param {String} name The key name
13983      * @param {Mixed} value The value to set
13984      */
13985     set : function(name, value){
13986         this.state[name] = value;
13987         this.fireEvent("statechange", this, name, value);
13988     },
13989     
13990     /**
13991      * Decodes a string previously encoded with {@link #encodeValue}.
13992      * @param {String} value The value to decode
13993      * @return {Mixed} The decoded value
13994      */
13995     decodeValue : function(cookie){
13996         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13997         var matches = re.exec(unescape(cookie));
13998         if(!matches || !matches[1]) return; // non state cookie
13999         var type = matches[1];
14000         var v = matches[2];
14001         switch(type){
14002             case "n":
14003                 return parseFloat(v);
14004             case "d":
14005                 return new Date(Date.parse(v));
14006             case "b":
14007                 return (v == "1");
14008             case "a":
14009                 var all = [];
14010                 var values = v.split("^");
14011                 for(var i = 0, len = values.length; i < len; i++){
14012                     all.push(this.decodeValue(values[i]));
14013                 }
14014                 return all;
14015            case "o":
14016                 var all = {};
14017                 var values = v.split("^");
14018                 for(var i = 0, len = values.length; i < len; i++){
14019                     var kv = values[i].split("=");
14020                     all[kv[0]] = this.decodeValue(kv[1]);
14021                 }
14022                 return all;
14023            default:
14024                 return v;
14025         }
14026     },
14027     
14028     /**
14029      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14030      * @param {Mixed} value The value to encode
14031      * @return {String} The encoded value
14032      */
14033     encodeValue : function(v){
14034         var enc;
14035         if(typeof v == "number"){
14036             enc = "n:" + v;
14037         }else if(typeof v == "boolean"){
14038             enc = "b:" + (v ? "1" : "0");
14039         }else if(v instanceof Date){
14040             enc = "d:" + v.toGMTString();
14041         }else if(v instanceof Array){
14042             var flat = "";
14043             for(var i = 0, len = v.length; i < len; i++){
14044                 flat += this.encodeValue(v[i]);
14045                 if(i != len-1) flat += "^";
14046             }
14047             enc = "a:" + flat;
14048         }else if(typeof v == "object"){
14049             var flat = "";
14050             for(var key in v){
14051                 if(typeof v[key] != "function"){
14052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14053                 }
14054             }
14055             enc = "o:" + flat.substring(0, flat.length-1);
14056         }else{
14057             enc = "s:" + v;
14058         }
14059         return escape(enc);        
14060     }
14061 });
14062
14063 /*
14064  * Based on:
14065  * Ext JS Library 1.1.1
14066  * Copyright(c) 2006-2007, Ext JS, LLC.
14067  *
14068  * Originally Released Under LGPL - original licence link has changed is not relivant.
14069  *
14070  * Fork - LGPL
14071  * <script type="text/javascript">
14072  */
14073 /**
14074  * @class Roo.state.Manager
14075  * This is the global state manager. By default all components that are "state aware" check this class
14076  * for state information if you don't pass them a custom state provider. In order for this class
14077  * to be useful, it must be initialized with a provider when your application initializes.
14078  <pre><code>
14079 // in your initialization function
14080 init : function(){
14081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14082    ...
14083    // supposed you have a {@link Roo.BorderLayout}
14084    var layout = new Roo.BorderLayout(...);
14085    layout.restoreState();
14086    // or a {Roo.BasicDialog}
14087    var dialog = new Roo.BasicDialog(...);
14088    dialog.restoreState();
14089  </code></pre>
14090  * @singleton
14091  */
14092 Roo.state.Manager = function(){
14093     var provider = new Roo.state.Provider();
14094     
14095     return {
14096         /**
14097          * Configures the default state provider for your application
14098          * @param {Provider} stateProvider The state provider to set
14099          */
14100         setProvider : function(stateProvider){
14101             provider = stateProvider;
14102         },
14103         
14104         /**
14105          * Returns the current value for a key
14106          * @param {String} name The key name
14107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14108          * @return {Mixed} The state data
14109          */
14110         get : function(key, defaultValue){
14111             return provider.get(key, defaultValue);
14112         },
14113         
14114         /**
14115          * Sets the value for a key
14116          * @param {String} name The key name
14117          * @param {Mixed} value The state data
14118          */
14119          set : function(key, value){
14120             provider.set(key, value);
14121         },
14122         
14123         /**
14124          * Clears a value from the state
14125          * @param {String} name The key name
14126          */
14127         clear : function(key){
14128             provider.clear(key);
14129         },
14130         
14131         /**
14132          * Gets the currently configured state provider
14133          * @return {Provider} The state provider
14134          */
14135         getProvider : function(){
14136             return provider;
14137         }
14138     };
14139 }();
14140 /*
14141  * Based on:
14142  * Ext JS Library 1.1.1
14143  * Copyright(c) 2006-2007, Ext JS, LLC.
14144  *
14145  * Originally Released Under LGPL - original licence link has changed is not relivant.
14146  *
14147  * Fork - LGPL
14148  * <script type="text/javascript">
14149  */
14150 /**
14151  * @class Roo.state.CookieProvider
14152  * @extends Roo.state.Provider
14153  * The default Provider implementation which saves state via cookies.
14154  * <br />Usage:
14155  <pre><code>
14156    var cp = new Roo.state.CookieProvider({
14157        path: "/cgi-bin/",
14158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14159        domain: "roojs.com"
14160    })
14161    Roo.state.Manager.setProvider(cp);
14162  </code></pre>
14163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14168  * domain the page is running on including the 'www' like 'www.roojs.com')
14169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14170  * @constructor
14171  * Create a new CookieProvider
14172  * @param {Object} config The configuration object
14173  */
14174 Roo.state.CookieProvider = function(config){
14175     Roo.state.CookieProvider.superclass.constructor.call(this);
14176     this.path = "/";
14177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14178     this.domain = null;
14179     this.secure = false;
14180     Roo.apply(this, config);
14181     this.state = this.readCookies();
14182 };
14183
14184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14185     // private
14186     set : function(name, value){
14187         if(typeof value == "undefined" || value === null){
14188             this.clear(name);
14189             return;
14190         }
14191         this.setCookie(name, value);
14192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14193     },
14194
14195     // private
14196     clear : function(name){
14197         this.clearCookie(name);
14198         Roo.state.CookieProvider.superclass.clear.call(this, name);
14199     },
14200
14201     // private
14202     readCookies : function(){
14203         var cookies = {};
14204         var c = document.cookie + ";";
14205         var re = /\s?(.*?)=(.*?);/g;
14206         var matches;
14207         while((matches = re.exec(c)) != null){
14208             var name = matches[1];
14209             var value = matches[2];
14210             if(name && name.substring(0,3) == "ys-"){
14211                 cookies[name.substr(3)] = this.decodeValue(value);
14212             }
14213         }
14214         return cookies;
14215     },
14216
14217     // private
14218     setCookie : function(name, value){
14219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14221            ((this.path == null) ? "" : ("; path=" + this.path)) +
14222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14223            ((this.secure == true) ? "; secure" : "");
14224     },
14225
14226     // private
14227     clearCookie : function(name){
14228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14229            ((this.path == null) ? "" : ("; path=" + this.path)) +
14230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14231            ((this.secure == true) ? "; secure" : "");
14232     }
14233 });/*
14234  * Based on:
14235  * Ext JS Library 1.1.1
14236  * Copyright(c) 2006-2007, Ext JS, LLC.
14237  *
14238  * Originally Released Under LGPL - original licence link has changed is not relivant.
14239  *
14240  * Fork - LGPL
14241  * <script type="text/javascript">
14242  */
14243
14244
14245
14246 /*
14247  * These classes are derivatives of the similarly named classes in the YUI Library.
14248  * The original license:
14249  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14250  * Code licensed under the BSD License:
14251  * http://developer.yahoo.net/yui/license.txt
14252  */
14253
14254 (function() {
14255
14256 var Event=Roo.EventManager;
14257 var Dom=Roo.lib.Dom;
14258
14259 /**
14260  * @class Roo.dd.DragDrop
14261  * Defines the interface and base operation of items that that can be
14262  * dragged or can be drop targets.  It was designed to be extended, overriding
14263  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14264  * Up to three html elements can be associated with a DragDrop instance:
14265  * <ul>
14266  * <li>linked element: the element that is passed into the constructor.
14267  * This is the element which defines the boundaries for interaction with
14268  * other DragDrop objects.</li>
14269  * <li>handle element(s): The drag operation only occurs if the element that
14270  * was clicked matches a handle element.  By default this is the linked
14271  * element, but there are times that you will want only a portion of the
14272  * linked element to initiate the drag operation, and the setHandleElId()
14273  * method provides a way to define this.</li>
14274  * <li>drag element: this represents the element that would be moved along
14275  * with the cursor during a drag operation.  By default, this is the linked
14276  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14277  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14278  * </li>
14279  * </ul>
14280  * This class should not be instantiated until the onload event to ensure that
14281  * the associated elements are available.
14282  * The following would define a DragDrop obj that would interact with any
14283  * other DragDrop obj in the "group1" group:
14284  * <pre>
14285  *  dd = new Roo.dd.DragDrop("div1", "group1");
14286  * </pre>
14287  * Since none of the event handlers have been implemented, nothing would
14288  * actually happen if you were to run the code above.  Normally you would
14289  * override this class or one of the default implementations, but you can
14290  * also override the methods you want on an instance of the class...
14291  * <pre>
14292  *  dd.onDragDrop = function(e, id) {
14293  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14294  *  }
14295  * </pre>
14296  * @constructor
14297  * @param {String} id of the element that is linked to this instance
14298  * @param {String} sGroup the group of related DragDrop objects
14299  * @param {object} config an object containing configurable attributes
14300  *                Valid properties for DragDrop:
14301  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14302  */
14303 Roo.dd.DragDrop = function(id, sGroup, config) {
14304     if (id) {
14305         this.init(id, sGroup, config);
14306     }
14307 };
14308
14309 Roo.dd.DragDrop.prototype = {
14310
14311     /**
14312      * The id of the element associated with this object.  This is what we
14313      * refer to as the "linked element" because the size and position of
14314      * this element is used to determine when the drag and drop objects have
14315      * interacted.
14316      * @property id
14317      * @type String
14318      */
14319     id: null,
14320
14321     /**
14322      * Configuration attributes passed into the constructor
14323      * @property config
14324      * @type object
14325      */
14326     config: null,
14327
14328     /**
14329      * The id of the element that will be dragged.  By default this is same
14330      * as the linked element , but could be changed to another element. Ex:
14331      * Roo.dd.DDProxy
14332      * @property dragElId
14333      * @type String
14334      * @private
14335      */
14336     dragElId: null,
14337
14338     /**
14339      * the id of the element that initiates the drag operation.  By default
14340      * this is the linked element, but could be changed to be a child of this
14341      * element.  This lets us do things like only starting the drag when the
14342      * header element within the linked html element is clicked.
14343      * @property handleElId
14344      * @type String
14345      * @private
14346      */
14347     handleElId: null,
14348
14349     /**
14350      * An associative array of HTML tags that will be ignored if clicked.
14351      * @property invalidHandleTypes
14352      * @type {string: string}
14353      */
14354     invalidHandleTypes: null,
14355
14356     /**
14357      * An associative array of ids for elements that will be ignored if clicked
14358      * @property invalidHandleIds
14359      * @type {string: string}
14360      */
14361     invalidHandleIds: null,
14362
14363     /**
14364      * An indexted array of css class names for elements that will be ignored
14365      * if clicked.
14366      * @property invalidHandleClasses
14367      * @type string[]
14368      */
14369     invalidHandleClasses: null,
14370
14371     /**
14372      * The linked element's absolute X position at the time the drag was
14373      * started
14374      * @property startPageX
14375      * @type int
14376      * @private
14377      */
14378     startPageX: 0,
14379
14380     /**
14381      * The linked element's absolute X position at the time the drag was
14382      * started
14383      * @property startPageY
14384      * @type int
14385      * @private
14386      */
14387     startPageY: 0,
14388
14389     /**
14390      * The group defines a logical collection of DragDrop objects that are
14391      * related.  Instances only get events when interacting with other
14392      * DragDrop object in the same group.  This lets us define multiple
14393      * groups using a single DragDrop subclass if we want.
14394      * @property groups
14395      * @type {string: string}
14396      */
14397     groups: null,
14398
14399     /**
14400      * Individual drag/drop instances can be locked.  This will prevent
14401      * onmousedown start drag.
14402      * @property locked
14403      * @type boolean
14404      * @private
14405      */
14406     locked: false,
14407
14408     /**
14409      * Lock this instance
14410      * @method lock
14411      */
14412     lock: function() { this.locked = true; },
14413
14414     /**
14415      * Unlock this instace
14416      * @method unlock
14417      */
14418     unlock: function() { this.locked = false; },
14419
14420     /**
14421      * By default, all insances can be a drop target.  This can be disabled by
14422      * setting isTarget to false.
14423      * @method isTarget
14424      * @type boolean
14425      */
14426     isTarget: true,
14427
14428     /**
14429      * The padding configured for this drag and drop object for calculating
14430      * the drop zone intersection with this object.
14431      * @method padding
14432      * @type int[]
14433      */
14434     padding: null,
14435
14436     /**
14437      * Cached reference to the linked element
14438      * @property _domRef
14439      * @private
14440      */
14441     _domRef: null,
14442
14443     /**
14444      * Internal typeof flag
14445      * @property __ygDragDrop
14446      * @private
14447      */
14448     __ygDragDrop: true,
14449
14450     /**
14451      * Set to true when horizontal contraints are applied
14452      * @property constrainX
14453      * @type boolean
14454      * @private
14455      */
14456     constrainX: false,
14457
14458     /**
14459      * Set to true when vertical contraints are applied
14460      * @property constrainY
14461      * @type boolean
14462      * @private
14463      */
14464     constrainY: false,
14465
14466     /**
14467      * The left constraint
14468      * @property minX
14469      * @type int
14470      * @private
14471      */
14472     minX: 0,
14473
14474     /**
14475      * The right constraint
14476      * @property maxX
14477      * @type int
14478      * @private
14479      */
14480     maxX: 0,
14481
14482     /**
14483      * The up constraint
14484      * @property minY
14485      * @type int
14486      * @type int
14487      * @private
14488      */
14489     minY: 0,
14490
14491     /**
14492      * The down constraint
14493      * @property maxY
14494      * @type int
14495      * @private
14496      */
14497     maxY: 0,
14498
14499     /**
14500      * Maintain offsets when we resetconstraints.  Set to true when you want
14501      * the position of the element relative to its parent to stay the same
14502      * when the page changes
14503      *
14504      * @property maintainOffset
14505      * @type boolean
14506      */
14507     maintainOffset: false,
14508
14509     /**
14510      * Array of pixel locations the element will snap to if we specified a
14511      * horizontal graduation/interval.  This array is generated automatically
14512      * when you define a tick interval.
14513      * @property xTicks
14514      * @type int[]
14515      */
14516     xTicks: null,
14517
14518     /**
14519      * Array of pixel locations the element will snap to if we specified a
14520      * vertical graduation/interval.  This array is generated automatically
14521      * when you define a tick interval.
14522      * @property yTicks
14523      * @type int[]
14524      */
14525     yTicks: null,
14526
14527     /**
14528      * By default the drag and drop instance will only respond to the primary
14529      * button click (left button for a right-handed mouse).  Set to true to
14530      * allow drag and drop to start with any mouse click that is propogated
14531      * by the browser
14532      * @property primaryButtonOnly
14533      * @type boolean
14534      */
14535     primaryButtonOnly: true,
14536
14537     /**
14538      * The availabe property is false until the linked dom element is accessible.
14539      * @property available
14540      * @type boolean
14541      */
14542     available: false,
14543
14544     /**
14545      * By default, drags can only be initiated if the mousedown occurs in the
14546      * region the linked element is.  This is done in part to work around a
14547      * bug in some browsers that mis-report the mousedown if the previous
14548      * mouseup happened outside of the window.  This property is set to true
14549      * if outer handles are defined.
14550      *
14551      * @property hasOuterHandles
14552      * @type boolean
14553      * @default false
14554      */
14555     hasOuterHandles: false,
14556
14557     /**
14558      * Code that executes immediately before the startDrag event
14559      * @method b4StartDrag
14560      * @private
14561      */
14562     b4StartDrag: function(x, y) { },
14563
14564     /**
14565      * Abstract method called after a drag/drop object is clicked
14566      * and the drag or mousedown time thresholds have beeen met.
14567      * @method startDrag
14568      * @param {int} X click location
14569      * @param {int} Y click location
14570      */
14571     startDrag: function(x, y) { /* override this */ },
14572
14573     /**
14574      * Code that executes immediately before the onDrag event
14575      * @method b4Drag
14576      * @private
14577      */
14578     b4Drag: function(e) { },
14579
14580     /**
14581      * Abstract method called during the onMouseMove event while dragging an
14582      * object.
14583      * @method onDrag
14584      * @param {Event} e the mousemove event
14585      */
14586     onDrag: function(e) { /* override this */ },
14587
14588     /**
14589      * Abstract method called when this element fist begins hovering over
14590      * another DragDrop obj
14591      * @method onDragEnter
14592      * @param {Event} e the mousemove event
14593      * @param {String|DragDrop[]} id In POINT mode, the element
14594      * id this is hovering over.  In INTERSECT mode, an array of one or more
14595      * dragdrop items being hovered over.
14596      */
14597     onDragEnter: function(e, id) { /* override this */ },
14598
14599     /**
14600      * Code that executes immediately before the onDragOver event
14601      * @method b4DragOver
14602      * @private
14603      */
14604     b4DragOver: function(e) { },
14605
14606     /**
14607      * Abstract method called when this element is hovering over another
14608      * DragDrop obj
14609      * @method onDragOver
14610      * @param {Event} e the mousemove event
14611      * @param {String|DragDrop[]} id In POINT mode, the element
14612      * id this is hovering over.  In INTERSECT mode, an array of dd items
14613      * being hovered over.
14614      */
14615     onDragOver: function(e, id) { /* override this */ },
14616
14617     /**
14618      * Code that executes immediately before the onDragOut event
14619      * @method b4DragOut
14620      * @private
14621      */
14622     b4DragOut: function(e) { },
14623
14624     /**
14625      * Abstract method called when we are no longer hovering over an element
14626      * @method onDragOut
14627      * @param {Event} e the mousemove event
14628      * @param {String|DragDrop[]} id In POINT mode, the element
14629      * id this was hovering over.  In INTERSECT mode, an array of dd items
14630      * that the mouse is no longer over.
14631      */
14632     onDragOut: function(e, id) { /* override this */ },
14633
14634     /**
14635      * Code that executes immediately before the onDragDrop event
14636      * @method b4DragDrop
14637      * @private
14638      */
14639     b4DragDrop: function(e) { },
14640
14641     /**
14642      * Abstract method called when this item is dropped on another DragDrop
14643      * obj
14644      * @method onDragDrop
14645      * @param {Event} e the mouseup event
14646      * @param {String|DragDrop[]} id In POINT mode, the element
14647      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14648      * was dropped on.
14649      */
14650     onDragDrop: function(e, id) { /* override this */ },
14651
14652     /**
14653      * Abstract method called when this item is dropped on an area with no
14654      * drop target
14655      * @method onInvalidDrop
14656      * @param {Event} e the mouseup event
14657      */
14658     onInvalidDrop: function(e) { /* override this */ },
14659
14660     /**
14661      * Code that executes immediately before the endDrag event
14662      * @method b4EndDrag
14663      * @private
14664      */
14665     b4EndDrag: function(e) { },
14666
14667     /**
14668      * Fired when we are done dragging the object
14669      * @method endDrag
14670      * @param {Event} e the mouseup event
14671      */
14672     endDrag: function(e) { /* override this */ },
14673
14674     /**
14675      * Code executed immediately before the onMouseDown event
14676      * @method b4MouseDown
14677      * @param {Event} e the mousedown event
14678      * @private
14679      */
14680     b4MouseDown: function(e) {  },
14681
14682     /**
14683      * Event handler that fires when a drag/drop obj gets a mousedown
14684      * @method onMouseDown
14685      * @param {Event} e the mousedown event
14686      */
14687     onMouseDown: function(e) { /* override this */ },
14688
14689     /**
14690      * Event handler that fires when a drag/drop obj gets a mouseup
14691      * @method onMouseUp
14692      * @param {Event} e the mouseup event
14693      */
14694     onMouseUp: function(e) { /* override this */ },
14695
14696     /**
14697      * Override the onAvailable method to do what is needed after the initial
14698      * position was determined.
14699      * @method onAvailable
14700      */
14701     onAvailable: function () {
14702     },
14703
14704     /*
14705      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14706      * @type Object
14707      */
14708     defaultPadding : {left:0, right:0, top:0, bottom:0},
14709
14710     /*
14711      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14712  *
14713  * Usage:
14714  <pre><code>
14715  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14716                 { dragElId: "existingProxyDiv" });
14717  dd.startDrag = function(){
14718      this.constrainTo("parent-id");
14719  };
14720  </code></pre>
14721  * Or you can initalize it using the {@link Roo.Element} object:
14722  <pre><code>
14723  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14724      startDrag : function(){
14725          this.constrainTo("parent-id");
14726      }
14727  });
14728  </code></pre>
14729      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14730      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14731      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14732      * an object containing the sides to pad. For example: {right:10, bottom:10}
14733      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14734      */
14735     constrainTo : function(constrainTo, pad, inContent){
14736         if(typeof pad == "number"){
14737             pad = {left: pad, right:pad, top:pad, bottom:pad};
14738         }
14739         pad = pad || this.defaultPadding;
14740         var b = Roo.get(this.getEl()).getBox();
14741         var ce = Roo.get(constrainTo);
14742         var s = ce.getScroll();
14743         var c, cd = ce.dom;
14744         if(cd == document.body){
14745             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14746         }else{
14747             xy = ce.getXY();
14748             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14749         }
14750
14751
14752         var topSpace = b.y - c.y;
14753         var leftSpace = b.x - c.x;
14754
14755         this.resetConstraints();
14756         this.setXConstraint(leftSpace - (pad.left||0), // left
14757                 c.width - leftSpace - b.width - (pad.right||0) //right
14758         );
14759         this.setYConstraint(topSpace - (pad.top||0), //top
14760                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14761         );
14762     },
14763
14764     /**
14765      * Returns a reference to the linked element
14766      * @method getEl
14767      * @return {HTMLElement} the html element
14768      */
14769     getEl: function() {
14770         if (!this._domRef) {
14771             this._domRef = Roo.getDom(this.id);
14772         }
14773
14774         return this._domRef;
14775     },
14776
14777     /**
14778      * Returns a reference to the actual element to drag.  By default this is
14779      * the same as the html element, but it can be assigned to another
14780      * element. An example of this can be found in Roo.dd.DDProxy
14781      * @method getDragEl
14782      * @return {HTMLElement} the html element
14783      */
14784     getDragEl: function() {
14785         return Roo.getDom(this.dragElId);
14786     },
14787
14788     /**
14789      * Sets up the DragDrop object.  Must be called in the constructor of any
14790      * Roo.dd.DragDrop subclass
14791      * @method init
14792      * @param id the id of the linked element
14793      * @param {String} sGroup the group of related items
14794      * @param {object} config configuration attributes
14795      */
14796     init: function(id, sGroup, config) {
14797         this.initTarget(id, sGroup, config);
14798         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14799         // Event.on(this.id, "selectstart", Event.preventDefault);
14800     },
14801
14802     /**
14803      * Initializes Targeting functionality only... the object does not
14804      * get a mousedown handler.
14805      * @method initTarget
14806      * @param id the id of the linked element
14807      * @param {String} sGroup the group of related items
14808      * @param {object} config configuration attributes
14809      */
14810     initTarget: function(id, sGroup, config) {
14811
14812         // configuration attributes
14813         this.config = config || {};
14814
14815         // create a local reference to the drag and drop manager
14816         this.DDM = Roo.dd.DDM;
14817         // initialize the groups array
14818         this.groups = {};
14819
14820         // assume that we have an element reference instead of an id if the
14821         // parameter is not a string
14822         if (typeof id !== "string") {
14823             id = Roo.id(id);
14824         }
14825
14826         // set the id
14827         this.id = id;
14828
14829         // add to an interaction group
14830         this.addToGroup((sGroup) ? sGroup : "default");
14831
14832         // We don't want to register this as the handle with the manager
14833         // so we just set the id rather than calling the setter.
14834         this.handleElId = id;
14835
14836         // the linked element is the element that gets dragged by default
14837         this.setDragElId(id);
14838
14839         // by default, clicked anchors will not start drag operations.
14840         this.invalidHandleTypes = { A: "A" };
14841         this.invalidHandleIds = {};
14842         this.invalidHandleClasses = [];
14843
14844         this.applyConfig();
14845
14846         this.handleOnAvailable();
14847     },
14848
14849     /**
14850      * Applies the configuration parameters that were passed into the constructor.
14851      * This is supposed to happen at each level through the inheritance chain.  So
14852      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14853      * DragDrop in order to get all of the parameters that are available in
14854      * each object.
14855      * @method applyConfig
14856      */
14857     applyConfig: function() {
14858
14859         // configurable properties:
14860         //    padding, isTarget, maintainOffset, primaryButtonOnly
14861         this.padding           = this.config.padding || [0, 0, 0, 0];
14862         this.isTarget          = (this.config.isTarget !== false);
14863         this.maintainOffset    = (this.config.maintainOffset);
14864         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14865
14866     },
14867
14868     /**
14869      * Executed when the linked element is available
14870      * @method handleOnAvailable
14871      * @private
14872      */
14873     handleOnAvailable: function() {
14874         this.available = true;
14875         this.resetConstraints();
14876         this.onAvailable();
14877     },
14878
14879      /**
14880      * Configures the padding for the target zone in px.  Effectively expands
14881      * (or reduces) the virtual object size for targeting calculations.
14882      * Supports css-style shorthand; if only one parameter is passed, all sides
14883      * will have that padding, and if only two are passed, the top and bottom
14884      * will have the first param, the left and right the second.
14885      * @method setPadding
14886      * @param {int} iTop    Top pad
14887      * @param {int} iRight  Right pad
14888      * @param {int} iBot    Bot pad
14889      * @param {int} iLeft   Left pad
14890      */
14891     setPadding: function(iTop, iRight, iBot, iLeft) {
14892         // this.padding = [iLeft, iRight, iTop, iBot];
14893         if (!iRight && 0 !== iRight) {
14894             this.padding = [iTop, iTop, iTop, iTop];
14895         } else if (!iBot && 0 !== iBot) {
14896             this.padding = [iTop, iRight, iTop, iRight];
14897         } else {
14898             this.padding = [iTop, iRight, iBot, iLeft];
14899         }
14900     },
14901
14902     /**
14903      * Stores the initial placement of the linked element.
14904      * @method setInitialPosition
14905      * @param {int} diffX   the X offset, default 0
14906      * @param {int} diffY   the Y offset, default 0
14907      */
14908     setInitPosition: function(diffX, diffY) {
14909         var el = this.getEl();
14910
14911         if (!this.DDM.verifyEl(el)) {
14912             return;
14913         }
14914
14915         var dx = diffX || 0;
14916         var dy = diffY || 0;
14917
14918         var p = Dom.getXY( el );
14919
14920         this.initPageX = p[0] - dx;
14921         this.initPageY = p[1] - dy;
14922
14923         this.lastPageX = p[0];
14924         this.lastPageY = p[1];
14925
14926
14927         this.setStartPosition(p);
14928     },
14929
14930     /**
14931      * Sets the start position of the element.  This is set when the obj
14932      * is initialized, the reset when a drag is started.
14933      * @method setStartPosition
14934      * @param pos current position (from previous lookup)
14935      * @private
14936      */
14937     setStartPosition: function(pos) {
14938         var p = pos || Dom.getXY( this.getEl() );
14939         this.deltaSetXY = null;
14940
14941         this.startPageX = p[0];
14942         this.startPageY = p[1];
14943     },
14944
14945     /**
14946      * Add this instance to a group of related drag/drop objects.  All
14947      * instances belong to at least one group, and can belong to as many
14948      * groups as needed.
14949      * @method addToGroup
14950      * @param sGroup {string} the name of the group
14951      */
14952     addToGroup: function(sGroup) {
14953         this.groups[sGroup] = true;
14954         this.DDM.regDragDrop(this, sGroup);
14955     },
14956
14957     /**
14958      * Remove's this instance from the supplied interaction group
14959      * @method removeFromGroup
14960      * @param {string}  sGroup  The group to drop
14961      */
14962     removeFromGroup: function(sGroup) {
14963         if (this.groups[sGroup]) {
14964             delete this.groups[sGroup];
14965         }
14966
14967         this.DDM.removeDDFromGroup(this, sGroup);
14968     },
14969
14970     /**
14971      * Allows you to specify that an element other than the linked element
14972      * will be moved with the cursor during a drag
14973      * @method setDragElId
14974      * @param id {string} the id of the element that will be used to initiate the drag
14975      */
14976     setDragElId: function(id) {
14977         this.dragElId = id;
14978     },
14979
14980     /**
14981      * Allows you to specify a child of the linked element that should be
14982      * used to initiate the drag operation.  An example of this would be if
14983      * you have a content div with text and links.  Clicking anywhere in the
14984      * content area would normally start the drag operation.  Use this method
14985      * to specify that an element inside of the content div is the element
14986      * that starts the drag operation.
14987      * @method setHandleElId
14988      * @param id {string} the id of the element that will be used to
14989      * initiate the drag.
14990      */
14991     setHandleElId: function(id) {
14992         if (typeof id !== "string") {
14993             id = Roo.id(id);
14994         }
14995         this.handleElId = id;
14996         this.DDM.regHandle(this.id, id);
14997     },
14998
14999     /**
15000      * Allows you to set an element outside of the linked element as a drag
15001      * handle
15002      * @method setOuterHandleElId
15003      * @param id the id of the element that will be used to initiate the drag
15004      */
15005     setOuterHandleElId: function(id) {
15006         if (typeof id !== "string") {
15007             id = Roo.id(id);
15008         }
15009         Event.on(id, "mousedown",
15010                 this.handleMouseDown, this);
15011         this.setHandleElId(id);
15012
15013         this.hasOuterHandles = true;
15014     },
15015
15016     /**
15017      * Remove all drag and drop hooks for this element
15018      * @method unreg
15019      */
15020     unreg: function() {
15021         Event.un(this.id, "mousedown",
15022                 this.handleMouseDown);
15023         this._domRef = null;
15024         this.DDM._remove(this);
15025     },
15026
15027     destroy : function(){
15028         this.unreg();
15029     },
15030
15031     /**
15032      * Returns true if this instance is locked, or the drag drop mgr is locked
15033      * (meaning that all drag/drop is disabled on the page.)
15034      * @method isLocked
15035      * @return {boolean} true if this obj or all drag/drop is locked, else
15036      * false
15037      */
15038     isLocked: function() {
15039         return (this.DDM.isLocked() || this.locked);
15040     },
15041
15042     /**
15043      * Fired when this object is clicked
15044      * @method handleMouseDown
15045      * @param {Event} e
15046      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15047      * @private
15048      */
15049     handleMouseDown: function(e, oDD){
15050         if (this.primaryButtonOnly && e.button != 0) {
15051             return;
15052         }
15053
15054         if (this.isLocked()) {
15055             return;
15056         }
15057
15058         this.DDM.refreshCache(this.groups);
15059
15060         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15061         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15062         } else {
15063             if (this.clickValidator(e)) {
15064
15065                 // set the initial element position
15066                 this.setStartPosition();
15067
15068
15069                 this.b4MouseDown(e);
15070                 this.onMouseDown(e);
15071
15072                 this.DDM.handleMouseDown(e, this);
15073
15074                 this.DDM.stopEvent(e);
15075             } else {
15076
15077
15078             }
15079         }
15080     },
15081
15082     clickValidator: function(e) {
15083         var target = e.getTarget();
15084         return ( this.isValidHandleChild(target) &&
15085                     (this.id == this.handleElId ||
15086                         this.DDM.handleWasClicked(target, this.id)) );
15087     },
15088
15089     /**
15090      * Allows you to specify a tag name that should not start a drag operation
15091      * when clicked.  This is designed to facilitate embedding links within a
15092      * drag handle that do something other than start the drag.
15093      * @method addInvalidHandleType
15094      * @param {string} tagName the type of element to exclude
15095      */
15096     addInvalidHandleType: function(tagName) {
15097         var type = tagName.toUpperCase();
15098         this.invalidHandleTypes[type] = type;
15099     },
15100
15101     /**
15102      * Lets you to specify an element id for a child of a drag handle
15103      * that should not initiate a drag
15104      * @method addInvalidHandleId
15105      * @param {string} id the element id of the element you wish to ignore
15106      */
15107     addInvalidHandleId: function(id) {
15108         if (typeof id !== "string") {
15109             id = Roo.id(id);
15110         }
15111         this.invalidHandleIds[id] = id;
15112     },
15113
15114     /**
15115      * Lets you specify a css class of elements that will not initiate a drag
15116      * @method addInvalidHandleClass
15117      * @param {string} cssClass the class of the elements you wish to ignore
15118      */
15119     addInvalidHandleClass: function(cssClass) {
15120         this.invalidHandleClasses.push(cssClass);
15121     },
15122
15123     /**
15124      * Unsets an excluded tag name set by addInvalidHandleType
15125      * @method removeInvalidHandleType
15126      * @param {string} tagName the type of element to unexclude
15127      */
15128     removeInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         // this.invalidHandleTypes[type] = null;
15131         delete this.invalidHandleTypes[type];
15132     },
15133
15134     /**
15135      * Unsets an invalid handle id
15136      * @method removeInvalidHandleId
15137      * @param {string} id the id of the element to re-enable
15138      */
15139     removeInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         delete this.invalidHandleIds[id];
15144     },
15145
15146     /**
15147      * Unsets an invalid css class
15148      * @method removeInvalidHandleClass
15149      * @param {string} cssClass the class of the element(s) you wish to
15150      * re-enable
15151      */
15152     removeInvalidHandleClass: function(cssClass) {
15153         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15154             if (this.invalidHandleClasses[i] == cssClass) {
15155                 delete this.invalidHandleClasses[i];
15156             }
15157         }
15158     },
15159
15160     /**
15161      * Checks the tag exclusion list to see if this click should be ignored
15162      * @method isValidHandleChild
15163      * @param {HTMLElement} node the HTMLElement to evaluate
15164      * @return {boolean} true if this is a valid tag type, false if not
15165      */
15166     isValidHandleChild: function(node) {
15167
15168         var valid = true;
15169         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15170         var nodeName;
15171         try {
15172             nodeName = node.nodeName.toUpperCase();
15173         } catch(e) {
15174             nodeName = node.nodeName;
15175         }
15176         valid = valid && !this.invalidHandleTypes[nodeName];
15177         valid = valid && !this.invalidHandleIds[node.id];
15178
15179         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15180             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15181         }
15182
15183
15184         return valid;
15185
15186     },
15187
15188     /**
15189      * Create the array of horizontal tick marks if an interval was specified
15190      * in setXConstraint().
15191      * @method setXTicks
15192      * @private
15193      */
15194     setXTicks: function(iStartX, iTickSize) {
15195         this.xTicks = [];
15196         this.xTickSize = iTickSize;
15197
15198         var tickMap = {};
15199
15200         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15201             if (!tickMap[i]) {
15202                 this.xTicks[this.xTicks.length] = i;
15203                 tickMap[i] = true;
15204             }
15205         }
15206
15207         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15208             if (!tickMap[i]) {
15209                 this.xTicks[this.xTicks.length] = i;
15210                 tickMap[i] = true;
15211             }
15212         }
15213
15214         this.xTicks.sort(this.DDM.numericSort) ;
15215     },
15216
15217     /**
15218      * Create the array of vertical tick marks if an interval was specified in
15219      * setYConstraint().
15220      * @method setYTicks
15221      * @private
15222      */
15223     setYTicks: function(iStartY, iTickSize) {
15224         this.yTicks = [];
15225         this.yTickSize = iTickSize;
15226
15227         var tickMap = {};
15228
15229         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15230             if (!tickMap[i]) {
15231                 this.yTicks[this.yTicks.length] = i;
15232                 tickMap[i] = true;
15233             }
15234         }
15235
15236         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.yTicks[this.yTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         this.yTicks.sort(this.DDM.numericSort) ;
15244     },
15245
15246     /**
15247      * By default, the element can be dragged any place on the screen.  Use
15248      * this method to limit the horizontal travel of the element.  Pass in
15249      * 0,0 for the parameters if you want to lock the drag to the y axis.
15250      * @method setXConstraint
15251      * @param {int} iLeft the number of pixels the element can move to the left
15252      * @param {int} iRight the number of pixels the element can move to the
15253      * right
15254      * @param {int} iTickSize optional parameter for specifying that the
15255      * element
15256      * should move iTickSize pixels at a time.
15257      */
15258     setXConstraint: function(iLeft, iRight, iTickSize) {
15259         this.leftConstraint = iLeft;
15260         this.rightConstraint = iRight;
15261
15262         this.minX = this.initPageX - iLeft;
15263         this.maxX = this.initPageX + iRight;
15264         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15265
15266         this.constrainX = true;
15267     },
15268
15269     /**
15270      * Clears any constraints applied to this instance.  Also clears ticks
15271      * since they can't exist independent of a constraint at this time.
15272      * @method clearConstraints
15273      */
15274     clearConstraints: function() {
15275         this.constrainX = false;
15276         this.constrainY = false;
15277         this.clearTicks();
15278     },
15279
15280     /**
15281      * Clears any tick interval defined for this instance
15282      * @method clearTicks
15283      */
15284     clearTicks: function() {
15285         this.xTicks = null;
15286         this.yTicks = null;
15287         this.xTickSize = 0;
15288         this.yTickSize = 0;
15289     },
15290
15291     /**
15292      * By default, the element can be dragged any place on the screen.  Set
15293      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15294      * parameters if you want to lock the drag to the x axis.
15295      * @method setYConstraint
15296      * @param {int} iUp the number of pixels the element can move up
15297      * @param {int} iDown the number of pixels the element can move down
15298      * @param {int} iTickSize optional parameter for specifying that the
15299      * element should move iTickSize pixels at a time.
15300      */
15301     setYConstraint: function(iUp, iDown, iTickSize) {
15302         this.topConstraint = iUp;
15303         this.bottomConstraint = iDown;
15304
15305         this.minY = this.initPageY - iUp;
15306         this.maxY = this.initPageY + iDown;
15307         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15308
15309         this.constrainY = true;
15310
15311     },
15312
15313     /**
15314      * resetConstraints must be called if you manually reposition a dd element.
15315      * @method resetConstraints
15316      * @param {boolean} maintainOffset
15317      */
15318     resetConstraints: function() {
15319
15320
15321         // Maintain offsets if necessary
15322         if (this.initPageX || this.initPageX === 0) {
15323             // figure out how much this thing has moved
15324             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15325             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15326
15327             this.setInitPosition(dx, dy);
15328
15329         // This is the first time we have detected the element's position
15330         } else {
15331             this.setInitPosition();
15332         }
15333
15334         if (this.constrainX) {
15335             this.setXConstraint( this.leftConstraint,
15336                                  this.rightConstraint,
15337                                  this.xTickSize        );
15338         }
15339
15340         if (this.constrainY) {
15341             this.setYConstraint( this.topConstraint,
15342                                  this.bottomConstraint,
15343                                  this.yTickSize         );
15344         }
15345     },
15346
15347     /**
15348      * Normally the drag element is moved pixel by pixel, but we can specify
15349      * that it move a number of pixels at a time.  This method resolves the
15350      * location when we have it set up like this.
15351      * @method getTick
15352      * @param {int} val where we want to place the object
15353      * @param {int[]} tickArray sorted array of valid points
15354      * @return {int} the closest tick
15355      * @private
15356      */
15357     getTick: function(val, tickArray) {
15358
15359         if (!tickArray) {
15360             // If tick interval is not defined, it is effectively 1 pixel,
15361             // so we return the value passed to us.
15362             return val;
15363         } else if (tickArray[0] >= val) {
15364             // The value is lower than the first tick, so we return the first
15365             // tick.
15366             return tickArray[0];
15367         } else {
15368             for (var i=0, len=tickArray.length; i<len; ++i) {
15369                 var next = i + 1;
15370                 if (tickArray[next] && tickArray[next] >= val) {
15371                     var diff1 = val - tickArray[i];
15372                     var diff2 = tickArray[next] - val;
15373                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15374                 }
15375             }
15376
15377             // The value is larger than the last tick, so we return the last
15378             // tick.
15379             return tickArray[tickArray.length - 1];
15380         }
15381     },
15382
15383     /**
15384      * toString method
15385      * @method toString
15386      * @return {string} string representation of the dd obj
15387      */
15388     toString: function() {
15389         return ("DragDrop " + this.id);
15390     }
15391
15392 };
15393
15394 })();
15395 /*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406
15407 /**
15408  * The drag and drop utility provides a framework for building drag and drop
15409  * applications.  In addition to enabling drag and drop for specific elements,
15410  * the drag and drop elements are tracked by the manager class, and the
15411  * interactions between the various elements are tracked during the drag and
15412  * the implementing code is notified about these important moments.
15413  */
15414
15415 // Only load the library once.  Rewriting the manager class would orphan
15416 // existing drag and drop instances.
15417 if (!Roo.dd.DragDropMgr) {
15418
15419 /**
15420  * @class Roo.dd.DragDropMgr
15421  * DragDropMgr is a singleton that tracks the element interaction for
15422  * all DragDrop items in the window.  Generally, you will not call
15423  * this class directly, but it does have helper methods that could
15424  * be useful in your DragDrop implementations.
15425  * @singleton
15426  */
15427 Roo.dd.DragDropMgr = function() {
15428
15429     var Event = Roo.EventManager;
15430
15431     return {
15432
15433         /**
15434          * Two dimensional Array of registered DragDrop objects.  The first
15435          * dimension is the DragDrop item group, the second the DragDrop
15436          * object.
15437          * @property ids
15438          * @type {string: string}
15439          * @private
15440          * @static
15441          */
15442         ids: {},
15443
15444         /**
15445          * Array of element ids defined as drag handles.  Used to determine
15446          * if the element that generated the mousedown event is actually the
15447          * handle and not the html element itself.
15448          * @property handleIds
15449          * @type {string: string}
15450          * @private
15451          * @static
15452          */
15453         handleIds: {},
15454
15455         /**
15456          * the DragDrop object that is currently being dragged
15457          * @property dragCurrent
15458          * @type DragDrop
15459          * @private
15460          * @static
15461          **/
15462         dragCurrent: null,
15463
15464         /**
15465          * the DragDrop object(s) that are being hovered over
15466          * @property dragOvers
15467          * @type Array
15468          * @private
15469          * @static
15470          */
15471         dragOvers: {},
15472
15473         /**
15474          * the X distance between the cursor and the object being dragged
15475          * @property deltaX
15476          * @type int
15477          * @private
15478          * @static
15479          */
15480         deltaX: 0,
15481
15482         /**
15483          * the Y distance between the cursor and the object being dragged
15484          * @property deltaY
15485          * @type int
15486          * @private
15487          * @static
15488          */
15489         deltaY: 0,
15490
15491         /**
15492          * Flag to determine if we should prevent the default behavior of the
15493          * events we define. By default this is true, but this can be set to
15494          * false if you need the default behavior (not recommended)
15495          * @property preventDefault
15496          * @type boolean
15497          * @static
15498          */
15499         preventDefault: true,
15500
15501         /**
15502          * Flag to determine if we should stop the propagation of the events
15503          * we generate. This is true by default but you may want to set it to
15504          * false if the html element contains other features that require the
15505          * mouse click.
15506          * @property stopPropagation
15507          * @type boolean
15508          * @static
15509          */
15510         stopPropagation: true,
15511
15512         /**
15513          * Internal flag that is set to true when drag and drop has been
15514          * intialized
15515          * @property initialized
15516          * @private
15517          * @static
15518          */
15519         initalized: false,
15520
15521         /**
15522          * All drag and drop can be disabled.
15523          * @property locked
15524          * @private
15525          * @static
15526          */
15527         locked: false,
15528
15529         /**
15530          * Called the first time an element is registered.
15531          * @method init
15532          * @private
15533          * @static
15534          */
15535         init: function() {
15536             this.initialized = true;
15537         },
15538
15539         /**
15540          * In point mode, drag and drop interaction is defined by the
15541          * location of the cursor during the drag/drop
15542          * @property POINT
15543          * @type int
15544          * @static
15545          */
15546         POINT: 0,
15547
15548         /**
15549          * In intersect mode, drag and drop interactio nis defined by the
15550          * overlap of two or more drag and drop objects.
15551          * @property INTERSECT
15552          * @type int
15553          * @static
15554          */
15555         INTERSECT: 1,
15556
15557         /**
15558          * The current drag and drop mode.  Default: POINT
15559          * @property mode
15560          * @type int
15561          * @static
15562          */
15563         mode: 0,
15564
15565         /**
15566          * Runs method on all drag and drop objects
15567          * @method _execOnAll
15568          * @private
15569          * @static
15570          */
15571         _execOnAll: function(sMethod, args) {
15572             for (var i in this.ids) {
15573                 for (var j in this.ids[i]) {
15574                     var oDD = this.ids[i][j];
15575                     if (! this.isTypeOfDD(oDD)) {
15576                         continue;
15577                     }
15578                     oDD[sMethod].apply(oDD, args);
15579                 }
15580             }
15581         },
15582
15583         /**
15584          * Drag and drop initialization.  Sets up the global event handlers
15585          * @method _onLoad
15586          * @private
15587          * @static
15588          */
15589         _onLoad: function() {
15590
15591             this.init();
15592
15593
15594             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15595             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15596             Event.on(window,   "unload",    this._onUnload, this, true);
15597             Event.on(window,   "resize",    this._onResize, this, true);
15598             // Event.on(window,   "mouseout",    this._test);
15599
15600         },
15601
15602         /**
15603          * Reset constraints on all drag and drop objs
15604          * @method _onResize
15605          * @private
15606          * @static
15607          */
15608         _onResize: function(e) {
15609             this._execOnAll("resetConstraints", []);
15610         },
15611
15612         /**
15613          * Lock all drag and drop functionality
15614          * @method lock
15615          * @static
15616          */
15617         lock: function() { this.locked = true; },
15618
15619         /**
15620          * Unlock all drag and drop functionality
15621          * @method unlock
15622          * @static
15623          */
15624         unlock: function() { this.locked = false; },
15625
15626         /**
15627          * Is drag and drop locked?
15628          * @method isLocked
15629          * @return {boolean} True if drag and drop is locked, false otherwise.
15630          * @static
15631          */
15632         isLocked: function() { return this.locked; },
15633
15634         /**
15635          * Location cache that is set for all drag drop objects when a drag is
15636          * initiated, cleared when the drag is finished.
15637          * @property locationCache
15638          * @private
15639          * @static
15640          */
15641         locationCache: {},
15642
15643         /**
15644          * Set useCache to false if you want to force object the lookup of each
15645          * drag and drop linked element constantly during a drag.
15646          * @property useCache
15647          * @type boolean
15648          * @static
15649          */
15650         useCache: true,
15651
15652         /**
15653          * The number of pixels that the mouse needs to move after the
15654          * mousedown before the drag is initiated.  Default=3;
15655          * @property clickPixelThresh
15656          * @type int
15657          * @static
15658          */
15659         clickPixelThresh: 3,
15660
15661         /**
15662          * The number of milliseconds after the mousedown event to initiate the
15663          * drag if we don't get a mouseup event. Default=1000
15664          * @property clickTimeThresh
15665          * @type int
15666          * @static
15667          */
15668         clickTimeThresh: 350,
15669
15670         /**
15671          * Flag that indicates that either the drag pixel threshold or the
15672          * mousdown time threshold has been met
15673          * @property dragThreshMet
15674          * @type boolean
15675          * @private
15676          * @static
15677          */
15678         dragThreshMet: false,
15679
15680         /**
15681          * Timeout used for the click time threshold
15682          * @property clickTimeout
15683          * @type Object
15684          * @private
15685          * @static
15686          */
15687         clickTimeout: null,
15688
15689         /**
15690          * The X position of the mousedown event stored for later use when a
15691          * drag threshold is met.
15692          * @property startX
15693          * @type int
15694          * @private
15695          * @static
15696          */
15697         startX: 0,
15698
15699         /**
15700          * The Y position of the mousedown event stored for later use when a
15701          * drag threshold is met.
15702          * @property startY
15703          * @type int
15704          * @private
15705          * @static
15706          */
15707         startY: 0,
15708
15709         /**
15710          * Each DragDrop instance must be registered with the DragDropMgr.
15711          * This is executed in DragDrop.init()
15712          * @method regDragDrop
15713          * @param {DragDrop} oDD the DragDrop object to register
15714          * @param {String} sGroup the name of the group this element belongs to
15715          * @static
15716          */
15717         regDragDrop: function(oDD, sGroup) {
15718             if (!this.initialized) { this.init(); }
15719
15720             if (!this.ids[sGroup]) {
15721                 this.ids[sGroup] = {};
15722             }
15723             this.ids[sGroup][oDD.id] = oDD;
15724         },
15725
15726         /**
15727          * Removes the supplied dd instance from the supplied group. Executed
15728          * by DragDrop.removeFromGroup, so don't call this function directly.
15729          * @method removeDDFromGroup
15730          * @private
15731          * @static
15732          */
15733         removeDDFromGroup: function(oDD, sGroup) {
15734             if (!this.ids[sGroup]) {
15735                 this.ids[sGroup] = {};
15736             }
15737
15738             var obj = this.ids[sGroup];
15739             if (obj && obj[oDD.id]) {
15740                 delete obj[oDD.id];
15741             }
15742         },
15743
15744         /**
15745          * Unregisters a drag and drop item.  This is executed in
15746          * DragDrop.unreg, use that method instead of calling this directly.
15747          * @method _remove
15748          * @private
15749          * @static
15750          */
15751         _remove: function(oDD) {
15752             for (var g in oDD.groups) {
15753                 if (g && this.ids[g][oDD.id]) {
15754                     delete this.ids[g][oDD.id];
15755                 }
15756             }
15757             delete this.handleIds[oDD.id];
15758         },
15759
15760         /**
15761          * Each DragDrop handle element must be registered.  This is done
15762          * automatically when executing DragDrop.setHandleElId()
15763          * @method regHandle
15764          * @param {String} sDDId the DragDrop id this element is a handle for
15765          * @param {String} sHandleId the id of the element that is the drag
15766          * handle
15767          * @static
15768          */
15769         regHandle: function(sDDId, sHandleId) {
15770             if (!this.handleIds[sDDId]) {
15771                 this.handleIds[sDDId] = {};
15772             }
15773             this.handleIds[sDDId][sHandleId] = sHandleId;
15774         },
15775
15776         /**
15777          * Utility function to determine if a given element has been
15778          * registered as a drag drop item.
15779          * @method isDragDrop
15780          * @param {String} id the element id to check
15781          * @return {boolean} true if this element is a DragDrop item,
15782          * false otherwise
15783          * @static
15784          */
15785         isDragDrop: function(id) {
15786             return ( this.getDDById(id) ) ? true : false;
15787         },
15788
15789         /**
15790          * Returns the drag and drop instances that are in all groups the
15791          * passed in instance belongs to.
15792          * @method getRelated
15793          * @param {DragDrop} p_oDD the obj to get related data for
15794          * @param {boolean} bTargetsOnly if true, only return targetable objs
15795          * @return {DragDrop[]} the related instances
15796          * @static
15797          */
15798         getRelated: function(p_oDD, bTargetsOnly) {
15799             var oDDs = [];
15800             for (var i in p_oDD.groups) {
15801                 for (j in this.ids[i]) {
15802                     var dd = this.ids[i][j];
15803                     if (! this.isTypeOfDD(dd)) {
15804                         continue;
15805                     }
15806                     if (!bTargetsOnly || dd.isTarget) {
15807                         oDDs[oDDs.length] = dd;
15808                     }
15809                 }
15810             }
15811
15812             return oDDs;
15813         },
15814
15815         /**
15816          * Returns true if the specified dd target is a legal target for
15817          * the specifice drag obj
15818          * @method isLegalTarget
15819          * @param {DragDrop} the drag obj
15820          * @param {DragDrop} the target
15821          * @return {boolean} true if the target is a legal target for the
15822          * dd obj
15823          * @static
15824          */
15825         isLegalTarget: function (oDD, oTargetDD) {
15826             var targets = this.getRelated(oDD, true);
15827             for (var i=0, len=targets.length;i<len;++i) {
15828                 if (targets[i].id == oTargetDD.id) {
15829                     return true;
15830                 }
15831             }
15832
15833             return false;
15834         },
15835
15836         /**
15837          * My goal is to be able to transparently determine if an object is
15838          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15839          * returns "object", oDD.constructor.toString() always returns
15840          * "DragDrop" and not the name of the subclass.  So for now it just
15841          * evaluates a well-known variable in DragDrop.
15842          * @method isTypeOfDD
15843          * @param {Object} the object to evaluate
15844          * @return {boolean} true if typeof oDD = DragDrop
15845          * @static
15846          */
15847         isTypeOfDD: function (oDD) {
15848             return (oDD && oDD.__ygDragDrop);
15849         },
15850
15851         /**
15852          * Utility function to determine if a given element has been
15853          * registered as a drag drop handle for the given Drag Drop object.
15854          * @method isHandle
15855          * @param {String} id the element id to check
15856          * @return {boolean} true if this element is a DragDrop handle, false
15857          * otherwise
15858          * @static
15859          */
15860         isHandle: function(sDDId, sHandleId) {
15861             return ( this.handleIds[sDDId] &&
15862                             this.handleIds[sDDId][sHandleId] );
15863         },
15864
15865         /**
15866          * Returns the DragDrop instance for a given id
15867          * @method getDDById
15868          * @param {String} id the id of the DragDrop object
15869          * @return {DragDrop} the drag drop object, null if it is not found
15870          * @static
15871          */
15872         getDDById: function(id) {
15873             for (var i in this.ids) {
15874                 if (this.ids[i][id]) {
15875                     return this.ids[i][id];
15876                 }
15877             }
15878             return null;
15879         },
15880
15881         /**
15882          * Fired after a registered DragDrop object gets the mousedown event.
15883          * Sets up the events required to track the object being dragged
15884          * @method handleMouseDown
15885          * @param {Event} e the event
15886          * @param oDD the DragDrop object being dragged
15887          * @private
15888          * @static
15889          */
15890         handleMouseDown: function(e, oDD) {
15891             if(Roo.QuickTips){
15892                 Roo.QuickTips.disable();
15893             }
15894             this.currentTarget = e.getTarget();
15895
15896             this.dragCurrent = oDD;
15897
15898             var el = oDD.getEl();
15899
15900             // track start position
15901             this.startX = e.getPageX();
15902             this.startY = e.getPageY();
15903
15904             this.deltaX = this.startX - el.offsetLeft;
15905             this.deltaY = this.startY - el.offsetTop;
15906
15907             this.dragThreshMet = false;
15908
15909             this.clickTimeout = setTimeout(
15910                     function() {
15911                         var DDM = Roo.dd.DDM;
15912                         DDM.startDrag(DDM.startX, DDM.startY);
15913                     },
15914                     this.clickTimeThresh );
15915         },
15916
15917         /**
15918          * Fired when either the drag pixel threshol or the mousedown hold
15919          * time threshold has been met.
15920          * @method startDrag
15921          * @param x {int} the X position of the original mousedown
15922          * @param y {int} the Y position of the original mousedown
15923          * @static
15924          */
15925         startDrag: function(x, y) {
15926             clearTimeout(this.clickTimeout);
15927             if (this.dragCurrent) {
15928                 this.dragCurrent.b4StartDrag(x, y);
15929                 this.dragCurrent.startDrag(x, y);
15930             }
15931             this.dragThreshMet = true;
15932         },
15933
15934         /**
15935          * Internal function to handle the mouseup event.  Will be invoked
15936          * from the context of the document.
15937          * @method handleMouseUp
15938          * @param {Event} e the event
15939          * @private
15940          * @static
15941          */
15942         handleMouseUp: function(e) {
15943
15944             if(Roo.QuickTips){
15945                 Roo.QuickTips.enable();
15946             }
15947             if (! this.dragCurrent) {
15948                 return;
15949             }
15950
15951             clearTimeout(this.clickTimeout);
15952
15953             if (this.dragThreshMet) {
15954                 this.fireEvents(e, true);
15955             } else {
15956             }
15957
15958             this.stopDrag(e);
15959
15960             this.stopEvent(e);
15961         },
15962
15963         /**
15964          * Utility to stop event propagation and event default, if these
15965          * features are turned on.
15966          * @method stopEvent
15967          * @param {Event} e the event as returned by this.getEvent()
15968          * @static
15969          */
15970         stopEvent: function(e){
15971             if(this.stopPropagation) {
15972                 e.stopPropagation();
15973             }
15974
15975             if (this.preventDefault) {
15976                 e.preventDefault();
15977             }
15978         },
15979
15980         /**
15981          * Internal function to clean up event handlers after the drag
15982          * operation is complete
15983          * @method stopDrag
15984          * @param {Event} e the event
15985          * @private
15986          * @static
15987          */
15988         stopDrag: function(e) {
15989             // Fire the drag end event for the item that was dragged
15990             if (this.dragCurrent) {
15991                 if (this.dragThreshMet) {
15992                     this.dragCurrent.b4EndDrag(e);
15993                     this.dragCurrent.endDrag(e);
15994                 }
15995
15996                 this.dragCurrent.onMouseUp(e);
15997             }
15998
15999             this.dragCurrent = null;
16000             this.dragOvers = {};
16001         },
16002
16003         /**
16004          * Internal function to handle the mousemove event.  Will be invoked
16005          * from the context of the html element.
16006          *
16007          * @TODO figure out what we can do about mouse events lost when the
16008          * user drags objects beyond the window boundary.  Currently we can
16009          * detect this in internet explorer by verifying that the mouse is
16010          * down during the mousemove event.  Firefox doesn't give us the
16011          * button state on the mousemove event.
16012          * @method handleMouseMove
16013          * @param {Event} e the event
16014          * @private
16015          * @static
16016          */
16017         handleMouseMove: function(e) {
16018             if (! this.dragCurrent) {
16019                 return true;
16020             }
16021
16022             // var button = e.which || e.button;
16023
16024             // check for IE mouseup outside of page boundary
16025             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16026                 this.stopEvent(e);
16027                 return this.handleMouseUp(e);
16028             }
16029
16030             if (!this.dragThreshMet) {
16031                 var diffX = Math.abs(this.startX - e.getPageX());
16032                 var diffY = Math.abs(this.startY - e.getPageY());
16033                 if (diffX > this.clickPixelThresh ||
16034                             diffY > this.clickPixelThresh) {
16035                     this.startDrag(this.startX, this.startY);
16036                 }
16037             }
16038
16039             if (this.dragThreshMet) {
16040                 this.dragCurrent.b4Drag(e);
16041                 this.dragCurrent.onDrag(e);
16042                 if(!this.dragCurrent.moveOnly){
16043                     this.fireEvents(e, false);
16044                 }
16045             }
16046
16047             this.stopEvent(e);
16048
16049             return true;
16050         },
16051
16052         /**
16053          * Iterates over all of the DragDrop elements to find ones we are
16054          * hovering over or dropping on
16055          * @method fireEvents
16056          * @param {Event} e the event
16057          * @param {boolean} isDrop is this a drop op or a mouseover op?
16058          * @private
16059          * @static
16060          */
16061         fireEvents: function(e, isDrop) {
16062             var dc = this.dragCurrent;
16063
16064             // If the user did the mouse up outside of the window, we could
16065             // get here even though we have ended the drag.
16066             if (!dc || dc.isLocked()) {
16067                 return;
16068             }
16069
16070             var pt = e.getPoint();
16071
16072             // cache the previous dragOver array
16073             var oldOvers = [];
16074
16075             var outEvts   = [];
16076             var overEvts  = [];
16077             var dropEvts  = [];
16078             var enterEvts = [];
16079
16080             // Check to see if the object(s) we were hovering over is no longer
16081             // being hovered over so we can fire the onDragOut event
16082             for (var i in this.dragOvers) {
16083
16084                 var ddo = this.dragOvers[i];
16085
16086                 if (! this.isTypeOfDD(ddo)) {
16087                     continue;
16088                 }
16089
16090                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16091                     outEvts.push( ddo );
16092                 }
16093
16094                 oldOvers[i] = true;
16095                 delete this.dragOvers[i];
16096             }
16097
16098             for (var sGroup in dc.groups) {
16099
16100                 if ("string" != typeof sGroup) {
16101                     continue;
16102                 }
16103
16104                 for (i in this.ids[sGroup]) {
16105                     var oDD = this.ids[sGroup][i];
16106                     if (! this.isTypeOfDD(oDD)) {
16107                         continue;
16108                     }
16109
16110                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16111                         if (this.isOverTarget(pt, oDD, this.mode)) {
16112                             // look for drop interactions
16113                             if (isDrop) {
16114                                 dropEvts.push( oDD );
16115                             // look for drag enter and drag over interactions
16116                             } else {
16117
16118                                 // initial drag over: dragEnter fires
16119                                 if (!oldOvers[oDD.id]) {
16120                                     enterEvts.push( oDD );
16121                                 // subsequent drag overs: dragOver fires
16122                                 } else {
16123                                     overEvts.push( oDD );
16124                                 }
16125
16126                                 this.dragOvers[oDD.id] = oDD;
16127                             }
16128                         }
16129                     }
16130                 }
16131             }
16132
16133             if (this.mode) {
16134                 if (outEvts.length) {
16135                     dc.b4DragOut(e, outEvts);
16136                     dc.onDragOut(e, outEvts);
16137                 }
16138
16139                 if (enterEvts.length) {
16140                     dc.onDragEnter(e, enterEvts);
16141                 }
16142
16143                 if (overEvts.length) {
16144                     dc.b4DragOver(e, overEvts);
16145                     dc.onDragOver(e, overEvts);
16146                 }
16147
16148                 if (dropEvts.length) {
16149                     dc.b4DragDrop(e, dropEvts);
16150                     dc.onDragDrop(e, dropEvts);
16151                 }
16152
16153             } else {
16154                 // fire dragout events
16155                 var len = 0;
16156                 for (i=0, len=outEvts.length; i<len; ++i) {
16157                     dc.b4DragOut(e, outEvts[i].id);
16158                     dc.onDragOut(e, outEvts[i].id);
16159                 }
16160
16161                 // fire enter events
16162                 for (i=0,len=enterEvts.length; i<len; ++i) {
16163                     // dc.b4DragEnter(e, oDD.id);
16164                     dc.onDragEnter(e, enterEvts[i].id);
16165                 }
16166
16167                 // fire over events
16168                 for (i=0,len=overEvts.length; i<len; ++i) {
16169                     dc.b4DragOver(e, overEvts[i].id);
16170                     dc.onDragOver(e, overEvts[i].id);
16171                 }
16172
16173                 // fire drop events
16174                 for (i=0, len=dropEvts.length; i<len; ++i) {
16175                     dc.b4DragDrop(e, dropEvts[i].id);
16176                     dc.onDragDrop(e, dropEvts[i].id);
16177                 }
16178
16179             }
16180
16181             // notify about a drop that did not find a target
16182             if (isDrop && !dropEvts.length) {
16183                 dc.onInvalidDrop(e);
16184             }
16185
16186         },
16187
16188         /**
16189          * Helper function for getting the best match from the list of drag
16190          * and drop objects returned by the drag and drop events when we are
16191          * in INTERSECT mode.  It returns either the first object that the
16192          * cursor is over, or the object that has the greatest overlap with
16193          * the dragged element.
16194          * @method getBestMatch
16195          * @param  {DragDrop[]} dds The array of drag and drop objects
16196          * targeted
16197          * @return {DragDrop}       The best single match
16198          * @static
16199          */
16200         getBestMatch: function(dds) {
16201             var winner = null;
16202             // Return null if the input is not what we expect
16203             //if (!dds || !dds.length || dds.length == 0) {
16204                // winner = null;
16205             // If there is only one item, it wins
16206             //} else if (dds.length == 1) {
16207
16208             var len = dds.length;
16209
16210             if (len == 1) {
16211                 winner = dds[0];
16212             } else {
16213                 // Loop through the targeted items
16214                 for (var i=0; i<len; ++i) {
16215                     var dd = dds[i];
16216                     // If the cursor is over the object, it wins.  If the
16217                     // cursor is over multiple matches, the first one we come
16218                     // to wins.
16219                     if (dd.cursorIsOver) {
16220                         winner = dd;
16221                         break;
16222                     // Otherwise the object with the most overlap wins
16223                     } else {
16224                         if (!winner ||
16225                             winner.overlap.getArea() < dd.overlap.getArea()) {
16226                             winner = dd;
16227                         }
16228                     }
16229                 }
16230             }
16231
16232             return winner;
16233         },
16234
16235         /**
16236          * Refreshes the cache of the top-left and bottom-right points of the
16237          * drag and drop objects in the specified group(s).  This is in the
16238          * format that is stored in the drag and drop instance, so typical
16239          * usage is:
16240          * <code>
16241          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16242          * </code>
16243          * Alternatively:
16244          * <code>
16245          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16246          * </code>
16247          * @TODO this really should be an indexed array.  Alternatively this
16248          * method could accept both.
16249          * @method refreshCache
16250          * @param {Object} groups an associative array of groups to refresh
16251          * @static
16252          */
16253         refreshCache: function(groups) {
16254             for (var sGroup in groups) {
16255                 if ("string" != typeof sGroup) {
16256                     continue;
16257                 }
16258                 for (var i in this.ids[sGroup]) {
16259                     var oDD = this.ids[sGroup][i];
16260
16261                     if (this.isTypeOfDD(oDD)) {
16262                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16263                         var loc = this.getLocation(oDD);
16264                         if (loc) {
16265                             this.locationCache[oDD.id] = loc;
16266                         } else {
16267                             delete this.locationCache[oDD.id];
16268                             // this will unregister the drag and drop object if
16269                             // the element is not in a usable state
16270                             // oDD.unreg();
16271                         }
16272                     }
16273                 }
16274             }
16275         },
16276
16277         /**
16278          * This checks to make sure an element exists and is in the DOM.  The
16279          * main purpose is to handle cases where innerHTML is used to remove
16280          * drag and drop objects from the DOM.  IE provides an 'unspecified
16281          * error' when trying to access the offsetParent of such an element
16282          * @method verifyEl
16283          * @param {HTMLElement} el the element to check
16284          * @return {boolean} true if the element looks usable
16285          * @static
16286          */
16287         verifyEl: function(el) {
16288             if (el) {
16289                 var parent;
16290                 if(Roo.isIE){
16291                     try{
16292                         parent = el.offsetParent;
16293                     }catch(e){}
16294                 }else{
16295                     parent = el.offsetParent;
16296                 }
16297                 if (parent) {
16298                     return true;
16299                 }
16300             }
16301
16302             return false;
16303         },
16304
16305         /**
16306          * Returns a Region object containing the drag and drop element's position
16307          * and size, including the padding configured for it
16308          * @method getLocation
16309          * @param {DragDrop} oDD the drag and drop object to get the
16310          *                       location for
16311          * @return {Roo.lib.Region} a Region object representing the total area
16312          *                             the element occupies, including any padding
16313          *                             the instance is configured for.
16314          * @static
16315          */
16316         getLocation: function(oDD) {
16317             if (! this.isTypeOfDD(oDD)) {
16318                 return null;
16319             }
16320
16321             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16322
16323             try {
16324                 pos= Roo.lib.Dom.getXY(el);
16325             } catch (e) { }
16326
16327             if (!pos) {
16328                 return null;
16329             }
16330
16331             x1 = pos[0];
16332             x2 = x1 + el.offsetWidth;
16333             y1 = pos[1];
16334             y2 = y1 + el.offsetHeight;
16335
16336             t = y1 - oDD.padding[0];
16337             r = x2 + oDD.padding[1];
16338             b = y2 + oDD.padding[2];
16339             l = x1 - oDD.padding[3];
16340
16341             return new Roo.lib.Region( t, r, b, l );
16342         },
16343
16344         /**
16345          * Checks the cursor location to see if it over the target
16346          * @method isOverTarget
16347          * @param {Roo.lib.Point} pt The point to evaluate
16348          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16349          * @return {boolean} true if the mouse is over the target
16350          * @private
16351          * @static
16352          */
16353         isOverTarget: function(pt, oTarget, intersect) {
16354             // use cache if available
16355             var loc = this.locationCache[oTarget.id];
16356             if (!loc || !this.useCache) {
16357                 loc = this.getLocation(oTarget);
16358                 this.locationCache[oTarget.id] = loc;
16359
16360             }
16361
16362             if (!loc) {
16363                 return false;
16364             }
16365
16366             oTarget.cursorIsOver = loc.contains( pt );
16367
16368             // DragDrop is using this as a sanity check for the initial mousedown
16369             // in this case we are done.  In POINT mode, if the drag obj has no
16370             // contraints, we are also done. Otherwise we need to evaluate the
16371             // location of the target as related to the actual location of the
16372             // dragged element.
16373             var dc = this.dragCurrent;
16374             if (!dc || !dc.getTargetCoord ||
16375                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16376                 return oTarget.cursorIsOver;
16377             }
16378
16379             oTarget.overlap = null;
16380
16381             // Get the current location of the drag element, this is the
16382             // location of the mouse event less the delta that represents
16383             // where the original mousedown happened on the element.  We
16384             // need to consider constraints and ticks as well.
16385             var pos = dc.getTargetCoord(pt.x, pt.y);
16386
16387             var el = dc.getDragEl();
16388             var curRegion = new Roo.lib.Region( pos.y,
16389                                                    pos.x + el.offsetWidth,
16390                                                    pos.y + el.offsetHeight,
16391                                                    pos.x );
16392
16393             var overlap = curRegion.intersect(loc);
16394
16395             if (overlap) {
16396                 oTarget.overlap = overlap;
16397                 return (intersect) ? true : oTarget.cursorIsOver;
16398             } else {
16399                 return false;
16400             }
16401         },
16402
16403         /**
16404          * unload event handler
16405          * @method _onUnload
16406          * @private
16407          * @static
16408          */
16409         _onUnload: function(e, me) {
16410             Roo.dd.DragDropMgr.unregAll();
16411         },
16412
16413         /**
16414          * Cleans up the drag and drop events and objects.
16415          * @method unregAll
16416          * @private
16417          * @static
16418          */
16419         unregAll: function() {
16420
16421             if (this.dragCurrent) {
16422                 this.stopDrag();
16423                 this.dragCurrent = null;
16424             }
16425
16426             this._execOnAll("unreg", []);
16427
16428             for (i in this.elementCache) {
16429                 delete this.elementCache[i];
16430             }
16431
16432             this.elementCache = {};
16433             this.ids = {};
16434         },
16435
16436         /**
16437          * A cache of DOM elements
16438          * @property elementCache
16439          * @private
16440          * @static
16441          */
16442         elementCache: {},
16443
16444         /**
16445          * Get the wrapper for the DOM element specified
16446          * @method getElWrapper
16447          * @param {String} id the id of the element to get
16448          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16449          * @private
16450          * @deprecated This wrapper isn't that useful
16451          * @static
16452          */
16453         getElWrapper: function(id) {
16454             var oWrapper = this.elementCache[id];
16455             if (!oWrapper || !oWrapper.el) {
16456                 oWrapper = this.elementCache[id] =
16457                     new this.ElementWrapper(Roo.getDom(id));
16458             }
16459             return oWrapper;
16460         },
16461
16462         /**
16463          * Returns the actual DOM element
16464          * @method getElement
16465          * @param {String} id the id of the elment to get
16466          * @return {Object} The element
16467          * @deprecated use Roo.getDom instead
16468          * @static
16469          */
16470         getElement: function(id) {
16471             return Roo.getDom(id);
16472         },
16473
16474         /**
16475          * Returns the style property for the DOM element (i.e.,
16476          * document.getElById(id).style)
16477          * @method getCss
16478          * @param {String} id the id of the elment to get
16479          * @return {Object} The style property of the element
16480          * @deprecated use Roo.getDom instead
16481          * @static
16482          */
16483         getCss: function(id) {
16484             var el = Roo.getDom(id);
16485             return (el) ? el.style : null;
16486         },
16487
16488         /**
16489          * Inner class for cached elements
16490          * @class DragDropMgr.ElementWrapper
16491          * @for DragDropMgr
16492          * @private
16493          * @deprecated
16494          */
16495         ElementWrapper: function(el) {
16496                 /**
16497                  * The element
16498                  * @property el
16499                  */
16500                 this.el = el || null;
16501                 /**
16502                  * The element id
16503                  * @property id
16504                  */
16505                 this.id = this.el && el.id;
16506                 /**
16507                  * A reference to the style property
16508                  * @property css
16509                  */
16510                 this.css = this.el && el.style;
16511             },
16512
16513         /**
16514          * Returns the X position of an html element
16515          * @method getPosX
16516          * @param el the element for which to get the position
16517          * @return {int} the X coordinate
16518          * @for DragDropMgr
16519          * @deprecated use Roo.lib.Dom.getX instead
16520          * @static
16521          */
16522         getPosX: function(el) {
16523             return Roo.lib.Dom.getX(el);
16524         },
16525
16526         /**
16527          * Returns the Y position of an html element
16528          * @method getPosY
16529          * @param el the element for which to get the position
16530          * @return {int} the Y coordinate
16531          * @deprecated use Roo.lib.Dom.getY instead
16532          * @static
16533          */
16534         getPosY: function(el) {
16535             return Roo.lib.Dom.getY(el);
16536         },
16537
16538         /**
16539          * Swap two nodes.  In IE, we use the native method, for others we
16540          * emulate the IE behavior
16541          * @method swapNode
16542          * @param n1 the first node to swap
16543          * @param n2 the other node to swap
16544          * @static
16545          */
16546         swapNode: function(n1, n2) {
16547             if (n1.swapNode) {
16548                 n1.swapNode(n2);
16549             } else {
16550                 var p = n2.parentNode;
16551                 var s = n2.nextSibling;
16552
16553                 if (s == n1) {
16554                     p.insertBefore(n1, n2);
16555                 } else if (n2 == n1.nextSibling) {
16556                     p.insertBefore(n2, n1);
16557                 } else {
16558                     n1.parentNode.replaceChild(n2, n1);
16559                     p.insertBefore(n1, s);
16560                 }
16561             }
16562         },
16563
16564         /**
16565          * Returns the current scroll position
16566          * @method getScroll
16567          * @private
16568          * @static
16569          */
16570         getScroll: function () {
16571             var t, l, dde=document.documentElement, db=document.body;
16572             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16573                 t = dde.scrollTop;
16574                 l = dde.scrollLeft;
16575             } else if (db) {
16576                 t = db.scrollTop;
16577                 l = db.scrollLeft;
16578             } else {
16579
16580             }
16581             return { top: t, left: l };
16582         },
16583
16584         /**
16585          * Returns the specified element style property
16586          * @method getStyle
16587          * @param {HTMLElement} el          the element
16588          * @param {string}      styleProp   the style property
16589          * @return {string} The value of the style property
16590          * @deprecated use Roo.lib.Dom.getStyle
16591          * @static
16592          */
16593         getStyle: function(el, styleProp) {
16594             return Roo.fly(el).getStyle(styleProp);
16595         },
16596
16597         /**
16598          * Gets the scrollTop
16599          * @method getScrollTop
16600          * @return {int} the document's scrollTop
16601          * @static
16602          */
16603         getScrollTop: function () { return this.getScroll().top; },
16604
16605         /**
16606          * Gets the scrollLeft
16607          * @method getScrollLeft
16608          * @return {int} the document's scrollTop
16609          * @static
16610          */
16611         getScrollLeft: function () { return this.getScroll().left; },
16612
16613         /**
16614          * Sets the x/y position of an element to the location of the
16615          * target element.
16616          * @method moveToEl
16617          * @param {HTMLElement} moveEl      The element to move
16618          * @param {HTMLElement} targetEl    The position reference element
16619          * @static
16620          */
16621         moveToEl: function (moveEl, targetEl) {
16622             var aCoord = Roo.lib.Dom.getXY(targetEl);
16623             Roo.lib.Dom.setXY(moveEl, aCoord);
16624         },
16625
16626         /**
16627          * Numeric array sort function
16628          * @method numericSort
16629          * @static
16630          */
16631         numericSort: function(a, b) { return (a - b); },
16632
16633         /**
16634          * Internal counter
16635          * @property _timeoutCount
16636          * @private
16637          * @static
16638          */
16639         _timeoutCount: 0,
16640
16641         /**
16642          * Trying to make the load order less important.  Without this we get
16643          * an error if this file is loaded before the Event Utility.
16644          * @method _addListeners
16645          * @private
16646          * @static
16647          */
16648         _addListeners: function() {
16649             var DDM = Roo.dd.DDM;
16650             if ( Roo.lib.Event && document ) {
16651                 DDM._onLoad();
16652             } else {
16653                 if (DDM._timeoutCount > 2000) {
16654                 } else {
16655                     setTimeout(DDM._addListeners, 10);
16656                     if (document && document.body) {
16657                         DDM._timeoutCount += 1;
16658                     }
16659                 }
16660             }
16661         },
16662
16663         /**
16664          * Recursively searches the immediate parent and all child nodes for
16665          * the handle element in order to determine wheter or not it was
16666          * clicked.
16667          * @method handleWasClicked
16668          * @param node the html element to inspect
16669          * @static
16670          */
16671         handleWasClicked: function(node, id) {
16672             if (this.isHandle(id, node.id)) {
16673                 return true;
16674             } else {
16675                 // check to see if this is a text node child of the one we want
16676                 var p = node.parentNode;
16677
16678                 while (p) {
16679                     if (this.isHandle(id, p.id)) {
16680                         return true;
16681                     } else {
16682                         p = p.parentNode;
16683                     }
16684                 }
16685             }
16686
16687             return false;
16688         }
16689
16690     };
16691
16692 }();
16693
16694 // shorter alias, save a few bytes
16695 Roo.dd.DDM = Roo.dd.DragDropMgr;
16696 Roo.dd.DDM._addListeners();
16697
16698 }/*
16699  * Based on:
16700  * Ext JS Library 1.1.1
16701  * Copyright(c) 2006-2007, Ext JS, LLC.
16702  *
16703  * Originally Released Under LGPL - original licence link has changed is not relivant.
16704  *
16705  * Fork - LGPL
16706  * <script type="text/javascript">
16707  */
16708
16709 /**
16710  * @class Roo.dd.DD
16711  * A DragDrop implementation where the linked element follows the
16712  * mouse cursor during a drag.
16713  * @extends Roo.dd.DragDrop
16714  * @constructor
16715  * @param {String} id the id of the linked element
16716  * @param {String} sGroup the group of related DragDrop items
16717  * @param {object} config an object containing configurable attributes
16718  *                Valid properties for DD:
16719  *                    scroll
16720  */
16721 Roo.dd.DD = function(id, sGroup, config) {
16722     if (id) {
16723         this.init(id, sGroup, config);
16724     }
16725 };
16726
16727 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16728
16729     /**
16730      * When set to true, the utility automatically tries to scroll the browser
16731      * window wehn a drag and drop element is dragged near the viewport boundary.
16732      * Defaults to true.
16733      * @property scroll
16734      * @type boolean
16735      */
16736     scroll: true,
16737
16738     /**
16739      * Sets the pointer offset to the distance between the linked element's top
16740      * left corner and the location the element was clicked
16741      * @method autoOffset
16742      * @param {int} iPageX the X coordinate of the click
16743      * @param {int} iPageY the Y coordinate of the click
16744      */
16745     autoOffset: function(iPageX, iPageY) {
16746         var x = iPageX - this.startPageX;
16747         var y = iPageY - this.startPageY;
16748         this.setDelta(x, y);
16749     },
16750
16751     /**
16752      * Sets the pointer offset.  You can call this directly to force the
16753      * offset to be in a particular location (e.g., pass in 0,0 to set it
16754      * to the center of the object)
16755      * @method setDelta
16756      * @param {int} iDeltaX the distance from the left
16757      * @param {int} iDeltaY the distance from the top
16758      */
16759     setDelta: function(iDeltaX, iDeltaY) {
16760         this.deltaX = iDeltaX;
16761         this.deltaY = iDeltaY;
16762     },
16763
16764     /**
16765      * Sets the drag element to the location of the mousedown or click event,
16766      * maintaining the cursor location relative to the location on the element
16767      * that was clicked.  Override this if you want to place the element in a
16768      * location other than where the cursor is.
16769      * @method setDragElPos
16770      * @param {int} iPageX the X coordinate of the mousedown or drag event
16771      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16772      */
16773     setDragElPos: function(iPageX, iPageY) {
16774         // the first time we do this, we are going to check to make sure
16775         // the element has css positioning
16776
16777         var el = this.getDragEl();
16778         this.alignElWithMouse(el, iPageX, iPageY);
16779     },
16780
16781     /**
16782      * Sets the element to the location of the mousedown or click event,
16783      * maintaining the cursor location relative to the location on the element
16784      * that was clicked.  Override this if you want to place the element in a
16785      * location other than where the cursor is.
16786      * @method alignElWithMouse
16787      * @param {HTMLElement} el the element to move
16788      * @param {int} iPageX the X coordinate of the mousedown or drag event
16789      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16790      */
16791     alignElWithMouse: function(el, iPageX, iPageY) {
16792         var oCoord = this.getTargetCoord(iPageX, iPageY);
16793         var fly = el.dom ? el : Roo.fly(el);
16794         if (!this.deltaSetXY) {
16795             var aCoord = [oCoord.x, oCoord.y];
16796             fly.setXY(aCoord);
16797             var newLeft = fly.getLeft(true);
16798             var newTop  = fly.getTop(true);
16799             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16800         } else {
16801             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16802         }
16803
16804         this.cachePosition(oCoord.x, oCoord.y);
16805         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16806         return oCoord;
16807     },
16808
16809     /**
16810      * Saves the most recent position so that we can reset the constraints and
16811      * tick marks on-demand.  We need to know this so that we can calculate the
16812      * number of pixels the element is offset from its original position.
16813      * @method cachePosition
16814      * @param iPageX the current x position (optional, this just makes it so we
16815      * don't have to look it up again)
16816      * @param iPageY the current y position (optional, this just makes it so we
16817      * don't have to look it up again)
16818      */
16819     cachePosition: function(iPageX, iPageY) {
16820         if (iPageX) {
16821             this.lastPageX = iPageX;
16822             this.lastPageY = iPageY;
16823         } else {
16824             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16825             this.lastPageX = aCoord[0];
16826             this.lastPageY = aCoord[1];
16827         }
16828     },
16829
16830     /**
16831      * Auto-scroll the window if the dragged object has been moved beyond the
16832      * visible window boundary.
16833      * @method autoScroll
16834      * @param {int} x the drag element's x position
16835      * @param {int} y the drag element's y position
16836      * @param {int} h the height of the drag element
16837      * @param {int} w the width of the drag element
16838      * @private
16839      */
16840     autoScroll: function(x, y, h, w) {
16841
16842         if (this.scroll) {
16843             // The client height
16844             var clientH = Roo.lib.Dom.getViewWidth();
16845
16846             // The client width
16847             var clientW = Roo.lib.Dom.getViewHeight();
16848
16849             // The amt scrolled down
16850             var st = this.DDM.getScrollTop();
16851
16852             // The amt scrolled right
16853             var sl = this.DDM.getScrollLeft();
16854
16855             // Location of the bottom of the element
16856             var bot = h + y;
16857
16858             // Location of the right of the element
16859             var right = w + x;
16860
16861             // The distance from the cursor to the bottom of the visible area,
16862             // adjusted so that we don't scroll if the cursor is beyond the
16863             // element drag constraints
16864             var toBot = (clientH + st - y - this.deltaY);
16865
16866             // The distance from the cursor to the right of the visible area
16867             var toRight = (clientW + sl - x - this.deltaX);
16868
16869
16870             // How close to the edge the cursor must be before we scroll
16871             // var thresh = (document.all) ? 100 : 40;
16872             var thresh = 40;
16873
16874             // How many pixels to scroll per autoscroll op.  This helps to reduce
16875             // clunky scrolling. IE is more sensitive about this ... it needs this
16876             // value to be higher.
16877             var scrAmt = (document.all) ? 80 : 30;
16878
16879             // Scroll down if we are near the bottom of the visible page and the
16880             // obj extends below the crease
16881             if ( bot > clientH && toBot < thresh ) {
16882                 window.scrollTo(sl, st + scrAmt);
16883             }
16884
16885             // Scroll up if the window is scrolled down and the top of the object
16886             // goes above the top border
16887             if ( y < st && st > 0 && y - st < thresh ) {
16888                 window.scrollTo(sl, st - scrAmt);
16889             }
16890
16891             // Scroll right if the obj is beyond the right border and the cursor is
16892             // near the border.
16893             if ( right > clientW && toRight < thresh ) {
16894                 window.scrollTo(sl + scrAmt, st);
16895             }
16896
16897             // Scroll left if the window has been scrolled to the right and the obj
16898             // extends past the left border
16899             if ( x < sl && sl > 0 && x - sl < thresh ) {
16900                 window.scrollTo(sl - scrAmt, st);
16901             }
16902         }
16903     },
16904
16905     /**
16906      * Finds the location the element should be placed if we want to move
16907      * it to where the mouse location less the click offset would place us.
16908      * @method getTargetCoord
16909      * @param {int} iPageX the X coordinate of the click
16910      * @param {int} iPageY the Y coordinate of the click
16911      * @return an object that contains the coordinates (Object.x and Object.y)
16912      * @private
16913      */
16914     getTargetCoord: function(iPageX, iPageY) {
16915
16916
16917         var x = iPageX - this.deltaX;
16918         var y = iPageY - this.deltaY;
16919
16920         if (this.constrainX) {
16921             if (x < this.minX) { x = this.minX; }
16922             if (x > this.maxX) { x = this.maxX; }
16923         }
16924
16925         if (this.constrainY) {
16926             if (y < this.minY) { y = this.minY; }
16927             if (y > this.maxY) { y = this.maxY; }
16928         }
16929
16930         x = this.getTick(x, this.xTicks);
16931         y = this.getTick(y, this.yTicks);
16932
16933
16934         return {x:x, y:y};
16935     },
16936
16937     /*
16938      * Sets up config options specific to this class. Overrides
16939      * Roo.dd.DragDrop, but all versions of this method through the
16940      * inheritance chain are called
16941      */
16942     applyConfig: function() {
16943         Roo.dd.DD.superclass.applyConfig.call(this);
16944         this.scroll = (this.config.scroll !== false);
16945     },
16946
16947     /*
16948      * Event that fires prior to the onMouseDown event.  Overrides
16949      * Roo.dd.DragDrop.
16950      */
16951     b4MouseDown: function(e) {
16952         // this.resetConstraints();
16953         this.autoOffset(e.getPageX(),
16954                             e.getPageY());
16955     },
16956
16957     /*
16958      * Event that fires prior to the onDrag event.  Overrides
16959      * Roo.dd.DragDrop.
16960      */
16961     b4Drag: function(e) {
16962         this.setDragElPos(e.getPageX(),
16963                             e.getPageY());
16964     },
16965
16966     toString: function() {
16967         return ("DD " + this.id);
16968     }
16969
16970     //////////////////////////////////////////////////////////////////////////
16971     // Debugging ygDragDrop events that can be overridden
16972     //////////////////////////////////////////////////////////////////////////
16973     /*
16974     startDrag: function(x, y) {
16975     },
16976
16977     onDrag: function(e) {
16978     },
16979
16980     onDragEnter: function(e, id) {
16981     },
16982
16983     onDragOver: function(e, id) {
16984     },
16985
16986     onDragOut: function(e, id) {
16987     },
16988
16989     onDragDrop: function(e, id) {
16990     },
16991
16992     endDrag: function(e) {
16993     }
16994
16995     */
16996
16997 });/*
16998  * Based on:
16999  * Ext JS Library 1.1.1
17000  * Copyright(c) 2006-2007, Ext JS, LLC.
17001  *
17002  * Originally Released Under LGPL - original licence link has changed is not relivant.
17003  *
17004  * Fork - LGPL
17005  * <script type="text/javascript">
17006  */
17007
17008 /**
17009  * @class Roo.dd.DDProxy
17010  * A DragDrop implementation that inserts an empty, bordered div into
17011  * the document that follows the cursor during drag operations.  At the time of
17012  * the click, the frame div is resized to the dimensions of the linked html
17013  * element, and moved to the exact location of the linked element.
17014  *
17015  * References to the "frame" element refer to the single proxy element that
17016  * was created to be dragged in place of all DDProxy elements on the
17017  * page.
17018  *
17019  * @extends Roo.dd.DD
17020  * @constructor
17021  * @param {String} id the id of the linked html element
17022  * @param {String} sGroup the group of related DragDrop objects
17023  * @param {object} config an object containing configurable attributes
17024  *                Valid properties for DDProxy in addition to those in DragDrop:
17025  *                   resizeFrame, centerFrame, dragElId
17026  */
17027 Roo.dd.DDProxy = function(id, sGroup, config) {
17028     if (id) {
17029         this.init(id, sGroup, config);
17030         this.initFrame();
17031     }
17032 };
17033
17034 /**
17035  * The default drag frame div id
17036  * @property Roo.dd.DDProxy.dragElId
17037  * @type String
17038  * @static
17039  */
17040 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17041
17042 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17043
17044     /**
17045      * By default we resize the drag frame to be the same size as the element
17046      * we want to drag (this is to get the frame effect).  We can turn it off
17047      * if we want a different behavior.
17048      * @property resizeFrame
17049      * @type boolean
17050      */
17051     resizeFrame: true,
17052
17053     /**
17054      * By default the frame is positioned exactly where the drag element is, so
17055      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17056      * you do not have constraints on the obj is to have the drag frame centered
17057      * around the cursor.  Set centerFrame to true for this effect.
17058      * @property centerFrame
17059      * @type boolean
17060      */
17061     centerFrame: false,
17062
17063     /**
17064      * Creates the proxy element if it does not yet exist
17065      * @method createFrame
17066      */
17067     createFrame: function() {
17068         var self = this;
17069         var body = document.body;
17070
17071         if (!body || !body.firstChild) {
17072             setTimeout( function() { self.createFrame(); }, 50 );
17073             return;
17074         }
17075
17076         var div = this.getDragEl();
17077
17078         if (!div) {
17079             div    = document.createElement("div");
17080             div.id = this.dragElId;
17081             var s  = div.style;
17082
17083             s.position   = "absolute";
17084             s.visibility = "hidden";
17085             s.cursor     = "move";
17086             s.border     = "2px solid #aaa";
17087             s.zIndex     = 999;
17088
17089             // appendChild can blow up IE if invoked prior to the window load event
17090             // while rendering a table.  It is possible there are other scenarios
17091             // that would cause this to happen as well.
17092             body.insertBefore(div, body.firstChild);
17093         }
17094     },
17095
17096     /**
17097      * Initialization for the drag frame element.  Must be called in the
17098      * constructor of all subclasses
17099      * @method initFrame
17100      */
17101     initFrame: function() {
17102         this.createFrame();
17103     },
17104
17105     applyConfig: function() {
17106         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17107
17108         this.resizeFrame = (this.config.resizeFrame !== false);
17109         this.centerFrame = (this.config.centerFrame);
17110         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17111     },
17112
17113     /**
17114      * Resizes the drag frame to the dimensions of the clicked object, positions
17115      * it over the object, and finally displays it
17116      * @method showFrame
17117      * @param {int} iPageX X click position
17118      * @param {int} iPageY Y click position
17119      * @private
17120      */
17121     showFrame: function(iPageX, iPageY) {
17122         var el = this.getEl();
17123         var dragEl = this.getDragEl();
17124         var s = dragEl.style;
17125
17126         this._resizeProxy();
17127
17128         if (this.centerFrame) {
17129             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17130                            Math.round(parseInt(s.height, 10)/2) );
17131         }
17132
17133         this.setDragElPos(iPageX, iPageY);
17134
17135         Roo.fly(dragEl).show();
17136     },
17137
17138     /**
17139      * The proxy is automatically resized to the dimensions of the linked
17140      * element when a drag is initiated, unless resizeFrame is set to false
17141      * @method _resizeProxy
17142      * @private
17143      */
17144     _resizeProxy: function() {
17145         if (this.resizeFrame) {
17146             var el = this.getEl();
17147             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17148         }
17149     },
17150
17151     // overrides Roo.dd.DragDrop
17152     b4MouseDown: function(e) {
17153         var x = e.getPageX();
17154         var y = e.getPageY();
17155         this.autoOffset(x, y);
17156         this.setDragElPos(x, y);
17157     },
17158
17159     // overrides Roo.dd.DragDrop
17160     b4StartDrag: function(x, y) {
17161         // show the drag frame
17162         this.showFrame(x, y);
17163     },
17164
17165     // overrides Roo.dd.DragDrop
17166     b4EndDrag: function(e) {
17167         Roo.fly(this.getDragEl()).hide();
17168     },
17169
17170     // overrides Roo.dd.DragDrop
17171     // By default we try to move the element to the last location of the frame.
17172     // This is so that the default behavior mirrors that of Roo.dd.DD.
17173     endDrag: function(e) {
17174
17175         var lel = this.getEl();
17176         var del = this.getDragEl();
17177
17178         // Show the drag frame briefly so we can get its position
17179         del.style.visibility = "";
17180
17181         this.beforeMove();
17182         // Hide the linked element before the move to get around a Safari
17183         // rendering bug.
17184         lel.style.visibility = "hidden";
17185         Roo.dd.DDM.moveToEl(lel, del);
17186         del.style.visibility = "hidden";
17187         lel.style.visibility = "";
17188
17189         this.afterDrag();
17190     },
17191
17192     beforeMove : function(){
17193
17194     },
17195
17196     afterDrag : function(){
17197
17198     },
17199
17200     toString: function() {
17201         return ("DDProxy " + this.id);
17202     }
17203
17204 });
17205 /*
17206  * Based on:
17207  * Ext JS Library 1.1.1
17208  * Copyright(c) 2006-2007, Ext JS, LLC.
17209  *
17210  * Originally Released Under LGPL - original licence link has changed is not relivant.
17211  *
17212  * Fork - LGPL
17213  * <script type="text/javascript">
17214  */
17215
17216  /**
17217  * @class Roo.dd.DDTarget
17218  * A DragDrop implementation that does not move, but can be a drop
17219  * target.  You would get the same result by simply omitting implementation
17220  * for the event callbacks, but this way we reduce the processing cost of the
17221  * event listener and the callbacks.
17222  * @extends Roo.dd.DragDrop
17223  * @constructor
17224  * @param {String} id the id of the element that is a drop target
17225  * @param {String} sGroup the group of related DragDrop objects
17226  * @param {object} config an object containing configurable attributes
17227  *                 Valid properties for DDTarget in addition to those in
17228  *                 DragDrop:
17229  *                    none
17230  */
17231 Roo.dd.DDTarget = function(id, sGroup, config) {
17232     if (id) {
17233         this.initTarget(id, sGroup, config);
17234     }
17235 };
17236
17237 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17238 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17239     toString: function() {
17240         return ("DDTarget " + this.id);
17241     }
17242 });
17243 /*
17244  * Based on:
17245  * Ext JS Library 1.1.1
17246  * Copyright(c) 2006-2007, Ext JS, LLC.
17247  *
17248  * Originally Released Under LGPL - original licence link has changed is not relivant.
17249  *
17250  * Fork - LGPL
17251  * <script type="text/javascript">
17252  */
17253  
17254
17255 /**
17256  * @class Roo.dd.ScrollManager
17257  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17258  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17259  * @singleton
17260  */
17261 Roo.dd.ScrollManager = function(){
17262     var ddm = Roo.dd.DragDropMgr;
17263     var els = {};
17264     var dragEl = null;
17265     var proc = {};
17266     
17267     var onStop = function(e){
17268         dragEl = null;
17269         clearProc();
17270     };
17271     
17272     var triggerRefresh = function(){
17273         if(ddm.dragCurrent){
17274              ddm.refreshCache(ddm.dragCurrent.groups);
17275         }
17276     };
17277     
17278     var doScroll = function(){
17279         if(ddm.dragCurrent){
17280             var dds = Roo.dd.ScrollManager;
17281             if(!dds.animate){
17282                 if(proc.el.scroll(proc.dir, dds.increment)){
17283                     triggerRefresh();
17284                 }
17285             }else{
17286                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17287             }
17288         }
17289     };
17290     
17291     var clearProc = function(){
17292         if(proc.id){
17293             clearInterval(proc.id);
17294         }
17295         proc.id = 0;
17296         proc.el = null;
17297         proc.dir = "";
17298     };
17299     
17300     var startProc = function(el, dir){
17301         clearProc();
17302         proc.el = el;
17303         proc.dir = dir;
17304         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17305     };
17306     
17307     var onFire = function(e, isDrop){
17308         if(isDrop || !ddm.dragCurrent){ return; }
17309         var dds = Roo.dd.ScrollManager;
17310         if(!dragEl || dragEl != ddm.dragCurrent){
17311             dragEl = ddm.dragCurrent;
17312             // refresh regions on drag start
17313             dds.refreshCache();
17314         }
17315         
17316         var xy = Roo.lib.Event.getXY(e);
17317         var pt = new Roo.lib.Point(xy[0], xy[1]);
17318         for(var id in els){
17319             var el = els[id], r = el._region;
17320             if(r && r.contains(pt) && el.isScrollable()){
17321                 if(r.bottom - pt.y <= dds.thresh){
17322                     if(proc.el != el){
17323                         startProc(el, "down");
17324                     }
17325                     return;
17326                 }else if(r.right - pt.x <= dds.thresh){
17327                     if(proc.el != el){
17328                         startProc(el, "left");
17329                     }
17330                     return;
17331                 }else if(pt.y - r.top <= dds.thresh){
17332                     if(proc.el != el){
17333                         startProc(el, "up");
17334                     }
17335                     return;
17336                 }else if(pt.x - r.left <= dds.thresh){
17337                     if(proc.el != el){
17338                         startProc(el, "right");
17339                     }
17340                     return;
17341                 }
17342             }
17343         }
17344         clearProc();
17345     };
17346     
17347     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17348     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17349     
17350     return {
17351         /**
17352          * Registers new overflow element(s) to auto scroll
17353          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17354          */
17355         register : function(el){
17356             if(el instanceof Array){
17357                 for(var i = 0, len = el.length; i < len; i++) {
17358                         this.register(el[i]);
17359                 }
17360             }else{
17361                 el = Roo.get(el);
17362                 els[el.id] = el;
17363             }
17364         },
17365         
17366         /**
17367          * Unregisters overflow element(s) so they are no longer scrolled
17368          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17369          */
17370         unregister : function(el){
17371             if(el instanceof Array){
17372                 for(var i = 0, len = el.length; i < len; i++) {
17373                         this.unregister(el[i]);
17374                 }
17375             }else{
17376                 el = Roo.get(el);
17377                 delete els[el.id];
17378             }
17379         },
17380         
17381         /**
17382          * The number of pixels from the edge of a container the pointer needs to be to 
17383          * trigger scrolling (defaults to 25)
17384          * @type Number
17385          */
17386         thresh : 25,
17387         
17388         /**
17389          * The number of pixels to scroll in each scroll increment (defaults to 50)
17390          * @type Number
17391          */
17392         increment : 100,
17393         
17394         /**
17395          * The frequency of scrolls in milliseconds (defaults to 500)
17396          * @type Number
17397          */
17398         frequency : 500,
17399         
17400         /**
17401          * True to animate the scroll (defaults to true)
17402          * @type Boolean
17403          */
17404         animate: true,
17405         
17406         /**
17407          * The animation duration in seconds - 
17408          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17409          * @type Number
17410          */
17411         animDuration: .4,
17412         
17413         /**
17414          * Manually trigger a cache refresh.
17415          */
17416         refreshCache : function(){
17417             for(var id in els){
17418                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17419                     els[id]._region = els[id].getRegion();
17420                 }
17421             }
17422         }
17423     };
17424 }();/*
17425  * Based on:
17426  * Ext JS Library 1.1.1
17427  * Copyright(c) 2006-2007, Ext JS, LLC.
17428  *
17429  * Originally Released Under LGPL - original licence link has changed is not relivant.
17430  *
17431  * Fork - LGPL
17432  * <script type="text/javascript">
17433  */
17434  
17435
17436 /**
17437  * @class Roo.dd.Registry
17438  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17439  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17440  * @singleton
17441  */
17442 Roo.dd.Registry = function(){
17443     var elements = {}; 
17444     var handles = {}; 
17445     var autoIdSeed = 0;
17446
17447     var getId = function(el, autogen){
17448         if(typeof el == "string"){
17449             return el;
17450         }
17451         var id = el.id;
17452         if(!id && autogen !== false){
17453             id = "roodd-" + (++autoIdSeed);
17454             el.id = id;
17455         }
17456         return id;
17457     };
17458     
17459     return {
17460     /**
17461      * Register a drag drop element
17462      * @param {String|HTMLElement} element The id or DOM node to register
17463      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17464      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17465      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17466      * populated in the data object (if applicable):
17467      * <pre>
17468 Value      Description<br />
17469 ---------  ------------------------------------------<br />
17470 handles    Array of DOM nodes that trigger dragging<br />
17471            for the element being registered<br />
17472 isHandle   True if the element passed in triggers<br />
17473            dragging itself, else false
17474 </pre>
17475      */
17476         register : function(el, data){
17477             data = data || {};
17478             if(typeof el == "string"){
17479                 el = document.getElementById(el);
17480             }
17481             data.ddel = el;
17482             elements[getId(el)] = data;
17483             if(data.isHandle !== false){
17484                 handles[data.ddel.id] = data;
17485             }
17486             if(data.handles){
17487                 var hs = data.handles;
17488                 for(var i = 0, len = hs.length; i < len; i++){
17489                         handles[getId(hs[i])] = data;
17490                 }
17491             }
17492         },
17493
17494     /**
17495      * Unregister a drag drop element
17496      * @param {String|HTMLElement}  element The id or DOM node to unregister
17497      */
17498         unregister : function(el){
17499             var id = getId(el, false);
17500             var data = elements[id];
17501             if(data){
17502                 delete elements[id];
17503                 if(data.handles){
17504                     var hs = data.handles;
17505                     for(var i = 0, len = hs.length; i < len; i++){
17506                         delete handles[getId(hs[i], false)];
17507                     }
17508                 }
17509             }
17510         },
17511
17512     /**
17513      * Returns the handle registered for a DOM Node by id
17514      * @param {String|HTMLElement} id The DOM node or id to look up
17515      * @return {Object} handle The custom handle data
17516      */
17517         getHandle : function(id){
17518             if(typeof id != "string"){ // must be element?
17519                 id = id.id;
17520             }
17521             return handles[id];
17522         },
17523
17524     /**
17525      * Returns the handle that is registered for the DOM node that is the target of the event
17526      * @param {Event} e The event
17527      * @return {Object} handle The custom handle data
17528      */
17529         getHandleFromEvent : function(e){
17530             var t = Roo.lib.Event.getTarget(e);
17531             return t ? handles[t.id] : null;
17532         },
17533
17534     /**
17535      * Returns a custom data object that is registered for a DOM node by id
17536      * @param {String|HTMLElement} id The DOM node or id to look up
17537      * @return {Object} data The custom data
17538      */
17539         getTarget : function(id){
17540             if(typeof id != "string"){ // must be element?
17541                 id = id.id;
17542             }
17543             return elements[id];
17544         },
17545
17546     /**
17547      * Returns a custom data object that is registered for the DOM node that is the target of the event
17548      * @param {Event} e The event
17549      * @return {Object} data The custom data
17550      */
17551         getTargetFromEvent : function(e){
17552             var t = Roo.lib.Event.getTarget(e);
17553             return t ? elements[t.id] || handles[t.id] : null;
17554         }
17555     };
17556 }();/*
17557  * Based on:
17558  * Ext JS Library 1.1.1
17559  * Copyright(c) 2006-2007, Ext JS, LLC.
17560  *
17561  * Originally Released Under LGPL - original licence link has changed is not relivant.
17562  *
17563  * Fork - LGPL
17564  * <script type="text/javascript">
17565  */
17566  
17567
17568 /**
17569  * @class Roo.dd.StatusProxy
17570  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17571  * default drag proxy used by all Roo.dd components.
17572  * @constructor
17573  * @param {Object} config
17574  */
17575 Roo.dd.StatusProxy = function(config){
17576     Roo.apply(this, config);
17577     this.id = this.id || Roo.id();
17578     this.el = new Roo.Layer({
17579         dh: {
17580             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17581                 {tag: "div", cls: "x-dd-drop-icon"},
17582                 {tag: "div", cls: "x-dd-drag-ghost"}
17583             ]
17584         }, 
17585         shadow: !config || config.shadow !== false
17586     });
17587     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17588     this.dropStatus = this.dropNotAllowed;
17589 };
17590
17591 Roo.dd.StatusProxy.prototype = {
17592     /**
17593      * @cfg {String} dropAllowed
17594      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17595      */
17596     dropAllowed : "x-dd-drop-ok",
17597     /**
17598      * @cfg {String} dropNotAllowed
17599      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17600      */
17601     dropNotAllowed : "x-dd-drop-nodrop",
17602
17603     /**
17604      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17605      * over the current target element.
17606      * @param {String} cssClass The css class for the new drop status indicator image
17607      */
17608     setStatus : function(cssClass){
17609         cssClass = cssClass || this.dropNotAllowed;
17610         if(this.dropStatus != cssClass){
17611             this.el.replaceClass(this.dropStatus, cssClass);
17612             this.dropStatus = cssClass;
17613         }
17614     },
17615
17616     /**
17617      * Resets the status indicator to the default dropNotAllowed value
17618      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17619      */
17620     reset : function(clearGhost){
17621         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17622         this.dropStatus = this.dropNotAllowed;
17623         if(clearGhost){
17624             this.ghost.update("");
17625         }
17626     },
17627
17628     /**
17629      * Updates the contents of the ghost element
17630      * @param {String} html The html that will replace the current innerHTML of the ghost element
17631      */
17632     update : function(html){
17633         if(typeof html == "string"){
17634             this.ghost.update(html);
17635         }else{
17636             this.ghost.update("");
17637             html.style.margin = "0";
17638             this.ghost.dom.appendChild(html);
17639         }
17640         // ensure float = none set?? cant remember why though.
17641         var el = this.ghost.dom.firstChild;
17642                 if(el){
17643                         Roo.fly(el).setStyle('float', 'none');
17644                 }
17645     },
17646     
17647     /**
17648      * Returns the underlying proxy {@link Roo.Layer}
17649      * @return {Roo.Layer} el
17650     */
17651     getEl : function(){
17652         return this.el;
17653     },
17654
17655     /**
17656      * Returns the ghost element
17657      * @return {Roo.Element} el
17658      */
17659     getGhost : function(){
17660         return this.ghost;
17661     },
17662
17663     /**
17664      * Hides the proxy
17665      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17666      */
17667     hide : function(clear){
17668         this.el.hide();
17669         if(clear){
17670             this.reset(true);
17671         }
17672     },
17673
17674     /**
17675      * Stops the repair animation if it's currently running
17676      */
17677     stop : function(){
17678         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17679             this.anim.stop();
17680         }
17681     },
17682
17683     /**
17684      * Displays this proxy
17685      */
17686     show : function(){
17687         this.el.show();
17688     },
17689
17690     /**
17691      * Force the Layer to sync its shadow and shim positions to the element
17692      */
17693     sync : function(){
17694         this.el.sync();
17695     },
17696
17697     /**
17698      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17699      * invalid drop operation by the item being dragged.
17700      * @param {Array} xy The XY position of the element ([x, y])
17701      * @param {Function} callback The function to call after the repair is complete
17702      * @param {Object} scope The scope in which to execute the callback
17703      */
17704     repair : function(xy, callback, scope){
17705         this.callback = callback;
17706         this.scope = scope;
17707         if(xy && this.animRepair !== false){
17708             this.el.addClass("x-dd-drag-repair");
17709             this.el.hideUnders(true);
17710             this.anim = this.el.shift({
17711                 duration: this.repairDuration || .5,
17712                 easing: 'easeOut',
17713                 xy: xy,
17714                 stopFx: true,
17715                 callback: this.afterRepair,
17716                 scope: this
17717             });
17718         }else{
17719             this.afterRepair();
17720         }
17721     },
17722
17723     // private
17724     afterRepair : function(){
17725         this.hide(true);
17726         if(typeof this.callback == "function"){
17727             this.callback.call(this.scope || this);
17728         }
17729         this.callback = null;
17730         this.scope = null;
17731     }
17732 };/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.dd.DragSource
17745  * @extends Roo.dd.DDProxy
17746  * A simple class that provides the basic implementation needed to make any element draggable.
17747  * @constructor
17748  * @param {String/HTMLElement/Element} el The container element
17749  * @param {Object} config
17750  */
17751 Roo.dd.DragSource = function(el, config){
17752     this.el = Roo.get(el);
17753     this.dragData = {};
17754     
17755     Roo.apply(this, config);
17756     
17757     if(!this.proxy){
17758         this.proxy = new Roo.dd.StatusProxy();
17759     }
17760
17761     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17762           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17763     
17764     this.dragging = false;
17765 };
17766
17767 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17768     /**
17769      * @cfg {String} dropAllowed
17770      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17771      */
17772     dropAllowed : "x-dd-drop-ok",
17773     /**
17774      * @cfg {String} dropNotAllowed
17775      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17776      */
17777     dropNotAllowed : "x-dd-drop-nodrop",
17778
17779     /**
17780      * Returns the data object associated with this drag source
17781      * @return {Object} data An object containing arbitrary data
17782      */
17783     getDragData : function(e){
17784         return this.dragData;
17785     },
17786
17787     // private
17788     onDragEnter : function(e, id){
17789         var target = Roo.dd.DragDropMgr.getDDById(id);
17790         this.cachedTarget = target;
17791         if(this.beforeDragEnter(target, e, id) !== false){
17792             if(target.isNotifyTarget){
17793                 var status = target.notifyEnter(this, e, this.dragData);
17794                 this.proxy.setStatus(status);
17795             }else{
17796                 this.proxy.setStatus(this.dropAllowed);
17797             }
17798             
17799             if(this.afterDragEnter){
17800                 /**
17801                  * An empty function by default, but provided so that you can perform a custom action
17802                  * when the dragged item enters the drop target by providing an implementation.
17803                  * @param {Roo.dd.DragDrop} target The drop target
17804                  * @param {Event} e The event object
17805                  * @param {String} id The id of the dragged element
17806                  * @method afterDragEnter
17807                  */
17808                 this.afterDragEnter(target, e, id);
17809             }
17810         }
17811     },
17812
17813     /**
17814      * An empty function by default, but provided so that you can perform a custom action
17815      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17816      * @param {Roo.dd.DragDrop} target The drop target
17817      * @param {Event} e The event object
17818      * @param {String} id The id of the dragged element
17819      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17820      */
17821     beforeDragEnter : function(target, e, id){
17822         return true;
17823     },
17824
17825     // private
17826     alignElWithMouse: function() {
17827         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17828         this.proxy.sync();
17829     },
17830
17831     // private
17832     onDragOver : function(e, id){
17833         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17834         if(this.beforeDragOver(target, e, id) !== false){
17835             if(target.isNotifyTarget){
17836                 var status = target.notifyOver(this, e, this.dragData);
17837                 this.proxy.setStatus(status);
17838             }
17839
17840             if(this.afterDragOver){
17841                 /**
17842                  * An empty function by default, but provided so that you can perform a custom action
17843                  * while the dragged item is over the drop target by providing an implementation.
17844                  * @param {Roo.dd.DragDrop} target The drop target
17845                  * @param {Event} e The event object
17846                  * @param {String} id The id of the dragged element
17847                  * @method afterDragOver
17848                  */
17849                 this.afterDragOver(target, e, id);
17850             }
17851         }
17852     },
17853
17854     /**
17855      * An empty function by default, but provided so that you can perform a custom action
17856      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17857      * @param {Roo.dd.DragDrop} target The drop target
17858      * @param {Event} e The event object
17859      * @param {String} id The id of the dragged element
17860      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17861      */
17862     beforeDragOver : function(target, e, id){
17863         return true;
17864     },
17865
17866     // private
17867     onDragOut : function(e, id){
17868         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17869         if(this.beforeDragOut(target, e, id) !== false){
17870             if(target.isNotifyTarget){
17871                 target.notifyOut(this, e, this.dragData);
17872             }
17873             this.proxy.reset();
17874             if(this.afterDragOut){
17875                 /**
17876                  * An empty function by default, but provided so that you can perform a custom action
17877                  * after the dragged item is dragged out of the target without dropping.
17878                  * @param {Roo.dd.DragDrop} target The drop target
17879                  * @param {Event} e The event object
17880                  * @param {String} id The id of the dragged element
17881                  * @method afterDragOut
17882                  */
17883                 this.afterDragOut(target, e, id);
17884             }
17885         }
17886         this.cachedTarget = null;
17887     },
17888
17889     /**
17890      * An empty function by default, but provided so that you can perform a custom action before the dragged
17891      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17892      * @param {Roo.dd.DragDrop} target The drop target
17893      * @param {Event} e The event object
17894      * @param {String} id The id of the dragged element
17895      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17896      */
17897     beforeDragOut : function(target, e, id){
17898         return true;
17899     },
17900     
17901     // private
17902     onDragDrop : function(e, id){
17903         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17904         if(this.beforeDragDrop(target, e, id) !== false){
17905             if(target.isNotifyTarget){
17906                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17907                     this.onValidDrop(target, e, id);
17908                 }else{
17909                     this.onInvalidDrop(target, e, id);
17910                 }
17911             }else{
17912                 this.onValidDrop(target, e, id);
17913             }
17914             
17915             if(this.afterDragDrop){
17916                 /**
17917                  * An empty function by default, but provided so that you can perform a custom action
17918                  * after a valid drag drop has occurred by providing an implementation.
17919                  * @param {Roo.dd.DragDrop} target The drop target
17920                  * @param {Event} e The event object
17921                  * @param {String} id The id of the dropped element
17922                  * @method afterDragDrop
17923                  */
17924                 this.afterDragDrop(target, e, id);
17925             }
17926         }
17927         delete this.cachedTarget;
17928     },
17929
17930     /**
17931      * An empty function by default, but provided so that you can perform a custom action before the dragged
17932      * item is dropped onto the target and optionally cancel the onDragDrop.
17933      * @param {Roo.dd.DragDrop} target The drop target
17934      * @param {Event} e The event object
17935      * @param {String} id The id of the dragged element
17936      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17937      */
17938     beforeDragDrop : function(target, e, id){
17939         return true;
17940     },
17941
17942     // private
17943     onValidDrop : function(target, e, id){
17944         this.hideProxy();
17945         if(this.afterValidDrop){
17946             /**
17947              * An empty function by default, but provided so that you can perform a custom action
17948              * after a valid drop has occurred by providing an implementation.
17949              * @param {Object} target The target DD 
17950              * @param {Event} e The event object
17951              * @param {String} id The id of the dropped element
17952              * @method afterInvalidDrop
17953              */
17954             this.afterValidDrop(target, e, id);
17955         }
17956     },
17957
17958     // private
17959     getRepairXY : function(e, data){
17960         return this.el.getXY();  
17961     },
17962
17963     // private
17964     onInvalidDrop : function(target, e, id){
17965         this.beforeInvalidDrop(target, e, id);
17966         if(this.cachedTarget){
17967             if(this.cachedTarget.isNotifyTarget){
17968                 this.cachedTarget.notifyOut(this, e, this.dragData);
17969             }
17970             this.cacheTarget = null;
17971         }
17972         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
17973
17974         if(this.afterInvalidDrop){
17975             /**
17976              * An empty function by default, but provided so that you can perform a custom action
17977              * after an invalid drop has occurred by providing an implementation.
17978              * @param {Event} e The event object
17979              * @param {String} id The id of the dropped element
17980              * @method afterInvalidDrop
17981              */
17982             this.afterInvalidDrop(e, id);
17983         }
17984     },
17985
17986     // private
17987     afterRepair : function(){
17988         if(Roo.enableFx){
17989             this.el.highlight(this.hlColor || "c3daf9");
17990         }
17991         this.dragging = false;
17992     },
17993
17994     /**
17995      * An empty function by default, but provided so that you can perform a custom action after an invalid
17996      * drop has occurred.
17997      * @param {Roo.dd.DragDrop} target The drop target
17998      * @param {Event} e The event object
17999      * @param {String} id The id of the dragged element
18000      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18001      */
18002     beforeInvalidDrop : function(target, e, id){
18003         return true;
18004     },
18005
18006     // private
18007     handleMouseDown : function(e){
18008         if(this.dragging) {
18009             return;
18010         }
18011         var data = this.getDragData(e);
18012         if(data && this.onBeforeDrag(data, e) !== false){
18013             this.dragData = data;
18014             this.proxy.stop();
18015             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18016         } 
18017     },
18018
18019     /**
18020      * An empty function by default, but provided so that you can perform a custom action before the initial
18021      * drag event begins and optionally cancel it.
18022      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18023      * @param {Event} e The event object
18024      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18025      */
18026     onBeforeDrag : function(data, e){
18027         return true;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action once the initial
18032      * drag event has begun.  The drag cannot be canceled from this function.
18033      * @param {Number} x The x position of the click on the dragged object
18034      * @param {Number} y The y position of the click on the dragged object
18035      */
18036     onStartDrag : Roo.emptyFn,
18037
18038     // private - YUI override
18039     startDrag : function(x, y){
18040         this.proxy.reset();
18041         this.dragging = true;
18042         this.proxy.update("");
18043         this.onInitDrag(x, y);
18044         this.proxy.show();
18045     },
18046
18047     // private
18048     onInitDrag : function(x, y){
18049         var clone = this.el.dom.cloneNode(true);
18050         clone.id = Roo.id(); // prevent duplicate ids
18051         this.proxy.update(clone);
18052         this.onStartDrag(x, y);
18053         return true;
18054     },
18055
18056     /**
18057      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18058      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18059      */
18060     getProxy : function(){
18061         return this.proxy;  
18062     },
18063
18064     /**
18065      * Hides the drag source's {@link Roo.dd.StatusProxy}
18066      */
18067     hideProxy : function(){
18068         this.proxy.hide();  
18069         this.proxy.reset(true);
18070         this.dragging = false;
18071     },
18072
18073     // private
18074     triggerCacheRefresh : function(){
18075         Roo.dd.DDM.refreshCache(this.groups);
18076     },
18077
18078     // private - override to prevent hiding
18079     b4EndDrag: function(e) {
18080     },
18081
18082     // private - override to prevent moving
18083     endDrag : function(e){
18084         this.onEndDrag(this.dragData, e);
18085     },
18086
18087     // private
18088     onEndDrag : function(data, e){
18089     },
18090     
18091     // private - pin to cursor
18092     autoOffset : function(x, y) {
18093         this.setDelta(-12, -20);
18094     }    
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105
18106
18107 /**
18108  * @class Roo.dd.DropTarget
18109  * @extends Roo.dd.DDTarget
18110  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18111  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18112  * @constructor
18113  * @param {String/HTMLElement/Element} el The container element
18114  * @param {Object} config
18115  */
18116 Roo.dd.DropTarget = function(el, config){
18117     this.el = Roo.get(el);
18118     
18119     Roo.apply(this, config);
18120     
18121     if(this.containerScroll){
18122         Roo.dd.ScrollManager.register(this.el);
18123     }
18124     
18125     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18126           {isTarget: true});
18127
18128 };
18129
18130 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18131     /**
18132      * @cfg {String} overClass
18133      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18134      */
18135     /**
18136      * @cfg {String} dropAllowed
18137      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18138      */
18139     dropAllowed : "x-dd-drop-ok",
18140     /**
18141      * @cfg {String} dropNotAllowed
18142      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18143      */
18144     dropNotAllowed : "x-dd-drop-nodrop",
18145
18146     // private
18147     isTarget : true,
18148
18149     // private
18150     isNotifyTarget : true,
18151
18152     /**
18153      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18154      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18155      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18157      * @param {Event} e The event
18158      * @param {Object} data An object containing arbitrary data supplied by the drag source
18159      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18160      * underlying {@link Roo.dd.StatusProxy} can be updated
18161      */
18162     notifyEnter : function(dd, e, data){
18163         if(this.overClass){
18164             this.el.addClass(this.overClass);
18165         }
18166         return this.dropAllowed;
18167     },
18168
18169     /**
18170      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18171      * This method will be called on every mouse movement while the drag source is over the drop target.
18172      * This default implementation simply returns the dropAllowed config value.
18173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18174      * @param {Event} e The event
18175      * @param {Object} data An object containing arbitrary data supplied by the drag source
18176      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18177      * underlying {@link Roo.dd.StatusProxy} can be updated
18178      */
18179     notifyOver : function(dd, e, data){
18180         return this.dropAllowed;
18181     },
18182
18183     /**
18184      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18185      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18186      * overClass (if any) from the drop element.
18187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18188      * @param {Event} e The event
18189      * @param {Object} data An object containing arbitrary data supplied by the drag source
18190      */
18191     notifyOut : function(dd, e, data){
18192         if(this.overClass){
18193             this.el.removeClass(this.overClass);
18194         }
18195     },
18196
18197     /**
18198      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18199      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18200      * implementation that does something to process the drop event and returns true so that the drag source's
18201      * repair action does not run.
18202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18203      * @param {Event} e The event
18204      * @param {Object} data An object containing arbitrary data supplied by the drag source
18205      * @return {Boolean} True if the drop was valid, else false
18206      */
18207     notifyDrop : function(dd, e, data){
18208         return false;
18209     }
18210 });/*
18211  * Based on:
18212  * Ext JS Library 1.1.1
18213  * Copyright(c) 2006-2007, Ext JS, LLC.
18214  *
18215  * Originally Released Under LGPL - original licence link has changed is not relivant.
18216  *
18217  * Fork - LGPL
18218  * <script type="text/javascript">
18219  */
18220
18221
18222 /**
18223  * @class Roo.dd.DragZone
18224  * @extends Roo.dd.DragSource
18225  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18226  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18227  * @constructor
18228  * @param {String/HTMLElement/Element} el The container element
18229  * @param {Object} config
18230  */
18231 Roo.dd.DragZone = function(el, config){
18232     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18233     if(this.containerScroll){
18234         Roo.dd.ScrollManager.register(this.el);
18235     }
18236 };
18237
18238 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18239     /**
18240      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18241      * for auto scrolling during drag operations.
18242      */
18243     /**
18244      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18245      * method after a failed drop (defaults to "c3daf9" - light blue)
18246      */
18247
18248     /**
18249      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18250      * for a valid target to drag based on the mouse down. Override this method
18251      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18252      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18253      * @param {EventObject} e The mouse down event
18254      * @return {Object} The dragData
18255      */
18256     getDragData : function(e){
18257         return Roo.dd.Registry.getHandleFromEvent(e);
18258     },
18259     
18260     /**
18261      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18262      * this.dragData.ddel
18263      * @param {Number} x The x position of the click on the dragged object
18264      * @param {Number} y The y position of the click on the dragged object
18265      * @return {Boolean} true to continue the drag, false to cancel
18266      */
18267     onInitDrag : function(x, y){
18268         this.proxy.update(this.dragData.ddel.cloneNode(true));
18269         this.onStartDrag(x, y);
18270         return true;
18271     },
18272     
18273     /**
18274      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18275      */
18276     afterRepair : function(){
18277         if(Roo.enableFx){
18278             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18279         }
18280         this.dragging = false;
18281     },
18282
18283     /**
18284      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18285      * the XY of this.dragData.ddel
18286      * @param {EventObject} e The mouse up event
18287      * @return {Array} The xy location (e.g. [100, 200])
18288      */
18289     getRepairXY : function(e){
18290         return Roo.Element.fly(this.dragData.ddel).getXY();  
18291     }
18292 });/*
18293  * Based on:
18294  * Ext JS Library 1.1.1
18295  * Copyright(c) 2006-2007, Ext JS, LLC.
18296  *
18297  * Originally Released Under LGPL - original licence link has changed is not relivant.
18298  *
18299  * Fork - LGPL
18300  * <script type="text/javascript">
18301  */
18302 /**
18303  * @class Roo.dd.DropZone
18304  * @extends Roo.dd.DropTarget
18305  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18306  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18307  * @constructor
18308  * @param {String/HTMLElement/Element} el The container element
18309  * @param {Object} config
18310  */
18311 Roo.dd.DropZone = function(el, config){
18312     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18313 };
18314
18315 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18316     /**
18317      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18318      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18319      * provide your own custom lookup.
18320      * @param {Event} e The event
18321      * @return {Object} data The custom data
18322      */
18323     getTargetFromEvent : function(e){
18324         return Roo.dd.Registry.getTargetFromEvent(e);
18325     },
18326
18327     /**
18328      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18329      * that it has registered.  This method has no default implementation and should be overridden to provide
18330      * node-specific processing if necessary.
18331      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18332      * {@link #getTargetFromEvent} for this node)
18333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18334      * @param {Event} e The event
18335      * @param {Object} data An object containing arbitrary data supplied by the drag source
18336      */
18337     onNodeEnter : function(n, dd, e, data){
18338         
18339     },
18340
18341     /**
18342      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18343      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18344      * overridden to provide the proper feedback.
18345      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18346      * {@link #getTargetFromEvent} for this node)
18347      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18348      * @param {Event} e The event
18349      * @param {Object} data An object containing arbitrary data supplied by the drag source
18350      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18351      * underlying {@link Roo.dd.StatusProxy} can be updated
18352      */
18353     onNodeOver : function(n, dd, e, data){
18354         return this.dropAllowed;
18355     },
18356
18357     /**
18358      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18359      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18360      * node-specific processing if necessary.
18361      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18362      * {@link #getTargetFromEvent} for this node)
18363      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18364      * @param {Event} e The event
18365      * @param {Object} data An object containing arbitrary data supplied by the drag source
18366      */
18367     onNodeOut : function(n, dd, e, data){
18368         
18369     },
18370
18371     /**
18372      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18373      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18374      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18375      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18376      * {@link #getTargetFromEvent} for this node)
18377      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18378      * @param {Event} e The event
18379      * @param {Object} data An object containing arbitrary data supplied by the drag source
18380      * @return {Boolean} True if the drop was valid, else false
18381      */
18382     onNodeDrop : function(n, dd, e, data){
18383         return false;
18384     },
18385
18386     /**
18387      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18388      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18389      * it should be overridden to provide the proper feedback if necessary.
18390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18391      * @param {Event} e The event
18392      * @param {Object} data An object containing arbitrary data supplied by the drag source
18393      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18394      * underlying {@link Roo.dd.StatusProxy} can be updated
18395      */
18396     onContainerOver : function(dd, e, data){
18397         return this.dropNotAllowed;
18398     },
18399
18400     /**
18401      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18402      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18403      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18404      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18405      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18406      * @param {Event} e The event
18407      * @param {Object} data An object containing arbitrary data supplied by the drag source
18408      * @return {Boolean} True if the drop was valid, else false
18409      */
18410     onContainerDrop : function(dd, e, data){
18411         return false;
18412     },
18413
18414     /**
18415      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18416      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18417      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18418      * you should override this method and provide a custom implementation.
18419      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18420      * @param {Event} e The event
18421      * @param {Object} data An object containing arbitrary data supplied by the drag source
18422      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18423      * underlying {@link Roo.dd.StatusProxy} can be updated
18424      */
18425     notifyEnter : function(dd, e, data){
18426         return this.dropNotAllowed;
18427     },
18428
18429     /**
18430      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18431      * This method will be called on every mouse movement while the drag source is over the drop zone.
18432      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18433      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18434      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18435      * registered node, it will call {@link #onContainerOver}.
18436      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18437      * @param {Event} e The event
18438      * @param {Object} data An object containing arbitrary data supplied by the drag source
18439      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18440      * underlying {@link Roo.dd.StatusProxy} can be updated
18441      */
18442     notifyOver : function(dd, e, data){
18443         var n = this.getTargetFromEvent(e);
18444         if(!n){ // not over valid drop target
18445             if(this.lastOverNode){
18446                 this.onNodeOut(this.lastOverNode, dd, e, data);
18447                 this.lastOverNode = null;
18448             }
18449             return this.onContainerOver(dd, e, data);
18450         }
18451         if(this.lastOverNode != n){
18452             if(this.lastOverNode){
18453                 this.onNodeOut(this.lastOverNode, dd, e, data);
18454             }
18455             this.onNodeEnter(n, dd, e, data);
18456             this.lastOverNode = n;
18457         }
18458         return this.onNodeOver(n, dd, e, data);
18459     },
18460
18461     /**
18462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18463      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18464      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18465      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18466      * @param {Event} e The event
18467      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18468      */
18469     notifyOut : function(dd, e, data){
18470         if(this.lastOverNode){
18471             this.onNodeOut(this.lastOverNode, dd, e, data);
18472             this.lastOverNode = null;
18473         }
18474     },
18475
18476     /**
18477      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18478      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18479      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18480      * otherwise it will call {@link #onContainerDrop}.
18481      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18482      * @param {Event} e The event
18483      * @param {Object} data An object containing arbitrary data supplied by the drag source
18484      * @return {Boolean} True if the drop was valid, else false
18485      */
18486     notifyDrop : function(dd, e, data){
18487         if(this.lastOverNode){
18488             this.onNodeOut(this.lastOverNode, dd, e, data);
18489             this.lastOverNode = null;
18490         }
18491         var n = this.getTargetFromEvent(e);
18492         return n ?
18493             this.onNodeDrop(n, dd, e, data) :
18494             this.onContainerDrop(dd, e, data);
18495     },
18496
18497     // private
18498     triggerCacheRefresh : function(){
18499         Roo.dd.DDM.refreshCache(this.groups);
18500     }  
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511
18512
18513 /**
18514  * @class Roo.data.SortTypes
18515  * @singleton
18516  * Defines the default sorting (casting?) comparison functions used when sorting data.
18517  */
18518 Roo.data.SortTypes = {
18519     /**
18520      * Default sort that does nothing
18521      * @param {Mixed} s The value being converted
18522      * @return {Mixed} The comparison value
18523      */
18524     none : function(s){
18525         return s;
18526     },
18527     
18528     /**
18529      * The regular expression used to strip tags
18530      * @type {RegExp}
18531      * @property
18532      */
18533     stripTagsRE : /<\/?[^>]+>/gi,
18534     
18535     /**
18536      * Strips all HTML tags to sort on text only
18537      * @param {Mixed} s The value being converted
18538      * @return {String} The comparison value
18539      */
18540     asText : function(s){
18541         return String(s).replace(this.stripTagsRE, "");
18542     },
18543     
18544     /**
18545      * Strips all HTML tags to sort on text only - Case insensitive
18546      * @param {Mixed} s The value being converted
18547      * @return {String} The comparison value
18548      */
18549     asUCText : function(s){
18550         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18551     },
18552     
18553     /**
18554      * Case insensitive string
18555      * @param {Mixed} s The value being converted
18556      * @return {String} The comparison value
18557      */
18558     asUCString : function(s) {
18559         return String(s).toUpperCase();
18560     },
18561     
18562     /**
18563      * Date sorting
18564      * @param {Mixed} s The value being converted
18565      * @return {Number} The comparison value
18566      */
18567     asDate : function(s) {
18568         if(!s){
18569             return 0;
18570         }
18571         if(s instanceof Date){
18572             return s.getTime();
18573         }
18574         return Date.parse(String(s));
18575     },
18576     
18577     /**
18578      * Float sorting
18579      * @param {Mixed} s The value being converted
18580      * @return {Float} The comparison value
18581      */
18582     asFloat : function(s) {
18583         var val = parseFloat(String(s).replace(/,/g, ""));
18584         if(isNaN(val)) val = 0;
18585         return val;
18586     },
18587     
18588     /**
18589      * Integer sorting
18590      * @param {Mixed} s The value being converted
18591      * @return {Number} The comparison value
18592      */
18593     asInt : function(s) {
18594         var val = parseInt(String(s).replace(/,/g, ""));
18595         if(isNaN(val)) val = 0;
18596         return val;
18597     }
18598 };/*
18599  * Based on:
18600  * Ext JS Library 1.1.1
18601  * Copyright(c) 2006-2007, Ext JS, LLC.
18602  *
18603  * Originally Released Under LGPL - original licence link has changed is not relivant.
18604  *
18605  * Fork - LGPL
18606  * <script type="text/javascript">
18607  */
18608
18609 /**
18610 * @class Roo.data.Record
18611  * Instances of this class encapsulate both record <em>definition</em> information, and record
18612  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18613  * to access Records cached in an {@link Roo.data.Store} object.<br>
18614  * <p>
18615  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18616  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18617  * objects.<br>
18618  * <p>
18619  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18620  * @constructor
18621  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18622  * {@link #create}. The parameters are the same.
18623  * @param {Array} data An associative Array of data values keyed by the field name.
18624  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18625  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18626  * not specified an integer id is generated.
18627  */
18628 Roo.data.Record = function(data, id){
18629     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18630     this.data = data;
18631 };
18632
18633 /**
18634  * Generate a constructor for a specific record layout.
18635  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18636  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18637  * Each field definition object may contain the following properties: <ul>
18638  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18639  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18640  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18641  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18642  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18643  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18644  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18645  * this may be omitted.</p></li>
18646  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18647  * <ul><li>auto (Default, implies no conversion)</li>
18648  * <li>string</li>
18649  * <li>int</li>
18650  * <li>float</li>
18651  * <li>boolean</li>
18652  * <li>date</li></ul></p></li>
18653  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18654  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18655  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18656  * by the Reader into an object that will be stored in the Record. It is passed the
18657  * following parameters:<ul>
18658  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18659  * </ul></p></li>
18660  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18661  * </ul>
18662  * <br>usage:<br><pre><code>
18663 var TopicRecord = Roo.data.Record.create(
18664     {name: 'title', mapping: 'topic_title'},
18665     {name: 'author', mapping: 'username'},
18666     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18667     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18668     {name: 'lastPoster', mapping: 'user2'},
18669     {name: 'excerpt', mapping: 'post_text'}
18670 );
18671
18672 var myNewRecord = new TopicRecord({
18673     title: 'Do my job please',
18674     author: 'noobie',
18675     totalPosts: 1,
18676     lastPost: new Date(),
18677     lastPoster: 'Animal',
18678     excerpt: 'No way dude!'
18679 });
18680 myStore.add(myNewRecord);
18681 </code></pre>
18682  * @method create
18683  * @static
18684  */
18685 Roo.data.Record.create = function(o){
18686     var f = function(){
18687         f.superclass.constructor.apply(this, arguments);
18688     };
18689     Roo.extend(f, Roo.data.Record);
18690     var p = f.prototype;
18691     p.fields = new Roo.util.MixedCollection(false, function(field){
18692         return field.name;
18693     });
18694     for(var i = 0, len = o.length; i < len; i++){
18695         p.fields.add(new Roo.data.Field(o[i]));
18696     }
18697     f.getField = function(name){
18698         return p.fields.get(name);  
18699     };
18700     return f;
18701 };
18702
18703 Roo.data.Record.AUTO_ID = 1000;
18704 Roo.data.Record.EDIT = 'edit';
18705 Roo.data.Record.REJECT = 'reject';
18706 Roo.data.Record.COMMIT = 'commit';
18707
18708 Roo.data.Record.prototype = {
18709     /**
18710      * Readonly flag - true if this record has been modified.
18711      * @type Boolean
18712      */
18713     dirty : false,
18714     editing : false,
18715     error: null,
18716     modified: null,
18717
18718     // private
18719     join : function(store){
18720         this.store = store;
18721     },
18722
18723     /**
18724      * Set the named field to the specified value.
18725      * @param {String} name The name of the field to set.
18726      * @param {Object} value The value to set the field to.
18727      */
18728     set : function(name, value){
18729         if(this.data[name] == value){
18730             return;
18731         }
18732         this.dirty = true;
18733         if(!this.modified){
18734             this.modified = {};
18735         }
18736         if(typeof this.modified[name] == 'undefined'){
18737             this.modified[name] = this.data[name];
18738         }
18739         this.data[name] = value;
18740         if(!this.editing){
18741             this.store.afterEdit(this);
18742         }       
18743     },
18744
18745     /**
18746      * Get the value of the named field.
18747      * @param {String} name The name of the field to get the value of.
18748      * @return {Object} The value of the field.
18749      */
18750     get : function(name){
18751         return this.data[name]; 
18752     },
18753
18754     // private
18755     beginEdit : function(){
18756         this.editing = true;
18757         this.modified = {}; 
18758     },
18759
18760     // private
18761     cancelEdit : function(){
18762         this.editing = false;
18763         delete this.modified;
18764     },
18765
18766     // private
18767     endEdit : function(){
18768         this.editing = false;
18769         if(this.dirty && this.store){
18770             this.store.afterEdit(this);
18771         }
18772     },
18773
18774     /**
18775      * Usually called by the {@link Roo.data.Store} which owns the Record.
18776      * Rejects all changes made to the Record since either creation, or the last commit operation.
18777      * Modified fields are reverted to their original values.
18778      * <p>
18779      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18780      * of reject operations.
18781      */
18782     reject : function(){
18783         var m = this.modified;
18784         for(var n in m){
18785             if(typeof m[n] != "function"){
18786                 this.data[n] = m[n];
18787             }
18788         }
18789         this.dirty = false;
18790         delete this.modified;
18791         this.editing = false;
18792         if(this.store){
18793             this.store.afterReject(this);
18794         }
18795     },
18796
18797     /**
18798      * Usually called by the {@link Roo.data.Store} which owns the Record.
18799      * Commits all changes made to the Record since either creation, or the last commit operation.
18800      * <p>
18801      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18802      * of commit operations.
18803      */
18804     commit : function(){
18805         this.dirty = false;
18806         delete this.modified;
18807         this.editing = false;
18808         if(this.store){
18809             this.store.afterCommit(this);
18810         }
18811     },
18812
18813     // private
18814     hasError : function(){
18815         return this.error != null;
18816     },
18817
18818     // private
18819     clearError : function(){
18820         this.error = null;
18821     },
18822
18823     /**
18824      * Creates a copy of this record.
18825      * @param {String} id (optional) A new record id if you don't want to use this record's id
18826      * @return {Record}
18827      */
18828     copy : function(newId) {
18829         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18830     }
18831 };/*
18832  * Based on:
18833  * Ext JS Library 1.1.1
18834  * Copyright(c) 2006-2007, Ext JS, LLC.
18835  *
18836  * Originally Released Under LGPL - original licence link has changed is not relivant.
18837  *
18838  * Fork - LGPL
18839  * <script type="text/javascript">
18840  */
18841
18842
18843
18844 /**
18845  * @class Roo.data.Store
18846  * @extends Roo.util.Observable
18847  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18848  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18849  * <p>
18850  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
18851  * has no knowledge of the format of the data returned by the Proxy.<br>
18852  * <p>
18853  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18854  * instances from the data object. These records are cached and made available through accessor functions.
18855  * @constructor
18856  * Creates a new Store.
18857  * @param {Object} config A config object containing the objects needed for the Store to access data,
18858  * and read the data into Records.
18859  */
18860 Roo.data.Store = function(config){
18861     this.data = new Roo.util.MixedCollection(false);
18862     this.data.getKey = function(o){
18863         return o.id;
18864     };
18865     this.baseParams = {};
18866     // private
18867     this.paramNames = {
18868         "start" : "start",
18869         "limit" : "limit",
18870         "sort" : "sort",
18871         "dir" : "dir"
18872     };
18873
18874     if(config && config.data){
18875         this.inlineData = config.data;
18876         delete config.data;
18877     }
18878
18879     Roo.apply(this, config);
18880     
18881     if(this.reader){ // reader passed
18882         this.reader = Roo.factory(this.reader, Roo.data);
18883         this.reader.xmodule = this.xmodule || false;
18884         if(!this.recordType){
18885             this.recordType = this.reader.recordType;
18886         }
18887         if(this.reader.onMetaChange){
18888             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18889         }
18890     }
18891
18892     if(this.recordType){
18893         this.fields = this.recordType.prototype.fields;
18894     }
18895     this.modified = [];
18896
18897     this.addEvents({
18898         /**
18899          * @event datachanged
18900          * Fires when the data cache has changed, and a widget which is using this Store
18901          * as a Record cache should refresh its view.
18902          * @param {Store} this
18903          */
18904         datachanged : true,
18905         /**
18906          * @event metachange
18907          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18908          * @param {Store} this
18909          * @param {Object} meta The JSON metadata
18910          */
18911         metachange : true,
18912         /**
18913          * @event add
18914          * Fires when Records have been added to the Store
18915          * @param {Store} this
18916          * @param {Roo.data.Record[]} records The array of Records added
18917          * @param {Number} index The index at which the record(s) were added
18918          */
18919         add : true,
18920         /**
18921          * @event remove
18922          * Fires when a Record has been removed from the Store
18923          * @param {Store} this
18924          * @param {Roo.data.Record} record The Record that was removed
18925          * @param {Number} index The index at which the record was removed
18926          */
18927         remove : true,
18928         /**
18929          * @event update
18930          * Fires when a Record has been updated
18931          * @param {Store} this
18932          * @param {Roo.data.Record} record The Record that was updated
18933          * @param {String} operation The update operation being performed.  Value may be one of:
18934          * <pre><code>
18935  Roo.data.Record.EDIT
18936  Roo.data.Record.REJECT
18937  Roo.data.Record.COMMIT
18938          * </code></pre>
18939          */
18940         update : true,
18941         /**
18942          * @event clear
18943          * Fires when the data cache has been cleared.
18944          * @param {Store} this
18945          */
18946         clear : true,
18947         /**
18948          * @event beforeload
18949          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18950          * the load action will be canceled.
18951          * @param {Store} this
18952          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18953          */
18954         beforeload : true,
18955         /**
18956          * @event load
18957          * Fires after a new set of Records has been loaded.
18958          * @param {Store} this
18959          * @param {Roo.data.Record[]} records The Records that were loaded
18960          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18961          */
18962         load : true,
18963         /**
18964          * @event loadexception
18965          * Fires if an exception occurs in the Proxy during loading.
18966          * Called with the signature of the Proxy's "loadexception" event.
18967          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
18968          * 
18969          * @param {Proxy} 
18970          * @param {Object} return from JsonData.reader() - success, totalRecords, records
18971          * @param {Object} load options 
18972          * @param {Object} jsonData from your request (normally this contains the Exception)
18973          */
18974         loadexception : true
18975     });
18976     
18977     if(this.proxy){
18978         this.proxy = Roo.factory(this.proxy, Roo.data);
18979         this.proxy.xmodule = this.xmodule || false;
18980         this.relayEvents(this.proxy,  ["loadexception"]);
18981     }
18982     this.sortToggle = {};
18983
18984     Roo.data.Store.superclass.constructor.call(this);
18985
18986     if(this.inlineData){
18987         this.loadData(this.inlineData);
18988         delete this.inlineData;
18989     }
18990 };
18991 Roo.extend(Roo.data.Store, Roo.util.Observable, {
18992      /**
18993     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
18994     * without a remote query - used by combo/forms at present.
18995     */
18996     
18997     /**
18998     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
18999     */
19000     /**
19001     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19002     */
19003     /**
19004     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19005     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19006     */
19007     /**
19008     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19009     * on any HTTP request
19010     */
19011     /**
19012     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19013     */
19014     /**
19015     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19016     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19017     */
19018     remoteSort : false,
19019
19020     /**
19021     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19022      * loaded or when a record is removed. (defaults to false).
19023     */
19024     pruneModifiedRecords : false,
19025
19026     // private
19027     lastOptions : null,
19028
19029     /**
19030      * Add Records to the Store and fires the add event.
19031      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19032      */
19033     add : function(records){
19034         records = [].concat(records);
19035         for(var i = 0, len = records.length; i < len; i++){
19036             records[i].join(this);
19037         }
19038         var index = this.data.length;
19039         this.data.addAll(records);
19040         this.fireEvent("add", this, records, index);
19041     },
19042
19043     /**
19044      * Remove a Record from the Store and fires the remove event.
19045      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19046      */
19047     remove : function(record){
19048         var index = this.data.indexOf(record);
19049         this.data.removeAt(index);
19050         if(this.pruneModifiedRecords){
19051             this.modified.remove(record);
19052         }
19053         this.fireEvent("remove", this, record, index);
19054     },
19055
19056     /**
19057      * Remove all Records from the Store and fires the clear event.
19058      */
19059     removeAll : function(){
19060         this.data.clear();
19061         if(this.pruneModifiedRecords){
19062             this.modified = [];
19063         }
19064         this.fireEvent("clear", this);
19065     },
19066
19067     /**
19068      * Inserts Records to the Store at the given index and fires the add event.
19069      * @param {Number} index The start index at which to insert the passed Records.
19070      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19071      */
19072     insert : function(index, records){
19073         records = [].concat(records);
19074         for(var i = 0, len = records.length; i < len; i++){
19075             this.data.insert(index, records[i]);
19076             records[i].join(this);
19077         }
19078         this.fireEvent("add", this, records, index);
19079     },
19080
19081     /**
19082      * Get the index within the cache of the passed Record.
19083      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19084      * @return {Number} The index of the passed Record. Returns -1 if not found.
19085      */
19086     indexOf : function(record){
19087         return this.data.indexOf(record);
19088     },
19089
19090     /**
19091      * Get the index within the cache of the Record with the passed id.
19092      * @param {String} id The id of the Record to find.
19093      * @return {Number} The index of the Record. Returns -1 if not found.
19094      */
19095     indexOfId : function(id){
19096         return this.data.indexOfKey(id);
19097     },
19098
19099     /**
19100      * Get the Record with the specified id.
19101      * @param {String} id The id of the Record to find.
19102      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19103      */
19104     getById : function(id){
19105         return this.data.key(id);
19106     },
19107
19108     /**
19109      * Get the Record at the specified index.
19110      * @param {Number} index The index of the Record to find.
19111      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19112      */
19113     getAt : function(index){
19114         return this.data.itemAt(index);
19115     },
19116
19117     /**
19118      * Returns a range of Records between specified indices.
19119      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19120      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19121      * @return {Roo.data.Record[]} An array of Records
19122      */
19123     getRange : function(start, end){
19124         return this.data.getRange(start, end);
19125     },
19126
19127     // private
19128     storeOptions : function(o){
19129         o = Roo.apply({}, o);
19130         delete o.callback;
19131         delete o.scope;
19132         this.lastOptions = o;
19133     },
19134
19135     /**
19136      * Loads the Record cache from the configured Proxy using the configured Reader.
19137      * <p>
19138      * If using remote paging, then the first load call must specify the <em>start</em>
19139      * and <em>limit</em> properties in the options.params property to establish the initial
19140      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19141      * <p>
19142      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19143      * and this call will return before the new data has been loaded. Perform any post-processing
19144      * in a callback function, or in a "load" event handler.</strong>
19145      * <p>
19146      * @param {Object} options An object containing properties which control loading options:<ul>
19147      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19148      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19149      * passed the following arguments:<ul>
19150      * <li>r : Roo.data.Record[]</li>
19151      * <li>options: Options object from the load call</li>
19152      * <li>success: Boolean success indicator</li></ul></li>
19153      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19154      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19155      * </ul>
19156      */
19157     load : function(options){
19158         options = options || {};
19159         if(this.fireEvent("beforeload", this, options) !== false){
19160             this.storeOptions(options);
19161             var p = Roo.apply(options.params || {}, this.baseParams);
19162             if(this.sortInfo && this.remoteSort){
19163                 var pn = this.paramNames;
19164                 p[pn["sort"]] = this.sortInfo.field;
19165                 p[pn["dir"]] = this.sortInfo.direction;
19166             }
19167             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19168         }
19169     },
19170
19171     /**
19172      * Reloads the Record cache from the configured Proxy using the configured Reader and
19173      * the options from the last load operation performed.
19174      * @param {Object} options (optional) An object containing properties which may override the options
19175      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19176      * the most recently used options are reused).
19177      */
19178     reload : function(options){
19179         this.load(Roo.applyIf(options||{}, this.lastOptions));
19180     },
19181
19182     // private
19183     // Called as a callback by the Reader during a load operation.
19184     loadRecords : function(o, options, success){
19185         if(!o || success === false){
19186             if(success !== false){
19187                 this.fireEvent("load", this, [], options);
19188             }
19189             if(options.callback){
19190                 options.callback.call(options.scope || this, [], options, false);
19191             }
19192             return;
19193         }
19194         // if data returned failure - throw an exception.
19195         if (o.success === false) {
19196             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19197             return;
19198         }
19199         var r = o.records, t = o.totalRecords || r.length;
19200         if(!options || options.add !== true){
19201             if(this.pruneModifiedRecords){
19202                 this.modified = [];
19203             }
19204             for(var i = 0, len = r.length; i < len; i++){
19205                 r[i].join(this);
19206             }
19207             if(this.snapshot){
19208                 this.data = this.snapshot;
19209                 delete this.snapshot;
19210             }
19211             this.data.clear();
19212             this.data.addAll(r);
19213             this.totalLength = t;
19214             this.applySort();
19215             this.fireEvent("datachanged", this);
19216         }else{
19217             this.totalLength = Math.max(t, this.data.length+r.length);
19218             this.add(r);
19219         }
19220         this.fireEvent("load", this, r, options);
19221         if(options.callback){
19222             options.callback.call(options.scope || this, r, options, true);
19223         }
19224     },
19225
19226     /**
19227      * Loads data from a passed data block. A Reader which understands the format of the data
19228      * must have been configured in the constructor.
19229      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19230      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19231      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19232      */
19233     loadData : function(o, append){
19234         var r = this.reader.readRecords(o);
19235         this.loadRecords(r, {add: append}, true);
19236     },
19237
19238     /**
19239      * Gets the number of cached records.
19240      * <p>
19241      * <em>If using paging, this may not be the total size of the dataset. If the data object
19242      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19243      * the data set size</em>
19244      */
19245     getCount : function(){
19246         return this.data.length || 0;
19247     },
19248
19249     /**
19250      * Gets the total number of records in the dataset as returned by the server.
19251      * <p>
19252      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19253      * the dataset size</em>
19254      */
19255     getTotalCount : function(){
19256         return this.totalLength || 0;
19257     },
19258
19259     /**
19260      * Returns the sort state of the Store as an object with two properties:
19261      * <pre><code>
19262  field {String} The name of the field by which the Records are sorted
19263  direction {String} The sort order, "ASC" or "DESC"
19264      * </code></pre>
19265      */
19266     getSortState : function(){
19267         return this.sortInfo;
19268     },
19269
19270     // private
19271     applySort : function(){
19272         if(this.sortInfo && !this.remoteSort){
19273             var s = this.sortInfo, f = s.field;
19274             var st = this.fields.get(f).sortType;
19275             var fn = function(r1, r2){
19276                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19277                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19278             };
19279             this.data.sort(s.direction, fn);
19280             if(this.snapshot && this.snapshot != this.data){
19281                 this.snapshot.sort(s.direction, fn);
19282             }
19283         }
19284     },
19285
19286     /**
19287      * Sets the default sort column and order to be used by the next load operation.
19288      * @param {String} fieldName The name of the field to sort by.
19289      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19290      */
19291     setDefaultSort : function(field, dir){
19292         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19293     },
19294
19295     /**
19296      * Sort the Records.
19297      * If remote sorting is used, the sort is performed on the server, and the cache is
19298      * reloaded. If local sorting is used, the cache is sorted internally.
19299      * @param {String} fieldName The name of the field to sort by.
19300      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19301      */
19302     sort : function(fieldName, dir){
19303         var f = this.fields.get(fieldName);
19304         if(!dir){
19305             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19306                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19307             }else{
19308                 dir = f.sortDir;
19309             }
19310         }
19311         this.sortToggle[f.name] = dir;
19312         this.sortInfo = {field: f.name, direction: dir};
19313         if(!this.remoteSort){
19314             this.applySort();
19315             this.fireEvent("datachanged", this);
19316         }else{
19317             this.load(this.lastOptions);
19318         }
19319     },
19320
19321     /**
19322      * Calls the specified function for each of the Records in the cache.
19323      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19324      * Returning <em>false</em> aborts and exits the iteration.
19325      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19326      */
19327     each : function(fn, scope){
19328         this.data.each(fn, scope);
19329     },
19330
19331     /**
19332      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19333      * (e.g., during paging).
19334      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19335      */
19336     getModifiedRecords : function(){
19337         return this.modified;
19338     },
19339
19340     // private
19341     createFilterFn : function(property, value, anyMatch){
19342         if(!value.exec){ // not a regex
19343             value = String(value);
19344             if(value.length == 0){
19345                 return false;
19346             }
19347             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19348         }
19349         return function(r){
19350             return value.test(r.data[property]);
19351         };
19352     },
19353
19354     /**
19355      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19356      * @param {String} property A field on your records
19357      * @param {Number} start The record index to start at (defaults to 0)
19358      * @param {Number} end The last record index to include (defaults to length - 1)
19359      * @return {Number} The sum
19360      */
19361     sum : function(property, start, end){
19362         var rs = this.data.items, v = 0;
19363         start = start || 0;
19364         end = (end || end === 0) ? end : rs.length-1;
19365
19366         for(var i = start; i <= end; i++){
19367             v += (rs[i].data[property] || 0);
19368         }
19369         return v;
19370     },
19371
19372     /**
19373      * Filter the records by a specified property.
19374      * @param {String} field A field on your records
19375      * @param {String/RegExp} value Either a string that the field
19376      * should start with or a RegExp to test against the field
19377      * @param {Boolean} anyMatch True to match any part not just the beginning
19378      */
19379     filter : function(property, value, anyMatch){
19380         var fn = this.createFilterFn(property, value, anyMatch);
19381         return fn ? this.filterBy(fn) : this.clearFilter();
19382     },
19383
19384     /**
19385      * Filter by a function. The specified function will be called with each
19386      * record in this data source. If the function returns true the record is included,
19387      * otherwise it is filtered.
19388      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19389      * @param {Object} scope (optional) The scope of the function (defaults to this)
19390      */
19391     filterBy : function(fn, scope){
19392         this.snapshot = this.snapshot || this.data;
19393         this.data = this.queryBy(fn, scope||this);
19394         this.fireEvent("datachanged", this);
19395     },
19396
19397     /**
19398      * Query the records by a specified property.
19399      * @param {String} field A field on your records
19400      * @param {String/RegExp} value Either a string that the field
19401      * should start with or a RegExp to test against the field
19402      * @param {Boolean} anyMatch True to match any part not just the beginning
19403      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19404      */
19405     query : function(property, value, anyMatch){
19406         var fn = this.createFilterFn(property, value, anyMatch);
19407         return fn ? this.queryBy(fn) : this.data.clone();
19408     },
19409
19410     /**
19411      * Query by a function. The specified function will be called with each
19412      * record in this data source. If the function returns true the record is included
19413      * in the results.
19414      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19415      * @param {Object} scope (optional) The scope of the function (defaults to this)
19416       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19417      **/
19418     queryBy : function(fn, scope){
19419         var data = this.snapshot || this.data;
19420         return data.filterBy(fn, scope||this);
19421     },
19422
19423     /**
19424      * Collects unique values for a particular dataIndex from this store.
19425      * @param {String} dataIndex The property to collect
19426      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19427      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19428      * @return {Array} An array of the unique values
19429      **/
19430     collect : function(dataIndex, allowNull, bypassFilter){
19431         var d = (bypassFilter === true && this.snapshot) ?
19432                 this.snapshot.items : this.data.items;
19433         var v, sv, r = [], l = {};
19434         for(var i = 0, len = d.length; i < len; i++){
19435             v = d[i].data[dataIndex];
19436             sv = String(v);
19437             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19438                 l[sv] = true;
19439                 r[r.length] = v;
19440             }
19441         }
19442         return r;
19443     },
19444
19445     /**
19446      * Revert to a view of the Record cache with no filtering applied.
19447      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19448      */
19449     clearFilter : function(suppressEvent){
19450         if(this.snapshot && this.snapshot != this.data){
19451             this.data = this.snapshot;
19452             delete this.snapshot;
19453             if(suppressEvent !== true){
19454                 this.fireEvent("datachanged", this);
19455             }
19456         }
19457     },
19458
19459     // private
19460     afterEdit : function(record){
19461         if(this.modified.indexOf(record) == -1){
19462             this.modified.push(record);
19463         }
19464         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19465     },
19466
19467     // private
19468     afterReject : function(record){
19469         this.modified.remove(record);
19470         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19471     },
19472
19473     // private
19474     afterCommit : function(record){
19475         this.modified.remove(record);
19476         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19477     },
19478
19479     /**
19480      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19481      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19482      */
19483     commitChanges : function(){
19484         var m = this.modified.slice(0);
19485         this.modified = [];
19486         for(var i = 0, len = m.length; i < len; i++){
19487             m[i].commit();
19488         }
19489     },
19490
19491     /**
19492      * Cancel outstanding changes on all changed records.
19493      */
19494     rejectChanges : function(){
19495         var m = this.modified.slice(0);
19496         this.modified = [];
19497         for(var i = 0, len = m.length; i < len; i++){
19498             m[i].reject();
19499         }
19500     },
19501
19502     onMetaChange : function(meta, rtype, o){
19503         this.recordType = rtype;
19504         this.fields = rtype.prototype.fields;
19505         delete this.snapshot;
19506         this.sortInfo = meta.sortInfo;
19507         this.modified = [];
19508         this.fireEvent('metachange', this, this.reader.meta);
19509     }
19510 });/*
19511  * Based on:
19512  * Ext JS Library 1.1.1
19513  * Copyright(c) 2006-2007, Ext JS, LLC.
19514  *
19515  * Originally Released Under LGPL - original licence link has changed is not relivant.
19516  *
19517  * Fork - LGPL
19518  * <script type="text/javascript">
19519  */
19520
19521 /**
19522  * @class Roo.data.SimpleStore
19523  * @extends Roo.data.Store
19524  * Small helper class to make creating Stores from Array data easier.
19525  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19526  * @cfg {Array} fields An array of field definition objects, or field name strings.
19527  * @cfg {Array} data The multi-dimensional array of data
19528  * @constructor
19529  * @param {Object} config
19530  */
19531 Roo.data.SimpleStore = function(config){
19532     Roo.data.SimpleStore.superclass.constructor.call(this, {
19533         isLocal : true,
19534         reader: new Roo.data.ArrayReader({
19535                 id: config.id
19536             },
19537             Roo.data.Record.create(config.fields)
19538         ),
19539         proxy : new Roo.data.MemoryProxy(config.data)
19540     });
19541     this.load();
19542 };
19543 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19544  * Based on:
19545  * Ext JS Library 1.1.1
19546  * Copyright(c) 2006-2007, Ext JS, LLC.
19547  *
19548  * Originally Released Under LGPL - original licence link has changed is not relivant.
19549  *
19550  * Fork - LGPL
19551  * <script type="text/javascript">
19552  */
19553
19554 /**
19555 /**
19556  * @extends Roo.data.Store
19557  * @class Roo.data.JsonStore
19558  * Small helper class to make creating Stores for JSON data easier. <br/>
19559 <pre><code>
19560 var store = new Roo.data.JsonStore({
19561     url: 'get-images.php',
19562     root: 'images',
19563     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19564 });
19565 </code></pre>
19566  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19567  * JsonReader and HttpProxy (unless inline data is provided).</b>
19568  * @cfg {Array} fields An array of field definition objects, or field name strings.
19569  * @constructor
19570  * @param {Object} config
19571  */
19572 Roo.data.JsonStore = function(c){
19573     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19574         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19575         reader: new Roo.data.JsonReader(c, c.fields)
19576     }));
19577 };
19578 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19579  * Based on:
19580  * Ext JS Library 1.1.1
19581  * Copyright(c) 2006-2007, Ext JS, LLC.
19582  *
19583  * Originally Released Under LGPL - original licence link has changed is not relivant.
19584  *
19585  * Fork - LGPL
19586  * <script type="text/javascript">
19587  */
19588
19589  
19590 Roo.data.Field = function(config){
19591     if(typeof config == "string"){
19592         config = {name: config};
19593     }
19594     Roo.apply(this, config);
19595     
19596     if(!this.type){
19597         this.type = "auto";
19598     }
19599     
19600     var st = Roo.data.SortTypes;
19601     // named sortTypes are supported, here we look them up
19602     if(typeof this.sortType == "string"){
19603         this.sortType = st[this.sortType];
19604     }
19605     
19606     // set default sortType for strings and dates
19607     if(!this.sortType){
19608         switch(this.type){
19609             case "string":
19610                 this.sortType = st.asUCString;
19611                 break;
19612             case "date":
19613                 this.sortType = st.asDate;
19614                 break;
19615             default:
19616                 this.sortType = st.none;
19617         }
19618     }
19619
19620     // define once
19621     var stripRe = /[\$,%]/g;
19622
19623     // prebuilt conversion function for this field, instead of
19624     // switching every time we're reading a value
19625     if(!this.convert){
19626         var cv, dateFormat = this.dateFormat;
19627         switch(this.type){
19628             case "":
19629             case "auto":
19630             case undefined:
19631                 cv = function(v){ return v; };
19632                 break;
19633             case "string":
19634                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19635                 break;
19636             case "int":
19637                 cv = function(v){
19638                     return v !== undefined && v !== null && v !== '' ?
19639                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19640                     };
19641                 break;
19642             case "float":
19643                 cv = function(v){
19644                     return v !== undefined && v !== null && v !== '' ?
19645                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19646                     };
19647                 break;
19648             case "bool":
19649             case "boolean":
19650                 cv = function(v){ return v === true || v === "true" || v == 1; };
19651                 break;
19652             case "date":
19653                 cv = function(v){
19654                     if(!v){
19655                         return '';
19656                     }
19657                     if(v instanceof Date){
19658                         return v;
19659                     }
19660                     if(dateFormat){
19661                         if(dateFormat == "timestamp"){
19662                             return new Date(v*1000);
19663                         }
19664                         return Date.parseDate(v, dateFormat);
19665                     }
19666                     var parsed = Date.parse(v);
19667                     return parsed ? new Date(parsed) : null;
19668                 };
19669              break;
19670             
19671         }
19672         this.convert = cv;
19673     }
19674 };
19675
19676 Roo.data.Field.prototype = {
19677     dateFormat: null,
19678     defaultValue: "",
19679     mapping: null,
19680     sortType : null,
19681     sortDir : "ASC"
19682 };/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693 // Base class for reading structured data from a data source.  This class is intended to be
19694 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19695
19696 /**
19697  * @class Roo.data.DataReader
19698  * Base class for reading structured data from a data source.  This class is intended to be
19699  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19700  */
19701
19702 Roo.data.DataReader = function(meta, recordType){
19703     
19704     this.meta = meta;
19705     
19706     this.recordType = recordType instanceof Array ? 
19707         Roo.data.Record.create(recordType) : recordType;
19708 };
19709
19710 Roo.data.DataReader.prototype = {
19711      /**
19712      * Create an empty record
19713      * @param {Object} data (optional) - overlay some values
19714      * @return {Roo.data.Record} record created.
19715      */
19716     newRow :  function(d) {
19717         var da =  {};
19718         this.recordType.prototype.fields.each(function(c) {
19719             switch( c.type) {
19720                 case 'int' : da[c.name] = 0; break;
19721                 case 'date' : da[c.name] = new Date(); break;
19722                 case 'float' : da[c.name] = 0.0; break;
19723                 case 'boolean' : da[c.name] = false; break;
19724                 default : da[c.name] = ""; break;
19725             }
19726             
19727         });
19728         return new this.recordType(Roo.apply(da, d));
19729     }
19730     
19731 };/*
19732  * Based on:
19733  * Ext JS Library 1.1.1
19734  * Copyright(c) 2006-2007, Ext JS, LLC.
19735  *
19736  * Originally Released Under LGPL - original licence link has changed is not relivant.
19737  *
19738  * Fork - LGPL
19739  * <script type="text/javascript">
19740  */
19741
19742 /**
19743  * @class Roo.data.DataProxy
19744  * @extends Roo.data.Observable
19745  * This class is an abstract base class for implementations which provide retrieval of
19746  * unformatted data objects.<br>
19747  * <p>
19748  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19749  * (of the appropriate type which knows how to parse the data object) to provide a block of
19750  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19751  * <p>
19752  * Custom implementations must implement the load method as described in
19753  * {@link Roo.data.HttpProxy#load}.
19754  */
19755 Roo.data.DataProxy = function(){
19756     this.addEvents({
19757         /**
19758          * @event beforeload
19759          * Fires before a network request is made to retrieve a data object.
19760          * @param {Object} This DataProxy object.
19761          * @param {Object} params The params parameter to the load function.
19762          */
19763         beforeload : true,
19764         /**
19765          * @event load
19766          * Fires before the load method's callback is called.
19767          * @param {Object} This DataProxy object.
19768          * @param {Object} o The data object.
19769          * @param {Object} arg The callback argument object passed to the load function.
19770          */
19771         load : true,
19772         /**
19773          * @event loadexception
19774          * Fires if an Exception occurs during data retrieval.
19775          * @param {Object} This DataProxy object.
19776          * @param {Object} o The data object.
19777          * @param {Object} arg The callback argument object passed to the load function.
19778          * @param {Object} e The Exception.
19779          */
19780         loadexception : true
19781     });
19782     Roo.data.DataProxy.superclass.constructor.call(this);
19783 };
19784
19785 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19786
19787     /**
19788      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19789      */
19790 /*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800 /**
19801  * @class Roo.data.MemoryProxy
19802  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19803  * to the Reader when its load method is called.
19804  * @constructor
19805  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19806  */
19807 Roo.data.MemoryProxy = function(data){
19808     if (data.data) {
19809         data = data.data;
19810     }
19811     Roo.data.MemoryProxy.superclass.constructor.call(this);
19812     this.data = data;
19813 };
19814
19815 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19816     /**
19817      * Load data from the requested source (in this case an in-memory
19818      * data object passed to the constructor), read the data object into
19819      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19820      * process that block using the passed callback.
19821      * @param {Object} params This parameter is not used by the MemoryProxy class.
19822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19823      * object into a block of Roo.data.Records.
19824      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19825      * The function must be passed <ul>
19826      * <li>The Record block object</li>
19827      * <li>The "arg" argument from the load function</li>
19828      * <li>A boolean success indicator</li>
19829      * </ul>
19830      * @param {Object} scope The scope in which to call the callback
19831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19832      */
19833     load : function(params, reader, callback, scope, arg){
19834         params = params || {};
19835         var result;
19836         try {
19837             result = reader.readRecords(this.data);
19838         }catch(e){
19839             this.fireEvent("loadexception", this, arg, null, e);
19840             callback.call(scope, null, arg, false);
19841             return;
19842         }
19843         callback.call(scope, result, arg, true);
19844     },
19845     
19846     // private
19847     update : function(params, records){
19848         
19849     }
19850 });/*
19851  * Based on:
19852  * Ext JS Library 1.1.1
19853  * Copyright(c) 2006-2007, Ext JS, LLC.
19854  *
19855  * Originally Released Under LGPL - original licence link has changed is not relivant.
19856  *
19857  * Fork - LGPL
19858  * <script type="text/javascript">
19859  */
19860 /**
19861  * @class Roo.data.HttpProxy
19862  * @extends Roo.data.DataProxy
19863  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19864  * configured to reference a certain URL.<br><br>
19865  * <p>
19866  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19867  * from which the running page was served.<br><br>
19868  * <p>
19869  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19870  * <p>
19871  * Be aware that to enable the browser to parse an XML document, the server must set
19872  * the Content-Type header in the HTTP response to "text/xml".
19873  * @constructor
19874  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19875  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19876  * will be used to make the request.
19877  */
19878 Roo.data.HttpProxy = function(conn){
19879     Roo.data.HttpProxy.superclass.constructor.call(this);
19880     // is conn a conn config or a real conn?
19881     this.conn = conn;
19882     this.useAjax = !conn || !conn.events;
19883   
19884 };
19885
19886 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19887     // thse are take from connection...
19888     
19889     /**
19890      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19891      */
19892     /**
19893      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19894      * extra parameters to each request made by this object. (defaults to undefined)
19895      */
19896     /**
19897      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19898      *  to each request made by this object. (defaults to undefined)
19899      */
19900     /**
19901      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
19902      */
19903     /**
19904      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19905      */
19906      /**
19907      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19908      * @type Boolean
19909      */
19910   
19911
19912     /**
19913      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19914      * @type Boolean
19915      */
19916     /**
19917      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19918      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19919      * a finer-grained basis than the DataProxy events.
19920      */
19921     getConnection : function(){
19922         return this.useAjax ? Roo.Ajax : this.conn;
19923     },
19924
19925     /**
19926      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19927      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19928      * process that block using the passed callback.
19929      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19930      * for the request to the remote server.
19931      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19932      * object into a block of Roo.data.Records.
19933      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19934      * The function must be passed <ul>
19935      * <li>The Record block object</li>
19936      * <li>The "arg" argument from the load function</li>
19937      * <li>A boolean success indicator</li>
19938      * </ul>
19939      * @param {Object} scope The scope in which to call the callback
19940      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19941      */
19942     load : function(params, reader, callback, scope, arg){
19943         if(this.fireEvent("beforeload", this, params) !== false){
19944             var  o = {
19945                 params : params || {},
19946                 request: {
19947                     callback : callback,
19948                     scope : scope,
19949                     arg : arg
19950                 },
19951                 reader: reader,
19952                 callback : this.loadResponse,
19953                 scope: this
19954             };
19955             if(this.useAjax){
19956                 Roo.applyIf(o, this.conn);
19957                 if(this.activeRequest){
19958                     Roo.Ajax.abort(this.activeRequest);
19959                 }
19960                 this.activeRequest = Roo.Ajax.request(o);
19961             }else{
19962                 this.conn.request(o);
19963             }
19964         }else{
19965             callback.call(scope||this, null, arg, false);
19966         }
19967     },
19968
19969     // private
19970     loadResponse : function(o, success, response){
19971         delete this.activeRequest;
19972         if(!success){
19973             this.fireEvent("loadexception", this, o, response);
19974             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19975             return;
19976         }
19977         var result;
19978         try {
19979             result = o.reader.read(response);
19980         }catch(e){
19981             this.fireEvent("loadexception", this, o, response, e);
19982             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19983             return;
19984         }
19985         
19986         this.fireEvent("load", this, o, o.request.arg);
19987         o.request.callback.call(o.request.scope, result, o.request.arg, true);
19988     },
19989
19990     // private
19991     update : function(dataSet){
19992
19993     },
19994
19995     // private
19996     updateResponse : function(dataSet){
19997
19998     }
19999 });/*
20000  * Based on:
20001  * Ext JS Library 1.1.1
20002  * Copyright(c) 2006-2007, Ext JS, LLC.
20003  *
20004  * Originally Released Under LGPL - original licence link has changed is not relivant.
20005  *
20006  * Fork - LGPL
20007  * <script type="text/javascript">
20008  */
20009
20010 /**
20011  * @class Roo.data.ScriptTagProxy
20012  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20013  * other than the originating domain of the running page.<br><br>
20014  * <p>
20015  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20016  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20017  * <p>
20018  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20019  * source code that is used as the source inside a &lt;script> tag.<br><br>
20020  * <p>
20021  * In order for the browser to process the returned data, the server must wrap the data object
20022  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20023  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20024  * depending on whether the callback name was passed:
20025  * <p>
20026  * <pre><code>
20027 boolean scriptTag = false;
20028 String cb = request.getParameter("callback");
20029 if (cb != null) {
20030     scriptTag = true;
20031     response.setContentType("text/javascript");
20032 } else {
20033     response.setContentType("application/x-json");
20034 }
20035 Writer out = response.getWriter();
20036 if (scriptTag) {
20037     out.write(cb + "(");
20038 }
20039 out.print(dataBlock.toJsonString());
20040 if (scriptTag) {
20041     out.write(");");
20042 }
20043 </pre></code>
20044  *
20045  * @constructor
20046  * @param {Object} config A configuration object.
20047  */
20048 Roo.data.ScriptTagProxy = function(config){
20049     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20050     Roo.apply(this, config);
20051     this.head = document.getElementsByTagName("head")[0];
20052 };
20053
20054 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20055
20056 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20057     /**
20058      * @cfg {String} url The URL from which to request the data object.
20059      */
20060     /**
20061      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20062      */
20063     timeout : 30000,
20064     /**
20065      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20066      * the server the name of the callback function set up by the load call to process the returned data object.
20067      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20068      * javascript output which calls this named function passing the data object as its only parameter.
20069      */
20070     callbackParam : "callback",
20071     /**
20072      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20073      * name to the request.
20074      */
20075     nocache : true,
20076
20077     /**
20078      * Load data from the configured URL, read the data object into
20079      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20080      * process that block using the passed callback.
20081      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20082      * for the request to the remote server.
20083      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20084      * object into a block of Roo.data.Records.
20085      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20086      * The function must be passed <ul>
20087      * <li>The Record block object</li>
20088      * <li>The "arg" argument from the load function</li>
20089      * <li>A boolean success indicator</li>
20090      * </ul>
20091      * @param {Object} scope The scope in which to call the callback
20092      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20093      */
20094     load : function(params, reader, callback, scope, arg){
20095         if(this.fireEvent("beforeload", this, params) !== false){
20096
20097             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20098
20099             var url = this.url;
20100             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20101             if(this.nocache){
20102                 url += "&_dc=" + (new Date().getTime());
20103             }
20104             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20105             var trans = {
20106                 id : transId,
20107                 cb : "stcCallback"+transId,
20108                 scriptId : "stcScript"+transId,
20109                 params : params,
20110                 arg : arg,
20111                 url : url,
20112                 callback : callback,
20113                 scope : scope,
20114                 reader : reader
20115             };
20116             var conn = this;
20117
20118             window[trans.cb] = function(o){
20119                 conn.handleResponse(o, trans);
20120             };
20121
20122             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20123
20124             if(this.autoAbort !== false){
20125                 this.abort();
20126             }
20127
20128             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20129
20130             var script = document.createElement("script");
20131             script.setAttribute("src", url);
20132             script.setAttribute("type", "text/javascript");
20133             script.setAttribute("id", trans.scriptId);
20134             this.head.appendChild(script);
20135
20136             this.trans = trans;
20137         }else{
20138             callback.call(scope||this, null, arg, false);
20139         }
20140     },
20141
20142     // private
20143     isLoading : function(){
20144         return this.trans ? true : false;
20145     },
20146
20147     /**
20148      * Abort the current server request.
20149      */
20150     abort : function(){
20151         if(this.isLoading()){
20152             this.destroyTrans(this.trans);
20153         }
20154     },
20155
20156     // private
20157     destroyTrans : function(trans, isLoaded){
20158         this.head.removeChild(document.getElementById(trans.scriptId));
20159         clearTimeout(trans.timeoutId);
20160         if(isLoaded){
20161             window[trans.cb] = undefined;
20162             try{
20163                 delete window[trans.cb];
20164             }catch(e){}
20165         }else{
20166             // if hasn't been loaded, wait for load to remove it to prevent script error
20167             window[trans.cb] = function(){
20168                 window[trans.cb] = undefined;
20169                 try{
20170                     delete window[trans.cb];
20171                 }catch(e){}
20172             };
20173         }
20174     },
20175
20176     // private
20177     handleResponse : function(o, trans){
20178         this.trans = false;
20179         this.destroyTrans(trans, true);
20180         var result;
20181         try {
20182             result = trans.reader.readRecords(o);
20183         }catch(e){
20184             this.fireEvent("loadexception", this, o, trans.arg, e);
20185             trans.callback.call(trans.scope||window, null, trans.arg, false);
20186             return;
20187         }
20188         this.fireEvent("load", this, o, trans.arg);
20189         trans.callback.call(trans.scope||window, result, trans.arg, true);
20190     },
20191
20192     // private
20193     handleFailure : function(trans){
20194         this.trans = false;
20195         this.destroyTrans(trans, false);
20196         this.fireEvent("loadexception", this, null, trans.arg);
20197         trans.callback.call(trans.scope||window, null, trans.arg, false);
20198     }
20199 });/*
20200  * Based on:
20201  * Ext JS Library 1.1.1
20202  * Copyright(c) 2006-2007, Ext JS, LLC.
20203  *
20204  * Originally Released Under LGPL - original licence link has changed is not relivant.
20205  *
20206  * Fork - LGPL
20207  * <script type="text/javascript">
20208  */
20209
20210 /**
20211  * @class Roo.data.JsonReader
20212  * @extends Roo.data.DataReader
20213  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20214  * based on mappings in a provided Roo.data.Record constructor.
20215  * <p>
20216  * Example code:
20217  * <pre><code>
20218 var RecordDef = Roo.data.Record.create([
20219     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20220     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20221 ]);
20222 var myReader = new Roo.data.JsonReader({
20223     totalProperty: "results",    // The property which contains the total dataset size (optional)
20224     root: "rows",                // The property which contains an Array of row objects
20225     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20226 }, RecordDef);
20227 </code></pre>
20228  * <p>
20229  * This would consume a JSON file like this:
20230  * <pre><code>
20231 { 'results': 2, 'rows': [
20232     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20233     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20234 }
20235 </code></pre>
20236  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20237  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20238  * paged from the remote server.
20239  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20240  * @cfg {String} root name of the property which contains the Array of row objects.
20241  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20242  * @constructor
20243  * Create a new JsonReader
20244  * @param {Object} meta Metadata configuration options
20245  * @param {Object} recordType Either an Array of field definition objects,
20246  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20247  */
20248 Roo.data.JsonReader = function(meta, recordType){
20249     
20250     meta = meta || {};
20251     // set some defaults:
20252     Roo.applyIf(meta, {
20253         totalProperty: 'total',
20254         successProperty : 'success',
20255         root : 'data',
20256         id : 'id'
20257     });
20258     
20259     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20260 };
20261 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20262     /**
20263      * This method is only used by a DataProxy which has retrieved data from a remote server.
20264      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20265      * @return {Object} data A data block which is used by an Roo.data.Store object as
20266      * a cache of Roo.data.Records.
20267      */
20268     read : function(response){
20269         var json = response.responseText;
20270         /* eval:var:o */
20271         var o = eval("("+json+")");
20272         if(!o) {
20273             throw {message: "JsonReader.read: Json object not found"};
20274         }
20275         
20276         if(o.metaData){
20277             delete this.ef;
20278             this.meta = o.metaData;
20279             this.recordType = Roo.data.Record.create(o.metaData.fields);
20280             this.onMetaChange(this.meta, this.recordType, o);
20281         }
20282         return this.readRecords(o);
20283     },
20284
20285     // private function a store will implement
20286     onMetaChange : function(meta, recordType, o){
20287
20288     },
20289
20290     /**
20291          * @ignore
20292          */
20293     simpleAccess: function(obj, subsc) {
20294         return obj[subsc];
20295     },
20296
20297         /**
20298          * @ignore
20299          */
20300     getJsonAccessor: function(){
20301         var re = /[\[\.]/;
20302         return function(expr) {
20303             try {
20304                 return(re.test(expr))
20305                     ? new Function("obj", "return obj." + expr)
20306                     : function(obj){
20307                         return obj[expr];
20308                     };
20309             } catch(e){}
20310             return Roo.emptyFn;
20311         };
20312     }(),
20313
20314     /**
20315      * Create a data block containing Roo.data.Records from an XML document.
20316      * @param {Object} o An object which contains an Array of row objects in the property specified
20317      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20318      * which contains the total size of the dataset.
20319      * @return {Object} data A data block which is used by an Roo.data.Store object as
20320      * a cache of Roo.data.Records.
20321      */
20322     readRecords : function(o){
20323         /**
20324          * After any data loads, the raw JSON data is available for further custom processing.
20325          * @type Object
20326          */
20327         this.jsonData = o;
20328         var s = this.meta, Record = this.recordType,
20329             f = Record.prototype.fields, fi = f.items, fl = f.length;
20330
20331 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20332         if (!this.ef) {
20333             if(s.totalProperty) {
20334                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20335                 }
20336                 if(s.successProperty) {
20337                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20338                 }
20339                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20340                 if (s.id) {
20341                         var g = this.getJsonAccessor(s.id);
20342                         this.getId = function(rec) {
20343                                 var r = g(rec);
20344                                 return (r === undefined || r === "") ? null : r;
20345                         };
20346                 } else {
20347                         this.getId = function(){return null;};
20348                 }
20349             this.ef = [];
20350             for(var i = 0; i < fl; i++){
20351                 f = fi[i];
20352                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20353                 this.ef[i] = this.getJsonAccessor(map);
20354             }
20355         }
20356
20357         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20358         if(s.totalProperty){
20359             var v = parseInt(this.getTotal(o), 10);
20360             if(!isNaN(v)){
20361                 totalRecords = v;
20362             }
20363         }
20364         if(s.successProperty){
20365             var v = this.getSuccess(o);
20366             if(v === false || v === 'false'){
20367                 success = false;
20368             }
20369         }
20370         var records = [];
20371             for(var i = 0; i < c; i++){
20372                     var n = root[i];
20373                 var values = {};
20374                 var id = this.getId(n);
20375                 for(var j = 0; j < fl; j++){
20376                     f = fi[j];
20377                 var v = this.ef[j](n);
20378                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20379                 }
20380                 var record = new Record(values, id);
20381                 record.json = n;
20382                 records[i] = record;
20383             }
20384             return {
20385                 success : success,
20386                 records : records,
20387                 totalRecords : totalRecords
20388             };
20389     }
20390 });/*
20391  * Based on:
20392  * Ext JS Library 1.1.1
20393  * Copyright(c) 2006-2007, Ext JS, LLC.
20394  *
20395  * Originally Released Under LGPL - original licence link has changed is not relivant.
20396  *
20397  * Fork - LGPL
20398  * <script type="text/javascript">
20399  */
20400
20401 /**
20402  * @class Roo.data.XmlReader
20403  * @extends Roo.data.DataReader
20404  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20405  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20406  * <p>
20407  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20408  * header in the HTTP response must be set to "text/xml".</em>
20409  * <p>
20410  * Example code:
20411  * <pre><code>
20412 var RecordDef = Roo.data.Record.create([
20413    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20414    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20415 ]);
20416 var myReader = new Roo.data.XmlReader({
20417    totalRecords: "results", // The element which contains the total dataset size (optional)
20418    record: "row",           // The repeated element which contains row information
20419    id: "id"                 // The element within the row that provides an ID for the record (optional)
20420 }, RecordDef);
20421 </code></pre>
20422  * <p>
20423  * This would consume an XML file like this:
20424  * <pre><code>
20425 &lt;?xml?>
20426 &lt;dataset>
20427  &lt;results>2&lt;/results>
20428  &lt;row>
20429    &lt;id>1&lt;/id>
20430    &lt;name>Bill&lt;/name>
20431    &lt;occupation>Gardener&lt;/occupation>
20432  &lt;/row>
20433  &lt;row>
20434    &lt;id>2&lt;/id>
20435    &lt;name>Ben&lt;/name>
20436    &lt;occupation>Horticulturalist&lt;/occupation>
20437  &lt;/row>
20438 &lt;/dataset>
20439 </code></pre>
20440  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20441  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20442  * paged from the remote server.
20443  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20444  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20445  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20446  * a record identifier value.
20447  * @constructor
20448  * Create a new XmlReader
20449  * @param {Object} meta Metadata configuration options
20450  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20451  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20452  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20453  */
20454 Roo.data.XmlReader = function(meta, recordType){
20455     meta = meta || {};
20456     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20457 };
20458 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20459     /**
20460      * This method is only used by a DataProxy which has retrieved data from a remote server.
20461          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20462          * to contain a method called 'responseXML' that returns an XML document object.
20463      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20464      * a cache of Roo.data.Records.
20465      */
20466     read : function(response){
20467         var doc = response.responseXML;
20468         if(!doc) {
20469             throw {message: "XmlReader.read: XML Document not available"};
20470         }
20471         return this.readRecords(doc);
20472     },
20473
20474     /**
20475      * Create a data block containing Roo.data.Records from an XML document.
20476          * @param {Object} doc A parsed XML document.
20477      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(doc){
20481         /**
20482          * After any data loads/reads, the raw XML Document is available for further custom processing.
20483          * @type XMLDocument
20484          */
20485         this.xmlData = doc;
20486         var root = doc.documentElement || doc;
20487         var q = Roo.DomQuery;
20488         var recordType = this.recordType, fields = recordType.prototype.fields;
20489         var sid = this.meta.id;
20490         var totalRecords = 0, success = true;
20491         if(this.meta.totalRecords){
20492             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20493         }
20494         
20495         if(this.meta.success){
20496             var sv = q.selectValue(this.meta.success, root, true);
20497             success = sv !== false && sv !== 'false';
20498         }
20499         var records = [];
20500         var ns = q.select(this.meta.record, root);
20501         for(var i = 0, len = ns.length; i < len; i++) {
20502                 var n = ns[i];
20503                 var values = {};
20504                 var id = sid ? q.selectValue(sid, n) : undefined;
20505                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20506                     var f = fields.items[j];
20507                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20508                     v = f.convert(v);
20509                     values[f.name] = v;
20510                 }
20511                 var record = new recordType(values, id);
20512                 record.node = n;
20513                 records[records.length] = record;
20514             }
20515
20516             return {
20517                 success : success,
20518                 records : records,
20519                 totalRecords : totalRecords || records.length
20520             };
20521     }
20522 });/*
20523  * Based on:
20524  * Ext JS Library 1.1.1
20525  * Copyright(c) 2006-2007, Ext JS, LLC.
20526  *
20527  * Originally Released Under LGPL - original licence link has changed is not relivant.
20528  *
20529  * Fork - LGPL
20530  * <script type="text/javascript">
20531  */
20532
20533 /**
20534  * @class Roo.data.ArrayReader
20535  * @extends Roo.data.DataReader
20536  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20537  * Each element of that Array represents a row of data fields. The
20538  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20539  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20540  * <p>
20541  * Example code:.
20542  * <pre><code>
20543 var RecordDef = Roo.data.Record.create([
20544     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20545     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20546 ]);
20547 var myReader = new Roo.data.ArrayReader({
20548     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20549 }, RecordDef);
20550 </code></pre>
20551  * <p>
20552  * This would consume an Array like this:
20553  * <pre><code>
20554 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20555   </code></pre>
20556  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20557  * @constructor
20558  * Create a new JsonReader
20559  * @param {Object} meta Metadata configuration options.
20560  * @param {Object} recordType Either an Array of field definition objects
20561  * as specified to {@link Roo.data.Record#create},
20562  * or an {@link Roo.data.Record} object
20563  * created using {@link Roo.data.Record#create}.
20564  */
20565 Roo.data.ArrayReader = function(meta, recordType){
20566     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20567 };
20568
20569 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20570     /**
20571      * Create a data block containing Roo.data.Records from an XML document.
20572      * @param {Object} o An Array of row objects which represents the dataset.
20573      * @return {Object} data A data block which is used by an Roo.data.Store object as
20574      * a cache of Roo.data.Records.
20575      */
20576     readRecords : function(o){
20577         var sid = this.meta ? this.meta.id : null;
20578         var recordType = this.recordType, fields = recordType.prototype.fields;
20579         var records = [];
20580         var root = o;
20581             for(var i = 0; i < root.length; i++){
20582                     var n = root[i];
20583                 var values = {};
20584                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20585                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20586                 var f = fields.items[j];
20587                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20588                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20589                 v = f.convert(v);
20590                 values[f.name] = v;
20591             }
20592                 var record = new recordType(values, id);
20593                 record.json = n;
20594                 records[records.length] = record;
20595             }
20596             return {
20597                 records : records,
20598                 totalRecords : records.length
20599             };
20600     }
20601 });/*
20602  * Based on:
20603  * Ext JS Library 1.1.1
20604  * Copyright(c) 2006-2007, Ext JS, LLC.
20605  *
20606  * Originally Released Under LGPL - original licence link has changed is not relivant.
20607  *
20608  * Fork - LGPL
20609  * <script type="text/javascript">
20610  */
20611
20612
20613 /**
20614  * @class Roo.data.Tree
20615  * @extends Roo.util.Observable
20616  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20617  * in the tree have most standard DOM functionality.
20618  * @constructor
20619  * @param {Node} root (optional) The root node
20620  */
20621 Roo.data.Tree = function(root){
20622    this.nodeHash = {};
20623    /**
20624     * The root node for this tree
20625     * @type Node
20626     */
20627    this.root = null;
20628    if(root){
20629        this.setRootNode(root);
20630    }
20631    this.addEvents({
20632        /**
20633         * @event append
20634         * Fires when a new child node is appended to a node in this tree.
20635         * @param {Tree} tree The owner tree
20636         * @param {Node} parent The parent node
20637         * @param {Node} node The newly appended node
20638         * @param {Number} index The index of the newly appended node
20639         */
20640        "append" : true,
20641        /**
20642         * @event remove
20643         * Fires when a child node is removed from a node in this tree.
20644         * @param {Tree} tree The owner tree
20645         * @param {Node} parent The parent node
20646         * @param {Node} node The child node removed
20647         */
20648        "remove" : true,
20649        /**
20650         * @event move
20651         * Fires when a node is moved to a new location in the tree
20652         * @param {Tree} tree The owner tree
20653         * @param {Node} node The node moved
20654         * @param {Node} oldParent The old parent of this node
20655         * @param {Node} newParent The new parent of this node
20656         * @param {Number} index The index it was moved to
20657         */
20658        "move" : true,
20659        /**
20660         * @event insert
20661         * Fires when a new child node is inserted in a node in this tree.
20662         * @param {Tree} tree The owner tree
20663         * @param {Node} parent The parent node
20664         * @param {Node} node The child node inserted
20665         * @param {Node} refNode The child node the node was inserted before
20666         */
20667        "insert" : true,
20668        /**
20669         * @event beforeappend
20670         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20671         * @param {Tree} tree The owner tree
20672         * @param {Node} parent The parent node
20673         * @param {Node} node The child node to be appended
20674         */
20675        "beforeappend" : true,
20676        /**
20677         * @event beforeremove
20678         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20679         * @param {Tree} tree The owner tree
20680         * @param {Node} parent The parent node
20681         * @param {Node} node The child node to be removed
20682         */
20683        "beforeremove" : true,
20684        /**
20685         * @event beforemove
20686         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20687         * @param {Tree} tree The owner tree
20688         * @param {Node} node The node being moved
20689         * @param {Node} oldParent The parent of the node
20690         * @param {Node} newParent The new parent the node is moving to
20691         * @param {Number} index The index it is being moved to
20692         */
20693        "beforemove" : true,
20694        /**
20695         * @event beforeinsert
20696         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20697         * @param {Tree} tree The owner tree
20698         * @param {Node} parent The parent node
20699         * @param {Node} node The child node to be inserted
20700         * @param {Node} refNode The child node the node is being inserted before
20701         */
20702        "beforeinsert" : true
20703    });
20704
20705     Roo.data.Tree.superclass.constructor.call(this);
20706 };
20707
20708 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20709     pathSeparator: "/",
20710
20711     proxyNodeEvent : function(){
20712         return this.fireEvent.apply(this, arguments);
20713     },
20714
20715     /**
20716      * Returns the root node for this tree.
20717      * @return {Node}
20718      */
20719     getRootNode : function(){
20720         return this.root;
20721     },
20722
20723     /**
20724      * Sets the root node for this tree.
20725      * @param {Node} node
20726      * @return {Node}
20727      */
20728     setRootNode : function(node){
20729         this.root = node;
20730         node.ownerTree = this;
20731         node.isRoot = true;
20732         this.registerNode(node);
20733         return node;
20734     },
20735
20736     /**
20737      * Gets a node in this tree by its id.
20738      * @param {String} id
20739      * @return {Node}
20740      */
20741     getNodeById : function(id){
20742         return this.nodeHash[id];
20743     },
20744
20745     registerNode : function(node){
20746         this.nodeHash[node.id] = node;
20747     },
20748
20749     unregisterNode : function(node){
20750         delete this.nodeHash[node.id];
20751     },
20752
20753     toString : function(){
20754         return "[Tree"+(this.id?" "+this.id:"")+"]";
20755     }
20756 });
20757
20758 /**
20759  * @class Roo.data.Node
20760  * @extends Roo.util.Observable
20761  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20762  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20763  * @constructor
20764  * @param {Object} attributes The attributes/config for the node
20765  */
20766 Roo.data.Node = function(attributes){
20767     /**
20768      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20769      * @type {Object}
20770      */
20771     this.attributes = attributes || {};
20772     this.leaf = this.attributes.leaf;
20773     /**
20774      * The node id. @type String
20775      */
20776     this.id = this.attributes.id;
20777     if(!this.id){
20778         this.id = Roo.id(null, "ynode-");
20779         this.attributes.id = this.id;
20780     }
20781     /**
20782      * All child nodes of this node. @type Array
20783      */
20784     this.childNodes = [];
20785     if(!this.childNodes.indexOf){ // indexOf is a must
20786         this.childNodes.indexOf = function(o){
20787             for(var i = 0, len = this.length; i < len; i++){
20788                 if(this[i] == o) return i;
20789             }
20790             return -1;
20791         };
20792     }
20793     /**
20794      * The parent node for this node. @type Node
20795      */
20796     this.parentNode = null;
20797     /**
20798      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20799      */
20800     this.firstChild = null;
20801     /**
20802      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20803      */
20804     this.lastChild = null;
20805     /**
20806      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20807      */
20808     this.previousSibling = null;
20809     /**
20810      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20811      */
20812     this.nextSibling = null;
20813
20814     this.addEvents({
20815        /**
20816         * @event append
20817         * Fires when a new child node is appended
20818         * @param {Tree} tree The owner tree
20819         * @param {Node} this This node
20820         * @param {Node} node The newly appended node
20821         * @param {Number} index The index of the newly appended node
20822         */
20823        "append" : true,
20824        /**
20825         * @event remove
20826         * Fires when a child node is removed
20827         * @param {Tree} tree The owner tree
20828         * @param {Node} this This node
20829         * @param {Node} node The removed node
20830         */
20831        "remove" : true,
20832        /**
20833         * @event move
20834         * Fires when this node is moved to a new location in the tree
20835         * @param {Tree} tree The owner tree
20836         * @param {Node} this This node
20837         * @param {Node} oldParent The old parent of this node
20838         * @param {Node} newParent The new parent of this node
20839         * @param {Number} index The index it was moved to
20840         */
20841        "move" : true,
20842        /**
20843         * @event insert
20844         * Fires when a new child node is inserted.
20845         * @param {Tree} tree The owner tree
20846         * @param {Node} this This node
20847         * @param {Node} node The child node inserted
20848         * @param {Node} refNode The child node the node was inserted before
20849         */
20850        "insert" : true,
20851        /**
20852         * @event beforeappend
20853         * Fires before a new child is appended, return false to cancel the append.
20854         * @param {Tree} tree The owner tree
20855         * @param {Node} this This node
20856         * @param {Node} node The child node to be appended
20857         */
20858        "beforeappend" : true,
20859        /**
20860         * @event beforeremove
20861         * Fires before a child is removed, return false to cancel the remove.
20862         * @param {Tree} tree The owner tree
20863         * @param {Node} this This node
20864         * @param {Node} node The child node to be removed
20865         */
20866        "beforeremove" : true,
20867        /**
20868         * @event beforemove
20869         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20870         * @param {Tree} tree The owner tree
20871         * @param {Node} this This node
20872         * @param {Node} oldParent The parent of this node
20873         * @param {Node} newParent The new parent this node is moving to
20874         * @param {Number} index The index it is being moved to
20875         */
20876        "beforemove" : true,
20877        /**
20878         * @event beforeinsert
20879         * Fires before a new child is inserted, return false to cancel the insert.
20880         * @param {Tree} tree The owner tree
20881         * @param {Node} this This node
20882         * @param {Node} node The child node to be inserted
20883         * @param {Node} refNode The child node the node is being inserted before
20884         */
20885        "beforeinsert" : true
20886    });
20887     this.listeners = this.attributes.listeners;
20888     Roo.data.Node.superclass.constructor.call(this);
20889 };
20890
20891 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20892     fireEvent : function(evtName){
20893         // first do standard event for this node
20894         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20895             return false;
20896         }
20897         // then bubble it up to the tree if the event wasn't cancelled
20898         var ot = this.getOwnerTree();
20899         if(ot){
20900             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20901                 return false;
20902             }
20903         }
20904         return true;
20905     },
20906
20907     /**
20908      * Returns true if this node is a leaf
20909      * @return {Boolean}
20910      */
20911     isLeaf : function(){
20912         return this.leaf === true;
20913     },
20914
20915     // private
20916     setFirstChild : function(node){
20917         this.firstChild = node;
20918     },
20919
20920     //private
20921     setLastChild : function(node){
20922         this.lastChild = node;
20923     },
20924
20925
20926     /**
20927      * Returns true if this node is the last child of its parent
20928      * @return {Boolean}
20929      */
20930     isLast : function(){
20931        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20932     },
20933
20934     /**
20935      * Returns true if this node is the first child of its parent
20936      * @return {Boolean}
20937      */
20938     isFirst : function(){
20939        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20940     },
20941
20942     hasChildNodes : function(){
20943         return !this.isLeaf() && this.childNodes.length > 0;
20944     },
20945
20946     /**
20947      * Insert node(s) as the last child node of this node.
20948      * @param {Node/Array} node The node or Array of nodes to append
20949      * @return {Node} The appended node if single append, or null if an array was passed
20950      */
20951     appendChild : function(node){
20952         var multi = false;
20953         if(node instanceof Array){
20954             multi = node;
20955         }else if(arguments.length > 1){
20956             multi = arguments;
20957         }
20958         // if passed an array or multiple args do them one by one
20959         if(multi){
20960             for(var i = 0, len = multi.length; i < len; i++) {
20961                 this.appendChild(multi[i]);
20962             }
20963         }else{
20964             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20965                 return false;
20966             }
20967             var index = this.childNodes.length;
20968             var oldParent = node.parentNode;
20969             // it's a move, make sure we move it cleanly
20970             if(oldParent){
20971                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
20972                     return false;
20973                 }
20974                 oldParent.removeChild(node);
20975             }
20976             index = this.childNodes.length;
20977             if(index == 0){
20978                 this.setFirstChild(node);
20979             }
20980             this.childNodes.push(node);
20981             node.parentNode = this;
20982             var ps = this.childNodes[index-1];
20983             if(ps){
20984                 node.previousSibling = ps;
20985                 ps.nextSibling = node;
20986             }else{
20987                 node.previousSibling = null;
20988             }
20989             node.nextSibling = null;
20990             this.setLastChild(node);
20991             node.setOwnerTree(this.getOwnerTree());
20992             this.fireEvent("append", this.ownerTree, this, node, index);
20993             if(oldParent){
20994                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
20995             }
20996             return node;
20997         }
20998     },
20999
21000     /**
21001      * Removes a child node from this node.
21002      * @param {Node} node The node to remove
21003      * @return {Node} The removed node
21004      */
21005     removeChild : function(node){
21006         var index = this.childNodes.indexOf(node);
21007         if(index == -1){
21008             return false;
21009         }
21010         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21011             return false;
21012         }
21013
21014         // remove it from childNodes collection
21015         this.childNodes.splice(index, 1);
21016
21017         // update siblings
21018         if(node.previousSibling){
21019             node.previousSibling.nextSibling = node.nextSibling;
21020         }
21021         if(node.nextSibling){
21022             node.nextSibling.previousSibling = node.previousSibling;
21023         }
21024
21025         // update child refs
21026         if(this.firstChild == node){
21027             this.setFirstChild(node.nextSibling);
21028         }
21029         if(this.lastChild == node){
21030             this.setLastChild(node.previousSibling);
21031         }
21032
21033         node.setOwnerTree(null);
21034         // clear any references from the node
21035         node.parentNode = null;
21036         node.previousSibling = null;
21037         node.nextSibling = null;
21038         this.fireEvent("remove", this.ownerTree, this, node);
21039         return node;
21040     },
21041
21042     /**
21043      * Inserts the first node before the second node in this nodes childNodes collection.
21044      * @param {Node} node The node to insert
21045      * @param {Node} refNode The node to insert before (if null the node is appended)
21046      * @return {Node} The inserted node
21047      */
21048     insertBefore : function(node, refNode){
21049         if(!refNode){ // like standard Dom, refNode can be null for append
21050             return this.appendChild(node);
21051         }
21052         // nothing to do
21053         if(node == refNode){
21054             return false;
21055         }
21056
21057         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21058             return false;
21059         }
21060         var index = this.childNodes.indexOf(refNode);
21061         var oldParent = node.parentNode;
21062         var refIndex = index;
21063
21064         // when moving internally, indexes will change after remove
21065         if(oldParent == this && this.childNodes.indexOf(node) < index){
21066             refIndex--;
21067         }
21068
21069         // it's a move, make sure we move it cleanly
21070         if(oldParent){
21071             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21072                 return false;
21073             }
21074             oldParent.removeChild(node);
21075         }
21076         if(refIndex == 0){
21077             this.setFirstChild(node);
21078         }
21079         this.childNodes.splice(refIndex, 0, node);
21080         node.parentNode = this;
21081         var ps = this.childNodes[refIndex-1];
21082         if(ps){
21083             node.previousSibling = ps;
21084             ps.nextSibling = node;
21085         }else{
21086             node.previousSibling = null;
21087         }
21088         node.nextSibling = refNode;
21089         refNode.previousSibling = node;
21090         node.setOwnerTree(this.getOwnerTree());
21091         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21092         if(oldParent){
21093             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21094         }
21095         return node;
21096     },
21097
21098     /**
21099      * Returns the child node at the specified index.
21100      * @param {Number} index
21101      * @return {Node}
21102      */
21103     item : function(index){
21104         return this.childNodes[index];
21105     },
21106
21107     /**
21108      * Replaces one child node in this node with another.
21109      * @param {Node} newChild The replacement node
21110      * @param {Node} oldChild The node to replace
21111      * @return {Node} The replaced node
21112      */
21113     replaceChild : function(newChild, oldChild){
21114         this.insertBefore(newChild, oldChild);
21115         this.removeChild(oldChild);
21116         return oldChild;
21117     },
21118
21119     /**
21120      * Returns the index of a child node
21121      * @param {Node} node
21122      * @return {Number} The index of the node or -1 if it was not found
21123      */
21124     indexOf : function(child){
21125         return this.childNodes.indexOf(child);
21126     },
21127
21128     /**
21129      * Returns the tree this node is in.
21130      * @return {Tree}
21131      */
21132     getOwnerTree : function(){
21133         // if it doesn't have one, look for one
21134         if(!this.ownerTree){
21135             var p = this;
21136             while(p){
21137                 if(p.ownerTree){
21138                     this.ownerTree = p.ownerTree;
21139                     break;
21140                 }
21141                 p = p.parentNode;
21142             }
21143         }
21144         return this.ownerTree;
21145     },
21146
21147     /**
21148      * Returns depth of this node (the root node has a depth of 0)
21149      * @return {Number}
21150      */
21151     getDepth : function(){
21152         var depth = 0;
21153         var p = this;
21154         while(p.parentNode){
21155             ++depth;
21156             p = p.parentNode;
21157         }
21158         return depth;
21159     },
21160
21161     // private
21162     setOwnerTree : function(tree){
21163         // if it's move, we need to update everyone
21164         if(tree != this.ownerTree){
21165             if(this.ownerTree){
21166                 this.ownerTree.unregisterNode(this);
21167             }
21168             this.ownerTree = tree;
21169             var cs = this.childNodes;
21170             for(var i = 0, len = cs.length; i < len; i++) {
21171                 cs[i].setOwnerTree(tree);
21172             }
21173             if(tree){
21174                 tree.registerNode(this);
21175             }
21176         }
21177     },
21178
21179     /**
21180      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21181      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21182      * @return {String} The path
21183      */
21184     getPath : function(attr){
21185         attr = attr || "id";
21186         var p = this.parentNode;
21187         var b = [this.attributes[attr]];
21188         while(p){
21189             b.unshift(p.attributes[attr]);
21190             p = p.parentNode;
21191         }
21192         var sep = this.getOwnerTree().pathSeparator;
21193         return sep + b.join(sep);
21194     },
21195
21196     /**
21197      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21198      * function call will be the scope provided or the current node. The arguments to the function
21199      * will be the args provided or the current node. If the function returns false at any point,
21200      * the bubble is stopped.
21201      * @param {Function} fn The function to call
21202      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21203      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21204      */
21205     bubble : function(fn, scope, args){
21206         var p = this;
21207         while(p){
21208             if(fn.call(scope || p, args || p) === false){
21209                 break;
21210             }
21211             p = p.parentNode;
21212         }
21213     },
21214
21215     /**
21216      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21217      * function call will be the scope provided or the current node. The arguments to the function
21218      * will be the args provided or the current node. If the function returns false at any point,
21219      * the cascade is stopped on that branch.
21220      * @param {Function} fn The function to call
21221      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21222      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21223      */
21224     cascade : function(fn, scope, args){
21225         if(fn.call(scope || this, args || this) !== false){
21226             var cs = this.childNodes;
21227             for(var i = 0, len = cs.length; i < len; i++) {
21228                 cs[i].cascade(fn, scope, args);
21229             }
21230         }
21231     },
21232
21233     /**
21234      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21235      * function call will be the scope provided or the current node. The arguments to the function
21236      * will be the args provided or the current node. If the function returns false at any point,
21237      * the iteration stops.
21238      * @param {Function} fn The function to call
21239      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21240      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21241      */
21242     eachChild : function(fn, scope, args){
21243         var cs = this.childNodes;
21244         for(var i = 0, len = cs.length; i < len; i++) {
21245                 if(fn.call(scope || this, args || cs[i]) === false){
21246                     break;
21247                 }
21248         }
21249     },
21250
21251     /**
21252      * Finds the first child that has the attribute with the specified value.
21253      * @param {String} attribute The attribute name
21254      * @param {Mixed} value The value to search for
21255      * @return {Node} The found child or null if none was found
21256      */
21257     findChild : function(attribute, value){
21258         var cs = this.childNodes;
21259         for(var i = 0, len = cs.length; i < len; i++) {
21260                 if(cs[i].attributes[attribute] == value){
21261                     return cs[i];
21262                 }
21263         }
21264         return null;
21265     },
21266
21267     /**
21268      * Finds the first child by a custom function. The child matches if the function passed
21269      * returns true.
21270      * @param {Function} fn
21271      * @param {Object} scope (optional)
21272      * @return {Node} The found child or null if none was found
21273      */
21274     findChildBy : function(fn, scope){
21275         var cs = this.childNodes;
21276         for(var i = 0, len = cs.length; i < len; i++) {
21277                 if(fn.call(scope||cs[i], cs[i]) === true){
21278                     return cs[i];
21279                 }
21280         }
21281         return null;
21282     },
21283
21284     /**
21285      * Sorts this nodes children using the supplied sort function
21286      * @param {Function} fn
21287      * @param {Object} scope (optional)
21288      */
21289     sort : function(fn, scope){
21290         var cs = this.childNodes;
21291         var len = cs.length;
21292         if(len > 0){
21293             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21294             cs.sort(sortFn);
21295             for(var i = 0; i < len; i++){
21296                 var n = cs[i];
21297                 n.previousSibling = cs[i-1];
21298                 n.nextSibling = cs[i+1];
21299                 if(i == 0){
21300                     this.setFirstChild(n);
21301                 }
21302                 if(i == len-1){
21303                     this.setLastChild(n);
21304                 }
21305             }
21306         }
21307     },
21308
21309     /**
21310      * Returns true if this node is an ancestor (at any point) of the passed node.
21311      * @param {Node} node
21312      * @return {Boolean}
21313      */
21314     contains : function(node){
21315         return node.isAncestor(this);
21316     },
21317
21318     /**
21319      * Returns true if the passed node is an ancestor (at any point) of this node.
21320      * @param {Node} node
21321      * @return {Boolean}
21322      */
21323     isAncestor : function(node){
21324         var p = this.parentNode;
21325         while(p){
21326             if(p == node){
21327                 return true;
21328             }
21329             p = p.parentNode;
21330         }
21331         return false;
21332     },
21333
21334     toString : function(){
21335         return "[Node"+(this.id?" "+this.id:"")+"]";
21336     }
21337 });/*
21338  * Based on:
21339  * Ext JS Library 1.1.1
21340  * Copyright(c) 2006-2007, Ext JS, LLC.
21341  *
21342  * Originally Released Under LGPL - original licence link has changed is not relivant.
21343  *
21344  * Fork - LGPL
21345  * <script type="text/javascript">
21346  */
21347  
21348
21349 /**
21350  * @class Roo.ComponentMgr
21351  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21352  * @singleton
21353  */
21354 Roo.ComponentMgr = function(){
21355     var all = new Roo.util.MixedCollection();
21356
21357     return {
21358         /**
21359          * Registers a component.
21360          * @param {Roo.Component} c The component
21361          */
21362         register : function(c){
21363             all.add(c);
21364         },
21365
21366         /**
21367          * Unregisters a component.
21368          * @param {Roo.Component} c The component
21369          */
21370         unregister : function(c){
21371             all.remove(c);
21372         },
21373
21374         /**
21375          * Returns a component by id
21376          * @param {String} id The component id
21377          */
21378         get : function(id){
21379             return all.get(id);
21380         },
21381
21382         /**
21383          * Registers a function that will be called when a specified component is added to ComponentMgr
21384          * @param {String} id The component id
21385          * @param {Funtction} fn The callback function
21386          * @param {Object} scope The scope of the callback
21387          */
21388         onAvailable : function(id, fn, scope){
21389             all.on("add", function(index, o){
21390                 if(o.id == id){
21391                     fn.call(scope || o, o);
21392                     all.un("add", fn, scope);
21393                 }
21394             });
21395         }
21396     };
21397 }();/*
21398  * Based on:
21399  * Ext JS Library 1.1.1
21400  * Copyright(c) 2006-2007, Ext JS, LLC.
21401  *
21402  * Originally Released Under LGPL - original licence link has changed is not relivant.
21403  *
21404  * Fork - LGPL
21405  * <script type="text/javascript">
21406  */
21407  
21408 /**
21409  * @class Roo.Component
21410  * @extends Roo.util.Observable
21411  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21412  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21413  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21414  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21415  * All visual components (widgets) that require rendering into a layout should subclass Component.
21416  * @constructor
21417  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21418  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21419  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21420  */
21421 Roo.Component = function(config){
21422     config = config || {};
21423     if(config.tagName || config.dom || typeof config == "string"){ // element object
21424         config = {el: config, id: config.id || config};
21425     }
21426     this.initialConfig = config;
21427
21428     Roo.apply(this, config);
21429     this.addEvents({
21430         /**
21431          * @event disable
21432          * Fires after the component is disabled.
21433              * @param {Roo.Component} this
21434              */
21435         disable : true,
21436         /**
21437          * @event enable
21438          * Fires after the component is enabled.
21439              * @param {Roo.Component} this
21440              */
21441         enable : true,
21442         /**
21443          * @event beforeshow
21444          * Fires before the component is shown.  Return false to stop the show.
21445              * @param {Roo.Component} this
21446              */
21447         beforeshow : true,
21448         /**
21449          * @event show
21450          * Fires after the component is shown.
21451              * @param {Roo.Component} this
21452              */
21453         show : true,
21454         /**
21455          * @event beforehide
21456          * Fires before the component is hidden. Return false to stop the hide.
21457              * @param {Roo.Component} this
21458              */
21459         beforehide : true,
21460         /**
21461          * @event hide
21462          * Fires after the component is hidden.
21463              * @param {Roo.Component} this
21464              */
21465         hide : true,
21466         /**
21467          * @event beforerender
21468          * Fires before the component is rendered. Return false to stop the render.
21469              * @param {Roo.Component} this
21470              */
21471         beforerender : true,
21472         /**
21473          * @event render
21474          * Fires after the component is rendered.
21475              * @param {Roo.Component} this
21476              */
21477         render : true,
21478         /**
21479          * @event beforedestroy
21480          * Fires before the component is destroyed. Return false to stop the destroy.
21481              * @param {Roo.Component} this
21482              */
21483         beforedestroy : true,
21484         /**
21485          * @event destroy
21486          * Fires after the component is destroyed.
21487              * @param {Roo.Component} this
21488              */
21489         destroy : true
21490     });
21491     if(!this.id){
21492         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21493     }
21494     Roo.ComponentMgr.register(this);
21495     Roo.Component.superclass.constructor.call(this);
21496     this.initComponent();
21497     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21498         this.render(this.renderTo);
21499         delete this.renderTo;
21500     }
21501 };
21502
21503 // private
21504 Roo.Component.AUTO_ID = 1000;
21505
21506 Roo.extend(Roo.Component, Roo.util.Observable, {
21507     /**
21508      * @property {Boolean} hidden
21509      * true if this component is hidden. Read-only.
21510      */
21511     hidden : false,
21512     /**
21513      * true if this component is disabled. Read-only.
21514      */
21515     disabled : false,
21516     /**
21517      * true if this component has been rendered. Read-only.
21518      */
21519     rendered : false,
21520     
21521     /** @cfg {String} disableClass
21522      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21523      */
21524     disabledClass : "x-item-disabled",
21525         /** @cfg {Boolean} allowDomMove
21526          * Whether the component can move the Dom node when rendering (defaults to true).
21527          */
21528     allowDomMove : true,
21529     /** @cfg {String} hideMode
21530      * How this component should hidden. Supported values are
21531      * "visibility" (css visibility), "offsets" (negative offset position) and
21532      * "display" (css display) - defaults to "display".
21533      */
21534     hideMode: 'display',
21535
21536     // private
21537     ctype : "Roo.Component",
21538
21539     /** @cfg {String} actionMode 
21540      * which property holds the element that used for  hide() / show() / disable() / enable()
21541      * default is 'el' 
21542      */
21543     actionMode : "el",
21544
21545     // private
21546     getActionEl : function(){
21547         return this[this.actionMode];
21548     },
21549
21550     initComponent : Roo.emptyFn,
21551     /**
21552      * If this is a lazy rendering component, render it to its container element.
21553      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21554      */
21555     render : function(container, position){
21556         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21557             if(!container && this.el){
21558                 this.el = Roo.get(this.el);
21559                 container = this.el.dom.parentNode;
21560                 this.allowDomMove = false;
21561             }
21562             this.container = Roo.get(container);
21563             this.rendered = true;
21564             if(position !== undefined){
21565                 if(typeof position == 'number'){
21566                     position = this.container.dom.childNodes[position];
21567                 }else{
21568                     position = Roo.getDom(position);
21569                 }
21570             }
21571             this.onRender(this.container, position || null);
21572             if(this.cls){
21573                 this.el.addClass(this.cls);
21574                 delete this.cls;
21575             }
21576             if(this.style){
21577                 this.el.applyStyles(this.style);
21578                 delete this.style;
21579             }
21580             this.fireEvent("render", this);
21581             this.afterRender(this.container);
21582             if(this.hidden){
21583                 this.hide();
21584             }
21585             if(this.disabled){
21586                 this.disable();
21587             }
21588         }
21589         return this;
21590     },
21591
21592     // private
21593     // default function is not really useful
21594     onRender : function(ct, position){
21595         if(this.el){
21596             this.el = Roo.get(this.el);
21597             if(this.allowDomMove !== false){
21598                 ct.dom.insertBefore(this.el.dom, position);
21599             }
21600         }
21601     },
21602
21603     // private
21604     getAutoCreate : function(){
21605         var cfg = typeof this.autoCreate == "object" ?
21606                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21607         if(this.id && !cfg.id){
21608             cfg.id = this.id;
21609         }
21610         return cfg;
21611     },
21612
21613     // private
21614     afterRender : Roo.emptyFn,
21615
21616     /**
21617      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21618      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21619      */
21620     destroy : function(){
21621         if(this.fireEvent("beforedestroy", this) !== false){
21622             this.purgeListeners();
21623             this.beforeDestroy();
21624             if(this.rendered){
21625                 this.el.removeAllListeners();
21626                 this.el.remove();
21627                 if(this.actionMode == "container"){
21628                     this.container.remove();
21629                 }
21630             }
21631             this.onDestroy();
21632             Roo.ComponentMgr.unregister(this);
21633             this.fireEvent("destroy", this);
21634         }
21635     },
21636
21637         // private
21638     beforeDestroy : function(){
21639
21640     },
21641
21642         // private
21643         onDestroy : function(){
21644
21645     },
21646
21647     /**
21648      * Returns the underlying {@link Roo.Element}.
21649      * @return {Roo.Element} The element
21650      */
21651     getEl : function(){
21652         return this.el;
21653     },
21654
21655     /**
21656      * Returns the id of this component.
21657      * @return {String}
21658      */
21659     getId : function(){
21660         return this.id;
21661     },
21662
21663     /**
21664      * Try to focus this component.
21665      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21666      * @return {Roo.Component} this
21667      */
21668     focus : function(selectText){
21669         if(this.rendered){
21670             this.el.focus();
21671             if(selectText === true){
21672                 this.el.dom.select();
21673             }
21674         }
21675         return this;
21676     },
21677
21678     // private
21679     blur : function(){
21680         if(this.rendered){
21681             this.el.blur();
21682         }
21683         return this;
21684     },
21685
21686     /**
21687      * Disable this component.
21688      * @return {Roo.Component} this
21689      */
21690     disable : function(){
21691         if(this.rendered){
21692             this.onDisable();
21693         }
21694         this.disabled = true;
21695         this.fireEvent("disable", this);
21696         return this;
21697     },
21698
21699         // private
21700     onDisable : function(){
21701         this.getActionEl().addClass(this.disabledClass);
21702         this.el.dom.disabled = true;
21703     },
21704
21705     /**
21706      * Enable this component.
21707      * @return {Roo.Component} this
21708      */
21709     enable : function(){
21710         if(this.rendered){
21711             this.onEnable();
21712         }
21713         this.disabled = false;
21714         this.fireEvent("enable", this);
21715         return this;
21716     },
21717
21718         // private
21719     onEnable : function(){
21720         this.getActionEl().removeClass(this.disabledClass);
21721         this.el.dom.disabled = false;
21722     },
21723
21724     /**
21725      * Convenience function for setting disabled/enabled by boolean.
21726      * @param {Boolean} disabled
21727      */
21728     setDisabled : function(disabled){
21729         this[disabled ? "disable" : "enable"]();
21730     },
21731
21732     /**
21733      * Show this component.
21734      * @return {Roo.Component} this
21735      */
21736     show: function(){
21737         if(this.fireEvent("beforeshow", this) !== false){
21738             this.hidden = false;
21739             if(this.rendered){
21740                 this.onShow();
21741             }
21742             this.fireEvent("show", this);
21743         }
21744         return this;
21745     },
21746
21747     // private
21748     onShow : function(){
21749         var ae = this.getActionEl();
21750         if(this.hideMode == 'visibility'){
21751             ae.dom.style.visibility = "visible";
21752         }else if(this.hideMode == 'offsets'){
21753             ae.removeClass('x-hidden');
21754         }else{
21755             ae.dom.style.display = "";
21756         }
21757     },
21758
21759     /**
21760      * Hide this component.
21761      * @return {Roo.Component} this
21762      */
21763     hide: function(){
21764         if(this.fireEvent("beforehide", this) !== false){
21765             this.hidden = true;
21766             if(this.rendered){
21767                 this.onHide();
21768             }
21769             this.fireEvent("hide", this);
21770         }
21771         return this;
21772     },
21773
21774     // private
21775     onHide : function(){
21776         var ae = this.getActionEl();
21777         if(this.hideMode == 'visibility'){
21778             ae.dom.style.visibility = "hidden";
21779         }else if(this.hideMode == 'offsets'){
21780             ae.addClass('x-hidden');
21781         }else{
21782             ae.dom.style.display = "none";
21783         }
21784     },
21785
21786     /**
21787      * Convenience function to hide or show this component by boolean.
21788      * @param {Boolean} visible True to show, false to hide
21789      * @return {Roo.Component} this
21790      */
21791     setVisible: function(visible){
21792         if(visible) {
21793             this.show();
21794         }else{
21795             this.hide();
21796         }
21797         return this;
21798     },
21799
21800     /**
21801      * Returns true if this component is visible.
21802      */
21803     isVisible : function(){
21804         return this.getActionEl().isVisible();
21805     },
21806
21807     cloneConfig : function(overrides){
21808         overrides = overrides || {};
21809         var id = overrides.id || Roo.id();
21810         var cfg = Roo.applyIf(overrides, this.initialConfig);
21811         cfg.id = id; // prevent dup id
21812         return new this.constructor(cfg);
21813     }
21814 });/*
21815  * Based on:
21816  * Ext JS Library 1.1.1
21817  * Copyright(c) 2006-2007, Ext JS, LLC.
21818  *
21819  * Originally Released Under LGPL - original licence link has changed is not relivant.
21820  *
21821  * Fork - LGPL
21822  * <script type="text/javascript">
21823  */
21824  (function(){ 
21825 /**
21826  * @class Roo.Layer
21827  * @extends Roo.Element
21828  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21829  * automatic maintaining of shadow/shim positions.
21830  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21831  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21832  * you can pass a string with a CSS class name. False turns off the shadow.
21833  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21834  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21835  * @cfg {String} cls CSS class to add to the element
21836  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21837  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21838  * @constructor
21839  * @param {Object} config An object with config options.
21840  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21841  */
21842
21843 Roo.Layer = function(config, existingEl){
21844     config = config || {};
21845     var dh = Roo.DomHelper;
21846     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21847     if(existingEl){
21848         this.dom = Roo.getDom(existingEl);
21849     }
21850     if(!this.dom){
21851         var o = config.dh || {tag: "div", cls: "x-layer"};
21852         this.dom = dh.append(pel, o);
21853     }
21854     if(config.cls){
21855         this.addClass(config.cls);
21856     }
21857     this.constrain = config.constrain !== false;
21858     this.visibilityMode = Roo.Element.VISIBILITY;
21859     if(config.id){
21860         this.id = this.dom.id = config.id;
21861     }else{
21862         this.id = Roo.id(this.dom);
21863     }
21864     this.zindex = config.zindex || this.getZIndex();
21865     this.position("absolute", this.zindex);
21866     if(config.shadow){
21867         this.shadowOffset = config.shadowOffset || 4;
21868         this.shadow = new Roo.Shadow({
21869             offset : this.shadowOffset,
21870             mode : config.shadow
21871         });
21872     }else{
21873         this.shadowOffset = 0;
21874     }
21875     this.useShim = config.shim !== false && Roo.useShims;
21876     this.useDisplay = config.useDisplay;
21877     this.hide();
21878 };
21879
21880 var supr = Roo.Element.prototype;
21881
21882 // shims are shared among layer to keep from having 100 iframes
21883 var shims = [];
21884
21885 Roo.extend(Roo.Layer, Roo.Element, {
21886
21887     getZIndex : function(){
21888         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21889     },
21890
21891     getShim : function(){
21892         if(!this.useShim){
21893             return null;
21894         }
21895         if(this.shim){
21896             return this.shim;
21897         }
21898         var shim = shims.shift();
21899         if(!shim){
21900             shim = this.createShim();
21901             shim.enableDisplayMode('block');
21902             shim.dom.style.display = 'none';
21903             shim.dom.style.visibility = 'visible';
21904         }
21905         var pn = this.dom.parentNode;
21906         if(shim.dom.parentNode != pn){
21907             pn.insertBefore(shim.dom, this.dom);
21908         }
21909         shim.setStyle('z-index', this.getZIndex()-2);
21910         this.shim = shim;
21911         return shim;
21912     },
21913
21914     hideShim : function(){
21915         if(this.shim){
21916             this.shim.setDisplayed(false);
21917             shims.push(this.shim);
21918             delete this.shim;
21919         }
21920     },
21921
21922     disableShadow : function(){
21923         if(this.shadow){
21924             this.shadowDisabled = true;
21925             this.shadow.hide();
21926             this.lastShadowOffset = this.shadowOffset;
21927             this.shadowOffset = 0;
21928         }
21929     },
21930
21931     enableShadow : function(show){
21932         if(this.shadow){
21933             this.shadowDisabled = false;
21934             this.shadowOffset = this.lastShadowOffset;
21935             delete this.lastShadowOffset;
21936             if(show){
21937                 this.sync(true);
21938             }
21939         }
21940     },
21941
21942     // private
21943     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21944     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21945     sync : function(doShow){
21946         var sw = this.shadow;
21947         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21948             var sh = this.getShim();
21949
21950             var w = this.getWidth(),
21951                 h = this.getHeight();
21952
21953             var l = this.getLeft(true),
21954                 t = this.getTop(true);
21955
21956             if(sw && !this.shadowDisabled){
21957                 if(doShow && !sw.isVisible()){
21958                     sw.show(this);
21959                 }else{
21960                     sw.realign(l, t, w, h);
21961                 }
21962                 if(sh){
21963                     if(doShow){
21964                        sh.show();
21965                     }
21966                     // fit the shim behind the shadow, so it is shimmed too
21967                     var a = sw.adjusts, s = sh.dom.style;
21968                     s.left = (Math.min(l, l+a.l))+"px";
21969                     s.top = (Math.min(t, t+a.t))+"px";
21970                     s.width = (w+a.w)+"px";
21971                     s.height = (h+a.h)+"px";
21972                 }
21973             }else if(sh){
21974                 if(doShow){
21975                    sh.show();
21976                 }
21977                 sh.setSize(w, h);
21978                 sh.setLeftTop(l, t);
21979             }
21980             
21981         }
21982     },
21983
21984     // private
21985     destroy : function(){
21986         this.hideShim();
21987         if(this.shadow){
21988             this.shadow.hide();
21989         }
21990         this.removeAllListeners();
21991         var pn = this.dom.parentNode;
21992         if(pn){
21993             pn.removeChild(this.dom);
21994         }
21995         Roo.Element.uncache(this.id);
21996     },
21997
21998     remove : function(){
21999         this.destroy();
22000     },
22001
22002     // private
22003     beginUpdate : function(){
22004         this.updating = true;
22005     },
22006
22007     // private
22008     endUpdate : function(){
22009         this.updating = false;
22010         this.sync(true);
22011     },
22012
22013     // private
22014     hideUnders : function(negOffset){
22015         if(this.shadow){
22016             this.shadow.hide();
22017         }
22018         this.hideShim();
22019     },
22020
22021     // private
22022     constrainXY : function(){
22023         if(this.constrain){
22024             var vw = Roo.lib.Dom.getViewWidth(),
22025                 vh = Roo.lib.Dom.getViewHeight();
22026             var s = Roo.get(document).getScroll();
22027
22028             var xy = this.getXY();
22029             var x = xy[0], y = xy[1];   
22030             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22031             // only move it if it needs it
22032             var moved = false;
22033             // first validate right/bottom
22034             if((x + w) > vw+s.left){
22035                 x = vw - w - this.shadowOffset;
22036                 moved = true;
22037             }
22038             if((y + h) > vh+s.top){
22039                 y = vh - h - this.shadowOffset;
22040                 moved = true;
22041             }
22042             // then make sure top/left isn't negative
22043             if(x < s.left){
22044                 x = s.left;
22045                 moved = true;
22046             }
22047             if(y < s.top){
22048                 y = s.top;
22049                 moved = true;
22050             }
22051             if(moved){
22052                 if(this.avoidY){
22053                     var ay = this.avoidY;
22054                     if(y <= ay && (y+h) >= ay){
22055                         y = ay-h-5;   
22056                     }
22057                 }
22058                 xy = [x, y];
22059                 this.storeXY(xy);
22060                 supr.setXY.call(this, xy);
22061                 this.sync();
22062             }
22063         }
22064     },
22065
22066     isVisible : function(){
22067         return this.visible;    
22068     },
22069
22070     // private
22071     showAction : function(){
22072         this.visible = true; // track visibility to prevent getStyle calls
22073         if(this.useDisplay === true){
22074             this.setDisplayed("");
22075         }else if(this.lastXY){
22076             supr.setXY.call(this, this.lastXY);
22077         }else if(this.lastLT){
22078             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22079         }
22080     },
22081
22082     // private
22083     hideAction : function(){
22084         this.visible = false;
22085         if(this.useDisplay === true){
22086             this.setDisplayed(false);
22087         }else{
22088             this.setLeftTop(-10000,-10000);
22089         }
22090     },
22091
22092     // overridden Element method
22093     setVisible : function(v, a, d, c, e){
22094         if(v){
22095             this.showAction();
22096         }
22097         if(a && v){
22098             var cb = function(){
22099                 this.sync(true);
22100                 if(c){
22101                     c();
22102                 }
22103             }.createDelegate(this);
22104             supr.setVisible.call(this, true, true, d, cb, e);
22105         }else{
22106             if(!v){
22107                 this.hideUnders(true);
22108             }
22109             var cb = c;
22110             if(a){
22111                 cb = function(){
22112                     this.hideAction();
22113                     if(c){
22114                         c();
22115                     }
22116                 }.createDelegate(this);
22117             }
22118             supr.setVisible.call(this, v, a, d, cb, e);
22119             if(v){
22120                 this.sync(true);
22121             }else if(!a){
22122                 this.hideAction();
22123             }
22124         }
22125     },
22126
22127     storeXY : function(xy){
22128         delete this.lastLT;
22129         this.lastXY = xy;
22130     },
22131
22132     storeLeftTop : function(left, top){
22133         delete this.lastXY;
22134         this.lastLT = [left, top];
22135     },
22136
22137     // private
22138     beforeFx : function(){
22139         this.beforeAction();
22140         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22141     },
22142
22143     // private
22144     afterFx : function(){
22145         Roo.Layer.superclass.afterFx.apply(this, arguments);
22146         this.sync(this.isVisible());
22147     },
22148
22149     // private
22150     beforeAction : function(){
22151         if(!this.updating && this.shadow){
22152             this.shadow.hide();
22153         }
22154     },
22155
22156     // overridden Element method
22157     setLeft : function(left){
22158         this.storeLeftTop(left, this.getTop(true));
22159         supr.setLeft.apply(this, arguments);
22160         this.sync();
22161     },
22162
22163     setTop : function(top){
22164         this.storeLeftTop(this.getLeft(true), top);
22165         supr.setTop.apply(this, arguments);
22166         this.sync();
22167     },
22168
22169     setLeftTop : function(left, top){
22170         this.storeLeftTop(left, top);
22171         supr.setLeftTop.apply(this, arguments);
22172         this.sync();
22173     },
22174
22175     setXY : function(xy, a, d, c, e){
22176         this.fixDisplay();
22177         this.beforeAction();
22178         this.storeXY(xy);
22179         var cb = this.createCB(c);
22180         supr.setXY.call(this, xy, a, d, cb, e);
22181         if(!a){
22182             cb();
22183         }
22184     },
22185
22186     // private
22187     createCB : function(c){
22188         var el = this;
22189         return function(){
22190             el.constrainXY();
22191             el.sync(true);
22192             if(c){
22193                 c();
22194             }
22195         };
22196     },
22197
22198     // overridden Element method
22199     setX : function(x, a, d, c, e){
22200         this.setXY([x, this.getY()], a, d, c, e);
22201     },
22202
22203     // overridden Element method
22204     setY : function(y, a, d, c, e){
22205         this.setXY([this.getX(), y], a, d, c, e);
22206     },
22207
22208     // overridden Element method
22209     setSize : function(w, h, a, d, c, e){
22210         this.beforeAction();
22211         var cb = this.createCB(c);
22212         supr.setSize.call(this, w, h, a, d, cb, e);
22213         if(!a){
22214             cb();
22215         }
22216     },
22217
22218     // overridden Element method
22219     setWidth : function(w, a, d, c, e){
22220         this.beforeAction();
22221         var cb = this.createCB(c);
22222         supr.setWidth.call(this, w, a, d, cb, e);
22223         if(!a){
22224             cb();
22225         }
22226     },
22227
22228     // overridden Element method
22229     setHeight : function(h, a, d, c, e){
22230         this.beforeAction();
22231         var cb = this.createCB(c);
22232         supr.setHeight.call(this, h, a, d, cb, e);
22233         if(!a){
22234             cb();
22235         }
22236     },
22237
22238     // overridden Element method
22239     setBounds : function(x, y, w, h, a, d, c, e){
22240         this.beforeAction();
22241         var cb = this.createCB(c);
22242         if(!a){
22243             this.storeXY([x, y]);
22244             supr.setXY.call(this, [x, y]);
22245             supr.setSize.call(this, w, h, a, d, cb, e);
22246             cb();
22247         }else{
22248             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22249         }
22250         return this;
22251     },
22252     
22253     /**
22254      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22255      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22256      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22257      * @param {Number} zindex The new z-index to set
22258      * @return {this} The Layer
22259      */
22260     setZIndex : function(zindex){
22261         this.zindex = zindex;
22262         this.setStyle("z-index", zindex + 2);
22263         if(this.shadow){
22264             this.shadow.setZIndex(zindex + 1);
22265         }
22266         if(this.shim){
22267             this.shim.setStyle("z-index", zindex);
22268         }
22269     }
22270 });
22271 })();/*
22272  * Based on:
22273  * Ext JS Library 1.1.1
22274  * Copyright(c) 2006-2007, Ext JS, LLC.
22275  *
22276  * Originally Released Under LGPL - original licence link has changed is not relivant.
22277  *
22278  * Fork - LGPL
22279  * <script type="text/javascript">
22280  */
22281
22282
22283 /**
22284  * @class Roo.Shadow
22285  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22286  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22287  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22288  * @constructor
22289  * Create a new Shadow
22290  * @param {Object} config The config object
22291  */
22292 Roo.Shadow = function(config){
22293     Roo.apply(this, config);
22294     if(typeof this.mode != "string"){
22295         this.mode = this.defaultMode;
22296     }
22297     var o = this.offset, a = {h: 0};
22298     var rad = Math.floor(this.offset/2);
22299     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22300         case "drop":
22301             a.w = 0;
22302             a.l = a.t = o;
22303             a.t -= 1;
22304             if(Roo.isIE){
22305                 a.l -= this.offset + rad;
22306                 a.t -= this.offset + rad;
22307                 a.w -= rad;
22308                 a.h -= rad;
22309                 a.t += 1;
22310             }
22311         break;
22312         case "sides":
22313             a.w = (o*2);
22314             a.l = -o;
22315             a.t = o-1;
22316             if(Roo.isIE){
22317                 a.l -= (this.offset - rad);
22318                 a.t -= this.offset + rad;
22319                 a.l += 1;
22320                 a.w -= (this.offset - rad)*2;
22321                 a.w -= rad + 1;
22322                 a.h -= 1;
22323             }
22324         break;
22325         case "frame":
22326             a.w = a.h = (o*2);
22327             a.l = a.t = -o;
22328             a.t += 1;
22329             a.h -= 2;
22330             if(Roo.isIE){
22331                 a.l -= (this.offset - rad);
22332                 a.t -= (this.offset - rad);
22333                 a.l += 1;
22334                 a.w -= (this.offset + rad + 1);
22335                 a.h -= (this.offset + rad);
22336                 a.h += 1;
22337             }
22338         break;
22339     };
22340
22341     this.adjusts = a;
22342 };
22343
22344 Roo.Shadow.prototype = {
22345     /**
22346      * @cfg {String} mode
22347      * The shadow display mode.  Supports the following options:<br />
22348      * sides: Shadow displays on both sides and bottom only<br />
22349      * frame: Shadow displays equally on all four sides<br />
22350      * drop: Traditional bottom-right drop shadow (default)
22351      */
22352     /**
22353      * @cfg {String} offset
22354      * The number of pixels to offset the shadow from the element (defaults to 4)
22355      */
22356     offset: 4,
22357
22358     // private
22359     defaultMode: "drop",
22360
22361     /**
22362      * Displays the shadow under the target element
22363      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22364      */
22365     show : function(target){
22366         target = Roo.get(target);
22367         if(!this.el){
22368             this.el = Roo.Shadow.Pool.pull();
22369             if(this.el.dom.nextSibling != target.dom){
22370                 this.el.insertBefore(target);
22371             }
22372         }
22373         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22374         if(Roo.isIE){
22375             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22376         }
22377         this.realign(
22378             target.getLeft(true),
22379             target.getTop(true),
22380             target.getWidth(),
22381             target.getHeight()
22382         );
22383         this.el.dom.style.display = "block";
22384     },
22385
22386     /**
22387      * Returns true if the shadow is visible, else false
22388      */
22389     isVisible : function(){
22390         return this.el ? true : false;  
22391     },
22392
22393     /**
22394      * Direct alignment when values are already available. Show must be called at least once before
22395      * calling this method to ensure it is initialized.
22396      * @param {Number} left The target element left position
22397      * @param {Number} top The target element top position
22398      * @param {Number} width The target element width
22399      * @param {Number} height The target element height
22400      */
22401     realign : function(l, t, w, h){
22402         if(!this.el){
22403             return;
22404         }
22405         var a = this.adjusts, d = this.el.dom, s = d.style;
22406         var iea = 0;
22407         s.left = (l+a.l)+"px";
22408         s.top = (t+a.t)+"px";
22409         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22410         if(s.width != sws || s.height != shs){
22411             s.width = sws;
22412             s.height = shs;
22413             if(!Roo.isIE){
22414                 var cn = d.childNodes;
22415                 var sww = Math.max(0, (sw-12))+"px";
22416                 cn[0].childNodes[1].style.width = sww;
22417                 cn[1].childNodes[1].style.width = sww;
22418                 cn[2].childNodes[1].style.width = sww;
22419                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22420             }
22421         }
22422     },
22423
22424     /**
22425      * Hides this shadow
22426      */
22427     hide : function(){
22428         if(this.el){
22429             this.el.dom.style.display = "none";
22430             Roo.Shadow.Pool.push(this.el);
22431             delete this.el;
22432         }
22433     },
22434
22435     /**
22436      * Adjust the z-index of this shadow
22437      * @param {Number} zindex The new z-index
22438      */
22439     setZIndex : function(z){
22440         this.zIndex = z;
22441         if(this.el){
22442             this.el.setStyle("z-index", z);
22443         }
22444     }
22445 };
22446
22447 // Private utility class that manages the internal Shadow cache
22448 Roo.Shadow.Pool = function(){
22449     var p = [];
22450     var markup = Roo.isIE ?
22451                  '<div class="x-ie-shadow"></div>' :
22452                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22453     return {
22454         pull : function(){
22455             var sh = p.shift();
22456             if(!sh){
22457                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22458                 sh.autoBoxAdjust = false;
22459             }
22460             return sh;
22461         },
22462
22463         push : function(sh){
22464             p.push(sh);
22465         }
22466     };
22467 }();/*
22468  * Based on:
22469  * Ext JS Library 1.1.1
22470  * Copyright(c) 2006-2007, Ext JS, LLC.
22471  *
22472  * Originally Released Under LGPL - original licence link has changed is not relivant.
22473  *
22474  * Fork - LGPL
22475  * <script type="text/javascript">
22476  */
22477
22478 /**
22479  * @class Roo.BoxComponent
22480  * @extends Roo.Component
22481  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22482  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22483  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22484  * layout containers.
22485  * @constructor
22486  * @param {Roo.Element/String/Object} config The configuration options.
22487  */
22488 Roo.BoxComponent = function(config){
22489     Roo.Component.call(this, config);
22490     this.addEvents({
22491         /**
22492          * @event resize
22493          * Fires after the component is resized.
22494              * @param {Roo.Component} this
22495              * @param {Number} adjWidth The box-adjusted width that was set
22496              * @param {Number} adjHeight The box-adjusted height that was set
22497              * @param {Number} rawWidth The width that was originally specified
22498              * @param {Number} rawHeight The height that was originally specified
22499              */
22500         resize : true,
22501         /**
22502          * @event move
22503          * Fires after the component is moved.
22504              * @param {Roo.Component} this
22505              * @param {Number} x The new x position
22506              * @param {Number} y The new y position
22507              */
22508         move : true
22509     });
22510 };
22511
22512 Roo.extend(Roo.BoxComponent, Roo.Component, {
22513     // private, set in afterRender to signify that the component has been rendered
22514     boxReady : false,
22515     // private, used to defer height settings to subclasses
22516     deferHeight: false,
22517     /** @cfg {Number} width
22518      * width (optional) size of component
22519      */
22520      /** @cfg {Number} height
22521      * height (optional) size of component
22522      */
22523      
22524     /**
22525      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22526      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22527      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22528      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22529      * @return {Roo.BoxComponent} this
22530      */
22531     setSize : function(w, h){
22532         // support for standard size objects
22533         if(typeof w == 'object'){
22534             h = w.height;
22535             w = w.width;
22536         }
22537         // not rendered
22538         if(!this.boxReady){
22539             this.width = w;
22540             this.height = h;
22541             return this;
22542         }
22543
22544         // prevent recalcs when not needed
22545         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22546             return this;
22547         }
22548         this.lastSize = {width: w, height: h};
22549
22550         var adj = this.adjustSize(w, h);
22551         var aw = adj.width, ah = adj.height;
22552         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22553             var rz = this.getResizeEl();
22554             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22555                 rz.setSize(aw, ah);
22556             }else if(!this.deferHeight && ah !== undefined){
22557                 rz.setHeight(ah);
22558             }else if(aw !== undefined){
22559                 rz.setWidth(aw);
22560             }
22561             this.onResize(aw, ah, w, h);
22562             this.fireEvent('resize', this, aw, ah, w, h);
22563         }
22564         return this;
22565     },
22566
22567     /**
22568      * Gets the current size of the component's underlying element.
22569      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22570      */
22571     getSize : function(){
22572         return this.el.getSize();
22573     },
22574
22575     /**
22576      * Gets the current XY position of the component's underlying element.
22577      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22578      * @return {Array} The XY position of the element (e.g., [100, 200])
22579      */
22580     getPosition : function(local){
22581         if(local === true){
22582             return [this.el.getLeft(true), this.el.getTop(true)];
22583         }
22584         return this.xy || this.el.getXY();
22585     },
22586
22587     /**
22588      * Gets the current box measurements of the component's underlying element.
22589      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22590      * @returns {Object} box An object in the format {x, y, width, height}
22591      */
22592     getBox : function(local){
22593         var s = this.el.getSize();
22594         if(local){
22595             s.x = this.el.getLeft(true);
22596             s.y = this.el.getTop(true);
22597         }else{
22598             var xy = this.xy || this.el.getXY();
22599             s.x = xy[0];
22600             s.y = xy[1];
22601         }
22602         return s;
22603     },
22604
22605     /**
22606      * Sets the current box measurements of the component's underlying element.
22607      * @param {Object} box An object in the format {x, y, width, height}
22608      * @returns {Roo.BoxComponent} this
22609      */
22610     updateBox : function(box){
22611         this.setSize(box.width, box.height);
22612         this.setPagePosition(box.x, box.y);
22613         return this;
22614     },
22615
22616     // protected
22617     getResizeEl : function(){
22618         return this.resizeEl || this.el;
22619     },
22620
22621     // protected
22622     getPositionEl : function(){
22623         return this.positionEl || this.el;
22624     },
22625
22626     /**
22627      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22628      * This method fires the move event.
22629      * @param {Number} left The new left
22630      * @param {Number} top The new top
22631      * @returns {Roo.BoxComponent} this
22632      */
22633     setPosition : function(x, y){
22634         this.x = x;
22635         this.y = y;
22636         if(!this.boxReady){
22637             return this;
22638         }
22639         var adj = this.adjustPosition(x, y);
22640         var ax = adj.x, ay = adj.y;
22641
22642         var el = this.getPositionEl();
22643         if(ax !== undefined || ay !== undefined){
22644             if(ax !== undefined && ay !== undefined){
22645                 el.setLeftTop(ax, ay);
22646             }else if(ax !== undefined){
22647                 el.setLeft(ax);
22648             }else if(ay !== undefined){
22649                 el.setTop(ay);
22650             }
22651             this.onPosition(ax, ay);
22652             this.fireEvent('move', this, ax, ay);
22653         }
22654         return this;
22655     },
22656
22657     /**
22658      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22659      * This method fires the move event.
22660      * @param {Number} x The new x position
22661      * @param {Number} y The new y position
22662      * @returns {Roo.BoxComponent} this
22663      */
22664     setPagePosition : function(x, y){
22665         this.pageX = x;
22666         this.pageY = y;
22667         if(!this.boxReady){
22668             return;
22669         }
22670         if(x === undefined || y === undefined){ // cannot translate undefined points
22671             return;
22672         }
22673         var p = this.el.translatePoints(x, y);
22674         this.setPosition(p.left, p.top);
22675         return this;
22676     },
22677
22678     // private
22679     onRender : function(ct, position){
22680         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22681         if(this.resizeEl){
22682             this.resizeEl = Roo.get(this.resizeEl);
22683         }
22684         if(this.positionEl){
22685             this.positionEl = Roo.get(this.positionEl);
22686         }
22687     },
22688
22689     // private
22690     afterRender : function(){
22691         Roo.BoxComponent.superclass.afterRender.call(this);
22692         this.boxReady = true;
22693         this.setSize(this.width, this.height);
22694         if(this.x || this.y){
22695             this.setPosition(this.x, this.y);
22696         }
22697         if(this.pageX || this.pageY){
22698             this.setPagePosition(this.pageX, this.pageY);
22699         }
22700     },
22701
22702     /**
22703      * Force the component's size to recalculate based on the underlying element's current height and width.
22704      * @returns {Roo.BoxComponent} this
22705      */
22706     syncSize : function(){
22707         delete this.lastSize;
22708         this.setSize(this.el.getWidth(), this.el.getHeight());
22709         return this;
22710     },
22711
22712     /**
22713      * Called after the component is resized, this method is empty by default but can be implemented by any
22714      * subclass that needs to perform custom logic after a resize occurs.
22715      * @param {Number} adjWidth The box-adjusted width that was set
22716      * @param {Number} adjHeight The box-adjusted height that was set
22717      * @param {Number} rawWidth The width that was originally specified
22718      * @param {Number} rawHeight The height that was originally specified
22719      */
22720     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22721
22722     },
22723
22724     /**
22725      * Called after the component is moved, this method is empty by default but can be implemented by any
22726      * subclass that needs to perform custom logic after a move occurs.
22727      * @param {Number} x The new x position
22728      * @param {Number} y The new y position
22729      */
22730     onPosition : function(x, y){
22731
22732     },
22733
22734     // private
22735     adjustSize : function(w, h){
22736         if(this.autoWidth){
22737             w = 'auto';
22738         }
22739         if(this.autoHeight){
22740             h = 'auto';
22741         }
22742         return {width : w, height: h};
22743     },
22744
22745     // private
22746     adjustPosition : function(x, y){
22747         return {x : x, y: y};
22748     }
22749 });/*
22750  * Based on:
22751  * Ext JS Library 1.1.1
22752  * Copyright(c) 2006-2007, Ext JS, LLC.
22753  *
22754  * Originally Released Under LGPL - original licence link has changed is not relivant.
22755  *
22756  * Fork - LGPL
22757  * <script type="text/javascript">
22758  */
22759
22760
22761 /**
22762  * @class Roo.SplitBar
22763  * @extends Roo.util.Observable
22764  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22765  * <br><br>
22766  * Usage:
22767  * <pre><code>
22768 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22769                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22770 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22771 split.minSize = 100;
22772 split.maxSize = 600;
22773 split.animate = true;
22774 split.on('moved', splitterMoved);
22775 </code></pre>
22776  * @constructor
22777  * Create a new SplitBar
22778  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22779  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22780  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22781  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22782                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22783                         position of the SplitBar).
22784  */
22785 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22786     
22787     /** @private */
22788     this.el = Roo.get(dragElement, true);
22789     this.el.dom.unselectable = "on";
22790     /** @private */
22791     this.resizingEl = Roo.get(resizingElement, true);
22792
22793     /**
22794      * @private
22795      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22797      * @type Number
22798      */
22799     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22800     
22801     /**
22802      * The minimum size of the resizing element. (Defaults to 0)
22803      * @type Number
22804      */
22805     this.minSize = 0;
22806     
22807     /**
22808      * The maximum size of the resizing element. (Defaults to 2000)
22809      * @type Number
22810      */
22811     this.maxSize = 2000;
22812     
22813     /**
22814      * Whether to animate the transition to the new size
22815      * @type Boolean
22816      */
22817     this.animate = false;
22818     
22819     /**
22820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22821      * @type Boolean
22822      */
22823     this.useShim = false;
22824     
22825     /** @private */
22826     this.shim = null;
22827     
22828     if(!existingProxy){
22829         /** @private */
22830         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22831     }else{
22832         this.proxy = Roo.get(existingProxy).dom;
22833     }
22834     /** @private */
22835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22836     
22837     /** @private */
22838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22839     
22840     /** @private */
22841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22842     
22843     /** @private */
22844     this.dragSpecs = {};
22845     
22846     /**
22847      * @private The adapter to use to positon and resize elements
22848      */
22849     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22850     this.adapter.init(this);
22851     
22852     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22853         /** @private */
22854         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22855         this.el.addClass("x-splitbar-h");
22856     }else{
22857         /** @private */
22858         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22859         this.el.addClass("x-splitbar-v");
22860     }
22861     
22862     this.addEvents({
22863         /**
22864          * @event resize
22865          * Fires when the splitter is moved (alias for {@link #event-moved})
22866          * @param {Roo.SplitBar} this
22867          * @param {Number} newSize the new width or height
22868          */
22869         "resize" : true,
22870         /**
22871          * @event moved
22872          * Fires when the splitter is moved
22873          * @param {Roo.SplitBar} this
22874          * @param {Number} newSize the new width or height
22875          */
22876         "moved" : true,
22877         /**
22878          * @event beforeresize
22879          * Fires before the splitter is dragged
22880          * @param {Roo.SplitBar} this
22881          */
22882         "beforeresize" : true,
22883
22884         "beforeapply" : true
22885     });
22886
22887     Roo.util.Observable.call(this);
22888 };
22889
22890 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22891     onStartProxyDrag : function(x, y){
22892         this.fireEvent("beforeresize", this);
22893         if(!this.overlay){
22894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22895             o.unselectable();
22896             o.enableDisplayMode("block");
22897             // all splitbars share the same overlay
22898             Roo.SplitBar.prototype.overlay = o;
22899         }
22900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22901         this.overlay.show();
22902         Roo.get(this.proxy).setDisplayed("block");
22903         var size = this.adapter.getElementSize(this);
22904         this.activeMinSize = this.getMinimumSize();;
22905         this.activeMaxSize = this.getMaximumSize();;
22906         var c1 = size - this.activeMinSize;
22907         var c2 = Math.max(this.activeMaxSize - size, 0);
22908         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22909             this.dd.resetConstraints();
22910             this.dd.setXConstraint(
22911                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22912                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22913             );
22914             this.dd.setYConstraint(0, 0);
22915         }else{
22916             this.dd.resetConstraints();
22917             this.dd.setXConstraint(0, 0);
22918             this.dd.setYConstraint(
22919                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22920                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22921             );
22922          }
22923         this.dragSpecs.startSize = size;
22924         this.dragSpecs.startPoint = [x, y];
22925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22926     },
22927     
22928     /** 
22929      * @private Called after the drag operation by the DDProxy
22930      */
22931     onEndProxyDrag : function(e){
22932         Roo.get(this.proxy).setDisplayed(false);
22933         var endPoint = Roo.lib.Event.getXY(e);
22934         if(this.overlay){
22935             this.overlay.hide();
22936         }
22937         var newSize;
22938         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22939             newSize = this.dragSpecs.startSize + 
22940                 (this.placement == Roo.SplitBar.LEFT ?
22941                     endPoint[0] - this.dragSpecs.startPoint[0] :
22942                     this.dragSpecs.startPoint[0] - endPoint[0]
22943                 );
22944         }else{
22945             newSize = this.dragSpecs.startSize + 
22946                 (this.placement == Roo.SplitBar.TOP ?
22947                     endPoint[1] - this.dragSpecs.startPoint[1] :
22948                     this.dragSpecs.startPoint[1] - endPoint[1]
22949                 );
22950         }
22951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22952         if(newSize != this.dragSpecs.startSize){
22953             if(this.fireEvent('beforeapply', this, newSize) !== false){
22954                 this.adapter.setElementSize(this, newSize);
22955                 this.fireEvent("moved", this, newSize);
22956                 this.fireEvent("resize", this, newSize);
22957             }
22958         }
22959     },
22960     
22961     /**
22962      * Get the adapter this SplitBar uses
22963      * @return The adapter object
22964      */
22965     getAdapter : function(){
22966         return this.adapter;
22967     },
22968     
22969     /**
22970      * Set the adapter this SplitBar uses
22971      * @param {Object} adapter A SplitBar adapter object
22972      */
22973     setAdapter : function(adapter){
22974         this.adapter = adapter;
22975         this.adapter.init(this);
22976     },
22977     
22978     /**
22979      * Gets the minimum size for the resizing element
22980      * @return {Number} The minimum size
22981      */
22982     getMinimumSize : function(){
22983         return this.minSize;
22984     },
22985     
22986     /**
22987      * Sets the minimum size for the resizing element
22988      * @param {Number} minSize The minimum size
22989      */
22990     setMinimumSize : function(minSize){
22991         this.minSize = minSize;
22992     },
22993     
22994     /**
22995      * Gets the maximum size for the resizing element
22996      * @return {Number} The maximum size
22997      */
22998     getMaximumSize : function(){
22999         return this.maxSize;
23000     },
23001     
23002     /**
23003      * Sets the maximum size for the resizing element
23004      * @param {Number} maxSize The maximum size
23005      */
23006     setMaximumSize : function(maxSize){
23007         this.maxSize = maxSize;
23008     },
23009     
23010     /**
23011      * Sets the initialize size for the resizing element
23012      * @param {Number} size The initial size
23013      */
23014     setCurrentSize : function(size){
23015         var oldAnimate = this.animate;
23016         this.animate = false;
23017         this.adapter.setElementSize(this, size);
23018         this.animate = oldAnimate;
23019     },
23020     
23021     /**
23022      * Destroy this splitbar. 
23023      * @param {Boolean} removeEl True to remove the element
23024      */
23025     destroy : function(removeEl){
23026         if(this.shim){
23027             this.shim.remove();
23028         }
23029         this.dd.unreg();
23030         this.proxy.parentNode.removeChild(this.proxy);
23031         if(removeEl){
23032             this.el.remove();
23033         }
23034     }
23035 });
23036
23037 /**
23038  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23039  */
23040 Roo.SplitBar.createProxy = function(dir){
23041     var proxy = new Roo.Element(document.createElement("div"));
23042     proxy.unselectable();
23043     var cls = 'x-splitbar-proxy';
23044     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23045     document.body.appendChild(proxy.dom);
23046     return proxy.dom;
23047 };
23048
23049 /** 
23050  * @class Roo.SplitBar.BasicLayoutAdapter
23051  * Default Adapter. It assumes the splitter and resizing element are not positioned
23052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23053  */
23054 Roo.SplitBar.BasicLayoutAdapter = function(){
23055 };
23056
23057 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23058     // do nothing for now
23059     init : function(s){
23060     
23061     },
23062     /**
23063      * Called before drag operations to get the current size of the resizing element. 
23064      * @param {Roo.SplitBar} s The SplitBar using this adapter
23065      */
23066      getElementSize : function(s){
23067         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23068             return s.resizingEl.getWidth();
23069         }else{
23070             return s.resizingEl.getHeight();
23071         }
23072     },
23073     
23074     /**
23075      * Called after drag operations to set the size of the resizing element.
23076      * @param {Roo.SplitBar} s The SplitBar using this adapter
23077      * @param {Number} newSize The new size to set
23078      * @param {Function} onComplete A function to be invoked when resizing is complete
23079      */
23080     setElementSize : function(s, newSize, onComplete){
23081         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23082             if(!s.animate){
23083                 s.resizingEl.setWidth(newSize);
23084                 if(onComplete){
23085                     onComplete(s, newSize);
23086                 }
23087             }else{
23088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23089             }
23090         }else{
23091             
23092             if(!s.animate){
23093                 s.resizingEl.setHeight(newSize);
23094                 if(onComplete){
23095                     onComplete(s, newSize);
23096                 }
23097             }else{
23098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23099             }
23100         }
23101     }
23102 };
23103
23104 /** 
23105  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23106  * @extends Roo.SplitBar.BasicLayoutAdapter
23107  * Adapter that  moves the splitter element to align with the resized sizing element. 
23108  * Used with an absolute positioned SplitBar.
23109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23110  * document.body, make sure you assign an id to the body element.
23111  */
23112 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23113     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23114     this.container = Roo.get(container);
23115 };
23116
23117 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23118     init : function(s){
23119         this.basic.init(s);
23120     },
23121     
23122     getElementSize : function(s){
23123         return this.basic.getElementSize(s);
23124     },
23125     
23126     setElementSize : function(s, newSize, onComplete){
23127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23128     },
23129     
23130     moveSplitter : function(s){
23131         var yes = Roo.SplitBar;
23132         switch(s.placement){
23133             case yes.LEFT:
23134                 s.el.setX(s.resizingEl.getRight());
23135                 break;
23136             case yes.RIGHT:
23137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23138                 break;
23139             case yes.TOP:
23140                 s.el.setY(s.resizingEl.getBottom());
23141                 break;
23142             case yes.BOTTOM:
23143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23144                 break;
23145         }
23146     }
23147 };
23148
23149 /**
23150  * Orientation constant - Create a vertical SplitBar
23151  * @static
23152  * @type Number
23153  */
23154 Roo.SplitBar.VERTICAL = 1;
23155
23156 /**
23157  * Orientation constant - Create a horizontal SplitBar
23158  * @static
23159  * @type Number
23160  */
23161 Roo.SplitBar.HORIZONTAL = 2;
23162
23163 /**
23164  * Placement constant - The resizing element is to the left of the splitter element
23165  * @static
23166  * @type Number
23167  */
23168 Roo.SplitBar.LEFT = 1;
23169
23170 /**
23171  * Placement constant - The resizing element is to the right of the splitter element
23172  * @static
23173  * @type Number
23174  */
23175 Roo.SplitBar.RIGHT = 2;
23176
23177 /**
23178  * Placement constant - The resizing element is positioned above the splitter element
23179  * @static
23180  * @type Number
23181  */
23182 Roo.SplitBar.TOP = 3;
23183
23184 /**
23185  * Placement constant - The resizing element is positioned under splitter element
23186  * @static
23187  * @type Number
23188  */
23189 Roo.SplitBar.BOTTOM = 4;
23190 /*
23191  * Based on:
23192  * Ext JS Library 1.1.1
23193  * Copyright(c) 2006-2007, Ext JS, LLC.
23194  *
23195  * Originally Released Under LGPL - original licence link has changed is not relivant.
23196  *
23197  * Fork - LGPL
23198  * <script type="text/javascript">
23199  */
23200
23201 /**
23202  * @class Roo.View
23203  * @extends Roo.util.Observable
23204  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23205  * This class also supports single and multi selection modes. <br>
23206  * Create a data model bound view:
23207  <pre><code>
23208  var store = new Roo.data.Store(...);
23209
23210  var view = new Roo.View("my-element",
23211  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23212  {
23213  singleSelect: true,
23214  selectedClass: "ydataview-selected",
23215  store: store
23216  });
23217
23218  // listen for node click?
23219  view.on("click", function(vw, index, node, e){
23220  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23221  });
23222
23223  // load XML data
23224  dataModel.load("foobar.xml");
23225  </code></pre>
23226  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23227  * <br><br>
23228  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23229  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23230  * @constructor
23231  * Create a new View
23232  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23233  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
23234  * @param {Object} config The config object
23235  */
23236 Roo.View = function(container, tpl, config){
23237     this.el = Roo.get(container);
23238     if(typeof tpl == "string"){
23239         tpl = new Roo.Template(tpl);
23240     }
23241     tpl.compile();
23242     /**
23243      * The template used by this View
23244      * @type {Roo.DomHelper.Template}
23245      */
23246     this.tpl = tpl;
23247
23248     Roo.apply(this, config);
23249
23250     /** @private */
23251     this.addEvents({
23252     /**
23253      * @event beforeclick
23254      * Fires before a click is processed. Returns false to cancel the default action.
23255      * @param {Roo.View} this
23256      * @param {Number} index The index of the target node
23257      * @param {HTMLElement} node The target node
23258      * @param {Roo.EventObject} e The raw event object
23259      */
23260         "beforeclick" : true,
23261     /**
23262      * @event click
23263      * Fires when a template node is clicked.
23264      * @param {Roo.View} this
23265      * @param {Number} index The index of the target node
23266      * @param {HTMLElement} node The target node
23267      * @param {Roo.EventObject} e The raw event object
23268      */
23269         "click" : true,
23270     /**
23271      * @event dblclick
23272      * Fires when a template node is double clicked.
23273      * @param {Roo.View} this
23274      * @param {Number} index The index of the target node
23275      * @param {HTMLElement} node The target node
23276      * @param {Roo.EventObject} e The raw event object
23277      */
23278         "dblclick" : true,
23279     /**
23280      * @event contextmenu
23281      * Fires when a template node is right clicked.
23282      * @param {Roo.View} this
23283      * @param {Number} index The index of the target node
23284      * @param {HTMLElement} node The target node
23285      * @param {Roo.EventObject} e The raw event object
23286      */
23287         "contextmenu" : true,
23288     /**
23289      * @event selectionchange
23290      * Fires when the selected nodes change.
23291      * @param {Roo.View} this
23292      * @param {Array} selections Array of the selected nodes
23293      */
23294         "selectionchange" : true,
23295
23296     /**
23297      * @event beforeselect
23298      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23299      * @param {Roo.View} this
23300      * @param {HTMLElement} node The node to be selected
23301      * @param {Array} selections Array of currently selected nodes
23302      */
23303         "beforeselect" : true
23304     });
23305
23306     this.el.on({
23307         "click": this.onClick,
23308         "dblclick": this.onDblClick,
23309         "contextmenu": this.onContextMenu,
23310         scope:this
23311     });
23312
23313     this.selections = [];
23314     this.nodes = [];
23315     this.cmp = new Roo.CompositeElementLite([]);
23316     if(this.store){
23317         this.store = Roo.factory(this.store, Roo.data);
23318         this.setStore(this.store, true);
23319     }
23320     Roo.View.superclass.constructor.call(this);
23321 };
23322
23323 Roo.extend(Roo.View, Roo.util.Observable, {
23324     /**
23325      * The css class to add to selected nodes
23326      * @type {Roo.DomHelper.Template}
23327      */
23328     selectedClass : "x-view-selected",
23329     
23330     emptyText : "",
23331     /**
23332      * Returns the element this view is bound to.
23333      * @return {Roo.Element}
23334      */
23335     getEl : function(){
23336         return this.el;
23337     },
23338
23339     /**
23340      * Refreshes the view.
23341      */
23342     refresh : function(){
23343         var t = this.tpl;
23344         this.clearSelections();
23345         this.el.update("");
23346         var html = [];
23347         var records = this.store.getRange();
23348         if(records.length < 1){
23349             this.el.update(this.emptyText);
23350             return;
23351         }
23352         for(var i = 0, len = records.length; i < len; i++){
23353             var data = this.prepareData(records[i].data, i, records[i]);
23354             html[html.length] = t.apply(data);
23355         }
23356         this.el.update(html.join(""));
23357         this.nodes = this.el.dom.childNodes;
23358         this.updateIndexes(0);
23359     },
23360
23361     /**
23362      * Function to override to reformat the data that is sent to
23363      * the template for each node.
23364      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23365      * a JSON object for an UpdateManager bound view).
23366      */
23367     prepareData : function(data){
23368         return data;
23369     },
23370
23371     onUpdate : function(ds, record){
23372         this.clearSelections();
23373         var index = this.store.indexOf(record);
23374         var n = this.nodes[index];
23375         this.tpl.insertBefore(n, this.prepareData(record.data));
23376         n.parentNode.removeChild(n);
23377         this.updateIndexes(index, index);
23378     },
23379
23380     onAdd : function(ds, records, index){
23381         this.clearSelections();
23382         if(this.nodes.length == 0){
23383             this.refresh();
23384             return;
23385         }
23386         var n = this.nodes[index];
23387         for(var i = 0, len = records.length; i < len; i++){
23388             var d = this.prepareData(records[i].data);
23389             if(n){
23390                 this.tpl.insertBefore(n, d);
23391             }else{
23392                 this.tpl.append(this.el, d);
23393             }
23394         }
23395         this.updateIndexes(index);
23396     },
23397
23398     onRemove : function(ds, record, index){
23399         this.clearSelections();
23400         this.el.dom.removeChild(this.nodes[index]);
23401         this.updateIndexes(index);
23402     },
23403
23404     /**
23405      * Refresh an individual node.
23406      * @param {Number} index
23407      */
23408     refreshNode : function(index){
23409         this.onUpdate(this.store, this.store.getAt(index));
23410     },
23411
23412     updateIndexes : function(startIndex, endIndex){
23413         var ns = this.nodes;
23414         startIndex = startIndex || 0;
23415         endIndex = endIndex || ns.length - 1;
23416         for(var i = startIndex; i <= endIndex; i++){
23417             ns[i].nodeIndex = i;
23418         }
23419     },
23420
23421     /**
23422      * Changes the data store this view uses and refresh the view.
23423      * @param {Store} store
23424      */
23425     setStore : function(store, initial){
23426         if(!initial && this.store){
23427             this.store.un("datachanged", this.refresh);
23428             this.store.un("add", this.onAdd);
23429             this.store.un("remove", this.onRemove);
23430             this.store.un("update", this.onUpdate);
23431             this.store.un("clear", this.refresh);
23432         }
23433         if(store){
23434           
23435             store.on("datachanged", this.refresh, this);
23436             store.on("add", this.onAdd, this);
23437             store.on("remove", this.onRemove, this);
23438             store.on("update", this.onUpdate, this);
23439             store.on("clear", this.refresh, this);
23440         }
23441         
23442         if(store){
23443             this.refresh();
23444         }
23445     },
23446
23447     /**
23448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23449      * @param {HTMLElement} node
23450      * @return {HTMLElement} The template node
23451      */
23452     findItemFromChild : function(node){
23453         var el = this.el.dom;
23454         if(!node || node.parentNode == el){
23455                     return node;
23456             }
23457             var p = node.parentNode;
23458             while(p && p != el){
23459             if(p.parentNode == el){
23460                 return p;
23461             }
23462             p = p.parentNode;
23463         }
23464             return null;
23465     },
23466
23467     /** @ignore */
23468     onClick : function(e){
23469         var item = this.findItemFromChild(e.getTarget());
23470         if(item){
23471             var index = this.indexOf(item);
23472             if(this.onItemClick(item, index, e) !== false){
23473                 this.fireEvent("click", this, index, item, e);
23474             }
23475         }else{
23476             this.clearSelections();
23477         }
23478     },
23479
23480     /** @ignore */
23481     onContextMenu : function(e){
23482         var item = this.findItemFromChild(e.getTarget());
23483         if(item){
23484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23485         }
23486     },
23487
23488     /** @ignore */
23489     onDblClick : function(e){
23490         var item = this.findItemFromChild(e.getTarget());
23491         if(item){
23492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23493         }
23494     },
23495
23496     onItemClick : function(item, index, e){
23497         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23498             return false;
23499         }
23500         if(this.multiSelect || this.singleSelect){
23501             if(this.multiSelect && e.shiftKey && this.lastSelection){
23502                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23503             }else{
23504                 this.select(item, this.multiSelect && e.ctrlKey);
23505                 this.lastSelection = item;
23506             }
23507             e.preventDefault();
23508         }
23509         return true;
23510     },
23511
23512     /**
23513      * Get the number of selected nodes.
23514      * @return {Number}
23515      */
23516     getSelectionCount : function(){
23517         return this.selections.length;
23518     },
23519
23520     /**
23521      * Get the currently selected nodes.
23522      * @return {Array} An array of HTMLElements
23523      */
23524     getSelectedNodes : function(){
23525         return this.selections;
23526     },
23527
23528     /**
23529      * Get the indexes of the selected nodes.
23530      * @return {Array}
23531      */
23532     getSelectedIndexes : function(){
23533         var indexes = [], s = this.selections;
23534         for(var i = 0, len = s.length; i < len; i++){
23535             indexes.push(s[i].nodeIndex);
23536         }
23537         return indexes;
23538     },
23539
23540     /**
23541      * Clear all selections
23542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23543      */
23544     clearSelections : function(suppressEvent){
23545         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23546             this.cmp.elements = this.selections;
23547             this.cmp.removeClass(this.selectedClass);
23548             this.selections = [];
23549             if(!suppressEvent){
23550                 this.fireEvent("selectionchange", this, this.selections);
23551             }
23552         }
23553     },
23554
23555     /**
23556      * Returns true if the passed node is selected
23557      * @param {HTMLElement/Number} node The node or node index
23558      * @return {Boolean}
23559      */
23560     isSelected : function(node){
23561         var s = this.selections;
23562         if(s.length < 1){
23563             return false;
23564         }
23565         node = this.getNode(node);
23566         return s.indexOf(node) !== -1;
23567     },
23568
23569     /**
23570      * Selects nodes.
23571      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
23572      * @param {Boolean} keepExisting (optional) true to keep existing selections
23573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23574      */
23575     select : function(nodeInfo, keepExisting, suppressEvent){
23576         if(nodeInfo instanceof Array){
23577             if(!keepExisting){
23578                 this.clearSelections(true);
23579             }
23580             for(var i = 0, len = nodeInfo.length; i < len; i++){
23581                 this.select(nodeInfo[i], true, true);
23582             }
23583         } else{
23584             var node = this.getNode(nodeInfo);
23585             if(node && !this.isSelected(node)){
23586                 if(!keepExisting){
23587                     this.clearSelections(true);
23588                 }
23589                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23590                     Roo.fly(node).addClass(this.selectedClass);
23591                     this.selections.push(node);
23592                     if(!suppressEvent){
23593                         this.fireEvent("selectionchange", this, this.selections);
23594                     }
23595                 }
23596             }
23597         }
23598     },
23599
23600     /**
23601      * Gets a template node.
23602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23603      * @return {HTMLElement} The node or null if it wasn't found
23604      */
23605     getNode : function(nodeInfo){
23606         if(typeof nodeInfo == "string"){
23607             return document.getElementById(nodeInfo);
23608         }else if(typeof nodeInfo == "number"){
23609             return this.nodes[nodeInfo];
23610         }
23611         return nodeInfo;
23612     },
23613
23614     /**
23615      * Gets a range template nodes.
23616      * @param {Number} startIndex
23617      * @param {Number} endIndex
23618      * @return {Array} An array of nodes
23619      */
23620     getNodes : function(start, end){
23621         var ns = this.nodes;
23622         start = start || 0;
23623         end = typeof end == "undefined" ? ns.length - 1 : end;
23624         var nodes = [];
23625         if(start <= end){
23626             for(var i = start; i <= end; i++){
23627                 nodes.push(ns[i]);
23628             }
23629         } else{
23630             for(var i = start; i >= end; i--){
23631                 nodes.push(ns[i]);
23632             }
23633         }
23634         return nodes;
23635     },
23636
23637     /**
23638      * Finds the index of the passed node
23639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23640      * @return {Number} The index of the node or -1
23641      */
23642     indexOf : function(node){
23643         node = this.getNode(node);
23644         if(typeof node.nodeIndex == "number"){
23645             return node.nodeIndex;
23646         }
23647         var ns = this.nodes;
23648         for(var i = 0, len = ns.length; i < len; i++){
23649             if(ns[i] == node){
23650                 return i;
23651             }
23652         }
23653         return -1;
23654     }
23655 });
23656 /*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666
23667 /**
23668  * @class Roo.JsonView
23669  * @extends Roo.View
23670  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23671 <pre><code>
23672 var view = new Roo.JsonView("my-element",
23673     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23674     { multiSelect: true, jsonRoot: "data" }
23675 );
23676
23677 // listen for node click?
23678 view.on("click", function(vw, index, node, e){
23679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23680 });
23681
23682 // direct load of JSON data
23683 view.load("foobar.php");
23684
23685 // Example from my blog list
23686 var tpl = new Roo.Template(
23687     '&lt;div class="entry"&gt;' +
23688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23690     "&lt;/div&gt;&lt;hr /&gt;"
23691 );
23692
23693 var moreView = new Roo.JsonView("entry-list", tpl, {
23694     jsonRoot: "posts"
23695 });
23696 moreView.on("beforerender", this.sortEntries, this);
23697 moreView.load({
23698     url: "/blog/get-posts.php",
23699     params: "allposts=true",
23700     text: "Loading Blog Entries..."
23701 });
23702 </code></pre>
23703  * @constructor
23704  * Create a new JsonView
23705  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23706  * @param {Template} tpl The rendering template
23707  * @param {Object} config The config object
23708  */
23709 Roo.JsonView = function(container, tpl, config){
23710     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
23711
23712     var um = this.el.getUpdateManager();
23713     um.setRenderer(this);
23714     um.on("update", this.onLoad, this);
23715     um.on("failure", this.onLoadException, this);
23716
23717     /**
23718      * @event beforerender
23719      * Fires before rendering of the downloaded JSON data.
23720      * @param {Roo.JsonView} this
23721      * @param {Object} data The JSON data loaded
23722      */
23723     /**
23724      * @event load
23725      * Fires when data is loaded.
23726      * @param {Roo.JsonView} this
23727      * @param {Object} data The JSON data loaded
23728      * @param {Object} response The raw Connect response object
23729      */
23730     /**
23731      * @event loadexception
23732      * Fires when loading fails.
23733      * @param {Roo.JsonView} this
23734      * @param {Object} response The raw Connect response object
23735      */
23736     this.addEvents({
23737         'beforerender' : true,
23738         'load' : true,
23739         'loadexception' : true
23740     });
23741 };
23742 Roo.extend(Roo.JsonView, Roo.View, {
23743     /**
23744      * The root property in the loaded JSON object that contains the data
23745      * @type {String}
23746      */
23747     jsonRoot : "",
23748
23749     /**
23750      * Refreshes the view.
23751      */
23752     refresh : function(){
23753         this.clearSelections();
23754         this.el.update("");
23755         var html = [];
23756         var o = this.jsonData;
23757         if(o && o.length > 0){
23758             for(var i = 0, len = o.length; i < len; i++){
23759                 var data = this.prepareData(o[i], i, o);
23760                 html[html.length] = this.tpl.apply(data);
23761             }
23762         }else{
23763             html.push(this.emptyText);
23764         }
23765         this.el.update(html.join(""));
23766         this.nodes = this.el.dom.childNodes;
23767         this.updateIndexes(0);
23768     },
23769
23770     /**
23771      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
23772      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
23773      <pre><code>
23774      view.load({
23775          url: "your-url.php",
23776          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23777          callback: yourFunction,
23778          scope: yourObject, //(optional scope)
23779          discardUrl: false,
23780          nocache: false,
23781          text: "Loading...",
23782          timeout: 30,
23783          scripts: false
23784      });
23785      </code></pre>
23786      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23787      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
23788      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
23789      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23790      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
23791      */
23792     load : function(){
23793         var um = this.el.getUpdateManager();
23794         um.update.apply(um, arguments);
23795     },
23796
23797     render : function(el, response){
23798         this.clearSelections();
23799         this.el.update("");
23800         var o;
23801         try{
23802             o = Roo.util.JSON.decode(response.responseText);
23803             if(this.jsonRoot){
23804                 
23805                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23806             }
23807         } catch(e){
23808         }
23809         /**
23810          * The current JSON data or null
23811          */
23812         this.jsonData = o;
23813         this.beforeRender();
23814         this.refresh();
23815     },
23816
23817 /**
23818  * Get the number of records in the current JSON dataset
23819  * @return {Number}
23820  */
23821     getCount : function(){
23822         return this.jsonData ? this.jsonData.length : 0;
23823     },
23824
23825 /**
23826  * Returns the JSON object for the specified node(s)
23827  * @param {HTMLElement/Array} node The node or an array of nodes
23828  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23829  * you get the JSON object for the node
23830  */
23831     getNodeData : function(node){
23832         if(node instanceof Array){
23833             var data = [];
23834             for(var i = 0, len = node.length; i < len; i++){
23835                 data.push(this.getNodeData(node[i]));
23836             }
23837             return data;
23838         }
23839         return this.jsonData[this.indexOf(node)] || null;
23840     },
23841
23842     beforeRender : function(){
23843         this.snapshot = this.jsonData;
23844         if(this.sortInfo){
23845             this.sort.apply(this, this.sortInfo);
23846         }
23847         this.fireEvent("beforerender", this, this.jsonData);
23848     },
23849
23850     onLoad : function(el, o){
23851         this.fireEvent("load", this, this.jsonData, o);
23852     },
23853
23854     onLoadException : function(el, o){
23855         this.fireEvent("loadexception", this, o);
23856     },
23857
23858 /**
23859  * Filter the data by a specific property.
23860  * @param {String} property A property on your JSON objects
23861  * @param {String/RegExp} value Either string that the property values
23862  * should start with, or a RegExp to test against the property
23863  */
23864     filter : function(property, value){
23865         if(this.jsonData){
23866             var data = [];
23867             var ss = this.snapshot;
23868             if(typeof value == "string"){
23869                 var vlen = value.length;
23870                 if(vlen == 0){
23871                     this.clearFilter();
23872                     return;
23873                 }
23874                 value = value.toLowerCase();
23875                 for(var i = 0, len = ss.length; i < len; i++){
23876                     var o = ss[i];
23877                     if(o[property].substr(0, vlen).toLowerCase() == value){
23878                         data.push(o);
23879                     }
23880                 }
23881             } else if(value.exec){ // regex?
23882                 for(var i = 0, len = ss.length; i < len; i++){
23883                     var o = ss[i];
23884                     if(value.test(o[property])){
23885                         data.push(o);
23886                     }
23887                 }
23888             } else{
23889                 return;
23890             }
23891             this.jsonData = data;
23892             this.refresh();
23893         }
23894     },
23895
23896 /**
23897  * Filter by a function. The passed function will be called with each
23898  * object in the current dataset. If the function returns true the value is kept,
23899  * otherwise it is filtered.
23900  * @param {Function} fn
23901  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23902  */
23903     filterBy : function(fn, scope){
23904         if(this.jsonData){
23905             var data = [];
23906             var ss = this.snapshot;
23907             for(var i = 0, len = ss.length; i < len; i++){
23908                 var o = ss[i];
23909                 if(fn.call(scope || this, o)){
23910                     data.push(o);
23911                 }
23912             }
23913             this.jsonData = data;
23914             this.refresh();
23915         }
23916     },
23917
23918 /**
23919  * Clears the current filter.
23920  */
23921     clearFilter : function(){
23922         if(this.snapshot && this.jsonData != this.snapshot){
23923             this.jsonData = this.snapshot;
23924             this.refresh();
23925         }
23926     },
23927
23928
23929 /**
23930  * Sorts the data for this view and refreshes it.
23931  * @param {String} property A property on your JSON objects to sort on
23932  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
23933  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
23934  */
23935     sort : function(property, dir, sortType){
23936         this.sortInfo = Array.prototype.slice.call(arguments, 0);
23937         if(this.jsonData){
23938             var p = property;
23939             var dsc = dir && dir.toLowerCase() == "desc";
23940             var f = function(o1, o2){
23941                 var v1 = sortType ? sortType(o1[p]) : o1[p];
23942                 var v2 = sortType ? sortType(o2[p]) : o2[p];
23943                 ;
23944                 if(v1 < v2){
23945                     return dsc ? +1 : -1;
23946                 } else if(v1 > v2){
23947                     return dsc ? -1 : +1;
23948                 } else{
23949                     return 0;
23950                 }
23951             };
23952             this.jsonData.sort(f);
23953             this.refresh();
23954             if(this.jsonData != this.snapshot){
23955                 this.snapshot.sort(f);
23956             }
23957         }
23958     }
23959 });/*
23960  * Based on:
23961  * Ext JS Library 1.1.1
23962  * Copyright(c) 2006-2007, Ext JS, LLC.
23963  *
23964  * Originally Released Under LGPL - original licence link has changed is not relivant.
23965  *
23966  * Fork - LGPL
23967  * <script type="text/javascript">
23968  */
23969  
23970
23971 /**
23972  * @class Roo.ColorPalette
23973  * @extends Roo.Component
23974  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
23975  * Here's an example of typical usage:
23976  * <pre><code>
23977 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
23978 cp.render('my-div');
23979
23980 cp.on('select', function(palette, selColor){
23981     // do something with selColor
23982 });
23983 </code></pre>
23984  * @constructor
23985  * Create a new ColorPalette
23986  * @param {Object} config The config object
23987  */
23988 Roo.ColorPalette = function(config){
23989     Roo.ColorPalette.superclass.constructor.call(this, config);
23990     this.addEvents({
23991         /**
23992              * @event select
23993              * Fires when a color is selected
23994              * @param {ColorPalette} this
23995              * @param {String} color The 6-digit color hex code (without the # symbol)
23996              */
23997         select: true
23998     });
23999
24000     if(this.handler){
24001         this.on("select", this.handler, this.scope, true);
24002     }
24003 };
24004 Roo.extend(Roo.ColorPalette, Roo.Component, {
24005     /**
24006      * @cfg {String} itemCls
24007      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24008      */
24009     itemCls : "x-color-palette",
24010     /**
24011      * @cfg {String} value
24012      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24013      * the hex codes are case-sensitive.
24014      */
24015     value : null,
24016     clickEvent:'click',
24017     // private
24018     ctype: "Roo.ColorPalette",
24019
24020     /**
24021      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24022      */
24023     allowReselect : false,
24024
24025     /**
24026      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24027      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24028      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24029      * of colors with the width setting until the box is symmetrical.</p>
24030      * <p>You can override individual colors if needed:</p>
24031      * <pre><code>
24032 var cp = new Roo.ColorPalette();
24033 cp.colors[0] = "FF0000";  // change the first box to red
24034 </code></pre>
24035
24036 Or you can provide a custom array of your own for complete control:
24037 <pre><code>
24038 var cp = new Roo.ColorPalette();
24039 cp.colors = ["000000", "993300", "333300"];
24040 </code></pre>
24041      * @type Array
24042      */
24043     colors : [
24044         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24045         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24046         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24047         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24048         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24049     ],
24050
24051     // private
24052     onRender : function(container, position){
24053         var t = new Roo.MasterTemplate(
24054             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24055         );
24056         var c = this.colors;
24057         for(var i = 0, len = c.length; i < len; i++){
24058             t.add([c[i]]);
24059         }
24060         var el = document.createElement("div");
24061         el.className = this.itemCls;
24062         t.overwrite(el);
24063         container.dom.insertBefore(el, position);
24064         this.el = Roo.get(el);
24065         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24066         if(this.clickEvent != 'click'){
24067             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24068         }
24069     },
24070
24071     // private
24072     afterRender : function(){
24073         Roo.ColorPalette.superclass.afterRender.call(this);
24074         if(this.value){
24075             var s = this.value;
24076             this.value = null;
24077             this.select(s);
24078         }
24079     },
24080
24081     // private
24082     handleClick : function(e, t){
24083         e.preventDefault();
24084         if(!this.disabled){
24085             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24086             this.select(c.toUpperCase());
24087         }
24088     },
24089
24090     /**
24091      * Selects the specified color in the palette (fires the select event)
24092      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24093      */
24094     select : function(color){
24095         color = color.replace("#", "");
24096         if(color != this.value || this.allowReselect){
24097             var el = this.el;
24098             if(this.value){
24099                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24100             }
24101             el.child("a.color-"+color).addClass("x-color-palette-sel");
24102             this.value = color;
24103             this.fireEvent("select", this, color);
24104         }
24105     }
24106 });/*
24107  * Based on:
24108  * Ext JS Library 1.1.1
24109  * Copyright(c) 2006-2007, Ext JS, LLC.
24110  *
24111  * Originally Released Under LGPL - original licence link has changed is not relivant.
24112  *
24113  * Fork - LGPL
24114  * <script type="text/javascript">
24115  */
24116  
24117 /**
24118  * @class Roo.DatePicker
24119  * @extends Roo.Component
24120  * Simple date picker class.
24121  * @constructor
24122  * Create a new DatePicker
24123  * @param {Object} config The config object
24124  */
24125 Roo.DatePicker = function(config){
24126     Roo.DatePicker.superclass.constructor.call(this, config);
24127
24128     this.value = config && config.value ?
24129                  config.value.clearTime() : new Date().clearTime();
24130
24131     this.addEvents({
24132         /**
24133              * @event select
24134              * Fires when a date is selected
24135              * @param {DatePicker} this
24136              * @param {Date} date The selected date
24137              */
24138         select: true
24139     });
24140
24141     if(this.handler){
24142         this.on("select", this.handler,  this.scope || this);
24143     }
24144     // build the disabledDatesRE
24145     if(!this.disabledDatesRE && this.disabledDates){
24146         var dd = this.disabledDates;
24147         var re = "(?:";
24148         for(var i = 0; i < dd.length; i++){
24149             re += dd[i];
24150             if(i != dd.length-1) re += "|";
24151         }
24152         this.disabledDatesRE = new RegExp(re + ")");
24153     }
24154 };
24155
24156 Roo.extend(Roo.DatePicker, Roo.Component, {
24157     /**
24158      * @cfg {String} todayText
24159      * The text to display on the button that selects the current date (defaults to "Today")
24160      */
24161     todayText : "Today",
24162     /**
24163      * @cfg {String} okText
24164      * The text to display on the ok button
24165      */
24166     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24167     /**
24168      * @cfg {String} cancelText
24169      * The text to display on the cancel button
24170      */
24171     cancelText : "Cancel",
24172     /**
24173      * @cfg {String} todayTip
24174      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24175      */
24176     todayTip : "{0} (Spacebar)",
24177     /**
24178      * @cfg {Date} minDate
24179      * Minimum allowable date (JavaScript date object, defaults to null)
24180      */
24181     minDate : null,
24182     /**
24183      * @cfg {Date} maxDate
24184      * Maximum allowable date (JavaScript date object, defaults to null)
24185      */
24186     maxDate : null,
24187     /**
24188      * @cfg {String} minText
24189      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24190      */
24191     minText : "This date is before the minimum date",
24192     /**
24193      * @cfg {String} maxText
24194      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24195      */
24196     maxText : "This date is after the maximum date",
24197     /**
24198      * @cfg {String} format
24199      * The default date format string which can be overriden for localization support.  The format must be
24200      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24201      */
24202     format : "m/d/y",
24203     /**
24204      * @cfg {Array} disabledDays
24205      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24206      */
24207     disabledDays : null,
24208     /**
24209      * @cfg {String} disabledDaysText
24210      * The tooltip to display when the date falls on a disabled day (defaults to "")
24211      */
24212     disabledDaysText : "",
24213     /**
24214      * @cfg {RegExp} disabledDatesRE
24215      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24216      */
24217     disabledDatesRE : null,
24218     /**
24219      * @cfg {String} disabledDatesText
24220      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24221      */
24222     disabledDatesText : "",
24223     /**
24224      * @cfg {Boolean} constrainToViewport
24225      * True to constrain the date picker to the viewport (defaults to true)
24226      */
24227     constrainToViewport : true,
24228     /**
24229      * @cfg {Array} monthNames
24230      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24231      */
24232     monthNames : Date.monthNames,
24233     /**
24234      * @cfg {Array} dayNames
24235      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24236      */
24237     dayNames : Date.dayNames,
24238     /**
24239      * @cfg {String} nextText
24240      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24241      */
24242     nextText: 'Next Month (Control+Right)',
24243     /**
24244      * @cfg {String} prevText
24245      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24246      */
24247     prevText: 'Previous Month (Control+Left)',
24248     /**
24249      * @cfg {String} monthYearText
24250      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24251      */
24252     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24253     /**
24254      * @cfg {Number} startDay
24255      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24256      */
24257     startDay : 0,
24258     /**
24259      * @cfg {Bool} showClear
24260      * Show a clear button (usefull for date form elements that can be blank.)
24261      */
24262     
24263     showClear: false,
24264     
24265     /**
24266      * Sets the value of the date field
24267      * @param {Date} value The date to set
24268      */
24269     setValue : function(value){
24270         var old = this.value;
24271         this.value = value.clearTime(true);
24272         if(this.el){
24273             this.update(this.value);
24274         }
24275     },
24276
24277     /**
24278      * Gets the current selected value of the date field
24279      * @return {Date} The selected date
24280      */
24281     getValue : function(){
24282         return this.value;
24283     },
24284
24285     // private
24286     focus : function(){
24287         if(this.el){
24288             this.update(this.activeDate);
24289         }
24290     },
24291
24292     // private
24293     onRender : function(container, position){
24294         var m = [
24295              '<table cellspacing="0">',
24296                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24297                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24298         var dn = this.dayNames;
24299         for(var i = 0; i < 7; i++){
24300             var d = this.startDay+i;
24301             if(d > 6){
24302                 d = d-7;
24303             }
24304             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24305         }
24306         m[m.length] = "</tr></thead><tbody><tr>";
24307         for(var i = 0; i < 42; i++) {
24308             if(i % 7 == 0 && i != 0){
24309                 m[m.length] = "</tr><tr>";
24310             }
24311             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24312         }
24313         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24314             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24315
24316         var el = document.createElement("div");
24317         el.className = "x-date-picker";
24318         el.innerHTML = m.join("");
24319
24320         container.dom.insertBefore(el, position);
24321
24322         this.el = Roo.get(el);
24323         this.eventEl = Roo.get(el.firstChild);
24324
24325         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24326             handler: this.showPrevMonth,
24327             scope: this,
24328             preventDefault:true,
24329             stopDefault:true
24330         });
24331
24332         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24333             handler: this.showNextMonth,
24334             scope: this,
24335             preventDefault:true,
24336             stopDefault:true
24337         });
24338
24339         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24340
24341         this.monthPicker = this.el.down('div.x-date-mp');
24342         this.monthPicker.enableDisplayMode('block');
24343         
24344         var kn = new Roo.KeyNav(this.eventEl, {
24345             "left" : function(e){
24346                 e.ctrlKey ?
24347                     this.showPrevMonth() :
24348                     this.update(this.activeDate.add("d", -1));
24349             },
24350
24351             "right" : function(e){
24352                 e.ctrlKey ?
24353                     this.showNextMonth() :
24354                     this.update(this.activeDate.add("d", 1));
24355             },
24356
24357             "up" : function(e){
24358                 e.ctrlKey ?
24359                     this.showNextYear() :
24360                     this.update(this.activeDate.add("d", -7));
24361             },
24362
24363             "down" : function(e){
24364                 e.ctrlKey ?
24365                     this.showPrevYear() :
24366                     this.update(this.activeDate.add("d", 7));
24367             },
24368
24369             "pageUp" : function(e){
24370                 this.showNextMonth();
24371             },
24372
24373             "pageDown" : function(e){
24374                 this.showPrevMonth();
24375             },
24376
24377             "enter" : function(e){
24378                 e.stopPropagation();
24379                 return true;
24380             },
24381
24382             scope : this
24383         });
24384
24385         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24386
24387         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24388
24389         this.el.unselectable();
24390         
24391         this.cells = this.el.select("table.x-date-inner tbody td");
24392         this.textNodes = this.el.query("table.x-date-inner tbody span");
24393
24394         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24395             text: "&#160;",
24396             tooltip: this.monthYearText
24397         });
24398
24399         this.mbtn.on('click', this.showMonthPicker, this);
24400         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24401
24402
24403         var today = (new Date()).dateFormat(this.format);
24404         
24405         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24406         baseTb.add({
24407             text: String.format(this.todayText, today),
24408             tooltip: String.format(this.todayTip, today),
24409             handler: this.selectToday,
24410             scope: this
24411         });
24412         
24413         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24414             
24415         //});
24416         if (this.showClear) {
24417             
24418             baseTb.add( new Roo.Toolbar.Fill());
24419             baseTb.add({
24420                 text: '&#160;',
24421                 cls: 'x-btn-icon x-btn-clear',
24422                 handler: function() {
24423                     //this.value = '';
24424                     this.fireEvent("select", this, '');
24425                 },
24426                 scope: this
24427             });
24428         }
24429         
24430         
24431         if(Roo.isIE){
24432             this.el.repaint();
24433         }
24434         this.update(this.value);
24435     },
24436
24437     createMonthPicker : function(){
24438         if(!this.monthPicker.dom.firstChild){
24439             var buf = ['<table border="0" cellspacing="0">'];
24440             for(var i = 0; i < 6; i++){
24441                 buf.push(
24442                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24443                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24444                     i == 0 ?
24445                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24446                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24447                 );
24448             }
24449             buf.push(
24450                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24451                     this.okText,
24452                     '</button><button type="button" class="x-date-mp-cancel">',
24453                     this.cancelText,
24454                     '</button></td></tr>',
24455                 '</table>'
24456             );
24457             this.monthPicker.update(buf.join(''));
24458             this.monthPicker.on('click', this.onMonthClick, this);
24459             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24460
24461             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24462             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24463
24464             this.mpMonths.each(function(m, a, i){
24465                 i += 1;
24466                 if((i%2) == 0){
24467                     m.dom.xmonth = 5 + Math.round(i * .5);
24468                 }else{
24469                     m.dom.xmonth = Math.round((i-1) * .5);
24470                 }
24471             });
24472         }
24473     },
24474
24475     showMonthPicker : function(){
24476         this.createMonthPicker();
24477         var size = this.el.getSize();
24478         this.monthPicker.setSize(size);
24479         this.monthPicker.child('table').setSize(size);
24480
24481         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24482         this.updateMPMonth(this.mpSelMonth);
24483         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24484         this.updateMPYear(this.mpSelYear);
24485
24486         this.monthPicker.slideIn('t', {duration:.2});
24487     },
24488
24489     updateMPYear : function(y){
24490         this.mpyear = y;
24491         var ys = this.mpYears.elements;
24492         for(var i = 1; i <= 10; i++){
24493             var td = ys[i-1], y2;
24494             if((i%2) == 0){
24495                 y2 = y + Math.round(i * .5);
24496                 td.firstChild.innerHTML = y2;
24497                 td.xyear = y2;
24498             }else{
24499                 y2 = y - (5-Math.round(i * .5));
24500                 td.firstChild.innerHTML = y2;
24501                 td.xyear = y2;
24502             }
24503             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24504         }
24505     },
24506
24507     updateMPMonth : function(sm){
24508         this.mpMonths.each(function(m, a, i){
24509             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24510         });
24511     },
24512
24513     selectMPMonth: function(m){
24514         
24515     },
24516
24517     onMonthClick : function(e, t){
24518         e.stopEvent();
24519         var el = new Roo.Element(t), pn;
24520         if(el.is('button.x-date-mp-cancel')){
24521             this.hideMonthPicker();
24522         }
24523         else if(el.is('button.x-date-mp-ok')){
24524             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24525             this.hideMonthPicker();
24526         }
24527         else if(pn = el.up('td.x-date-mp-month', 2)){
24528             this.mpMonths.removeClass('x-date-mp-sel');
24529             pn.addClass('x-date-mp-sel');
24530             this.mpSelMonth = pn.dom.xmonth;
24531         }
24532         else if(pn = el.up('td.x-date-mp-year', 2)){
24533             this.mpYears.removeClass('x-date-mp-sel');
24534             pn.addClass('x-date-mp-sel');
24535             this.mpSelYear = pn.dom.xyear;
24536         }
24537         else if(el.is('a.x-date-mp-prev')){
24538             this.updateMPYear(this.mpyear-10);
24539         }
24540         else if(el.is('a.x-date-mp-next')){
24541             this.updateMPYear(this.mpyear+10);
24542         }
24543     },
24544
24545     onMonthDblClick : function(e, t){
24546         e.stopEvent();
24547         var el = new Roo.Element(t), pn;
24548         if(pn = el.up('td.x-date-mp-month', 2)){
24549             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24550             this.hideMonthPicker();
24551         }
24552         else if(pn = el.up('td.x-date-mp-year', 2)){
24553             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24554             this.hideMonthPicker();
24555         }
24556     },
24557
24558     hideMonthPicker : function(disableAnim){
24559         if(this.monthPicker){
24560             if(disableAnim === true){
24561                 this.monthPicker.hide();
24562             }else{
24563                 this.monthPicker.slideOut('t', {duration:.2});
24564             }
24565         }
24566     },
24567
24568     // private
24569     showPrevMonth : function(e){
24570         this.update(this.activeDate.add("mo", -1));
24571     },
24572
24573     // private
24574     showNextMonth : function(e){
24575         this.update(this.activeDate.add("mo", 1));
24576     },
24577
24578     // private
24579     showPrevYear : function(){
24580         this.update(this.activeDate.add("y", -1));
24581     },
24582
24583     // private
24584     showNextYear : function(){
24585         this.update(this.activeDate.add("y", 1));
24586     },
24587
24588     // private
24589     handleMouseWheel : function(e){
24590         var delta = e.getWheelDelta();
24591         if(delta > 0){
24592             this.showPrevMonth();
24593             e.stopEvent();
24594         } else if(delta < 0){
24595             this.showNextMonth();
24596             e.stopEvent();
24597         }
24598     },
24599
24600     // private
24601     handleDateClick : function(e, t){
24602         e.stopEvent();
24603         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24604             this.setValue(new Date(t.dateValue));
24605             this.fireEvent("select", this, this.value);
24606         }
24607     },
24608
24609     // private
24610     selectToday : function(){
24611         this.setValue(new Date().clearTime());
24612         this.fireEvent("select", this, this.value);
24613     },
24614
24615     // private
24616     update : function(date){
24617         var vd = this.activeDate;
24618         this.activeDate = date;
24619         if(vd && this.el){
24620             var t = date.getTime();
24621             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24622                 this.cells.removeClass("x-date-selected");
24623                 this.cells.each(function(c){
24624                    if(c.dom.firstChild.dateValue == t){
24625                        c.addClass("x-date-selected");
24626                        setTimeout(function(){
24627                             try{c.dom.firstChild.focus();}catch(e){}
24628                        }, 50);
24629                        return false;
24630                    }
24631                 });
24632                 return;
24633             }
24634         }
24635         var days = date.getDaysInMonth();
24636         var firstOfMonth = date.getFirstDateOfMonth();
24637         var startingPos = firstOfMonth.getDay()-this.startDay;
24638
24639         if(startingPos <= this.startDay){
24640             startingPos += 7;
24641         }
24642
24643         var pm = date.add("mo", -1);
24644         var prevStart = pm.getDaysInMonth()-startingPos;
24645
24646         var cells = this.cells.elements;
24647         var textEls = this.textNodes;
24648         days += startingPos;
24649
24650         // convert everything to numbers so it's fast
24651         var day = 86400000;
24652         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24653         var today = new Date().clearTime().getTime();
24654         var sel = date.clearTime().getTime();
24655         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24656         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24657         var ddMatch = this.disabledDatesRE;
24658         var ddText = this.disabledDatesText;
24659         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24660         var ddaysText = this.disabledDaysText;
24661         var format = this.format;
24662
24663         var setCellClass = function(cal, cell){
24664             cell.title = "";
24665             var t = d.getTime();
24666             cell.firstChild.dateValue = t;
24667             if(t == today){
24668                 cell.className += " x-date-today";
24669                 cell.title = cal.todayText;
24670             }
24671             if(t == sel){
24672                 cell.className += " x-date-selected";
24673                 setTimeout(function(){
24674                     try{cell.firstChild.focus();}catch(e){}
24675                 }, 50);
24676             }
24677             // disabling
24678             if(t < min) {
24679                 cell.className = " x-date-disabled";
24680                 cell.title = cal.minText;
24681                 return;
24682             }
24683             if(t > max) {
24684                 cell.className = " x-date-disabled";
24685                 cell.title = cal.maxText;
24686                 return;
24687             }
24688             if(ddays){
24689                 if(ddays.indexOf(d.getDay()) != -1){
24690                     cell.title = ddaysText;
24691                     cell.className = " x-date-disabled";
24692                 }
24693             }
24694             if(ddMatch && format){
24695                 var fvalue = d.dateFormat(format);
24696                 if(ddMatch.test(fvalue)){
24697                     cell.title = ddText.replace("%0", fvalue);
24698                     cell.className = " x-date-disabled";
24699                 }
24700             }
24701         };
24702
24703         var i = 0;
24704         for(; i < startingPos; i++) {
24705             textEls[i].innerHTML = (++prevStart);
24706             d.setDate(d.getDate()+1);
24707             cells[i].className = "x-date-prevday";
24708             setCellClass(this, cells[i]);
24709         }
24710         for(; i < days; i++){
24711             intDay = i - startingPos + 1;
24712             textEls[i].innerHTML = (intDay);
24713             d.setDate(d.getDate()+1);
24714             cells[i].className = "x-date-active";
24715             setCellClass(this, cells[i]);
24716         }
24717         var extraDays = 0;
24718         for(; i < 42; i++) {
24719              textEls[i].innerHTML = (++extraDays);
24720              d.setDate(d.getDate()+1);
24721              cells[i].className = "x-date-nextday";
24722              setCellClass(this, cells[i]);
24723         }
24724
24725         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24726
24727         if(!this.internalRender){
24728             var main = this.el.dom.firstChild;
24729             var w = main.offsetWidth;
24730             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24731             Roo.fly(main).setWidth(w);
24732             this.internalRender = true;
24733             // opera does not respect the auto grow header center column
24734             // then, after it gets a width opera refuses to recalculate
24735             // without a second pass
24736             if(Roo.isOpera && !this.secondPass){
24737                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24738                 this.secondPass = true;
24739                 this.update.defer(10, this, [date]);
24740             }
24741         }
24742     }
24743 });/*
24744  * Based on:
24745  * Ext JS Library 1.1.1
24746  * Copyright(c) 2006-2007, Ext JS, LLC.
24747  *
24748  * Originally Released Under LGPL - original licence link has changed is not relivant.
24749  *
24750  * Fork - LGPL
24751  * <script type="text/javascript">
24752  */
24753 /**
24754  * @class Roo.TabPanel
24755  * @extends Roo.util.Observable
24756  * A lightweight tab container.
24757  * <br><br>
24758  * Usage:
24759  * <pre><code>
24760 // basic tabs 1, built from existing content
24761 var tabs = new Roo.TabPanel("tabs1");
24762 tabs.addTab("script", "View Script");
24763 tabs.addTab("markup", "View Markup");
24764 tabs.activate("script");
24765
24766 // more advanced tabs, built from javascript
24767 var jtabs = new Roo.TabPanel("jtabs");
24768 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24769
24770 // set up the UpdateManager
24771 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24772 var updater = tab2.getUpdateManager();
24773 updater.setDefaultUrl("ajax1.htm");
24774 tab2.on('activate', updater.refresh, updater, true);
24775
24776 // Use setUrl for Ajax loading
24777 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24778 tab3.setUrl("ajax2.htm", null, true);
24779
24780 // Disabled tab
24781 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24782 tab4.disable();
24783
24784 jtabs.activate("jtabs-1");
24785  * </code></pre>
24786  * @constructor
24787  * Create a new TabPanel.
24788  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24789  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24790  */
24791 Roo.TabPanel = function(container, config){
24792     /**
24793     * The container element for this TabPanel.
24794     * @type Roo.Element
24795     */
24796     this.el = Roo.get(container, true);
24797     if(config){
24798         if(typeof config == "boolean"){
24799             this.tabPosition = config ? "bottom" : "top";
24800         }else{
24801             Roo.apply(this, config);
24802         }
24803     }
24804     if(this.tabPosition == "bottom"){
24805         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24806         this.el.addClass("x-tabs-bottom");
24807     }
24808     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24809     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24810     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24811     if(Roo.isIE){
24812         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24813     }
24814     if(this.tabPosition != "bottom"){
24815     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24816      * @type Roo.Element
24817      */
24818       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24819       this.el.addClass("x-tabs-top");
24820     }
24821     this.items = [];
24822
24823     this.bodyEl.setStyle("position", "relative");
24824
24825     this.active = null;
24826     this.activateDelegate = this.activate.createDelegate(this);
24827
24828     this.addEvents({
24829         /**
24830          * @event tabchange
24831          * Fires when the active tab changes
24832          * @param {Roo.TabPanel} this
24833          * @param {Roo.TabPanelItem} activePanel The new active tab
24834          */
24835         "tabchange": true,
24836         /**
24837          * @event beforetabchange
24838          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24839          * @param {Roo.TabPanel} this
24840          * @param {Object} e Set cancel to true on this object to cancel the tab change
24841          * @param {Roo.TabPanelItem} tab The tab being changed to
24842          */
24843         "beforetabchange" : true
24844     });
24845
24846     Roo.EventManager.onWindowResize(this.onResize, this);
24847     this.cpad = this.el.getPadding("lr");
24848     this.hiddenCount = 0;
24849
24850     Roo.TabPanel.superclass.constructor.call(this);
24851 };
24852
24853 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24854         /*
24855          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24856          */
24857     tabPosition : "top",
24858         /*
24859          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24860          */
24861     currentTabWidth : 0,
24862         /*
24863          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24864          */
24865     minTabWidth : 40,
24866         /*
24867          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24868          */
24869     maxTabWidth : 250,
24870         /*
24871          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24872          */
24873     preferredTabWidth : 175,
24874         /*
24875          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24876          */
24877     resizeTabs : false,
24878         /*
24879          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24880          */
24881     monitorResize : true,
24882
24883     /**
24884      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24885      * @param {String} id The id of the div to use <b>or create</b>
24886      * @param {String} text The text for the tab
24887      * @param {String} content (optional) Content to put in the TabPanelItem body
24888      * @param {Boolean} closable (optional) True to create a close icon on the tab
24889      * @return {Roo.TabPanelItem} The created TabPanelItem
24890      */
24891     addTab : function(id, text, content, closable){
24892         var item = new Roo.TabPanelItem(this, id, text, closable);
24893         this.addTabItem(item);
24894         if(content){
24895             item.setContent(content);
24896         }
24897         return item;
24898     },
24899
24900     /**
24901      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24902      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24903      * @return {Roo.TabPanelItem}
24904      */
24905     getTab : function(id){
24906         return this.items[id];
24907     },
24908
24909     /**
24910      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24911      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24912      */
24913     hideTab : function(id){
24914         var t = this.items[id];
24915         if(!t.isHidden()){
24916            t.setHidden(true);
24917            this.hiddenCount++;
24918            this.autoSizeTabs();
24919         }
24920     },
24921
24922     /**
24923      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24924      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24925      */
24926     unhideTab : function(id){
24927         var t = this.items[id];
24928         if(t.isHidden()){
24929            t.setHidden(false);
24930            this.hiddenCount--;
24931            this.autoSizeTabs();
24932         }
24933     },
24934
24935     /**
24936      * Adds an existing {@link Roo.TabPanelItem}.
24937      * @param {Roo.TabPanelItem} item The TabPanelItem to add
24938      */
24939     addTabItem : function(item){
24940         this.items[item.id] = item;
24941         this.items.push(item);
24942         if(this.resizeTabs){
24943            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
24944            this.autoSizeTabs();
24945         }else{
24946             item.autoSize();
24947         }
24948     },
24949
24950     /**
24951      * Removes a {@link Roo.TabPanelItem}.
24952      * @param {String/Number} id The id or index of the TabPanelItem to remove.
24953      */
24954     removeTab : function(id){
24955         var items = this.items;
24956         var tab = items[id];
24957         if(!tab) return;
24958         var index = items.indexOf(tab);
24959         if(this.active == tab && items.length > 1){
24960             var newTab = this.getNextAvailable(index);
24961             if(newTab)newTab.activate();
24962         }
24963         this.stripEl.dom.removeChild(tab.pnode.dom);
24964         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
24965             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
24966         }
24967         items.splice(index, 1);
24968         delete this.items[tab.id];
24969         tab.fireEvent("close", tab);
24970         tab.purgeListeners();
24971         this.autoSizeTabs();
24972     },
24973
24974     getNextAvailable : function(start){
24975         var items = this.items;
24976         var index = start;
24977         // look for a next tab that will slide over to
24978         // replace the one being removed
24979         while(index < items.length){
24980             var item = items[++index];
24981             if(item && !item.isHidden()){
24982                 return item;
24983             }
24984         }
24985         // if one isn't found select the previous tab (on the left)
24986         index = start;
24987         while(index >= 0){
24988             var item = items[--index];
24989             if(item && !item.isHidden()){
24990                 return item;
24991             }
24992         }
24993         return null;
24994     },
24995
24996     /**
24997      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
24998      * @param {String/Number} id The id or index of the TabPanelItem to disable.
24999      */
25000     disableTab : function(id){
25001         var tab = this.items[id];
25002         if(tab && this.active != tab){
25003             tab.disable();
25004         }
25005     },
25006
25007     /**
25008      * Enables a {@link Roo.TabPanelItem} that is disabled.
25009      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25010      */
25011     enableTab : function(id){
25012         var tab = this.items[id];
25013         tab.enable();
25014     },
25015
25016     /**
25017      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25018      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25019      * @return {Roo.TabPanelItem} The TabPanelItem.
25020      */
25021     activate : function(id){
25022         var tab = this.items[id];
25023         if(!tab){
25024             return null;
25025         }
25026         if(tab == this.active || tab.disabled){
25027             return tab;
25028         }
25029         var e = {};
25030         this.fireEvent("beforetabchange", this, e, tab);
25031         if(e.cancel !== true && !tab.disabled){
25032             if(this.active){
25033                 this.active.hide();
25034             }
25035             this.active = this.items[id];
25036             this.active.show();
25037             this.fireEvent("tabchange", this, this.active);
25038         }
25039         return tab;
25040     },
25041
25042     /**
25043      * Gets the active {@link Roo.TabPanelItem}.
25044      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25045      */
25046     getActiveTab : function(){
25047         return this.active;
25048     },
25049
25050     /**
25051      * Updates the tab body element to fit the height of the container element
25052      * for overflow scrolling
25053      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25054      */
25055     syncHeight : function(targetHeight){
25056         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25057         var bm = this.bodyEl.getMargins();
25058         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25059         this.bodyEl.setHeight(newHeight);
25060         return newHeight;
25061     },
25062
25063     onResize : function(){
25064         if(this.monitorResize){
25065             this.autoSizeTabs();
25066         }
25067     },
25068
25069     /**
25070      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25071      */
25072     beginUpdate : function(){
25073         this.updating = true;
25074     },
25075
25076     /**
25077      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25078      */
25079     endUpdate : function(){
25080         this.updating = false;
25081         this.autoSizeTabs();
25082     },
25083
25084     /**
25085      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25086      */
25087     autoSizeTabs : function(){
25088         var count = this.items.length;
25089         var vcount = count - this.hiddenCount;
25090         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25091         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25092         var availWidth = Math.floor(w / vcount);
25093         var b = this.stripBody;
25094         if(b.getWidth() > w){
25095             var tabs = this.items;
25096             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25097             if(availWidth < this.minTabWidth){
25098                 /*if(!this.sleft){    // incomplete scrolling code
25099                     this.createScrollButtons();
25100                 }
25101                 this.showScroll();
25102                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25103             }
25104         }else{
25105             if(this.currentTabWidth < this.preferredTabWidth){
25106                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25107             }
25108         }
25109     },
25110
25111     /**
25112      * Returns the number of tabs in this TabPanel.
25113      * @return {Number}
25114      */
25115      getCount : function(){
25116          return this.items.length;
25117      },
25118
25119     /**
25120      * Resizes all the tabs to the passed width
25121      * @param {Number} The new width
25122      */
25123     setTabWidth : function(width){
25124         this.currentTabWidth = width;
25125         for(var i = 0, len = this.items.length; i < len; i++) {
25126                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25127         }
25128     },
25129
25130     /**
25131      * Destroys this TabPanel
25132      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25133      */
25134     destroy : function(removeEl){
25135         Roo.EventManager.removeResizeListener(this.onResize, this);
25136         for(var i = 0, len = this.items.length; i < len; i++){
25137             this.items[i].purgeListeners();
25138         }
25139         if(removeEl === true){
25140             this.el.update("");
25141             this.el.remove();
25142         }
25143     }
25144 });
25145
25146 /**
25147  * @class Roo.TabPanelItem
25148  * @extends Roo.util.Observable
25149  * Represents an individual item (tab plus body) in a TabPanel.
25150  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25151  * @param {String} id The id of this TabPanelItem
25152  * @param {String} text The text for the tab of this TabPanelItem
25153  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25154  */
25155 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25156     /**
25157      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25158      * @type Roo.TabPanel
25159      */
25160     this.tabPanel = tabPanel;
25161     /**
25162      * The id for this TabPanelItem
25163      * @type String
25164      */
25165     this.id = id;
25166     /** @private */
25167     this.disabled = false;
25168     /** @private */
25169     this.text = text;
25170     /** @private */
25171     this.loaded = false;
25172     this.closable = closable;
25173
25174     /**
25175      * The body element for this TabPanelItem.
25176      * @type Roo.Element
25177      */
25178     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25179     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25180     this.bodyEl.setStyle("display", "block");
25181     this.bodyEl.setStyle("zoom", "1");
25182     this.hideAction();
25183
25184     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25185     /** @private */
25186     this.el = Roo.get(els.el, true);
25187     this.inner = Roo.get(els.inner, true);
25188     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25189     this.pnode = Roo.get(els.el.parentNode, true);
25190     this.el.on("mousedown", this.onTabMouseDown, this);
25191     this.el.on("click", this.onTabClick, this);
25192     /** @private */
25193     if(closable){
25194         var c = Roo.get(els.close, true);
25195         c.dom.title = this.closeText;
25196         c.addClassOnOver("close-over");
25197         c.on("click", this.closeClick, this);
25198      }
25199
25200     this.addEvents({
25201          /**
25202          * @event activate
25203          * Fires when this tab becomes the active tab.
25204          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25205          * @param {Roo.TabPanelItem} this
25206          */
25207         "activate": true,
25208         /**
25209          * @event beforeclose
25210          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25211          * @param {Roo.TabPanelItem} this
25212          * @param {Object} e Set cancel to true on this object to cancel the close.
25213          */
25214         "beforeclose": true,
25215         /**
25216          * @event close
25217          * Fires when this tab is closed.
25218          * @param {Roo.TabPanelItem} this
25219          */
25220          "close": true,
25221         /**
25222          * @event deactivate
25223          * Fires when this tab is no longer the active tab.
25224          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25225          * @param {Roo.TabPanelItem} this
25226          */
25227          "deactivate" : true
25228     });
25229     this.hidden = false;
25230
25231     Roo.TabPanelItem.superclass.constructor.call(this);
25232 };
25233
25234 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25235     purgeListeners : function(){
25236        Roo.util.Observable.prototype.purgeListeners.call(this);
25237        this.el.removeAllListeners();
25238     },
25239     /**
25240      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25241      */
25242     show : function(){
25243         this.pnode.addClass("on");
25244         this.showAction();
25245         if(Roo.isOpera){
25246             this.tabPanel.stripWrap.repaint();
25247         }
25248         this.fireEvent("activate", this.tabPanel, this);
25249     },
25250
25251     /**
25252      * Returns true if this tab is the active tab.
25253      * @return {Boolean}
25254      */
25255     isActive : function(){
25256         return this.tabPanel.getActiveTab() == this;
25257     },
25258
25259     /**
25260      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25261      */
25262     hide : function(){
25263         this.pnode.removeClass("on");
25264         this.hideAction();
25265         this.fireEvent("deactivate", this.tabPanel, this);
25266     },
25267
25268     hideAction : function(){
25269         this.bodyEl.hide();
25270         this.bodyEl.setStyle("position", "absolute");
25271         this.bodyEl.setLeft("-20000px");
25272         this.bodyEl.setTop("-20000px");
25273     },
25274
25275     showAction : function(){
25276         this.bodyEl.setStyle("position", "relative");
25277         this.bodyEl.setTop("");
25278         this.bodyEl.setLeft("");
25279         this.bodyEl.show();
25280     },
25281
25282     /**
25283      * Set the tooltip for the tab.
25284      * @param {String} tooltip The tab's tooltip
25285      */
25286     setTooltip : function(text){
25287         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25288             this.textEl.dom.qtip = text;
25289             this.textEl.dom.removeAttribute('title');
25290         }else{
25291             this.textEl.dom.title = text;
25292         }
25293     },
25294
25295     onTabClick : function(e){
25296         e.preventDefault();
25297         this.tabPanel.activate(this.id);
25298     },
25299
25300     onTabMouseDown : function(e){
25301         e.preventDefault();
25302         this.tabPanel.activate(this.id);
25303     },
25304
25305     getWidth : function(){
25306         return this.inner.getWidth();
25307     },
25308
25309     setWidth : function(width){
25310         var iwidth = width - this.pnode.getPadding("lr");
25311         this.inner.setWidth(iwidth);
25312         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25313         this.pnode.setWidth(width);
25314     },
25315
25316     /**
25317      * Show or hide the tab
25318      * @param {Boolean} hidden True to hide or false to show.
25319      */
25320     setHidden : function(hidden){
25321         this.hidden = hidden;
25322         this.pnode.setStyle("display", hidden ? "none" : "");
25323     },
25324
25325     /**
25326      * Returns true if this tab is "hidden"
25327      * @return {Boolean}
25328      */
25329     isHidden : function(){
25330         return this.hidden;
25331     },
25332
25333     /**
25334      * Returns the text for this tab
25335      * @return {String}
25336      */
25337     getText : function(){
25338         return this.text;
25339     },
25340
25341     autoSize : function(){
25342         //this.el.beginMeasure();
25343         this.textEl.setWidth(1);
25344         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25345         //this.el.endMeasure();
25346     },
25347
25348     /**
25349      * Sets the text for the tab (Note: this also sets the tooltip text)
25350      * @param {String} text The tab's text and tooltip
25351      */
25352     setText : function(text){
25353         this.text = text;
25354         this.textEl.update(text);
25355         this.setTooltip(text);
25356         if(!this.tabPanel.resizeTabs){
25357             this.autoSize();
25358         }
25359     },
25360     /**
25361      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25362      */
25363     activate : function(){
25364         this.tabPanel.activate(this.id);
25365     },
25366
25367     /**
25368      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25369      */
25370     disable : function(){
25371         if(this.tabPanel.active != this){
25372             this.disabled = true;
25373             this.pnode.addClass("disabled");
25374         }
25375     },
25376
25377     /**
25378      * Enables this TabPanelItem if it was previously disabled.
25379      */
25380     enable : function(){
25381         this.disabled = false;
25382         this.pnode.removeClass("disabled");
25383     },
25384
25385     /**
25386      * Sets the content for this TabPanelItem.
25387      * @param {String} content The content
25388      * @param {Boolean} loadScripts true to look for and load scripts
25389      */
25390     setContent : function(content, loadScripts){
25391         this.bodyEl.update(content, loadScripts);
25392     },
25393
25394     /**
25395      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25396      * @return {Roo.UpdateManager} The UpdateManager
25397      */
25398     getUpdateManager : function(){
25399         return this.bodyEl.getUpdateManager();
25400     },
25401
25402     /**
25403      * Set a URL to be used to load the content for this TabPanelItem.
25404      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25405      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25406      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25407      * @return {Roo.UpdateManager} The UpdateManager
25408      */
25409     setUrl : function(url, params, loadOnce){
25410         if(this.refreshDelegate){
25411             this.un('activate', this.refreshDelegate);
25412         }
25413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25414         this.on("activate", this.refreshDelegate);
25415         return this.bodyEl.getUpdateManager();
25416     },
25417
25418     /** @private */
25419     _handleRefresh : function(url, params, loadOnce){
25420         if(!loadOnce || !this.loaded){
25421             var updater = this.bodyEl.getUpdateManager();
25422             updater.update(url, params, this._setLoaded.createDelegate(this));
25423         }
25424     },
25425
25426     /**
25427      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25428      *   Will fail silently if the setUrl method has not been called.
25429      *   This does not activate the panel, just updates its content.
25430      */
25431     refresh : function(){
25432         if(this.refreshDelegate){
25433            this.loaded = false;
25434            this.refreshDelegate();
25435         }
25436     },
25437
25438     /** @private */
25439     _setLoaded : function(){
25440         this.loaded = true;
25441     },
25442
25443     /** @private */
25444     closeClick : function(e){
25445         var o = {};
25446         e.stopEvent();
25447         this.fireEvent("beforeclose", this, o);
25448         if(o.cancel !== true){
25449             this.tabPanel.removeTab(this.id);
25450         }
25451     },
25452     /**
25453      * The text displayed in the tooltip for the close icon.
25454      * @type String
25455      */
25456     closeText : "Close this tab"
25457 });
25458
25459 /** @private */
25460 Roo.TabPanel.prototype.createStrip = function(container){
25461     var strip = document.createElement("div");
25462     strip.className = "x-tabs-wrap";
25463     container.appendChild(strip);
25464     return strip;
25465 };
25466 /** @private */
25467 Roo.TabPanel.prototype.createStripList = function(strip){
25468     // div wrapper for retard IE
25469     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25470     return strip.firstChild.firstChild.firstChild.firstChild;
25471 };
25472 /** @private */
25473 Roo.TabPanel.prototype.createBody = function(container){
25474     var body = document.createElement("div");
25475     Roo.id(body, "tab-body");
25476     Roo.fly(body).addClass("x-tabs-body");
25477     container.appendChild(body);
25478     return body;
25479 };
25480 /** @private */
25481 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25482     var body = Roo.getDom(id);
25483     if(!body){
25484         body = document.createElement("div");
25485         body.id = id;
25486     }
25487     Roo.fly(body).addClass("x-tabs-item-body");
25488     bodyEl.insertBefore(body, bodyEl.firstChild);
25489     return body;
25490 };
25491 /** @private */
25492 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25493     var td = document.createElement("td");
25494     stripEl.appendChild(td);
25495     if(closable){
25496         td.className = "x-tabs-closable";
25497         if(!this.closeTpl){
25498             this.closeTpl = new Roo.Template(
25499                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25500                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25501                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25502             );
25503         }
25504         var el = this.closeTpl.overwrite(td, {"text": text});
25505         var close = el.getElementsByTagName("div")[0];
25506         var inner = el.getElementsByTagName("em")[0];
25507         return {"el": el, "close": close, "inner": inner};
25508     } else {
25509         if(!this.tabTpl){
25510             this.tabTpl = new Roo.Template(
25511                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25512                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25513             );
25514         }
25515         var el = this.tabTpl.overwrite(td, {"text": text});
25516         var inner = el.getElementsByTagName("em")[0];
25517         return {"el": el, "inner": inner};
25518     }
25519 };/*
25520  * Based on:
25521  * Ext JS Library 1.1.1
25522  * Copyright(c) 2006-2007, Ext JS, LLC.
25523  *
25524  * Originally Released Under LGPL - original licence link has changed is not relivant.
25525  *
25526  * Fork - LGPL
25527  * <script type="text/javascript">
25528  */
25529
25530 /**
25531  * @class Roo.Button
25532  * @extends Roo.util.Observable
25533  * Simple Button class
25534  * @cfg {String} text The button text
25535  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25536  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25537  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25538  * @cfg {Object} scope The scope of the handler
25539  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25540  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25541  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25542  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25543  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25544  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25545    applies if enableToggle = true)
25546  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25547  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25548   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25549  * @constructor
25550  * Create a new button
25551  * @param {Object} config The config object
25552  */
25553 Roo.Button = function(renderTo, config)
25554 {
25555     if (!config) {
25556         config = renderTo;
25557         renderTo = config.renderTo || false;
25558     }
25559     
25560     Roo.apply(this, config);
25561     this.addEvents({
25562         /**
25563              * @event click
25564              * Fires when this button is clicked
25565              * @param {Button} this
25566              * @param {EventObject} e The click event
25567              */
25568             "click" : true,
25569         /**
25570              * @event toggle
25571              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25572              * @param {Button} this
25573              * @param {Boolean} pressed
25574              */
25575             "toggle" : true,
25576         /**
25577              * @event mouseover
25578              * Fires when the mouse hovers over the button
25579              * @param {Button} this
25580              * @param {Event} e The event object
25581              */
25582         'mouseover' : true,
25583         /**
25584              * @event mouseout
25585              * Fires when the mouse exits the button
25586              * @param {Button} this
25587              * @param {Event} e The event object
25588              */
25589         'mouseout': true,
25590          /**
25591              * @event render
25592              * Fires when the button is rendered
25593              * @param {Button} this
25594              */
25595         'render': true
25596     });
25597     if(this.menu){
25598         this.menu = Roo.menu.MenuMgr.get(this.menu);
25599     }
25600     if(renderTo){
25601         this.render(renderTo);
25602     }
25603     
25604     Roo.util.Observable.call(this);
25605 };
25606
25607 Roo.extend(Roo.Button, Roo.util.Observable, {
25608     /**
25609      * 
25610      */
25611     
25612     /**
25613      * Read-only. True if this button is hidden
25614      * @type Boolean
25615      */
25616     hidden : false,
25617     /**
25618      * Read-only. True if this button is disabled
25619      * @type Boolean
25620      */
25621     disabled : false,
25622     /**
25623      * Read-only. True if this button is pressed (only if enableToggle = true)
25624      * @type Boolean
25625      */
25626     pressed : false,
25627
25628     /**
25629      * @cfg {Number} tabIndex 
25630      * The DOM tabIndex for this button (defaults to undefined)
25631      */
25632     tabIndex : undefined,
25633
25634     /**
25635      * @cfg {Boolean} enableToggle
25636      * True to enable pressed/not pressed toggling (defaults to false)
25637      */
25638     enableToggle: false,
25639     /**
25640      * @cfg {Mixed} menu
25641      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25642      */
25643     menu : undefined,
25644     /**
25645      * @cfg {String} menuAlign
25646      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25647      */
25648     menuAlign : "tl-bl?",
25649
25650     /**
25651      * @cfg {String} iconCls
25652      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25653      */
25654     iconCls : undefined,
25655     /**
25656      * @cfg {String} type
25657      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25658      */
25659     type : 'button',
25660
25661     // private
25662     menuClassTarget: 'tr',
25663
25664     /**
25665      * @cfg {String} clickEvent
25666      * The type of event to map to the button's event handler (defaults to 'click')
25667      */
25668     clickEvent : 'click',
25669
25670     /**
25671      * @cfg {Boolean} handleMouseEvents
25672      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25673      */
25674     handleMouseEvents : true,
25675
25676     /**
25677      * @cfg {String} tooltipType
25678      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25679      */
25680     tooltipType : 'qtip',
25681
25682     /**
25683      * @cfg {String} cls
25684      * A CSS class to apply to the button's main element.
25685      */
25686     
25687     /**
25688      * @cfg {Roo.Template} template (Optional)
25689      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25690      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25691      * require code modifications if required elements (e.g. a button) aren't present.
25692      */
25693
25694     // private
25695     render : function(renderTo){
25696         var btn;
25697         if(this.hideParent){
25698             this.parentEl = Roo.get(renderTo);
25699         }
25700         if(!this.dhconfig){
25701             if(!this.template){
25702                 if(!Roo.Button.buttonTemplate){
25703                     // hideous table template
25704                     Roo.Button.buttonTemplate = new Roo.Template(
25705                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25706                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
25707                         "</tr></tbody></table>");
25708                 }
25709                 this.template = Roo.Button.buttonTemplate;
25710             }
25711             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25712             var btnEl = btn.child("button:first");
25713             btnEl.on('focus', this.onFocus, this);
25714             btnEl.on('blur', this.onBlur, this);
25715             if(this.cls){
25716                 btn.addClass(this.cls);
25717             }
25718             if(this.icon){
25719                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25720             }
25721             if(this.iconCls){
25722                 btnEl.addClass(this.iconCls);
25723                 if(!this.cls){
25724                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25725                 }
25726             }
25727             if(this.tabIndex !== undefined){
25728                 btnEl.dom.tabIndex = this.tabIndex;
25729             }
25730             if(this.tooltip){
25731                 if(typeof this.tooltip == 'object'){
25732                     Roo.QuickTips.tips(Roo.apply({
25733                           target: btnEl.id
25734                     }, this.tooltip));
25735                 } else {
25736                     btnEl.dom[this.tooltipType] = this.tooltip;
25737                 }
25738             }
25739         }else{
25740             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25741         }
25742         this.el = btn;
25743         if(this.id){
25744             this.el.dom.id = this.el.id = this.id;
25745         }
25746         if(this.menu){
25747             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25748             this.menu.on("show", this.onMenuShow, this);
25749             this.menu.on("hide", this.onMenuHide, this);
25750         }
25751         btn.addClass("x-btn");
25752         if(Roo.isIE && !Roo.isIE7){
25753             this.autoWidth.defer(1, this);
25754         }else{
25755             this.autoWidth();
25756         }
25757         if(this.handleMouseEvents){
25758             btn.on("mouseover", this.onMouseOver, this);
25759             btn.on("mouseout", this.onMouseOut, this);
25760             btn.on("mousedown", this.onMouseDown, this);
25761         }
25762         btn.on(this.clickEvent, this.onClick, this);
25763         //btn.on("mouseup", this.onMouseUp, this);
25764         if(this.hidden){
25765             this.hide();
25766         }
25767         if(this.disabled){
25768             this.disable();
25769         }
25770         Roo.ButtonToggleMgr.register(this);
25771         if(this.pressed){
25772             this.el.addClass("x-btn-pressed");
25773         }
25774         if(this.repeat){
25775             var repeater = new Roo.util.ClickRepeater(btn,
25776                 typeof this.repeat == "object" ? this.repeat : {}
25777             );
25778             repeater.on("click", this.onClick,  this);
25779         }
25780         this.fireEvent('render', this);
25781         
25782     },
25783     /**
25784      * Returns the button's underlying element
25785      * @return {Roo.Element} The element
25786      */
25787     getEl : function(){
25788         return this.el;  
25789     },
25790     
25791     /**
25792      * Destroys this Button and removes any listeners.
25793      */
25794     destroy : function(){
25795         Roo.ButtonToggleMgr.unregister(this);
25796         this.el.removeAllListeners();
25797         this.purgeListeners();
25798         this.el.remove();
25799     },
25800
25801     // private
25802     autoWidth : function(){
25803         if(this.el){
25804             this.el.setWidth("auto");
25805             if(Roo.isIE7 && Roo.isStrict){
25806                 var ib = this.el.child('button');
25807                 if(ib && ib.getWidth() > 20){
25808                     ib.clip();
25809                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25810                 }
25811             }
25812             if(this.minWidth){
25813                 if(this.hidden){
25814                     this.el.beginMeasure();
25815                 }
25816                 if(this.el.getWidth() < this.minWidth){
25817                     this.el.setWidth(this.minWidth);
25818                 }
25819                 if(this.hidden){
25820                     this.el.endMeasure();
25821                 }
25822             }
25823         }
25824     },
25825
25826     /**
25827      * Assigns this button's click handler
25828      * @param {Function} handler The function to call when the button is clicked
25829      * @param {Object} scope (optional) Scope for the function passed in
25830      */
25831     setHandler : function(handler, scope){
25832         this.handler = handler;
25833         this.scope = scope;  
25834     },
25835     
25836     /**
25837      * Sets this button's text
25838      * @param {String} text The button text
25839      */
25840     setText : function(text){
25841         this.text = text;
25842         if(this.el){
25843             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25844         }
25845         this.autoWidth();
25846     },
25847     
25848     /**
25849      * Gets the text for this button
25850      * @return {String} The button text
25851      */
25852     getText : function(){
25853         return this.text;  
25854     },
25855     
25856     /**
25857      * Show this button
25858      */
25859     show: function(){
25860         this.hidden = false;
25861         if(this.el){
25862             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25863         }
25864     },
25865     
25866     /**
25867      * Hide this button
25868      */
25869     hide: function(){
25870         this.hidden = true;
25871         if(this.el){
25872             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25873         }
25874     },
25875     
25876     /**
25877      * Convenience function for boolean show/hide
25878      * @param {Boolean} visible True to show, false to hide
25879      */
25880     setVisible: function(visible){
25881         if(visible) {
25882             this.show();
25883         }else{
25884             this.hide();
25885         }
25886     },
25887     
25888     /**
25889      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25890      * @param {Boolean} state (optional) Force a particular state
25891      */
25892     toggle : function(state){
25893         state = state === undefined ? !this.pressed : state;
25894         if(state != this.pressed){
25895             if(state){
25896                 this.el.addClass("x-btn-pressed");
25897                 this.pressed = true;
25898                 this.fireEvent("toggle", this, true);
25899             }else{
25900                 this.el.removeClass("x-btn-pressed");
25901                 this.pressed = false;
25902                 this.fireEvent("toggle", this, false);
25903             }
25904             if(this.toggleHandler){
25905                 this.toggleHandler.call(this.scope || this, this, state);
25906             }
25907         }
25908     },
25909     
25910     /**
25911      * Focus the button
25912      */
25913     focus : function(){
25914         this.el.child('button:first').focus();
25915     },
25916     
25917     /**
25918      * Disable this button
25919      */
25920     disable : function(){
25921         if(this.el){
25922             this.el.addClass("x-btn-disabled");
25923         }
25924         this.disabled = true;
25925     },
25926     
25927     /**
25928      * Enable this button
25929      */
25930     enable : function(){
25931         if(this.el){
25932             this.el.removeClass("x-btn-disabled");
25933         }
25934         this.disabled = false;
25935     },
25936
25937     /**
25938      * Convenience function for boolean enable/disable
25939      * @param {Boolean} enabled True to enable, false to disable
25940      */
25941     setDisabled : function(v){
25942         this[v !== true ? "enable" : "disable"]();
25943     },
25944
25945     // private
25946     onClick : function(e){
25947         if(e){
25948             e.preventDefault();
25949         }
25950         if(e.button != 0){
25951             return;
25952         }
25953         if(!this.disabled){
25954             if(this.enableToggle){
25955                 this.toggle();
25956             }
25957             if(this.menu && !this.menu.isVisible()){
25958                 this.menu.show(this.el, this.menuAlign);
25959             }
25960             this.fireEvent("click", this, e);
25961             if(this.handler){
25962                 this.el.removeClass("x-btn-over");
25963                 this.handler.call(this.scope || this, this, e);
25964             }
25965         }
25966     },
25967     // private
25968     onMouseOver : function(e){
25969         if(!this.disabled){
25970             this.el.addClass("x-btn-over");
25971             this.fireEvent('mouseover', this, e);
25972         }
25973     },
25974     // private
25975     onMouseOut : function(e){
25976         if(!e.within(this.el,  true)){
25977             this.el.removeClass("x-btn-over");
25978             this.fireEvent('mouseout', this, e);
25979         }
25980     },
25981     // private
25982     onFocus : function(e){
25983         if(!this.disabled){
25984             this.el.addClass("x-btn-focus");
25985         }
25986     },
25987     // private
25988     onBlur : function(e){
25989         this.el.removeClass("x-btn-focus");
25990     },
25991     // private
25992     onMouseDown : function(e){
25993         if(!this.disabled && e.button == 0){
25994             this.el.addClass("x-btn-click");
25995             Roo.get(document).on('mouseup', this.onMouseUp, this);
25996         }
25997     },
25998     // private
25999     onMouseUp : function(e){
26000         if(e.button == 0){
26001             this.el.removeClass("x-btn-click");
26002             Roo.get(document).un('mouseup', this.onMouseUp, this);
26003         }
26004     },
26005     // private
26006     onMenuShow : function(e){
26007         this.el.addClass("x-btn-menu-active");
26008     },
26009     // private
26010     onMenuHide : function(e){
26011         this.el.removeClass("x-btn-menu-active");
26012     }   
26013 });
26014
26015 // Private utility class used by Button
26016 Roo.ButtonToggleMgr = function(){
26017    var groups = {};
26018    
26019    function toggleGroup(btn, state){
26020        if(state){
26021            var g = groups[btn.toggleGroup];
26022            for(var i = 0, l = g.length; i < l; i++){
26023                if(g[i] != btn){
26024                    g[i].toggle(false);
26025                }
26026            }
26027        }
26028    }
26029    
26030    return {
26031        register : function(btn){
26032            if(!btn.toggleGroup){
26033                return;
26034            }
26035            var g = groups[btn.toggleGroup];
26036            if(!g){
26037                g = groups[btn.toggleGroup] = [];
26038            }
26039            g.push(btn);
26040            btn.on("toggle", toggleGroup);
26041        },
26042        
26043        unregister : function(btn){
26044            if(!btn.toggleGroup){
26045                return;
26046            }
26047            var g = groups[btn.toggleGroup];
26048            if(g){
26049                g.remove(btn);
26050                btn.un("toggle", toggleGroup);
26051            }
26052        }
26053    };
26054 }();/*
26055  * Based on:
26056  * Ext JS Library 1.1.1
26057  * Copyright(c) 2006-2007, Ext JS, LLC.
26058  *
26059  * Originally Released Under LGPL - original licence link has changed is not relivant.
26060  *
26061  * Fork - LGPL
26062  * <script type="text/javascript">
26063  */
26064  
26065 /**
26066  * @class Roo.SplitButton
26067  * @extends Roo.Button
26068  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26069  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26070  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26071  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26072  * @cfg {String} arrowTooltip The title attribute of the arrow
26073  * @constructor
26074  * Create a new menu button
26075  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26076  * @param {Object} config The config object
26077  */
26078 Roo.SplitButton = function(renderTo, config){
26079     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26080     /**
26081      * @event arrowclick
26082      * Fires when this button's arrow is clicked
26083      * @param {SplitButton} this
26084      * @param {EventObject} e The click event
26085      */
26086     this.addEvents({"arrowclick":true});
26087 };
26088
26089 Roo.extend(Roo.SplitButton, Roo.Button, {
26090     render : function(renderTo){
26091         // this is one sweet looking template!
26092         var tpl = new Roo.Template(
26093             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26094             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26095             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26096             "</tbody></table></td><td>",
26097             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26098             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26099             "</tbody></table></td></tr></table>"
26100         );
26101         var btn = tpl.append(renderTo, [this.text, this.type], true);
26102         var btnEl = btn.child("button");
26103         if(this.cls){
26104             btn.addClass(this.cls);
26105         }
26106         if(this.icon){
26107             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26108         }
26109         if(this.iconCls){
26110             btnEl.addClass(this.iconCls);
26111             if(!this.cls){
26112                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26113             }
26114         }
26115         this.el = btn;
26116         if(this.handleMouseEvents){
26117             btn.on("mouseover", this.onMouseOver, this);
26118             btn.on("mouseout", this.onMouseOut, this);
26119             btn.on("mousedown", this.onMouseDown, this);
26120             btn.on("mouseup", this.onMouseUp, this);
26121         }
26122         btn.on(this.clickEvent, this.onClick, this);
26123         if(this.tooltip){
26124             if(typeof this.tooltip == 'object'){
26125                 Roo.QuickTips.tips(Roo.apply({
26126                       target: btnEl.id
26127                 }, this.tooltip));
26128             } else {
26129                 btnEl.dom[this.tooltipType] = this.tooltip;
26130             }
26131         }
26132         if(this.arrowTooltip){
26133             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26134         }
26135         if(this.hidden){
26136             this.hide();
26137         }
26138         if(this.disabled){
26139             this.disable();
26140         }
26141         if(this.pressed){
26142             this.el.addClass("x-btn-pressed");
26143         }
26144         if(Roo.isIE && !Roo.isIE7){
26145             this.autoWidth.defer(1, this);
26146         }else{
26147             this.autoWidth();
26148         }
26149         if(this.menu){
26150             this.menu.on("show", this.onMenuShow, this);
26151             this.menu.on("hide", this.onMenuHide, this);
26152         }
26153         this.fireEvent('render', this);
26154     },
26155
26156     // private
26157     autoWidth : function(){
26158         if(this.el){
26159             var tbl = this.el.child("table:first");
26160             var tbl2 = this.el.child("table:last");
26161             this.el.setWidth("auto");
26162             tbl.setWidth("auto");
26163             if(Roo.isIE7 && Roo.isStrict){
26164                 var ib = this.el.child('button:first');
26165                 if(ib && ib.getWidth() > 20){
26166                     ib.clip();
26167                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26168                 }
26169             }
26170             if(this.minWidth){
26171                 if(this.hidden){
26172                     this.el.beginMeasure();
26173                 }
26174                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26175                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26176                 }
26177                 if(this.hidden){
26178                     this.el.endMeasure();
26179                 }
26180             }
26181             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26182         } 
26183     },
26184     /**
26185      * Sets this button's click handler
26186      * @param {Function} handler The function to call when the button is clicked
26187      * @param {Object} scope (optional) Scope for the function passed above
26188      */
26189     setHandler : function(handler, scope){
26190         this.handler = handler;
26191         this.scope = scope;  
26192     },
26193     
26194     /**
26195      * Sets this button's arrow click handler
26196      * @param {Function} handler The function to call when the arrow is clicked
26197      * @param {Object} scope (optional) Scope for the function passed above
26198      */
26199     setArrowHandler : function(handler, scope){
26200         this.arrowHandler = handler;
26201         this.scope = scope;  
26202     },
26203     
26204     /**
26205      * Focus the button
26206      */
26207     focus : function(){
26208         if(this.el){
26209             this.el.child("button:first").focus();
26210         }
26211     },
26212
26213     // private
26214     onClick : function(e){
26215         e.preventDefault();
26216         if(!this.disabled){
26217             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26218                 if(this.menu && !this.menu.isVisible()){
26219                     this.menu.show(this.el, this.menuAlign);
26220                 }
26221                 this.fireEvent("arrowclick", this, e);
26222                 if(this.arrowHandler){
26223                     this.arrowHandler.call(this.scope || this, this, e);
26224                 }
26225             }else{
26226                 this.fireEvent("click", this, e);
26227                 if(this.handler){
26228                     this.handler.call(this.scope || this, this, e);
26229                 }
26230             }
26231         }
26232     },
26233     // private
26234     onMouseDown : function(e){
26235         if(!this.disabled){
26236             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26237         }
26238     },
26239     // private
26240     onMouseUp : function(e){
26241         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26242     }   
26243 });
26244
26245
26246 // backwards compat
26247 Roo.MenuButton = Roo.SplitButton;/*
26248  * Based on:
26249  * Ext JS Library 1.1.1
26250  * Copyright(c) 2006-2007, Ext JS, LLC.
26251  *
26252  * Originally Released Under LGPL - original licence link has changed is not relivant.
26253  *
26254  * Fork - LGPL
26255  * <script type="text/javascript">
26256  */
26257
26258 /**
26259  * @class Roo.Toolbar
26260  * Basic Toolbar class.
26261  * @constructor
26262  * Creates a new Toolbar
26263  * @param {Object} config The config object
26264  */ 
26265 Roo.Toolbar = function(container, buttons, config)
26266 {
26267     /// old consturctor format still supported..
26268     if(container instanceof Array){ // omit the container for later rendering
26269         buttons = container;
26270         config = buttons;
26271         container = null;
26272     }
26273     if (typeof(container) == 'object' && container.xtype) {
26274         config = container;
26275         container = config.container;
26276         buttons = config.buttons; // not really - use items!!
26277     }
26278     var xitems = [];
26279     if (config && config.items) {
26280         xitems = config.items;
26281         delete config.items;
26282     }
26283     Roo.apply(this, config);
26284     this.buttons = buttons;
26285     
26286     if(container){
26287         this.render(container);
26288     }
26289     Roo.each(xitems, function(b) {
26290         this.add(b);
26291     }, this);
26292     
26293 };
26294
26295 Roo.Toolbar.prototype = {
26296     /**
26297      * @cfg {Roo.data.Store} items
26298      * array of button configs or elements to add
26299      */
26300     
26301     /**
26302      * @cfg {String/HTMLElement/Element} container
26303      * The id or element that will contain the toolbar
26304      */
26305     // private
26306     render : function(ct){
26307         this.el = Roo.get(ct);
26308         if(this.cls){
26309             this.el.addClass(this.cls);
26310         }
26311         // using a table allows for vertical alignment
26312         // 100% width is needed by Safari...
26313         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26314         this.tr = this.el.child("tr", true);
26315         var autoId = 0;
26316         this.items = new Roo.util.MixedCollection(false, function(o){
26317             return o.id || ("item" + (++autoId));
26318         });
26319         if(this.buttons){
26320             this.add.apply(this, this.buttons);
26321             delete this.buttons;
26322         }
26323     },
26324
26325     /**
26326      * Adds element(s) to the toolbar -- this function takes a variable number of 
26327      * arguments of mixed type and adds them to the toolbar.
26328      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26329      * <ul>
26330      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26331      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26332      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26333      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26334      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26335      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26336      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26337      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26338      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26339      * </ul>
26340      * @param {Mixed} arg2
26341      * @param {Mixed} etc.
26342      */
26343     add : function(){
26344         var a = arguments, l = a.length;
26345         for(var i = 0; i < l; i++){
26346             this._add(a[i]);
26347         }
26348     },
26349     // private..
26350     _add : function(el) {
26351         
26352         if (el.xtype) {
26353             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26354         }
26355         
26356         if (el.applyTo){ // some kind of form field
26357             return this.addField(el);
26358         } 
26359         if (el.render){ // some kind of Toolbar.Item
26360             return this.addItem(el);
26361         }
26362         if (typeof el == "string"){ // string
26363             if(el == "separator" || el == "-"){
26364                 return this.addSeparator();
26365             }
26366             if (el == " "){
26367                 return this.addSpacer();
26368             }
26369             if(el == "->"){
26370                 return this.addFill();
26371             }
26372             return this.addText(el);
26373             
26374         }
26375         if(el.tagName){ // element
26376             return this.addElement(el);
26377         }
26378         if(typeof el == "object"){ // must be button config?
26379             return this.addButton(el);
26380         }
26381         // and now what?!?!
26382         return false;
26383         
26384     },
26385     
26386     /**
26387      * Add an Xtype element
26388      * @param {Object} xtype Xtype Object
26389      * @return {Object} created Object
26390      */
26391     addxtype : function(e){
26392         return this.add(e);  
26393     },
26394     
26395     /**
26396      * Returns the Element for this toolbar.
26397      * @return {Roo.Element}
26398      */
26399     getEl : function(){
26400         return this.el;  
26401     },
26402     
26403     /**
26404      * Adds a separator
26405      * @return {Roo.Toolbar.Item} The separator item
26406      */
26407     addSeparator : function(){
26408         return this.addItem(new Roo.Toolbar.Separator());
26409     },
26410
26411     /**
26412      * Adds a spacer element
26413      * @return {Roo.Toolbar.Spacer} The spacer item
26414      */
26415     addSpacer : function(){
26416         return this.addItem(new Roo.Toolbar.Spacer());
26417     },
26418
26419     /**
26420      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26421      * @return {Roo.Toolbar.Fill} The fill item
26422      */
26423     addFill : function(){
26424         return this.addItem(new Roo.Toolbar.Fill());
26425     },
26426
26427     /**
26428      * Adds any standard HTML element to the toolbar
26429      * @param {String/HTMLElement/Element} el The element or id of the element to add
26430      * @return {Roo.Toolbar.Item} The element's item
26431      */
26432     addElement : function(el){
26433         return this.addItem(new Roo.Toolbar.Item(el));
26434     },
26435     /**
26436      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26437      * @type Roo.util.MixedCollection  
26438      */
26439     items : false,
26440      
26441     /**
26442      * Adds any Toolbar.Item or subclass
26443      * @param {Roo.Toolbar.Item} item
26444      * @return {Roo.Toolbar.Item} The item
26445      */
26446     addItem : function(item){
26447         var td = this.nextBlock();
26448         item.render(td);
26449         this.items.add(item);
26450         return item;
26451     },
26452     
26453     /**
26454      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26455      * @param {Object/Array} config A button config or array of configs
26456      * @return {Roo.Toolbar.Button/Array}
26457      */
26458     addButton : function(config){
26459         if(config instanceof Array){
26460             var buttons = [];
26461             for(var i = 0, len = config.length; i < len; i++) {
26462                 buttons.push(this.addButton(config[i]));
26463             }
26464             return buttons;
26465         }
26466         var b = config;
26467         if(!(config instanceof Roo.Toolbar.Button)){
26468             b = config.split ?
26469                 new Roo.Toolbar.SplitButton(config) :
26470                 new Roo.Toolbar.Button(config);
26471         }
26472         var td = this.nextBlock();
26473         b.render(td);
26474         this.items.add(b);
26475         return b;
26476     },
26477     
26478     /**
26479      * Adds text to the toolbar
26480      * @param {String} text The text to add
26481      * @return {Roo.Toolbar.Item} The element's item
26482      */
26483     addText : function(text){
26484         return this.addItem(new Roo.Toolbar.TextItem(text));
26485     },
26486     
26487     /**
26488      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26489      * @param {Number} index The index where the item is to be inserted
26490      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26491      * @return {Roo.Toolbar.Button/Item}
26492      */
26493     insertButton : function(index, item){
26494         if(item instanceof Array){
26495             var buttons = [];
26496             for(var i = 0, len = item.length; i < len; i++) {
26497                buttons.push(this.insertButton(index + i, item[i]));
26498             }
26499             return buttons;
26500         }
26501         if (!(item instanceof Roo.Toolbar.Button)){
26502            item = new Roo.Toolbar.Button(item);
26503         }
26504         var td = document.createElement("td");
26505         this.tr.insertBefore(td, this.tr.childNodes[index]);
26506         item.render(td);
26507         this.items.insert(index, item);
26508         return item;
26509     },
26510     
26511     /**
26512      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26513      * @param {Object} config
26514      * @return {Roo.Toolbar.Item} The element's item
26515      */
26516     addDom : function(config, returnEl){
26517         var td = this.nextBlock();
26518         Roo.DomHelper.overwrite(td, config);
26519         var ti = new Roo.Toolbar.Item(td.firstChild);
26520         ti.render(td);
26521         this.items.add(ti);
26522         return ti;
26523     },
26524
26525     /**
26526      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26527      * @type Roo.util.MixedCollection  
26528      */
26529     fields : false,
26530     
26531     /**
26532      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26533      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26534      * @param {Roo.form.Field} field
26535      * @return {Roo.ToolbarItem}
26536      */
26537      
26538       
26539     addField : function(field) {
26540         if (!this.fields) {
26541             var autoId = 0;
26542             this.fields = new Roo.util.MixedCollection(false, function(o){
26543                 return o.id || ("item" + (++autoId));
26544             });
26545
26546         }
26547         
26548         var td = this.nextBlock();
26549         field.render(td);
26550         var ti = new Roo.Toolbar.Item(td.firstChild);
26551         ti.render(td);
26552         this.items.add(ti);
26553         this.fields.add(field);
26554         return ti;
26555     },
26556     /**
26557      * Hide the toolbar
26558      * @method hide
26559      */
26560      
26561       
26562     hide : function()
26563     {
26564         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26565         this.el.child('div').hide();
26566     },
26567     /**
26568      * Show the toolbar
26569      * @method show
26570      */
26571     show : function()
26572     {
26573         this.el.child('div').show();
26574     },
26575       
26576     // private
26577     nextBlock : function(){
26578         var td = document.createElement("td");
26579         this.tr.appendChild(td);
26580         return td;
26581     },
26582
26583     // private
26584     destroy : function(){
26585         if(this.items){ // rendered?
26586             Roo.destroy.apply(Roo, this.items.items);
26587         }
26588         if(this.fields){ // rendered?
26589             Roo.destroy.apply(Roo, this.fields.items);
26590         }
26591         Roo.Element.uncache(this.el, this.tr);
26592     }
26593 };
26594
26595 /**
26596  * @class Roo.Toolbar.Item
26597  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26598  * @constructor
26599  * Creates a new Item
26600  * @param {HTMLElement} el 
26601  */
26602 Roo.Toolbar.Item = function(el){
26603     this.el = Roo.getDom(el);
26604     this.id = Roo.id(this.el);
26605     this.hidden = false;
26606 };
26607
26608 Roo.Toolbar.Item.prototype = {
26609     
26610     /**
26611      * Get this item's HTML Element
26612      * @return {HTMLElement}
26613      */
26614     getEl : function(){
26615        return this.el;  
26616     },
26617
26618     // private
26619     render : function(td){
26620         this.td = td;
26621         td.appendChild(this.el);
26622     },
26623     
26624     /**
26625      * Removes and destroys this item.
26626      */
26627     destroy : function(){
26628         this.td.parentNode.removeChild(this.td);
26629     },
26630     
26631     /**
26632      * Shows this item.
26633      */
26634     show: function(){
26635         this.hidden = false;
26636         this.td.style.display = "";
26637     },
26638     
26639     /**
26640      * Hides this item.
26641      */
26642     hide: function(){
26643         this.hidden = true;
26644         this.td.style.display = "none";
26645     },
26646     
26647     /**
26648      * Convenience function for boolean show/hide.
26649      * @param {Boolean} visible true to show/false to hide
26650      */
26651     setVisible: function(visible){
26652         if(visible) {
26653             this.show();
26654         }else{
26655             this.hide();
26656         }
26657     },
26658     
26659     /**
26660      * Try to focus this item.
26661      */
26662     focus : function(){
26663         Roo.fly(this.el).focus();
26664     },
26665     
26666     /**
26667      * Disables this item.
26668      */
26669     disable : function(){
26670         Roo.fly(this.td).addClass("x-item-disabled");
26671         this.disabled = true;
26672         this.el.disabled = true;
26673     },
26674     
26675     /**
26676      * Enables this item.
26677      */
26678     enable : function(){
26679         Roo.fly(this.td).removeClass("x-item-disabled");
26680         this.disabled = false;
26681         this.el.disabled = false;
26682     }
26683 };
26684
26685
26686 /**
26687  * @class Roo.Toolbar.Separator
26688  * @extends Roo.Toolbar.Item
26689  * A simple toolbar separator class
26690  * @constructor
26691  * Creates a new Separator
26692  */
26693 Roo.Toolbar.Separator = function(){
26694     var s = document.createElement("span");
26695     s.className = "ytb-sep";
26696     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26697 };
26698 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26699     enable:Roo.emptyFn,
26700     disable:Roo.emptyFn,
26701     focus:Roo.emptyFn
26702 });
26703
26704 /**
26705  * @class Roo.Toolbar.Spacer
26706  * @extends Roo.Toolbar.Item
26707  * A simple element that adds extra horizontal space to a toolbar.
26708  * @constructor
26709  * Creates a new Spacer
26710  */
26711 Roo.Toolbar.Spacer = function(){
26712     var s = document.createElement("div");
26713     s.className = "ytb-spacer";
26714     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26715 };
26716 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26717     enable:Roo.emptyFn,
26718     disable:Roo.emptyFn,
26719     focus:Roo.emptyFn
26720 });
26721
26722 /**
26723  * @class Roo.Toolbar.Fill
26724  * @extends Roo.Toolbar.Spacer
26725  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26726  * @constructor
26727  * Creates a new Spacer
26728  */
26729 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26730     // private
26731     render : function(td){
26732         td.style.width = '100%';
26733         Roo.Toolbar.Fill.superclass.render.call(this, td);
26734     }
26735 });
26736
26737 /**
26738  * @class Roo.Toolbar.TextItem
26739  * @extends Roo.Toolbar.Item
26740  * A simple class that renders text directly into a toolbar.
26741  * @constructor
26742  * Creates a new TextItem
26743  * @param {String} text
26744  */
26745 Roo.Toolbar.TextItem = function(text){
26746     if (typeof(text) == 'object') {
26747         text = text.text;
26748     }
26749     var s = document.createElement("span");
26750     s.className = "ytb-text";
26751     s.innerHTML = text;
26752     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26753 };
26754 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26755     enable:Roo.emptyFn,
26756     disable:Roo.emptyFn,
26757     focus:Roo.emptyFn
26758 });
26759
26760 /**
26761  * @class Roo.Toolbar.Button
26762  * @extends Roo.Button
26763  * A button that renders into a toolbar.
26764  * @constructor
26765  * Creates a new Button
26766  * @param {Object} config A standard {@link Roo.Button} config object
26767  */
26768 Roo.Toolbar.Button = function(config){
26769     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26770 };
26771 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26772     render : function(td){
26773         this.td = td;
26774         Roo.Toolbar.Button.superclass.render.call(this, td);
26775     },
26776     
26777     /**
26778      * Removes and destroys this button
26779      */
26780     destroy : function(){
26781         Roo.Toolbar.Button.superclass.destroy.call(this);
26782         this.td.parentNode.removeChild(this.td);
26783     },
26784     
26785     /**
26786      * Shows this button
26787      */
26788     show: function(){
26789         this.hidden = false;
26790         this.td.style.display = "";
26791     },
26792     
26793     /**
26794      * Hides this button
26795      */
26796     hide: function(){
26797         this.hidden = true;
26798         this.td.style.display = "none";
26799     },
26800
26801     /**
26802      * Disables this item
26803      */
26804     disable : function(){
26805         Roo.fly(this.td).addClass("x-item-disabled");
26806         this.disabled = true;
26807     },
26808
26809     /**
26810      * Enables this item
26811      */
26812     enable : function(){
26813         Roo.fly(this.td).removeClass("x-item-disabled");
26814         this.disabled = false;
26815     }
26816 });
26817 // backwards compat
26818 Roo.ToolbarButton = Roo.Toolbar.Button;
26819
26820 /**
26821  * @class Roo.Toolbar.SplitButton
26822  * @extends Roo.SplitButton
26823  * A menu button that renders into a toolbar.
26824  * @constructor
26825  * Creates a new SplitButton
26826  * @param {Object} config A standard {@link Roo.SplitButton} config object
26827  */
26828 Roo.Toolbar.SplitButton = function(config){
26829     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26830 };
26831 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26832     render : function(td){
26833         this.td = td;
26834         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26835     },
26836     
26837     /**
26838      * Removes and destroys this button
26839      */
26840     destroy : function(){
26841         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26842         this.td.parentNode.removeChild(this.td);
26843     },
26844     
26845     /**
26846      * Shows this button
26847      */
26848     show: function(){
26849         this.hidden = false;
26850         this.td.style.display = "";
26851     },
26852     
26853     /**
26854      * Hides this button
26855      */
26856     hide: function(){
26857         this.hidden = true;
26858         this.td.style.display = "none";
26859     }
26860 });
26861
26862 // backwards compat
26863 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26864  * Based on:
26865  * Ext JS Library 1.1.1
26866  * Copyright(c) 2006-2007, Ext JS, LLC.
26867  *
26868  * Originally Released Under LGPL - original licence link has changed is not relivant.
26869  *
26870  * Fork - LGPL
26871  * <script type="text/javascript">
26872  */
26873  
26874 /**
26875  * @class Roo.PagingToolbar
26876  * @extends Roo.Toolbar
26877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26878  * @constructor
26879  * Create a new PagingToolbar
26880  * @param {Object} config The config object
26881  */
26882 Roo.PagingToolbar = function(el, ds, config)
26883 {
26884     // old args format still supported... - xtype is prefered..
26885     if (typeof(el) == 'object' && el.xtype) {
26886         // created from xtype...
26887         config = el;
26888         ds = el.dataSource;
26889         el = config.container;
26890     }
26891     
26892     
26893     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26894     this.ds = ds;
26895     this.cursor = 0;
26896     this.renderButtons(this.el);
26897     this.bind(ds);
26898 };
26899
26900 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26901     /**
26902      * @cfg {Roo.data.Store} dataSource
26903      * The underlying data store providing the paged data
26904      */
26905     /**
26906      * @cfg {String/HTMLElement/Element} container
26907      * container The id or element that will contain the toolbar
26908      */
26909     /**
26910      * @cfg {Boolean} displayInfo
26911      * True to display the displayMsg (defaults to false)
26912      */
26913     /**
26914      * @cfg {Number} pageSize
26915      * The number of records to display per page (defaults to 20)
26916      */
26917     pageSize: 20,
26918     /**
26919      * @cfg {String} displayMsg
26920      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26921      */
26922     displayMsg : 'Displaying {0} - {1} of {2}',
26923     /**
26924      * @cfg {String} emptyMsg
26925      * The message to display when no records are found (defaults to "No data to display")
26926      */
26927     emptyMsg : 'No data to display',
26928     /**
26929      * Customizable piece of the default paging text (defaults to "Page")
26930      * @type String
26931      */
26932     beforePageText : "Page",
26933     /**
26934      * Customizable piece of the default paging text (defaults to "of %0")
26935      * @type String
26936      */
26937     afterPageText : "of {0}",
26938     /**
26939      * Customizable piece of the default paging text (defaults to "First Page")
26940      * @type String
26941      */
26942     firstText : "First Page",
26943     /**
26944      * Customizable piece of the default paging text (defaults to "Previous Page")
26945      * @type String
26946      */
26947     prevText : "Previous Page",
26948     /**
26949      * Customizable piece of the default paging text (defaults to "Next Page")
26950      * @type String
26951      */
26952     nextText : "Next Page",
26953     /**
26954      * Customizable piece of the default paging text (defaults to "Last Page")
26955      * @type String
26956      */
26957     lastText : "Last Page",
26958     /**
26959      * Customizable piece of the default paging text (defaults to "Refresh")
26960      * @type String
26961      */
26962     refreshText : "Refresh",
26963
26964     // private
26965     renderButtons : function(el){
26966         Roo.PagingToolbar.superclass.render.call(this, el);
26967         this.first = this.addButton({
26968             tooltip: this.firstText,
26969             cls: "x-btn-icon x-grid-page-first",
26970             disabled: true,
26971             handler: this.onClick.createDelegate(this, ["first"])
26972         });
26973         this.prev = this.addButton({
26974             tooltip: this.prevText,
26975             cls: "x-btn-icon x-grid-page-prev",
26976             disabled: true,
26977             handler: this.onClick.createDelegate(this, ["prev"])
26978         });
26979         this.addSeparator();
26980         this.add(this.beforePageText);
26981         this.field = Roo.get(this.addDom({
26982            tag: "input",
26983            type: "text",
26984            size: "3",
26985            value: "1",
26986            cls: "x-grid-page-number"
26987         }).el);
26988         this.field.on("keydown", this.onPagingKeydown, this);
26989         this.field.on("focus", function(){this.dom.select();});
26990         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
26991         this.field.setHeight(18);
26992         this.addSeparator();
26993         this.next = this.addButton({
26994             tooltip: this.nextText,
26995             cls: "x-btn-icon x-grid-page-next",
26996             disabled: true,
26997             handler: this.onClick.createDelegate(this, ["next"])
26998         });
26999         this.last = this.addButton({
27000             tooltip: this.lastText,
27001             cls: "x-btn-icon x-grid-page-last",
27002             disabled: true,
27003             handler: this.onClick.createDelegate(this, ["last"])
27004         });
27005         this.addSeparator();
27006         this.loading = this.addButton({
27007             tooltip: this.refreshText,
27008             cls: "x-btn-icon x-grid-loading",
27009             handler: this.onClick.createDelegate(this, ["refresh"])
27010         });
27011
27012         if(this.displayInfo){
27013             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27014         }
27015     },
27016
27017     // private
27018     updateInfo : function(){
27019         if(this.displayEl){
27020             var count = this.ds.getCount();
27021             var msg = count == 0 ?
27022                 this.emptyMsg :
27023                 String.format(
27024                     this.displayMsg,
27025                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27026                 );
27027             this.displayEl.update(msg);
27028         }
27029     },
27030
27031     // private
27032     onLoad : function(ds, r, o){
27033        this.cursor = o.params ? o.params.start : 0;
27034        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27035
27036        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27037        this.field.dom.value = ap;
27038        this.first.setDisabled(ap == 1);
27039        this.prev.setDisabled(ap == 1);
27040        this.next.setDisabled(ap == ps);
27041        this.last.setDisabled(ap == ps);
27042        this.loading.enable();
27043        this.updateInfo();
27044     },
27045
27046     // private
27047     getPageData : function(){
27048         var total = this.ds.getTotalCount();
27049         return {
27050             total : total,
27051             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27052             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27053         };
27054     },
27055
27056     // private
27057     onLoadError : function(){
27058         this.loading.enable();
27059     },
27060
27061     // private
27062     onPagingKeydown : function(e){
27063         var k = e.getKey();
27064         var d = this.getPageData();
27065         if(k == e.RETURN){
27066             var v = this.field.dom.value, pageNum;
27067             if(!v || isNaN(pageNum = parseInt(v, 10))){
27068                 this.field.dom.value = d.activePage;
27069                 return;
27070             }
27071             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27072             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27073             e.stopEvent();
27074         }
27075         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27076         {
27077           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27078           this.field.dom.value = pageNum;
27079           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27080           e.stopEvent();
27081         }
27082         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27083         {
27084           var v = this.field.dom.value, pageNum; 
27085           var increment = (e.shiftKey) ? 10 : 1;
27086           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27087             increment *= -1;
27088           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27089             this.field.dom.value = d.activePage;
27090             return;
27091           }
27092           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27093           {
27094             this.field.dom.value = parseInt(v, 10) + increment;
27095             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27096             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27097           }
27098           e.stopEvent();
27099         }
27100     },
27101
27102     // private
27103     beforeLoad : function(){
27104         if(this.loading){
27105             this.loading.disable();
27106         }
27107     },
27108
27109     // private
27110     onClick : function(which){
27111         var ds = this.ds;
27112         switch(which){
27113             case "first":
27114                 ds.load({params:{start: 0, limit: this.pageSize}});
27115             break;
27116             case "prev":
27117                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27118             break;
27119             case "next":
27120                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27121             break;
27122             case "last":
27123                 var total = ds.getTotalCount();
27124                 var extra = total % this.pageSize;
27125                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27126                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27127             break;
27128             case "refresh":
27129                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27130             break;
27131         }
27132     },
27133
27134     /**
27135      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27136      * @param {Roo.data.Store} store The data store to unbind
27137      */
27138     unbind : function(ds){
27139         ds.un("beforeload", this.beforeLoad, this);
27140         ds.un("load", this.onLoad, this);
27141         ds.un("loadexception", this.onLoadError, this);
27142         ds.un("remove", this.updateInfo, this);
27143         ds.un("add", this.updateInfo, this);
27144         this.ds = undefined;
27145     },
27146
27147     /**
27148      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27149      * @param {Roo.data.Store} store The data store to bind
27150      */
27151     bind : function(ds){
27152         ds.on("beforeload", this.beforeLoad, this);
27153         ds.on("load", this.onLoad, this);
27154         ds.on("loadexception", this.onLoadError, this);
27155         ds.on("remove", this.updateInfo, this);
27156         ds.on("add", this.updateInfo, this);
27157         this.ds = ds;
27158     }
27159 });/*
27160  * Based on:
27161  * Ext JS Library 1.1.1
27162  * Copyright(c) 2006-2007, Ext JS, LLC.
27163  *
27164  * Originally Released Under LGPL - original licence link has changed is not relivant.
27165  *
27166  * Fork - LGPL
27167  * <script type="text/javascript">
27168  */
27169
27170 /**
27171  * @class Roo.Resizable
27172  * @extends Roo.util.Observable
27173  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27174  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27175  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27176  * the element will be wrapped for you automatically.</p>
27177  * <p>Here is the list of valid resize handles:</p>
27178  * <pre>
27179 Value   Description
27180 ------  -------------------
27181  'n'     north
27182  's'     south
27183  'e'     east
27184  'w'     west
27185  'nw'    northwest
27186  'sw'    southwest
27187  'se'    southeast
27188  'ne'    northeast
27189  'all'   all
27190 </pre>
27191  * <p>Here's an example showing the creation of a typical Resizable:</p>
27192  * <pre><code>
27193 var resizer = new Roo.Resizable("element-id", {
27194     handles: 'all',
27195     minWidth: 200,
27196     minHeight: 100,
27197     maxWidth: 500,
27198     maxHeight: 400,
27199     pinned: true
27200 });
27201 resizer.on("resize", myHandler);
27202 </code></pre>
27203  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27204  * resizer.east.setDisplayed(false);</p>
27205  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27206  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27207  * resize operation's new size (defaults to [0, 0])
27208  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27209  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27210  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27211  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27212  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27213  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27214  * @cfg {Number} width The width of the element in pixels (defaults to null)
27215  * @cfg {Number} height The height of the element in pixels (defaults to null)
27216  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27217  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27218  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27219  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27220  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27221  * in favor of the handles config option (defaults to false)
27222  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27223  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27224  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27225  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27226  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27227  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27228  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27229  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27230  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27231  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27232  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27233  * @constructor
27234  * Create a new resizable component
27235  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27236  * @param {Object} config configuration options
27237   */
27238 Roo.Resizable = function(el, config){
27239     this.el = Roo.get(el);
27240
27241     if(config && config.wrap){
27242         config.resizeChild = this.el;
27243         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27244         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27245         this.el.setStyle("overflow", "hidden");
27246         this.el.setPositioning(config.resizeChild.getPositioning());
27247         config.resizeChild.clearPositioning();
27248         if(!config.width || !config.height){
27249             var csize = config.resizeChild.getSize();
27250             this.el.setSize(csize.width, csize.height);
27251         }
27252         if(config.pinned && !config.adjustments){
27253             config.adjustments = "auto";
27254         }
27255     }
27256
27257     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27258     this.proxy.unselectable();
27259     this.proxy.enableDisplayMode('block');
27260
27261     Roo.apply(this, config);
27262
27263     if(this.pinned){
27264         this.disableTrackOver = true;
27265         this.el.addClass("x-resizable-pinned");
27266     }
27267     // if the element isn't positioned, make it relative
27268     var position = this.el.getStyle("position");
27269     if(position != "absolute" && position != "fixed"){
27270         this.el.setStyle("position", "relative");
27271     }
27272     if(!this.handles){ // no handles passed, must be legacy style
27273         this.handles = 's,e,se';
27274         if(this.multiDirectional){
27275             this.handles += ',n,w';
27276         }
27277     }
27278     if(this.handles == "all"){
27279         this.handles = "n s e w ne nw se sw";
27280     }
27281     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27282     var ps = Roo.Resizable.positions;
27283     for(var i = 0, len = hs.length; i < len; i++){
27284         if(hs[i] && ps[hs[i]]){
27285             var pos = ps[hs[i]];
27286             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27287         }
27288     }
27289     // legacy
27290     this.corner = this.southeast;
27291
27292     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27293         this.updateBox = true;
27294     }
27295
27296     this.activeHandle = null;
27297
27298     if(this.resizeChild){
27299         if(typeof this.resizeChild == "boolean"){
27300             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27301         }else{
27302             this.resizeChild = Roo.get(this.resizeChild, true);
27303         }
27304     }
27305
27306     if(this.adjustments == "auto"){
27307         var rc = this.resizeChild;
27308         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27309         if(rc && (hw || hn)){
27310             rc.position("relative");
27311             rc.setLeft(hw ? hw.el.getWidth() : 0);
27312             rc.setTop(hn ? hn.el.getHeight() : 0);
27313         }
27314         this.adjustments = [
27315             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27316             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27317         ];
27318     }
27319
27320     if(this.draggable){
27321         this.dd = this.dynamic ?
27322             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27323         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27324     }
27325
27326     // public events
27327     this.addEvents({
27328         /**
27329          * @event beforeresize
27330          * Fired before resize is allowed. Set enabled to false to cancel resize.
27331          * @param {Roo.Resizable} this
27332          * @param {Roo.EventObject} e The mousedown event
27333          */
27334         "beforeresize" : true,
27335         /**
27336          * @event resize
27337          * Fired after a resize.
27338          * @param {Roo.Resizable} this
27339          * @param {Number} width The new width
27340          * @param {Number} height The new height
27341          * @param {Roo.EventObject} e The mouseup event
27342          */
27343         "resize" : true
27344     });
27345
27346     if(this.width !== null && this.height !== null){
27347         this.resizeTo(this.width, this.height);
27348     }else{
27349         this.updateChildSize();
27350     }
27351     if(Roo.isIE){
27352         this.el.dom.style.zoom = 1;
27353     }
27354     Roo.Resizable.superclass.constructor.call(this);
27355 };
27356
27357 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27358         resizeChild : false,
27359         adjustments : [0, 0],
27360         minWidth : 5,
27361         minHeight : 5,
27362         maxWidth : 10000,
27363         maxHeight : 10000,
27364         enabled : true,
27365         animate : false,
27366         duration : .35,
27367         dynamic : false,
27368         handles : false,
27369         multiDirectional : false,
27370         disableTrackOver : false,
27371         easing : 'easeOutStrong',
27372         widthIncrement : 0,
27373         heightIncrement : 0,
27374         pinned : false,
27375         width : null,
27376         height : null,
27377         preserveRatio : false,
27378         transparent: false,
27379         minX: 0,
27380         minY: 0,
27381         draggable: false,
27382
27383         /**
27384          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27385          */
27386         constrainTo: undefined,
27387         /**
27388          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27389          */
27390         resizeRegion: undefined,
27391
27392
27393     /**
27394      * Perform a manual resize
27395      * @param {Number} width
27396      * @param {Number} height
27397      */
27398     resizeTo : function(width, height){
27399         this.el.setSize(width, height);
27400         this.updateChildSize();
27401         this.fireEvent("resize", this, width, height, null);
27402     },
27403
27404     // private
27405     startSizing : function(e, handle){
27406         this.fireEvent("beforeresize", this, e);
27407         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27408
27409             if(!this.overlay){
27410                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27411                 this.overlay.unselectable();
27412                 this.overlay.enableDisplayMode("block");
27413                 this.overlay.on("mousemove", this.onMouseMove, this);
27414                 this.overlay.on("mouseup", this.onMouseUp, this);
27415             }
27416             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27417
27418             this.resizing = true;
27419             this.startBox = this.el.getBox();
27420             this.startPoint = e.getXY();
27421             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27422                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27423
27424             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27425             this.overlay.show();
27426
27427             if(this.constrainTo) {
27428                 var ct = Roo.get(this.constrainTo);
27429                 this.resizeRegion = ct.getRegion().adjust(
27430                     ct.getFrameWidth('t'),
27431                     ct.getFrameWidth('l'),
27432                     -ct.getFrameWidth('b'),
27433                     -ct.getFrameWidth('r')
27434                 );
27435             }
27436
27437             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27438             this.proxy.show();
27439             this.proxy.setBox(this.startBox);
27440             if(!this.dynamic){
27441                 this.proxy.setStyle('visibility', 'visible');
27442             }
27443         }
27444     },
27445
27446     // private
27447     onMouseDown : function(handle, e){
27448         if(this.enabled){
27449             e.stopEvent();
27450             this.activeHandle = handle;
27451             this.startSizing(e, handle);
27452         }
27453     },
27454
27455     // private
27456     onMouseUp : function(e){
27457         var size = this.resizeElement();
27458         this.resizing = false;
27459         this.handleOut();
27460         this.overlay.hide();
27461         this.proxy.hide();
27462         this.fireEvent("resize", this, size.width, size.height, e);
27463     },
27464
27465     // private
27466     updateChildSize : function(){
27467         if(this.resizeChild){
27468             var el = this.el;
27469             var child = this.resizeChild;
27470             var adj = this.adjustments;
27471             if(el.dom.offsetWidth){
27472                 var b = el.getSize(true);
27473                 child.setSize(b.width+adj[0], b.height+adj[1]);
27474             }
27475             // Second call here for IE
27476             // The first call enables instant resizing and
27477             // the second call corrects scroll bars if they
27478             // exist
27479             if(Roo.isIE){
27480                 setTimeout(function(){
27481                     if(el.dom.offsetWidth){
27482                         var b = el.getSize(true);
27483                         child.setSize(b.width+adj[0], b.height+adj[1]);
27484                     }
27485                 }, 10);
27486             }
27487         }
27488     },
27489
27490     // private
27491     snap : function(value, inc, min){
27492         if(!inc || !value) return value;
27493         var newValue = value;
27494         var m = value % inc;
27495         if(m > 0){
27496             if(m > (inc/2)){
27497                 newValue = value + (inc-m);
27498             }else{
27499                 newValue = value - m;
27500             }
27501         }
27502         return Math.max(min, newValue);
27503     },
27504
27505     // private
27506     resizeElement : function(){
27507         var box = this.proxy.getBox();
27508         if(this.updateBox){
27509             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27510         }else{
27511             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27512         }
27513         this.updateChildSize();
27514         if(!this.dynamic){
27515             this.proxy.hide();
27516         }
27517         return box;
27518     },
27519
27520     // private
27521     constrain : function(v, diff, m, mx){
27522         if(v - diff < m){
27523             diff = v - m;
27524         }else if(v - diff > mx){
27525             diff = mx - v;
27526         }
27527         return diff;
27528     },
27529
27530     // private
27531     onMouseMove : function(e){
27532         if(this.enabled){
27533             try{// try catch so if something goes wrong the user doesn't get hung
27534
27535             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27536                 return;
27537             }
27538
27539             //var curXY = this.startPoint;
27540             var curSize = this.curSize || this.startBox;
27541             var x = this.startBox.x, y = this.startBox.y;
27542             var ox = x, oy = y;
27543             var w = curSize.width, h = curSize.height;
27544             var ow = w, oh = h;
27545             var mw = this.minWidth, mh = this.minHeight;
27546             var mxw = this.maxWidth, mxh = this.maxHeight;
27547             var wi = this.widthIncrement;
27548             var hi = this.heightIncrement;
27549
27550             var eventXY = e.getXY();
27551             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27552             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27553
27554             var pos = this.activeHandle.position;
27555
27556             switch(pos){
27557                 case "east":
27558                     w += diffX;
27559                     w = Math.min(Math.max(mw, w), mxw);
27560                     break;
27561                 case "south":
27562                     h += diffY;
27563                     h = Math.min(Math.max(mh, h), mxh);
27564                     break;
27565                 case "southeast":
27566                     w += diffX;
27567                     h += diffY;
27568                     w = Math.min(Math.max(mw, w), mxw);
27569                     h = Math.min(Math.max(mh, h), mxh);
27570                     break;
27571                 case "north":
27572                     diffY = this.constrain(h, diffY, mh, mxh);
27573                     y += diffY;
27574                     h -= diffY;
27575                     break;
27576                 case "west":
27577                     diffX = this.constrain(w, diffX, mw, mxw);
27578                     x += diffX;
27579                     w -= diffX;
27580                     break;
27581                 case "northeast":
27582                     w += diffX;
27583                     w = Math.min(Math.max(mw, w), mxw);
27584                     diffY = this.constrain(h, diffY, mh, mxh);
27585                     y += diffY;
27586                     h -= diffY;
27587                     break;
27588                 case "northwest":
27589                     diffX = this.constrain(w, diffX, mw, mxw);
27590                     diffY = this.constrain(h, diffY, mh, mxh);
27591                     y += diffY;
27592                     h -= diffY;
27593                     x += diffX;
27594                     w -= diffX;
27595                     break;
27596                case "southwest":
27597                     diffX = this.constrain(w, diffX, mw, mxw);
27598                     h += diffY;
27599                     h = Math.min(Math.max(mh, h), mxh);
27600                     x += diffX;
27601                     w -= diffX;
27602                     break;
27603             }
27604
27605             var sw = this.snap(w, wi, mw);
27606             var sh = this.snap(h, hi, mh);
27607             if(sw != w || sh != h){
27608                 switch(pos){
27609                     case "northeast":
27610                         y -= sh - h;
27611                     break;
27612                     case "north":
27613                         y -= sh - h;
27614                         break;
27615                     case "southwest":
27616                         x -= sw - w;
27617                     break;
27618                     case "west":
27619                         x -= sw - w;
27620                         break;
27621                     case "northwest":
27622                         x -= sw - w;
27623                         y -= sh - h;
27624                     break;
27625                 }
27626                 w = sw;
27627                 h = sh;
27628             }
27629
27630             if(this.preserveRatio){
27631                 switch(pos){
27632                     case "southeast":
27633                     case "east":
27634                         h = oh * (w/ow);
27635                         h = Math.min(Math.max(mh, h), mxh);
27636                         w = ow * (h/oh);
27637                        break;
27638                     case "south":
27639                         w = ow * (h/oh);
27640                         w = Math.min(Math.max(mw, w), mxw);
27641                         h = oh * (w/ow);
27642                         break;
27643                     case "northeast":
27644                         w = ow * (h/oh);
27645                         w = Math.min(Math.max(mw, w), mxw);
27646                         h = oh * (w/ow);
27647                     break;
27648                     case "north":
27649                         var tw = w;
27650                         w = ow * (h/oh);
27651                         w = Math.min(Math.max(mw, w), mxw);
27652                         h = oh * (w/ow);
27653                         x += (tw - w) / 2;
27654                         break;
27655                     case "southwest":
27656                         h = oh * (w/ow);
27657                         h = Math.min(Math.max(mh, h), mxh);
27658                         var tw = w;
27659                         w = ow * (h/oh);
27660                         x += tw - w;
27661                         break;
27662                     case "west":
27663                         var th = h;
27664                         h = oh * (w/ow);
27665                         h = Math.min(Math.max(mh, h), mxh);
27666                         y += (th - h) / 2;
27667                         var tw = w;
27668                         w = ow * (h/oh);
27669                         x += tw - w;
27670                        break;
27671                     case "northwest":
27672                         var tw = w;
27673                         var th = h;
27674                         h = oh * (w/ow);
27675                         h = Math.min(Math.max(mh, h), mxh);
27676                         w = ow * (h/oh);
27677                         y += th - h;
27678                          x += tw - w;
27679                        break;
27680
27681                 }
27682             }
27683             this.proxy.setBounds(x, y, w, h);
27684             if(this.dynamic){
27685                 this.resizeElement();
27686             }
27687             }catch(e){}
27688         }
27689     },
27690
27691     // private
27692     handleOver : function(){
27693         if(this.enabled){
27694             this.el.addClass("x-resizable-over");
27695         }
27696     },
27697
27698     // private
27699     handleOut : function(){
27700         if(!this.resizing){
27701             this.el.removeClass("x-resizable-over");
27702         }
27703     },
27704
27705     /**
27706      * Returns the element this component is bound to.
27707      * @return {Roo.Element}
27708      */
27709     getEl : function(){
27710         return this.el;
27711     },
27712
27713     /**
27714      * Returns the resizeChild element (or null).
27715      * @return {Roo.Element}
27716      */
27717     getResizeChild : function(){
27718         return this.resizeChild;
27719     },
27720
27721     /**
27722      * Destroys this resizable. If the element was wrapped and
27723      * removeEl is not true then the element remains.
27724      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27725      */
27726     destroy : function(removeEl){
27727         this.proxy.remove();
27728         if(this.overlay){
27729             this.overlay.removeAllListeners();
27730             this.overlay.remove();
27731         }
27732         var ps = Roo.Resizable.positions;
27733         for(var k in ps){
27734             if(typeof ps[k] != "function" && this[ps[k]]){
27735                 var h = this[ps[k]];
27736                 h.el.removeAllListeners();
27737                 h.el.remove();
27738             }
27739         }
27740         if(removeEl){
27741             this.el.update("");
27742             this.el.remove();
27743         }
27744     }
27745 });
27746
27747 // private
27748 // hash to map config positions to true positions
27749 Roo.Resizable.positions = {
27750     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27751 };
27752
27753 // private
27754 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27755     if(!this.tpl){
27756         // only initialize the template if resizable is used
27757         var tpl = Roo.DomHelper.createTemplate(
27758             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27759         );
27760         tpl.compile();
27761         Roo.Resizable.Handle.prototype.tpl = tpl;
27762     }
27763     this.position = pos;
27764     this.rz = rz;
27765     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27766     this.el.unselectable();
27767     if(transparent){
27768         this.el.setOpacity(0);
27769     }
27770     this.el.on("mousedown", this.onMouseDown, this);
27771     if(!disableTrackOver){
27772         this.el.on("mouseover", this.onMouseOver, this);
27773         this.el.on("mouseout", this.onMouseOut, this);
27774     }
27775 };
27776
27777 // private
27778 Roo.Resizable.Handle.prototype = {
27779     afterResize : function(rz){
27780         // do nothing
27781     },
27782     // private
27783     onMouseDown : function(e){
27784         this.rz.onMouseDown(this, e);
27785     },
27786     // private
27787     onMouseOver : function(e){
27788         this.rz.handleOver(this, e);
27789     },
27790     // private
27791     onMouseOut : function(e){
27792         this.rz.handleOut(this, e);
27793     }
27794 };/*
27795  * Based on:
27796  * Ext JS Library 1.1.1
27797  * Copyright(c) 2006-2007, Ext JS, LLC.
27798  *
27799  * Originally Released Under LGPL - original licence link has changed is not relivant.
27800  *
27801  * Fork - LGPL
27802  * <script type="text/javascript">
27803  */
27804
27805 /**
27806  * @class Roo.Editor
27807  * @extends Roo.Component
27808  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27809  * @constructor
27810  * Create a new Editor
27811  * @param {Roo.form.Field} field The Field object (or descendant)
27812  * @param {Object} config The config object
27813  */
27814 Roo.Editor = function(field, config){
27815     Roo.Editor.superclass.constructor.call(this, config);
27816     this.field = field;
27817     this.addEvents({
27818         /**
27819              * @event beforestartedit
27820              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27821              * false from the handler of this event.
27822              * @param {Editor} this
27823              * @param {Roo.Element} boundEl The underlying element bound to this editor
27824              * @param {Mixed} value The field value being set
27825              */
27826         "beforestartedit" : true,
27827         /**
27828              * @event startedit
27829              * Fires when this editor is displayed
27830              * @param {Roo.Element} boundEl The underlying element bound to this editor
27831              * @param {Mixed} value The starting field value
27832              */
27833         "startedit" : true,
27834         /**
27835              * @event beforecomplete
27836              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27837              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27838              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27839              * event will not fire since no edit actually occurred.
27840              * @param {Editor} this
27841              * @param {Mixed} value The current field value
27842              * @param {Mixed} startValue The original field value
27843              */
27844         "beforecomplete" : true,
27845         /**
27846              * @event complete
27847              * Fires after editing is complete and any changed value has been written to the underlying field.
27848              * @param {Editor} this
27849              * @param {Mixed} value The current field value
27850              * @param {Mixed} startValue The original field value
27851              */
27852         "complete" : true,
27853         /**
27854          * @event specialkey
27855          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27856          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27857          * @param {Roo.form.Field} this
27858          * @param {Roo.EventObject} e The event object
27859          */
27860         "specialkey" : true
27861     });
27862 };
27863
27864 Roo.extend(Roo.Editor, Roo.Component, {
27865     /**
27866      * @cfg {Boolean/String} autosize
27867      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27868      * or "height" to adopt the height only (defaults to false)
27869      */
27870     /**
27871      * @cfg {Boolean} revertInvalid
27872      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27873      * validation fails (defaults to true)
27874      */
27875     /**
27876      * @cfg {Boolean} ignoreNoChange
27877      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27878      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27879      * will never be ignored.
27880      */
27881     /**
27882      * @cfg {Boolean} hideEl
27883      * False to keep the bound element visible while the editor is displayed (defaults to true)
27884      */
27885     /**
27886      * @cfg {Mixed} value
27887      * The data value of the underlying field (defaults to "")
27888      */
27889     value : "",
27890     /**
27891      * @cfg {String} alignment
27892      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27893      */
27894     alignment: "c-c?",
27895     /**
27896      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27897      * for bottom-right shadow (defaults to "frame")
27898      */
27899     shadow : "frame",
27900     /**
27901      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27902      */
27903     constrain : false,
27904     /**
27905      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27906      */
27907     completeOnEnter : false,
27908     /**
27909      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27910      */
27911     cancelOnEsc : false,
27912     /**
27913      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27914      */
27915     updateEl : false,
27916
27917     // private
27918     onRender : function(ct, position){
27919         this.el = new Roo.Layer({
27920             shadow: this.shadow,
27921             cls: "x-editor",
27922             parentEl : ct,
27923             shim : this.shim,
27924             shadowOffset:4,
27925             id: this.id,
27926             constrain: this.constrain
27927         });
27928         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
27929         if(this.field.msgTarget != 'title'){
27930             this.field.msgTarget = 'qtip';
27931         }
27932         this.field.render(this.el);
27933         if(Roo.isGecko){
27934             this.field.el.dom.setAttribute('autocomplete', 'off');
27935         }
27936         this.field.on("specialkey", this.onSpecialKey, this);
27937         if(this.swallowKeys){
27938             this.field.el.swallowEvent(['keydown','keypress']);
27939         }
27940         this.field.show();
27941         this.field.on("blur", this.onBlur, this);
27942         if(this.field.grow){
27943             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
27944         }
27945     },
27946
27947     onSpecialKey : function(field, e){
27948         if(this.completeOnEnter && e.getKey() == e.ENTER){
27949             e.stopEvent();
27950             this.completeEdit();
27951         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
27952             this.cancelEdit();
27953         }else{
27954             this.fireEvent('specialkey', field, e);
27955         }
27956     },
27957
27958     /**
27959      * Starts the editing process and shows the editor.
27960      * @param {String/HTMLElement/Element} el The element to edit
27961      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
27962       * to the innerHTML of el.
27963      */
27964     startEdit : function(el, value){
27965         if(this.editing){
27966             this.completeEdit();
27967         }
27968         this.boundEl = Roo.get(el);
27969         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
27970         if(!this.rendered){
27971             this.render(this.parentEl || document.body);
27972         }
27973         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
27974             return;
27975         }
27976         this.startValue = v;
27977         this.field.setValue(v);
27978         if(this.autoSize){
27979             var sz = this.boundEl.getSize();
27980             switch(this.autoSize){
27981                 case "width":
27982                 this.setSize(sz.width,  "");
27983                 break;
27984                 case "height":
27985                 this.setSize("",  sz.height);
27986                 break;
27987                 default:
27988                 this.setSize(sz.width,  sz.height);
27989             }
27990         }
27991         this.el.alignTo(this.boundEl, this.alignment);
27992         this.editing = true;
27993         if(Roo.QuickTips){
27994             Roo.QuickTips.disable();
27995         }
27996         this.show();
27997     },
27998
27999     /**
28000      * Sets the height and width of this editor.
28001      * @param {Number} width The new width
28002      * @param {Number} height The new height
28003      */
28004     setSize : function(w, h){
28005         this.field.setSize(w, h);
28006         if(this.el){
28007             this.el.sync();
28008         }
28009     },
28010
28011     /**
28012      * Realigns the editor to the bound field based on the current alignment config value.
28013      */
28014     realign : function(){
28015         this.el.alignTo(this.boundEl, this.alignment);
28016     },
28017
28018     /**
28019      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28020      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28021      */
28022     completeEdit : function(remainVisible){
28023         if(!this.editing){
28024             return;
28025         }
28026         var v = this.getValue();
28027         if(this.revertInvalid !== false && !this.field.isValid()){
28028             v = this.startValue;
28029             this.cancelEdit(true);
28030         }
28031         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28032             this.editing = false;
28033             this.hide();
28034             return;
28035         }
28036         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28037             this.editing = false;
28038             if(this.updateEl && this.boundEl){
28039                 this.boundEl.update(v);
28040             }
28041             if(remainVisible !== true){
28042                 this.hide();
28043             }
28044             this.fireEvent("complete", this, v, this.startValue);
28045         }
28046     },
28047
28048     // private
28049     onShow : function(){
28050         this.el.show();
28051         if(this.hideEl !== false){
28052             this.boundEl.hide();
28053         }
28054         this.field.show();
28055         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28056             this.fixIEFocus = true;
28057             this.deferredFocus.defer(50, this);
28058         }else{
28059             this.field.focus();
28060         }
28061         this.fireEvent("startedit", this.boundEl, this.startValue);
28062     },
28063
28064     deferredFocus : function(){
28065         if(this.editing){
28066             this.field.focus();
28067         }
28068     },
28069
28070     /**
28071      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28072      * reverted to the original starting value.
28073      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28074      * cancel (defaults to false)
28075      */
28076     cancelEdit : function(remainVisible){
28077         if(this.editing){
28078             this.setValue(this.startValue);
28079             if(remainVisible !== true){
28080                 this.hide();
28081             }
28082         }
28083     },
28084
28085     // private
28086     onBlur : function(){
28087         if(this.allowBlur !== true && this.editing){
28088             this.completeEdit();
28089         }
28090     },
28091
28092     // private
28093     onHide : function(){
28094         if(this.editing){
28095             this.completeEdit();
28096             return;
28097         }
28098         this.field.blur();
28099         if(this.field.collapse){
28100             this.field.collapse();
28101         }
28102         this.el.hide();
28103         if(this.hideEl !== false){
28104             this.boundEl.show();
28105         }
28106         if(Roo.QuickTips){
28107             Roo.QuickTips.enable();
28108         }
28109     },
28110
28111     /**
28112      * Sets the data value of the editor
28113      * @param {Mixed} value Any valid value supported by the underlying field
28114      */
28115     setValue : function(v){
28116         this.field.setValue(v);
28117     },
28118
28119     /**
28120      * Gets the data value of the editor
28121      * @return {Mixed} The data value
28122      */
28123     getValue : function(){
28124         return this.field.getValue();
28125     }
28126 });/*
28127  * Based on:
28128  * Ext JS Library 1.1.1
28129  * Copyright(c) 2006-2007, Ext JS, LLC.
28130  *
28131  * Originally Released Under LGPL - original licence link has changed is not relivant.
28132  *
28133  * Fork - LGPL
28134  * <script type="text/javascript">
28135  */
28136  
28137 /**
28138  * @class Roo.BasicDialog
28139  * @extends Roo.util.Observable
28140  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28141  * <pre><code>
28142 var dlg = new Roo.BasicDialog("my-dlg", {
28143     height: 200,
28144     width: 300,
28145     minHeight: 100,
28146     minWidth: 150,
28147     modal: true,
28148     proxyDrag: true,
28149     shadow: true
28150 });
28151 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28152 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28153 dlg.addButton('Cancel', dlg.hide, dlg);
28154 dlg.show();
28155 </code></pre>
28156   <b>A Dialog should always be a direct child of the body element.</b>
28157  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28158  * @cfg {String} title Default text to display in the title bar (defaults to null)
28159  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28160  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28161  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28162  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28163  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28164  * (defaults to null with no animation)
28165  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28166  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28167  * property for valid values (defaults to 'all')
28168  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28169  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28170  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28171  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28172  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28173  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28174  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28175  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28176  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28177  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28178  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28179  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28180  * draggable = true (defaults to false)
28181  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28182  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28183  * shadow (defaults to false)
28184  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28185  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28186  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28187  * @cfg {Array} buttons Array of buttons
28188  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28189  * @constructor
28190  * Create a new BasicDialog.
28191  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28192  * @param {Object} config Configuration options
28193  */
28194 Roo.BasicDialog = function(el, config){
28195     this.el = Roo.get(el);
28196     var dh = Roo.DomHelper;
28197     if(!this.el && config && config.autoCreate){
28198         if(typeof config.autoCreate == "object"){
28199             if(!config.autoCreate.id){
28200                 config.autoCreate.id = el;
28201             }
28202             this.el = dh.append(document.body,
28203                         config.autoCreate, true);
28204         }else{
28205             this.el = dh.append(document.body,
28206                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28207         }
28208     }
28209     el = this.el;
28210     el.setDisplayed(true);
28211     el.hide = this.hideAction;
28212     this.id = el.id;
28213     el.addClass("x-dlg");
28214
28215     Roo.apply(this, config);
28216
28217     this.proxy = el.createProxy("x-dlg-proxy");
28218     this.proxy.hide = this.hideAction;
28219     this.proxy.setOpacity(.5);
28220     this.proxy.hide();
28221
28222     if(config.width){
28223         el.setWidth(config.width);
28224     }
28225     if(config.height){
28226         el.setHeight(config.height);
28227     }
28228     this.size = el.getSize();
28229     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28230         this.xy = [config.x,config.y];
28231     }else{
28232         this.xy = el.getCenterXY(true);
28233     }
28234     /** The header element @type Roo.Element */
28235     this.header = el.child("> .x-dlg-hd");
28236     /** The body element @type Roo.Element */
28237     this.body = el.child("> .x-dlg-bd");
28238     /** The footer element @type Roo.Element */
28239     this.footer = el.child("> .x-dlg-ft");
28240
28241     if(!this.header){
28242         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28243     }
28244     if(!this.body){
28245         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28246     }
28247
28248     this.header.unselectable();
28249     if(this.title){
28250         this.header.update(this.title);
28251     }
28252     // this element allows the dialog to be focused for keyboard event
28253     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28254     this.focusEl.swallowEvent("click", true);
28255
28256     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28257
28258     // wrap the body and footer for special rendering
28259     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28260     if(this.footer){
28261         this.bwrap.dom.appendChild(this.footer.dom);
28262     }
28263
28264     this.bg = this.el.createChild({
28265         tag: "div", cls:"x-dlg-bg",
28266         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28267     });
28268     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28269
28270
28271     if(this.autoScroll !== false && !this.autoTabs){
28272         this.body.setStyle("overflow", "auto");
28273     }
28274
28275     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28276
28277     if(this.closable !== false){
28278         this.el.addClass("x-dlg-closable");
28279         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28280         this.close.on("click", this.closeClick, this);
28281         this.close.addClassOnOver("x-dlg-close-over");
28282     }
28283     if(this.collapsible !== false){
28284         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28285         this.collapseBtn.on("click", this.collapseClick, this);
28286         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28287         this.header.on("dblclick", this.collapseClick, this);
28288     }
28289     if(this.resizable !== false){
28290         this.el.addClass("x-dlg-resizable");
28291         this.resizer = new Roo.Resizable(el, {
28292             minWidth: this.minWidth || 80,
28293             minHeight:this.minHeight || 80,
28294             handles: this.resizeHandles || "all",
28295             pinned: true
28296         });
28297         this.resizer.on("beforeresize", this.beforeResize, this);
28298         this.resizer.on("resize", this.onResize, this);
28299     }
28300     if(this.draggable !== false){
28301         el.addClass("x-dlg-draggable");
28302         if (!this.proxyDrag) {
28303             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28304         }
28305         else {
28306             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28307         }
28308         dd.setHandleElId(this.header.id);
28309         dd.endDrag = this.endMove.createDelegate(this);
28310         dd.startDrag = this.startMove.createDelegate(this);
28311         dd.onDrag = this.onDrag.createDelegate(this);
28312         dd.scroll = false;
28313         this.dd = dd;
28314     }
28315     if(this.modal){
28316         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28317         this.mask.enableDisplayMode("block");
28318         this.mask.hide();
28319         this.el.addClass("x-dlg-modal");
28320     }
28321     if(this.shadow){
28322         this.shadow = new Roo.Shadow({
28323             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28324             offset : this.shadowOffset
28325         });
28326     }else{
28327         this.shadowOffset = 0;
28328     }
28329     if(Roo.useShims && this.shim !== false){
28330         this.shim = this.el.createShim();
28331         this.shim.hide = this.hideAction;
28332         this.shim.hide();
28333     }else{
28334         this.shim = false;
28335     }
28336     if(this.autoTabs){
28337         this.initTabs();
28338     }
28339     if (this.buttons) { 
28340         var bts= this.buttons;
28341         this.buttons = [];
28342         Roo.each(bts, function(b) {
28343             this.addButton(b);
28344         }, this);
28345     }
28346     
28347     
28348     this.addEvents({
28349         /**
28350          * @event keydown
28351          * Fires when a key is pressed
28352          * @param {Roo.BasicDialog} this
28353          * @param {Roo.EventObject} e
28354          */
28355         "keydown" : true,
28356         /**
28357          * @event move
28358          * Fires when this dialog is moved by the user.
28359          * @param {Roo.BasicDialog} this
28360          * @param {Number} x The new page X
28361          * @param {Number} y The new page Y
28362          */
28363         "move" : true,
28364         /**
28365          * @event resize
28366          * Fires when this dialog is resized by the user.
28367          * @param {Roo.BasicDialog} this
28368          * @param {Number} width The new width
28369          * @param {Number} height The new height
28370          */
28371         "resize" : true,
28372         /**
28373          * @event beforehide
28374          * Fires before this dialog is hidden.
28375          * @param {Roo.BasicDialog} this
28376          */
28377         "beforehide" : true,
28378         /**
28379          * @event hide
28380          * Fires when this dialog is hidden.
28381          * @param {Roo.BasicDialog} this
28382          */
28383         "hide" : true,
28384         /**
28385          * @event beforeshow
28386          * Fires before this dialog is shown.
28387          * @param {Roo.BasicDialog} this
28388          */
28389         "beforeshow" : true,
28390         /**
28391          * @event show
28392          * Fires when this dialog is shown.
28393          * @param {Roo.BasicDialog} this
28394          */
28395         "show" : true
28396     });
28397     el.on("keydown", this.onKeyDown, this);
28398     el.on("mousedown", this.toFront, this);
28399     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28400     this.el.hide();
28401     Roo.DialogManager.register(this);
28402     Roo.BasicDialog.superclass.constructor.call(this);
28403 };
28404
28405 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28406     shadowOffset: Roo.isIE ? 6 : 5,
28407     minHeight: 80,
28408     minWidth: 200,
28409     minButtonWidth: 75,
28410     defaultButton: null,
28411     buttonAlign: "right",
28412     tabTag: 'div',
28413     firstShow: true,
28414
28415     /**
28416      * Sets the dialog title text
28417      * @param {String} text The title text to display
28418      * @return {Roo.BasicDialog} this
28419      */
28420     setTitle : function(text){
28421         this.header.update(text);
28422         return this;
28423     },
28424
28425     // private
28426     closeClick : function(){
28427         this.hide();
28428     },
28429
28430     // private
28431     collapseClick : function(){
28432         this[this.collapsed ? "expand" : "collapse"]();
28433     },
28434
28435     /**
28436      * Collapses the dialog to its minimized state (only the title bar is visible).
28437      * Equivalent to the user clicking the collapse dialog button.
28438      */
28439     collapse : function(){
28440         if(!this.collapsed){
28441             this.collapsed = true;
28442             this.el.addClass("x-dlg-collapsed");
28443             this.restoreHeight = this.el.getHeight();
28444             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28445         }
28446     },
28447
28448     /**
28449      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28450      * clicking the expand dialog button.
28451      */
28452     expand : function(){
28453         if(this.collapsed){
28454             this.collapsed = false;
28455             this.el.removeClass("x-dlg-collapsed");
28456             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28457         }
28458     },
28459
28460     /**
28461      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28462      * @return {Roo.TabPanel} The tabs component
28463      */
28464     initTabs : function(){
28465         var tabs = this.getTabs();
28466         while(tabs.getTab(0)){
28467             tabs.removeTab(0);
28468         }
28469         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28470             var dom = el.dom;
28471             tabs.addTab(Roo.id(dom), dom.title);
28472             dom.title = "";
28473         });
28474         tabs.activate(0);
28475         return tabs;
28476     },
28477
28478     // private
28479     beforeResize : function(){
28480         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28481     },
28482
28483     // private
28484     onResize : function(){
28485         this.refreshSize();
28486         this.syncBodyHeight();
28487         this.adjustAssets();
28488         this.focus();
28489         this.fireEvent("resize", this, this.size.width, this.size.height);
28490     },
28491
28492     // private
28493     onKeyDown : function(e){
28494         if(this.isVisible()){
28495             this.fireEvent("keydown", this, e);
28496         }
28497     },
28498
28499     /**
28500      * Resizes the dialog.
28501      * @param {Number} width
28502      * @param {Number} height
28503      * @return {Roo.BasicDialog} this
28504      */
28505     resizeTo : function(width, height){
28506         this.el.setSize(width, height);
28507         this.size = {width: width, height: height};
28508         this.syncBodyHeight();
28509         if(this.fixedcenter){
28510             this.center();
28511         }
28512         if(this.isVisible()){
28513             this.constrainXY();
28514             this.adjustAssets();
28515         }
28516         this.fireEvent("resize", this, width, height);
28517         return this;
28518     },
28519
28520
28521     /**
28522      * Resizes the dialog to fit the specified content size.
28523      * @param {Number} width
28524      * @param {Number} height
28525      * @return {Roo.BasicDialog} this
28526      */
28527     setContentSize : function(w, h){
28528         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28529         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28530         //if(!this.el.isBorderBox()){
28531             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28532             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28533         //}
28534         if(this.tabs){
28535             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28536             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28537         }
28538         this.resizeTo(w, h);
28539         return this;
28540     },
28541
28542     /**
28543      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28544      * executed in response to a particular key being pressed while the dialog is active.
28545      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28546      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28547      * @param {Function} fn The function to call
28548      * @param {Object} scope (optional) The scope of the function
28549      * @return {Roo.BasicDialog} this
28550      */
28551     addKeyListener : function(key, fn, scope){
28552         var keyCode, shift, ctrl, alt;
28553         if(typeof key == "object" && !(key instanceof Array)){
28554             keyCode = key["key"];
28555             shift = key["shift"];
28556             ctrl = key["ctrl"];
28557             alt = key["alt"];
28558         }else{
28559             keyCode = key;
28560         }
28561         var handler = function(dlg, e){
28562             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28563                 var k = e.getKey();
28564                 if(keyCode instanceof Array){
28565                     for(var i = 0, len = keyCode.length; i < len; i++){
28566                         if(keyCode[i] == k){
28567                           fn.call(scope || window, dlg, k, e);
28568                           return;
28569                         }
28570                     }
28571                 }else{
28572                     if(k == keyCode){
28573                         fn.call(scope || window, dlg, k, e);
28574                     }
28575                 }
28576             }
28577         };
28578         this.on("keydown", handler);
28579         return this;
28580     },
28581
28582     /**
28583      * Returns the TabPanel component (creates it if it doesn't exist).
28584      * Note: If you wish to simply check for the existence of tabs without creating them,
28585      * check for a null 'tabs' property.
28586      * @return {Roo.TabPanel} The tabs component
28587      */
28588     getTabs : function(){
28589         if(!this.tabs){
28590             this.el.addClass("x-dlg-auto-tabs");
28591             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28592             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28593         }
28594         return this.tabs;
28595     },
28596
28597     /**
28598      * Adds a button to the footer section of the dialog.
28599      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28600      * object or a valid Roo.DomHelper element config
28601      * @param {Function} handler The function called when the button is clicked
28602      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28603      * @return {Roo.Button} The new button
28604      */
28605     addButton : function(config, handler, scope){
28606         var dh = Roo.DomHelper;
28607         if(!this.footer){
28608             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28609         }
28610         if(!this.btnContainer){
28611             var tb = this.footer.createChild({
28612
28613                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28614                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28615             }, null, true);
28616             this.btnContainer = tb.firstChild.firstChild.firstChild;
28617         }
28618         var bconfig = {
28619             handler: handler,
28620             scope: scope,
28621             minWidth: this.minButtonWidth,
28622             hideParent:true
28623         };
28624         if(typeof config == "string"){
28625             bconfig.text = config;
28626         }else{
28627             if(config.tag){
28628                 bconfig.dhconfig = config;
28629             }else{
28630                 Roo.apply(bconfig, config);
28631             }
28632         }
28633         var fc = false;
28634         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28635             bconfig.position = Math.max(0, bconfig.position);
28636             fc = this.btnContainer.childNodes[bconfig.position];
28637         }
28638          
28639         var btn = new Roo.Button(
28640             fc ? 
28641                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28642                 : this.btnContainer.appendChild(document.createElement("td")),
28643             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28644             bconfig
28645         );
28646         this.syncBodyHeight();
28647         if(!this.buttons){
28648             /**
28649              * Array of all the buttons that have been added to this dialog via addButton
28650              * @type Array
28651              */
28652             this.buttons = [];
28653         }
28654         this.buttons.push(btn);
28655         return btn;
28656     },
28657
28658     /**
28659      * Sets the default button to be focused when the dialog is displayed.
28660      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28661      * @return {Roo.BasicDialog} this
28662      */
28663     setDefaultButton : function(btn){
28664         this.defaultButton = btn;
28665         return this;
28666     },
28667
28668     // private
28669     getHeaderFooterHeight : function(safe){
28670         var height = 0;
28671         if(this.header){
28672            height += this.header.getHeight();
28673         }
28674         if(this.footer){
28675            var fm = this.footer.getMargins();
28676             height += (this.footer.getHeight()+fm.top+fm.bottom);
28677         }
28678         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28679         height += this.centerBg.getPadding("tb");
28680         return height;
28681     },
28682
28683     // private
28684     syncBodyHeight : function(){
28685         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28686         var height = this.size.height - this.getHeaderFooterHeight(false);
28687         bd.setHeight(height-bd.getMargins("tb"));
28688         var hh = this.header.getHeight();
28689         var h = this.size.height-hh;
28690         cb.setHeight(h);
28691         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28692         bw.setHeight(h-cb.getPadding("tb"));
28693         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28694         bd.setWidth(bw.getWidth(true));
28695         if(this.tabs){
28696             this.tabs.syncHeight();
28697             if(Roo.isIE){
28698                 this.tabs.el.repaint();
28699             }
28700         }
28701     },
28702
28703     /**
28704      * Restores the previous state of the dialog if Roo.state is configured.
28705      * @return {Roo.BasicDialog} this
28706      */
28707     restoreState : function(){
28708         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28709         if(box && box.width){
28710             this.xy = [box.x, box.y];
28711             this.resizeTo(box.width, box.height);
28712         }
28713         return this;
28714     },
28715
28716     // private
28717     beforeShow : function(){
28718         this.expand();
28719         if(this.fixedcenter){
28720             this.xy = this.el.getCenterXY(true);
28721         }
28722         if(this.modal){
28723             Roo.get(document.body).addClass("x-body-masked");
28724             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28725             this.mask.show();
28726         }
28727         this.constrainXY();
28728     },
28729
28730     // private
28731     animShow : function(){
28732         var b = Roo.get(this.animateTarget, true).getBox();
28733         this.proxy.setSize(b.width, b.height);
28734         this.proxy.setLocation(b.x, b.y);
28735         this.proxy.show();
28736         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28737                     true, .35, this.showEl.createDelegate(this));
28738     },
28739
28740     /**
28741      * Shows the dialog.
28742      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28743      * @return {Roo.BasicDialog} this
28744      */
28745     show : function(animateTarget){
28746         if (this.fireEvent("beforeshow", this) === false){
28747             return;
28748         }
28749         if(this.syncHeightBeforeShow){
28750             this.syncBodyHeight();
28751         }else if(this.firstShow){
28752             this.firstShow = false;
28753             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28754         }
28755         this.animateTarget = animateTarget || this.animateTarget;
28756         if(!this.el.isVisible()){
28757             this.beforeShow();
28758             if(this.animateTarget){
28759                 this.animShow();
28760             }else{
28761                 this.showEl();
28762             }
28763         }
28764         return this;
28765     },
28766
28767     // private
28768     showEl : function(){
28769         this.proxy.hide();
28770         this.el.setXY(this.xy);
28771         this.el.show();
28772         this.adjustAssets(true);
28773         this.toFront();
28774         this.focus();
28775         // IE peekaboo bug - fix found by Dave Fenwick
28776         if(Roo.isIE){
28777             this.el.repaint();
28778         }
28779         this.fireEvent("show", this);
28780     },
28781
28782     /**
28783      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28784      * dialog itself will receive focus.
28785      */
28786     focus : function(){
28787         if(this.defaultButton){
28788             this.defaultButton.focus();
28789         }else{
28790             this.focusEl.focus();
28791         }
28792     },
28793
28794     // private
28795     constrainXY : function(){
28796         if(this.constraintoviewport !== false){
28797             if(!this.viewSize){
28798                 if(this.container){
28799                     var s = this.container.getSize();
28800                     this.viewSize = [s.width, s.height];
28801                 }else{
28802                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28803                 }
28804             }
28805             var s = Roo.get(this.container||document).getScroll();
28806
28807             var x = this.xy[0], y = this.xy[1];
28808             var w = this.size.width, h = this.size.height;
28809             var vw = this.viewSize[0], vh = this.viewSize[1];
28810             // only move it if it needs it
28811             var moved = false;
28812             // first validate right/bottom
28813             if(x + w > vw+s.left){
28814                 x = vw - w;
28815                 moved = true;
28816             }
28817             if(y + h > vh+s.top){
28818                 y = vh - h;
28819                 moved = true;
28820             }
28821             // then make sure top/left isn't negative
28822             if(x < s.left){
28823                 x = s.left;
28824                 moved = true;
28825             }
28826             if(y < s.top){
28827                 y = s.top;
28828                 moved = true;
28829             }
28830             if(moved){
28831                 // cache xy
28832                 this.xy = [x, y];
28833                 if(this.isVisible()){
28834                     this.el.setLocation(x, y);
28835                     this.adjustAssets();
28836                 }
28837             }
28838         }
28839     },
28840
28841     // private
28842     onDrag : function(){
28843         if(!this.proxyDrag){
28844             this.xy = this.el.getXY();
28845             this.adjustAssets();
28846         }
28847     },
28848
28849     // private
28850     adjustAssets : function(doShow){
28851         var x = this.xy[0], y = this.xy[1];
28852         var w = this.size.width, h = this.size.height;
28853         if(doShow === true){
28854             if(this.shadow){
28855                 this.shadow.show(this.el);
28856             }
28857             if(this.shim){
28858                 this.shim.show();
28859             }
28860         }
28861         if(this.shadow && this.shadow.isVisible()){
28862             this.shadow.show(this.el);
28863         }
28864         if(this.shim && this.shim.isVisible()){
28865             this.shim.setBounds(x, y, w, h);
28866         }
28867     },
28868
28869     // private
28870     adjustViewport : function(w, h){
28871         if(!w || !h){
28872             w = Roo.lib.Dom.getViewWidth();
28873             h = Roo.lib.Dom.getViewHeight();
28874         }
28875         // cache the size
28876         this.viewSize = [w, h];
28877         if(this.modal && this.mask.isVisible()){
28878             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28879             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28880         }
28881         if(this.isVisible()){
28882             this.constrainXY();
28883         }
28884     },
28885
28886     /**
28887      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28888      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28889      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28890      */
28891     destroy : function(removeEl){
28892         if(this.isVisible()){
28893             this.animateTarget = null;
28894             this.hide();
28895         }
28896         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28897         if(this.tabs){
28898             this.tabs.destroy(removeEl);
28899         }
28900         Roo.destroy(
28901              this.shim,
28902              this.proxy,
28903              this.resizer,
28904              this.close,
28905              this.mask
28906         );
28907         if(this.dd){
28908             this.dd.unreg();
28909         }
28910         if(this.buttons){
28911            for(var i = 0, len = this.buttons.length; i < len; i++){
28912                this.buttons[i].destroy();
28913            }
28914         }
28915         this.el.removeAllListeners();
28916         if(removeEl === true){
28917             this.el.update("");
28918             this.el.remove();
28919         }
28920         Roo.DialogManager.unregister(this);
28921     },
28922
28923     // private
28924     startMove : function(){
28925         if(this.proxyDrag){
28926             this.proxy.show();
28927         }
28928         if(this.constraintoviewport !== false){
28929             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
28930         }
28931     },
28932
28933     // private
28934     endMove : function(){
28935         if(!this.proxyDrag){
28936             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
28937         }else{
28938             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
28939             this.proxy.hide();
28940         }
28941         this.refreshSize();
28942         this.adjustAssets();
28943         this.focus();
28944         this.fireEvent("move", this, this.xy[0], this.xy[1]);
28945     },
28946
28947     /**
28948      * Brings this dialog to the front of any other visible dialogs
28949      * @return {Roo.BasicDialog} this
28950      */
28951     toFront : function(){
28952         Roo.DialogManager.bringToFront(this);
28953         return this;
28954     },
28955
28956     /**
28957      * Sends this dialog to the back (under) of any other visible dialogs
28958      * @return {Roo.BasicDialog} this
28959      */
28960     toBack : function(){
28961         Roo.DialogManager.sendToBack(this);
28962         return this;
28963     },
28964
28965     /**
28966      * Centers this dialog in the viewport
28967      * @return {Roo.BasicDialog} this
28968      */
28969     center : function(){
28970         var xy = this.el.getCenterXY(true);
28971         this.moveTo(xy[0], xy[1]);
28972         return this;
28973     },
28974
28975     /**
28976      * Moves the dialog's top-left corner to the specified point
28977      * @param {Number} x
28978      * @param {Number} y
28979      * @return {Roo.BasicDialog} this
28980      */
28981     moveTo : function(x, y){
28982         this.xy = [x,y];
28983         if(this.isVisible()){
28984             this.el.setXY(this.xy);
28985             this.adjustAssets();
28986         }
28987         return this;
28988     },
28989
28990     /**
28991      * Aligns the dialog to the specified element
28992      * @param {String/HTMLElement/Roo.Element} element The element to align to.
28993      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
28994      * @param {Array} offsets (optional) Offset the positioning by [x, y]
28995      * @return {Roo.BasicDialog} this
28996      */
28997     alignTo : function(element, position, offsets){
28998         this.xy = this.el.getAlignToXY(element, position, offsets);
28999         if(this.isVisible()){
29000             this.el.setXY(this.xy);
29001             this.adjustAssets();
29002         }
29003         return this;
29004     },
29005
29006     /**
29007      * Anchors an element to another element and realigns it when the window is resized.
29008      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29009      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29010      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29011      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29012      * is a number, it is used as the buffer delay (defaults to 50ms).
29013      * @return {Roo.BasicDialog} this
29014      */
29015     anchorTo : function(el, alignment, offsets, monitorScroll){
29016         var action = function(){
29017             this.alignTo(el, alignment, offsets);
29018         };
29019         Roo.EventManager.onWindowResize(action, this);
29020         var tm = typeof monitorScroll;
29021         if(tm != 'undefined'){
29022             Roo.EventManager.on(window, 'scroll', action, this,
29023                 {buffer: tm == 'number' ? monitorScroll : 50});
29024         }
29025         action.call(this);
29026         return this;
29027     },
29028
29029     /**
29030      * Returns true if the dialog is visible
29031      * @return {Boolean}
29032      */
29033     isVisible : function(){
29034         return this.el.isVisible();
29035     },
29036
29037     // private
29038     animHide : function(callback){
29039         var b = Roo.get(this.animateTarget).getBox();
29040         this.proxy.show();
29041         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29042         this.el.hide();
29043         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29044                     this.hideEl.createDelegate(this, [callback]));
29045     },
29046
29047     /**
29048      * Hides the dialog.
29049      * @param {Function} callback (optional) Function to call when the dialog is hidden
29050      * @return {Roo.BasicDialog} this
29051      */
29052     hide : function(callback){
29053         if (this.fireEvent("beforehide", this) === false){
29054             return;
29055         }
29056         if(this.shadow){
29057             this.shadow.hide();
29058         }
29059         if(this.shim) {
29060           this.shim.hide();
29061         }
29062         if(this.animateTarget){
29063            this.animHide(callback);
29064         }else{
29065             this.el.hide();
29066             this.hideEl(callback);
29067         }
29068         return this;
29069     },
29070
29071     // private
29072     hideEl : function(callback){
29073         this.proxy.hide();
29074         if(this.modal){
29075             this.mask.hide();
29076             Roo.get(document.body).removeClass("x-body-masked");
29077         }
29078         this.fireEvent("hide", this);
29079         if(typeof callback == "function"){
29080             callback();
29081         }
29082     },
29083
29084     // private
29085     hideAction : function(){
29086         this.setLeft("-10000px");
29087         this.setTop("-10000px");
29088         this.setStyle("visibility", "hidden");
29089     },
29090
29091     // private
29092     refreshSize : function(){
29093         this.size = this.el.getSize();
29094         this.xy = this.el.getXY();
29095         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29096     },
29097
29098     // private
29099     // z-index is managed by the DialogManager and may be overwritten at any time
29100     setZIndex : function(index){
29101         if(this.modal){
29102             this.mask.setStyle("z-index", index);
29103         }
29104         if(this.shim){
29105             this.shim.setStyle("z-index", ++index);
29106         }
29107         if(this.shadow){
29108             this.shadow.setZIndex(++index);
29109         }
29110         this.el.setStyle("z-index", ++index);
29111         if(this.proxy){
29112             this.proxy.setStyle("z-index", ++index);
29113         }
29114         if(this.resizer){
29115             this.resizer.proxy.setStyle("z-index", ++index);
29116         }
29117
29118         this.lastZIndex = index;
29119     },
29120
29121     /**
29122      * Returns the element for this dialog
29123      * @return {Roo.Element} The underlying dialog Element
29124      */
29125     getEl : function(){
29126         return this.el;
29127     }
29128 });
29129
29130 /**
29131  * @class Roo.DialogManager
29132  * Provides global access to BasicDialogs that have been created and
29133  * support for z-indexing (layering) multiple open dialogs.
29134  */
29135 Roo.DialogManager = function(){
29136     var list = {};
29137     var accessList = [];
29138     var front = null;
29139
29140     // private
29141     var sortDialogs = function(d1, d2){
29142         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29143     };
29144
29145     // private
29146     var orderDialogs = function(){
29147         accessList.sort(sortDialogs);
29148         var seed = Roo.DialogManager.zseed;
29149         for(var i = 0, len = accessList.length; i < len; i++){
29150             var dlg = accessList[i];
29151             if(dlg){
29152                 dlg.setZIndex(seed + (i*10));
29153             }
29154         }
29155     };
29156
29157     return {
29158         /**
29159          * The starting z-index for BasicDialogs (defaults to 9000)
29160          * @type Number The z-index value
29161          */
29162         zseed : 9000,
29163
29164         // private
29165         register : function(dlg){
29166             list[dlg.id] = dlg;
29167             accessList.push(dlg);
29168         },
29169
29170         // private
29171         unregister : function(dlg){
29172             delete list[dlg.id];
29173             var i=0;
29174             var len=0;
29175             if(!accessList.indexOf){
29176                 for(  i = 0, len = accessList.length; i < len; i++){
29177                     if(accessList[i] == dlg){
29178                         accessList.splice(i, 1);
29179                         return;
29180                     }
29181                 }
29182             }else{
29183                  i = accessList.indexOf(dlg);
29184                 if(i != -1){
29185                     accessList.splice(i, 1);
29186                 }
29187             }
29188         },
29189
29190         /**
29191          * Gets a registered dialog by id
29192          * @param {String/Object} id The id of the dialog or a dialog
29193          * @return {Roo.BasicDialog} this
29194          */
29195         get : function(id){
29196             return typeof id == "object" ? id : list[id];
29197         },
29198
29199         /**
29200          * Brings the specified dialog to the front
29201          * @param {String/Object} dlg The id of the dialog or a dialog
29202          * @return {Roo.BasicDialog} this
29203          */
29204         bringToFront : function(dlg){
29205             dlg = this.get(dlg);
29206             if(dlg != front){
29207                 front = dlg;
29208                 dlg._lastAccess = new Date().getTime();
29209                 orderDialogs();
29210             }
29211             return dlg;
29212         },
29213
29214         /**
29215          * Sends the specified dialog to the back
29216          * @param {String/Object} dlg The id of the dialog or a dialog
29217          * @return {Roo.BasicDialog} this
29218          */
29219         sendToBack : function(dlg){
29220             dlg = this.get(dlg);
29221             dlg._lastAccess = -(new Date().getTime());
29222             orderDialogs();
29223             return dlg;
29224         },
29225
29226         /**
29227          * Hides all dialogs
29228          */
29229         hideAll : function(){
29230             for(var id in list){
29231                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29232                     list[id].hide();
29233                 }
29234             }
29235         }
29236     };
29237 }();
29238
29239 /**
29240  * @class Roo.LayoutDialog
29241  * @extends Roo.BasicDialog
29242  * Dialog which provides adjustments for working with a layout in a Dialog.
29243  * Add your necessary layout config options to the dialog's config.<br>
29244  * Example usage (including a nested layout):
29245  * <pre><code>
29246 if(!dialog){
29247     dialog = new Roo.LayoutDialog("download-dlg", {
29248         modal: true,
29249         width:600,
29250         height:450,
29251         shadow:true,
29252         minWidth:500,
29253         minHeight:350,
29254         autoTabs:true,
29255         proxyDrag:true,
29256         // layout config merges with the dialog config
29257         center:{
29258             tabPosition: "top",
29259             alwaysShowTabs: true
29260         }
29261     });
29262     dialog.addKeyListener(27, dialog.hide, dialog);
29263     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29264     dialog.addButton("Build It!", this.getDownload, this);
29265
29266     // we can even add nested layouts
29267     var innerLayout = new Roo.BorderLayout("dl-inner", {
29268         east: {
29269             initialSize: 200,
29270             autoScroll:true,
29271             split:true
29272         },
29273         center: {
29274             autoScroll:true
29275         }
29276     });
29277     innerLayout.beginUpdate();
29278     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29279     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29280     innerLayout.endUpdate(true);
29281
29282     var layout = dialog.getLayout();
29283     layout.beginUpdate();
29284     layout.add("center", new Roo.ContentPanel("standard-panel",
29285                         {title: "Download the Source", fitToFrame:true}));
29286     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29287                {title: "Build your own roo.js"}));
29288     layout.getRegion("center").showPanel(sp);
29289     layout.endUpdate();
29290 }
29291 </code></pre>
29292     * @constructor
29293     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29294     * @param {Object} config configuration options
29295   */
29296 Roo.LayoutDialog = function(el, cfg){
29297     
29298     var config=  cfg;
29299     if (typeof(cfg) == 'undefined') {
29300         config = Roo.apply({}, el);
29301         el = Roo.get( document.documentElement || document.body).createChild();
29302         //config.autoCreate = true;
29303     }
29304     
29305     
29306     config.autoTabs = false;
29307     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29308     this.body.setStyle({overflow:"hidden", position:"relative"});
29309     this.layout = new Roo.BorderLayout(this.body.dom, config);
29310     this.layout.monitorWindowResize = false;
29311     this.el.addClass("x-dlg-auto-layout");
29312     // fix case when center region overwrites center function
29313     this.center = Roo.BasicDialog.prototype.center;
29314     this.on("show", this.layout.layout, this.layout, true);
29315     if (config.items) {
29316         var xitems = config.items;
29317         delete config.items;
29318         Roo.each(xitems, this.addxtype, this);
29319     }
29320     
29321     
29322 };
29323 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29324     /**
29325      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29326      * @deprecated
29327      */
29328     endUpdate : function(){
29329         this.layout.endUpdate();
29330     },
29331
29332     /**
29333      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29334      *  @deprecated
29335      */
29336     beginUpdate : function(){
29337         this.layout.beginUpdate();
29338     },
29339
29340     /**
29341      * Get the BorderLayout for this dialog
29342      * @return {Roo.BorderLayout}
29343      */
29344     getLayout : function(){
29345         return this.layout;
29346     },
29347
29348     showEl : function(){
29349         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29350         if(Roo.isIE7){
29351             this.layout.layout();
29352         }
29353     },
29354
29355     // private
29356     // Use the syncHeightBeforeShow config option to control this automatically
29357     syncBodyHeight : function(){
29358         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29359         if(this.layout){this.layout.layout();}
29360     },
29361     
29362       /**
29363      * Add an xtype element (actually adds to the layout.)
29364      * @return {Object} xdata xtype object data.
29365      */
29366     
29367     addxtype : function(c) {
29368         return this.layout.addxtype(c);
29369     }
29370 });/*
29371  * Based on:
29372  * Ext JS Library 1.1.1
29373  * Copyright(c) 2006-2007, Ext JS, LLC.
29374  *
29375  * Originally Released Under LGPL - original licence link has changed is not relivant.
29376  *
29377  * Fork - LGPL
29378  * <script type="text/javascript">
29379  */
29380  
29381 /**
29382  * @class Roo.MessageBox
29383  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29384  * Example usage:
29385  *<pre><code>
29386 // Basic alert:
29387 Roo.Msg.alert('Status', 'Changes saved successfully.');
29388
29389 // Prompt for user data:
29390 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29391     if (btn == 'ok'){
29392         // process text value...
29393     }
29394 });
29395
29396 // Show a dialog using config options:
29397 Roo.Msg.show({
29398    title:'Save Changes?',
29399    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29400    buttons: Roo.Msg.YESNOCANCEL,
29401    fn: processResult,
29402    animEl: 'elId'
29403 });
29404 </code></pre>
29405  * @singleton
29406  */
29407 Roo.MessageBox = function(){
29408     var dlg, opt, mask, waitTimer;
29409     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29410     var buttons, activeTextEl, bwidth;
29411
29412     // private
29413     var handleButton = function(button){
29414         dlg.hide();
29415         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29416     };
29417
29418     // private
29419     var handleHide = function(){
29420         if(opt && opt.cls){
29421             dlg.el.removeClass(opt.cls);
29422         }
29423         if(waitTimer){
29424             Roo.TaskMgr.stop(waitTimer);
29425             waitTimer = null;
29426         }
29427     };
29428
29429     // private
29430     var updateButtons = function(b){
29431         var width = 0;
29432         if(!b){
29433             buttons["ok"].hide();
29434             buttons["cancel"].hide();
29435             buttons["yes"].hide();
29436             buttons["no"].hide();
29437             dlg.footer.dom.style.display = 'none';
29438             return width;
29439         }
29440         dlg.footer.dom.style.display = '';
29441         for(var k in buttons){
29442             if(typeof buttons[k] != "function"){
29443                 if(b[k]){
29444                     buttons[k].show();
29445                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29446                     width += buttons[k].el.getWidth()+15;
29447                 }else{
29448                     buttons[k].hide();
29449                 }
29450             }
29451         }
29452         return width;
29453     };
29454
29455     // private
29456     var handleEsc = function(d, k, e){
29457         if(opt && opt.closable !== false){
29458             dlg.hide();
29459         }
29460         if(e){
29461             e.stopEvent();
29462         }
29463     };
29464
29465     return {
29466         /**
29467          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29468          * @return {Roo.BasicDialog} The BasicDialog element
29469          */
29470         getDialog : function(){
29471            if(!dlg){
29472                 dlg = new Roo.BasicDialog("x-msg-box", {
29473                     autoCreate : true,
29474                     shadow: true,
29475                     draggable: true,
29476                     resizable:false,
29477                     constraintoviewport:false,
29478                     fixedcenter:true,
29479                     collapsible : false,
29480                     shim:true,
29481                     modal: true,
29482                     width:400, height:100,
29483                     buttonAlign:"center",
29484                     closeClick : function(){
29485                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29486                             handleButton("no");
29487                         }else{
29488                             handleButton("cancel");
29489                         }
29490                     }
29491                 });
29492                 dlg.on("hide", handleHide);
29493                 mask = dlg.mask;
29494                 dlg.addKeyListener(27, handleEsc);
29495                 buttons = {};
29496                 var bt = this.buttonText;
29497                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29498                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29499                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29500                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29501                 bodyEl = dlg.body.createChild({
29502
29503                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
29504                 });
29505                 msgEl = bodyEl.dom.firstChild;
29506                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29507                 textboxEl.enableDisplayMode();
29508                 textboxEl.addKeyListener([10,13], function(){
29509                     if(dlg.isVisible() && opt && opt.buttons){
29510                         if(opt.buttons.ok){
29511                             handleButton("ok");
29512                         }else if(opt.buttons.yes){
29513                             handleButton("yes");
29514                         }
29515                     }
29516                 });
29517                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29518                 textareaEl.enableDisplayMode();
29519                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29520                 progressEl.enableDisplayMode();
29521                 var pf = progressEl.dom.firstChild;
29522                 if (pf) {
29523                     pp = Roo.get(pf.firstChild);
29524                     pp.setHeight(pf.offsetHeight);
29525                 }
29526                 
29527             }
29528             return dlg;
29529         },
29530
29531         /**
29532          * Updates the message box body text
29533          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29534          * the XHTML-compliant non-breaking space character '&amp;#160;')
29535          * @return {Roo.MessageBox} This message box
29536          */
29537         updateText : function(text){
29538             if(!dlg.isVisible() && !opt.width){
29539                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29540             }
29541             msgEl.innerHTML = text || '&#160;';
29542             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29543                         Math.max(opt.minWidth || this.minWidth, bwidth));
29544             if(opt.prompt){
29545                 activeTextEl.setWidth(w);
29546             }
29547             if(dlg.isVisible()){
29548                 dlg.fixedcenter = false;
29549             }
29550             dlg.setContentSize(w, bodyEl.getHeight());
29551             if(dlg.isVisible()){
29552                 dlg.fixedcenter = true;
29553             }
29554             return this;
29555         },
29556
29557         /**
29558          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29559          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29560          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29561          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29562          * @return {Roo.MessageBox} This message box
29563          */
29564         updateProgress : function(value, text){
29565             if(text){
29566                 this.updateText(text);
29567             }
29568             if (pp) { // weird bug on my firefox - for some reason this is not defined
29569                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29570             }
29571             return this;
29572         },        
29573
29574         /**
29575          * Returns true if the message box is currently displayed
29576          * @return {Boolean} True if the message box is visible, else false
29577          */
29578         isVisible : function(){
29579             return dlg && dlg.isVisible();  
29580         },
29581
29582         /**
29583          * Hides the message box if it is displayed
29584          */
29585         hide : function(){
29586             if(this.isVisible()){
29587                 dlg.hide();
29588             }  
29589         },
29590
29591         /**
29592          * Displays a new message box, or reinitializes an existing message box, based on the config options
29593          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29594          * The following config object properties are supported:
29595          * <pre>
29596 Property    Type             Description
29597 ----------  ---------------  ------------------------------------------------------------------------------------
29598 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29599                                    closes (defaults to undefined)
29600 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29601                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29602 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29603                                    progress and wait dialogs will ignore this property and always hide the
29604                                    close button as they can only be closed programmatically.
29605 cls               String           A custom CSS class to apply to the message box element
29606 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29607                                    displayed (defaults to 75)
29608 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29609                                    function will be btn (the name of the button that was clicked, if applicable,
29610                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29611                                    Progress and wait dialogs will ignore this option since they do not respond to
29612                                    user actions and can only be closed programmatically, so any required function
29613                                    should be called by the same code after it closes the dialog.
29614 icon              String           A CSS class that provides a background image to be used as an icon for
29615                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29616 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29617 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29618 modal             Boolean          False to allow user interaction with the page while the message box is
29619                                    displayed (defaults to true)
29620 msg               String           A string that will replace the existing message box body text (defaults
29621                                    to the XHTML-compliant non-breaking space character '&#160;')
29622 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29623 progress          Boolean          True to display a progress bar (defaults to false)
29624 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29625 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29626 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29627 title             String           The title text
29628 value             String           The string value to set into the active textbox element if displayed
29629 wait              Boolean          True to display a progress bar (defaults to false)
29630 width             Number           The width of the dialog in pixels
29631 </pre>
29632          *
29633          * Example usage:
29634          * <pre><code>
29635 Roo.Msg.show({
29636    title: 'Address',
29637    msg: 'Please enter your address:',
29638    width: 300,
29639    buttons: Roo.MessageBox.OKCANCEL,
29640    multiline: true,
29641    fn: saveAddress,
29642    animEl: 'addAddressBtn'
29643 });
29644 </code></pre>
29645          * @param {Object} config Configuration options
29646          * @return {Roo.MessageBox} This message box
29647          */
29648         show : function(options){
29649             if(this.isVisible()){
29650                 this.hide();
29651             }
29652             var d = this.getDialog();
29653             opt = options;
29654             d.setTitle(opt.title || "&#160;");
29655             d.close.setDisplayed(opt.closable !== false);
29656             activeTextEl = textboxEl;
29657             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29658             if(opt.prompt){
29659                 if(opt.multiline){
29660                     textboxEl.hide();
29661                     textareaEl.show();
29662                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29663                         opt.multiline : this.defaultTextHeight);
29664                     activeTextEl = textareaEl;
29665                 }else{
29666                     textboxEl.show();
29667                     textareaEl.hide();
29668                 }
29669             }else{
29670                 textboxEl.hide();
29671                 textareaEl.hide();
29672             }
29673             progressEl.setDisplayed(opt.progress === true);
29674             this.updateProgress(0);
29675             activeTextEl.dom.value = opt.value || "";
29676             if(opt.prompt){
29677                 dlg.setDefaultButton(activeTextEl);
29678             }else{
29679                 var bs = opt.buttons;
29680                 var db = null;
29681                 if(bs && bs.ok){
29682                     db = buttons["ok"];
29683                 }else if(bs && bs.yes){
29684                     db = buttons["yes"];
29685                 }
29686                 dlg.setDefaultButton(db);
29687             }
29688             bwidth = updateButtons(opt.buttons);
29689             this.updateText(opt.msg);
29690             if(opt.cls){
29691                 d.el.addClass(opt.cls);
29692             }
29693             d.proxyDrag = opt.proxyDrag === true;
29694             d.modal = opt.modal !== false;
29695             d.mask = opt.modal !== false ? mask : false;
29696             if(!d.isVisible()){
29697                 // force it to the end of the z-index stack so it gets a cursor in FF
29698                 document.body.appendChild(dlg.el.dom);
29699                 d.animateTarget = null;
29700                 d.show(options.animEl);
29701             }
29702             return this;
29703         },
29704
29705         /**
29706          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29707          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29708          * and closing the message box when the process is complete.
29709          * @param {String} title The title bar text
29710          * @param {String} msg The message box body text
29711          * @return {Roo.MessageBox} This message box
29712          */
29713         progress : function(title, msg){
29714             this.show({
29715                 title : title,
29716                 msg : msg,
29717                 buttons: false,
29718                 progress:true,
29719                 closable:false,
29720                 minWidth: this.minProgressWidth,
29721                 modal : true
29722             });
29723             return this;
29724         },
29725
29726         /**
29727          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29728          * If a callback function is passed it will be called after the user clicks the button, and the
29729          * id of the button that was clicked will be passed as the only parameter to the callback
29730          * (could also be the top-right close button).
29731          * @param {String} title The title bar text
29732          * @param {String} msg The message box body text
29733          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29734          * @param {Object} scope (optional) The scope of the callback function
29735          * @return {Roo.MessageBox} This message box
29736          */
29737         alert : function(title, msg, fn, scope){
29738             this.show({
29739                 title : title,
29740                 msg : msg,
29741                 buttons: this.OK,
29742                 fn: fn,
29743                 scope : scope,
29744                 modal : true
29745             });
29746             return this;
29747         },
29748
29749         /**
29750          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29751          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29752          * You are responsible for closing the message box when the process is complete.
29753          * @param {String} msg The message box body text
29754          * @param {String} title (optional) The title bar text
29755          * @return {Roo.MessageBox} This message box
29756          */
29757         wait : function(msg, title){
29758             this.show({
29759                 title : title,
29760                 msg : msg,
29761                 buttons: false,
29762                 closable:false,
29763                 progress:true,
29764                 modal:true,
29765                 width:300,
29766                 wait:true
29767             });
29768             waitTimer = Roo.TaskMgr.start({
29769                 run: function(i){
29770                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29771                 },
29772                 interval: 1000
29773             });
29774             return this;
29775         },
29776
29777         /**
29778          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29779          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29780          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29781          * @param {String} title The title bar text
29782          * @param {String} msg The message box body text
29783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29784          * @param {Object} scope (optional) The scope of the callback function
29785          * @return {Roo.MessageBox} This message box
29786          */
29787         confirm : function(title, msg, fn, scope){
29788             this.show({
29789                 title : title,
29790                 msg : msg,
29791                 buttons: this.YESNO,
29792                 fn: fn,
29793                 scope : scope,
29794                 modal : true
29795             });
29796             return this;
29797         },
29798
29799         /**
29800          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29801          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29802          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29803          * (could also be the top-right close button) and the text that was entered will be passed as the two
29804          * parameters to the callback.
29805          * @param {String} title The title bar text
29806          * @param {String} msg The message box body text
29807          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29808          * @param {Object} scope (optional) The scope of the callback function
29809          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29810          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29811          * @return {Roo.MessageBox} This message box
29812          */
29813         prompt : function(title, msg, fn, scope, multiline){
29814             this.show({
29815                 title : title,
29816                 msg : msg,
29817                 buttons: this.OKCANCEL,
29818                 fn: fn,
29819                 minWidth:250,
29820                 scope : scope,
29821                 prompt:true,
29822                 multiline: multiline,
29823                 modal : true
29824             });
29825             return this;
29826         },
29827
29828         /**
29829          * Button config that displays a single OK button
29830          * @type Object
29831          */
29832         OK : {ok:true},
29833         /**
29834          * Button config that displays Yes and No buttons
29835          * @type Object
29836          */
29837         YESNO : {yes:true, no:true},
29838         /**
29839          * Button config that displays OK and Cancel buttons
29840          * @type Object
29841          */
29842         OKCANCEL : {ok:true, cancel:true},
29843         /**
29844          * Button config that displays Yes, No and Cancel buttons
29845          * @type Object
29846          */
29847         YESNOCANCEL : {yes:true, no:true, cancel:true},
29848
29849         /**
29850          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29851          * @type Number
29852          */
29853         defaultTextHeight : 75,
29854         /**
29855          * The maximum width in pixels of the message box (defaults to 600)
29856          * @type Number
29857          */
29858         maxWidth : 600,
29859         /**
29860          * The minimum width in pixels of the message box (defaults to 100)
29861          * @type Number
29862          */
29863         minWidth : 100,
29864         /**
29865          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29866          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29867          * @type Number
29868          */
29869         minProgressWidth : 250,
29870         /**
29871          * An object containing the default button text strings that can be overriden for localized language support.
29872          * Supported properties are: ok, cancel, yes and no.
29873          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29874          * @type Object
29875          */
29876         buttonText : {
29877             ok : "OK",
29878             cancel : "Cancel",
29879             yes : "Yes",
29880             no : "No"
29881         }
29882     };
29883 }();
29884
29885 /**
29886  * Shorthand for {@link Roo.MessageBox}
29887  */
29888 Roo.Msg = Roo.MessageBox;/*
29889  * Based on:
29890  * Ext JS Library 1.1.1
29891  * Copyright(c) 2006-2007, Ext JS, LLC.
29892  *
29893  * Originally Released Under LGPL - original licence link has changed is not relivant.
29894  *
29895  * Fork - LGPL
29896  * <script type="text/javascript">
29897  */
29898 /**
29899  * @class Roo.QuickTips
29900  * Provides attractive and customizable tooltips for any element.
29901  * @singleton
29902  */
29903 Roo.QuickTips = function(){
29904     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29905     var ce, bd, xy, dd;
29906     var visible = false, disabled = true, inited = false;
29907     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29908     
29909     var onOver = function(e){
29910         if(disabled){
29911             return;
29912         }
29913         var t = e.getTarget();
29914         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29915             return;
29916         }
29917         if(ce && t == ce.el){
29918             clearTimeout(hideProc);
29919             return;
29920         }
29921         if(t && tagEls[t.id]){
29922             tagEls[t.id].el = t;
29923             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
29924             return;
29925         }
29926         var ttp, et = Roo.fly(t);
29927         var ns = cfg.namespace;
29928         if(tm.interceptTitles && t.title){
29929             ttp = t.title;
29930             t.qtip = ttp;
29931             t.removeAttribute("title");
29932             e.preventDefault();
29933         }else{
29934             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
29935         }
29936         if(ttp){
29937             showProc = show.defer(tm.showDelay, tm, [{
29938                 el: t, 
29939                 text: ttp, 
29940                 width: et.getAttributeNS(ns, cfg.width),
29941                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
29942                 title: et.getAttributeNS(ns, cfg.title),
29943                     cls: et.getAttributeNS(ns, cfg.cls)
29944             }]);
29945         }
29946     };
29947     
29948     var onOut = function(e){
29949         clearTimeout(showProc);
29950         var t = e.getTarget();
29951         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
29952             hideProc = setTimeout(hide, tm.hideDelay);
29953         }
29954     };
29955     
29956     var onMove = function(e){
29957         if(disabled){
29958             return;
29959         }
29960         xy = e.getXY();
29961         xy[1] += 18;
29962         if(tm.trackMouse && ce){
29963             el.setXY(xy);
29964         }
29965     };
29966     
29967     var onDown = function(e){
29968         clearTimeout(showProc);
29969         clearTimeout(hideProc);
29970         if(!e.within(el)){
29971             if(tm.hideOnClick){
29972                 hide();
29973                 tm.disable();
29974                 tm.enable.defer(100, tm);
29975             }
29976         }
29977     };
29978     
29979     var getPad = function(){
29980         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
29981     };
29982
29983     var show = function(o){
29984         if(disabled){
29985             return;
29986         }
29987         clearTimeout(dismissProc);
29988         ce = o;
29989         if(removeCls){ // in case manually hidden
29990             el.removeClass(removeCls);
29991             removeCls = null;
29992         }
29993         if(ce.cls){
29994             el.addClass(ce.cls);
29995             removeCls = ce.cls;
29996         }
29997         if(ce.title){
29998             tipTitle.update(ce.title);
29999             tipTitle.show();
30000         }else{
30001             tipTitle.update('');
30002             tipTitle.hide();
30003         }
30004         el.dom.style.width  = tm.maxWidth+'px';
30005         //tipBody.dom.style.width = '';
30006         tipBodyText.update(o.text);
30007         var p = getPad(), w = ce.width;
30008         if(!w){
30009             var td = tipBodyText.dom;
30010             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30011             if(aw > tm.maxWidth){
30012                 w = tm.maxWidth;
30013             }else if(aw < tm.minWidth){
30014                 w = tm.minWidth;
30015             }else{
30016                 w = aw;
30017             }
30018         }
30019         //tipBody.setWidth(w);
30020         el.setWidth(parseInt(w, 10) + p);
30021         if(ce.autoHide === false){
30022             close.setDisplayed(true);
30023             if(dd){
30024                 dd.unlock();
30025             }
30026         }else{
30027             close.setDisplayed(false);
30028             if(dd){
30029                 dd.lock();
30030             }
30031         }
30032         if(xy){
30033             el.avoidY = xy[1]-18;
30034             el.setXY(xy);
30035         }
30036         if(tm.animate){
30037             el.setOpacity(.1);
30038             el.setStyle("visibility", "visible");
30039             el.fadeIn({callback: afterShow});
30040         }else{
30041             afterShow();
30042         }
30043     };
30044     
30045     var afterShow = function(){
30046         if(ce){
30047             el.show();
30048             esc.enable();
30049             if(tm.autoDismiss && ce.autoHide !== false){
30050                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30051             }
30052         }
30053     };
30054     
30055     var hide = function(noanim){
30056         clearTimeout(dismissProc);
30057         clearTimeout(hideProc);
30058         ce = null;
30059         if(el.isVisible()){
30060             esc.disable();
30061             if(noanim !== true && tm.animate){
30062                 el.fadeOut({callback: afterHide});
30063             }else{
30064                 afterHide();
30065             } 
30066         }
30067     };
30068     
30069     var afterHide = function(){
30070         el.hide();
30071         if(removeCls){
30072             el.removeClass(removeCls);
30073             removeCls = null;
30074         }
30075     };
30076     
30077     return {
30078         /**
30079         * @cfg {Number} minWidth
30080         * The minimum width of the quick tip (defaults to 40)
30081         */
30082        minWidth : 40,
30083         /**
30084         * @cfg {Number} maxWidth
30085         * The maximum width of the quick tip (defaults to 300)
30086         */
30087        maxWidth : 300,
30088         /**
30089         * @cfg {Boolean} interceptTitles
30090         * True to automatically use the element's DOM title value if available (defaults to false)
30091         */
30092        interceptTitles : false,
30093         /**
30094         * @cfg {Boolean} trackMouse
30095         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30096         */
30097        trackMouse : false,
30098         /**
30099         * @cfg {Boolean} hideOnClick
30100         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30101         */
30102        hideOnClick : true,
30103         /**
30104         * @cfg {Number} showDelay
30105         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30106         */
30107        showDelay : 500,
30108         /**
30109         * @cfg {Number} hideDelay
30110         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30111         */
30112        hideDelay : 200,
30113         /**
30114         * @cfg {Boolean} autoHide
30115         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30116         * Used in conjunction with hideDelay.
30117         */
30118        autoHide : true,
30119         /**
30120         * @cfg {Boolean}
30121         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30122         * (defaults to true).  Used in conjunction with autoDismissDelay.
30123         */
30124        autoDismiss : true,
30125         /**
30126         * @cfg {Number}
30127         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30128         */
30129        autoDismissDelay : 5000,
30130        /**
30131         * @cfg {Boolean} animate
30132         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30133         */
30134        animate : false,
30135
30136        /**
30137         * @cfg {String} title
30138         * Title text to display (defaults to '').  This can be any valid HTML markup.
30139         */
30140         title: '',
30141        /**
30142         * @cfg {String} text
30143         * Body text to display (defaults to '').  This can be any valid HTML markup.
30144         */
30145         text : '',
30146        /**
30147         * @cfg {String} cls
30148         * A CSS class to apply to the base quick tip element (defaults to '').
30149         */
30150         cls : '',
30151        /**
30152         * @cfg {Number} width
30153         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30154         * minWidth or maxWidth.
30155         */
30156         width : null,
30157
30158     /**
30159      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30160      * or display QuickTips in a page.
30161      */
30162        init : function(){
30163           tm = Roo.QuickTips;
30164           cfg = tm.tagConfig;
30165           if(!inited){
30166               if(!Roo.isReady){ // allow calling of init() before onReady
30167                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30168                   return;
30169               }
30170               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30171               el.fxDefaults = {stopFx: true};
30172               // maximum custom styling
30173               //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>');
30174               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>');              
30175               tipTitle = el.child('h3');
30176               tipTitle.enableDisplayMode("block");
30177               tipBody = el.child('div.x-tip-bd');
30178               tipBodyText = el.child('div.x-tip-bd-inner');
30179               //bdLeft = el.child('div.x-tip-bd-left');
30180               //bdRight = el.child('div.x-tip-bd-right');
30181               close = el.child('div.x-tip-close');
30182               close.enableDisplayMode("block");
30183               close.on("click", hide);
30184               var d = Roo.get(document);
30185               d.on("mousedown", onDown);
30186               d.on("mouseover", onOver);
30187               d.on("mouseout", onOut);
30188               d.on("mousemove", onMove);
30189               esc = d.addKeyListener(27, hide);
30190               esc.disable();
30191               if(Roo.dd.DD){
30192                   dd = el.initDD("default", null, {
30193                       onDrag : function(){
30194                           el.sync();  
30195                       }
30196                   });
30197                   dd.setHandleElId(tipTitle.id);
30198                   dd.lock();
30199               }
30200               inited = true;
30201           }
30202           this.enable(); 
30203        },
30204
30205     /**
30206      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30207      * are supported:
30208      * <pre>
30209 Property    Type                   Description
30210 ----------  ---------------------  ------------------------------------------------------------------------
30211 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30212      * </ul>
30213      * @param {Object} config The config object
30214      */
30215        register : function(config){
30216            var cs = config instanceof Array ? config : arguments;
30217            for(var i = 0, len = cs.length; i < len; i++) {
30218                var c = cs[i];
30219                var target = c.target;
30220                if(target){
30221                    if(target instanceof Array){
30222                        for(var j = 0, jlen = target.length; j < jlen; j++){
30223                            tagEls[target[j]] = c;
30224                        }
30225                    }else{
30226                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30227                    }
30228                }
30229            }
30230        },
30231
30232     /**
30233      * Removes this quick tip from its element and destroys it.
30234      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30235      */
30236        unregister : function(el){
30237            delete tagEls[Roo.id(el)];
30238        },
30239
30240     /**
30241      * Enable this quick tip.
30242      */
30243        enable : function(){
30244            if(inited && disabled){
30245                locks.pop();
30246                if(locks.length < 1){
30247                    disabled = false;
30248                }
30249            }
30250        },
30251
30252     /**
30253      * Disable this quick tip.
30254      */
30255        disable : function(){
30256           disabled = true;
30257           clearTimeout(showProc);
30258           clearTimeout(hideProc);
30259           clearTimeout(dismissProc);
30260           if(ce){
30261               hide(true);
30262           }
30263           locks.push(1);
30264        },
30265
30266     /**
30267      * Returns true if the quick tip is enabled, else false.
30268      */
30269        isEnabled : function(){
30270             return !disabled;
30271        },
30272
30273         // private
30274        tagConfig : {
30275            namespace : "ext",
30276            attribute : "qtip",
30277            width : "width",
30278            target : "target",
30279            title : "qtitle",
30280            hide : "hide",
30281            cls : "qclass"
30282        }
30283    };
30284 }();
30285
30286 // backwards compat
30287 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30288  * Based on:
30289  * Ext JS Library 1.1.1
30290  * Copyright(c) 2006-2007, Ext JS, LLC.
30291  *
30292  * Originally Released Under LGPL - original licence link has changed is not relivant.
30293  *
30294  * Fork - LGPL
30295  * <script type="text/javascript">
30296  */
30297  
30298
30299 /**
30300  * @class Roo.tree.TreePanel
30301  * @extends Roo.data.Tree
30302
30303  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30304  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30305  * @cfg {Boolean} enableDD true to enable drag and drop
30306  * @cfg {Boolean} enableDrag true to enable just drag
30307  * @cfg {Boolean} enableDrop true to enable just drop
30308  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30309  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30310  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30311  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30312  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30313  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30314  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30315  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30316  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30317  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30318  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30319  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30320  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30321  * @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>
30322  * @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>
30323  * 
30324  * @constructor
30325  * @param {String/HTMLElement/Element} el The container element
30326  * @param {Object} config
30327  */
30328 Roo.tree.TreePanel = function(el, config){
30329     var root = false;
30330     var loader = false;
30331     if (config.root) {
30332         root = config.root;
30333         delete config.root;
30334     }
30335     if (config.loader) {
30336         loader = config.loader;
30337         delete config.loader;
30338     }
30339     
30340     Roo.apply(this, config);
30341     Roo.tree.TreePanel.superclass.constructor.call(this);
30342     this.el = Roo.get(el);
30343     this.el.addClass('x-tree');
30344     //console.log(root);
30345     if (root) {
30346         this.setRootNode( Roo.factory(root, Roo.tree));
30347     }
30348     if (loader) {
30349         this.loader = Roo.factory(loader, Roo.tree);
30350     }
30351    /**
30352     * Read-only. The id of the container element becomes this TreePanel's id.
30353     */
30354    this.id = this.el.id;
30355    this.addEvents({
30356         /**
30357         * @event beforeload
30358         * Fires before a node is loaded, return false to cancel
30359         * @param {Node} node The node being loaded
30360         */
30361         "beforeload" : true,
30362         /**
30363         * @event load
30364         * Fires when a node is loaded
30365         * @param {Node} node The node that was loaded
30366         */
30367         "load" : true,
30368         /**
30369         * @event textchange
30370         * Fires when the text for a node is changed
30371         * @param {Node} node The node
30372         * @param {String} text The new text
30373         * @param {String} oldText The old text
30374         */
30375         "textchange" : true,
30376         /**
30377         * @event beforeexpand
30378         * Fires before a node is expanded, return false to cancel.
30379         * @param {Node} node The node
30380         * @param {Boolean} deep
30381         * @param {Boolean} anim
30382         */
30383         "beforeexpand" : true,
30384         /**
30385         * @event beforecollapse
30386         * Fires before a node is collapsed, return false to cancel.
30387         * @param {Node} node The node
30388         * @param {Boolean} deep
30389         * @param {Boolean} anim
30390         */
30391         "beforecollapse" : true,
30392         /**
30393         * @event expand
30394         * Fires when a node is expanded
30395         * @param {Node} node The node
30396         */
30397         "expand" : true,
30398         /**
30399         * @event disabledchange
30400         * Fires when the disabled status of a node changes
30401         * @param {Node} node The node
30402         * @param {Boolean} disabled
30403         */
30404         "disabledchange" : true,
30405         /**
30406         * @event collapse
30407         * Fires when a node is collapsed
30408         * @param {Node} node The node
30409         */
30410         "collapse" : true,
30411         /**
30412         * @event beforeclick
30413         * Fires before click processing on a node. Return false to cancel the default action.
30414         * @param {Node} node The node
30415         * @param {Roo.EventObject} e The event object
30416         */
30417         "beforeclick":true,
30418         /**
30419         * @event checkchange
30420         * Fires when a node with a checkbox's checked property changes
30421         * @param {Node} this This node
30422         * @param {Boolean} checked
30423         */
30424         "checkchange":true,
30425         /**
30426         * @event click
30427         * Fires when a node is clicked
30428         * @param {Node} node The node
30429         * @param {Roo.EventObject} e The event object
30430         */
30431         "click":true,
30432         /**
30433         * @event dblclick
30434         * Fires when a node is double clicked
30435         * @param {Node} node The node
30436         * @param {Roo.EventObject} e The event object
30437         */
30438         "dblclick":true,
30439         /**
30440         * @event contextmenu
30441         * Fires when a node is right clicked
30442         * @param {Node} node The node
30443         * @param {Roo.EventObject} e The event object
30444         */
30445         "contextmenu":true,
30446         /**
30447         * @event beforechildrenrendered
30448         * Fires right before the child nodes for a node are rendered
30449         * @param {Node} node The node
30450         */
30451         "beforechildrenrendered":true,
30452        /**
30453              * @event startdrag
30454              * Fires when a node starts being dragged
30455              * @param {Roo.tree.TreePanel} this
30456              * @param {Roo.tree.TreeNode} node
30457              * @param {event} e The raw browser event
30458              */ 
30459             "startdrag" : true,
30460             /**
30461              * @event enddrag
30462              * Fires when a drag operation is complete
30463              * @param {Roo.tree.TreePanel} this
30464              * @param {Roo.tree.TreeNode} node
30465              * @param {event} e The raw browser event
30466              */
30467             "enddrag" : true,
30468             /**
30469              * @event dragdrop
30470              * Fires when a dragged node is dropped on a valid DD target
30471              * @param {Roo.tree.TreePanel} this
30472              * @param {Roo.tree.TreeNode} node
30473              * @param {DD} dd The dd it was dropped on
30474              * @param {event} e The raw browser event
30475              */
30476             "dragdrop" : true,
30477             /**
30478              * @event beforenodedrop
30479              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30480              * passed to handlers has the following properties:<br />
30481              * <ul style="padding:5px;padding-left:16px;">
30482              * <li>tree - The TreePanel</li>
30483              * <li>target - The node being targeted for the drop</li>
30484              * <li>data - The drag data from the drag source</li>
30485              * <li>point - The point of the drop - append, above or below</li>
30486              * <li>source - The drag source</li>
30487              * <li>rawEvent - Raw mouse event</li>
30488              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30489              * to be inserted by setting them on this object.</li>
30490              * <li>cancel - Set this to true to cancel the drop.</li>
30491              * </ul>
30492              * @param {Object} dropEvent
30493              */
30494             "beforenodedrop" : true,
30495             /**
30496              * @event nodedrop
30497              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30498              * passed to handlers has the following properties:<br />
30499              * <ul style="padding:5px;padding-left:16px;">
30500              * <li>tree - The TreePanel</li>
30501              * <li>target - The node being targeted for the drop</li>
30502              * <li>data - The drag data from the drag source</li>
30503              * <li>point - The point of the drop - append, above or below</li>
30504              * <li>source - The drag source</li>
30505              * <li>rawEvent - Raw mouse event</li>
30506              * <li>dropNode - Dropped node(s).</li>
30507              * </ul>
30508              * @param {Object} dropEvent
30509              */
30510             "nodedrop" : true,
30511              /**
30512              * @event nodedragover
30513              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30514              * passed to handlers has the following properties:<br />
30515              * <ul style="padding:5px;padding-left:16px;">
30516              * <li>tree - The TreePanel</li>
30517              * <li>target - The node being targeted for the drop</li>
30518              * <li>data - The drag data from the drag source</li>
30519              * <li>point - The point of the drop - append, above or below</li>
30520              * <li>source - The drag source</li>
30521              * <li>rawEvent - Raw mouse event</li>
30522              * <li>dropNode - Drop node(s) provided by the source.</li>
30523              * <li>cancel - Set this to true to signal drop not allowed.</li>
30524              * </ul>
30525              * @param {Object} dragOverEvent
30526              */
30527             "nodedragover" : true
30528         
30529    });
30530    if(this.singleExpand){
30531        this.on("beforeexpand", this.restrictExpand, this);
30532    }
30533 };
30534 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30535     rootVisible : true,
30536     animate: Roo.enableFx,
30537     lines : true,
30538     enableDD : false,
30539     hlDrop : Roo.enableFx,
30540   
30541     renderer: false,
30542     
30543     rendererTip: false,
30544     // private
30545     restrictExpand : function(node){
30546         var p = node.parentNode;
30547         if(p){
30548             if(p.expandedChild && p.expandedChild.parentNode == p){
30549                 p.expandedChild.collapse();
30550             }
30551             p.expandedChild = node;
30552         }
30553     },
30554
30555     // private override
30556     setRootNode : function(node){
30557         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30558         if(!this.rootVisible){
30559             node.ui = new Roo.tree.RootTreeNodeUI(node);
30560         }
30561         return node;
30562     },
30563
30564     /**
30565      * Returns the container element for this TreePanel
30566      */
30567     getEl : function(){
30568         return this.el;
30569     },
30570
30571     /**
30572      * Returns the default TreeLoader for this TreePanel
30573      */
30574     getLoader : function(){
30575         return this.loader;
30576     },
30577
30578     /**
30579      * Expand all nodes
30580      */
30581     expandAll : function(){
30582         this.root.expand(true);
30583     },
30584
30585     /**
30586      * Collapse all nodes
30587      */
30588     collapseAll : function(){
30589         this.root.collapse(true);
30590     },
30591
30592     /**
30593      * Returns the selection model used by this TreePanel
30594      */
30595     getSelectionModel : function(){
30596         if(!this.selModel){
30597             this.selModel = new Roo.tree.DefaultSelectionModel();
30598         }
30599         return this.selModel;
30600     },
30601
30602     /**
30603      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30604      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30605      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30606      * @return {Array}
30607      */
30608     getChecked : function(a, startNode){
30609         startNode = startNode || this.root;
30610         var r = [];
30611         var f = function(){
30612             if(this.attributes.checked){
30613                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30614             }
30615         }
30616         startNode.cascade(f);
30617         return r;
30618     },
30619
30620     /**
30621      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30622      * @param {String} path
30623      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30624      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30625      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30626      */
30627     expandPath : function(path, attr, callback){
30628         attr = attr || "id";
30629         var keys = path.split(this.pathSeparator);
30630         var curNode = this.root;
30631         if(curNode.attributes[attr] != keys[1]){ // invalid root
30632             if(callback){
30633                 callback(false, null);
30634             }
30635             return;
30636         }
30637         var index = 1;
30638         var f = function(){
30639             if(++index == keys.length){
30640                 if(callback){
30641                     callback(true, curNode);
30642                 }
30643                 return;
30644             }
30645             var c = curNode.findChild(attr, keys[index]);
30646             if(!c){
30647                 if(callback){
30648                     callback(false, curNode);
30649                 }
30650                 return;
30651             }
30652             curNode = c;
30653             c.expand(false, false, f);
30654         };
30655         curNode.expand(false, false, f);
30656     },
30657
30658     /**
30659      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30660      * @param {String} path
30661      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30662      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30663      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30664      */
30665     selectPath : function(path, attr, callback){
30666         attr = attr || "id";
30667         var keys = path.split(this.pathSeparator);
30668         var v = keys.pop();
30669         if(keys.length > 0){
30670             var f = function(success, node){
30671                 if(success && node){
30672                     var n = node.findChild(attr, v);
30673                     if(n){
30674                         n.select();
30675                         if(callback){
30676                             callback(true, n);
30677                         }
30678                     }else if(callback){
30679                         callback(false, n);
30680                     }
30681                 }else{
30682                     if(callback){
30683                         callback(false, n);
30684                     }
30685                 }
30686             };
30687             this.expandPath(keys.join(this.pathSeparator), attr, f);
30688         }else{
30689             this.root.select();
30690             if(callback){
30691                 callback(true, this.root);
30692             }
30693         }
30694     },
30695
30696     getTreeEl : function(){
30697         return this.el;
30698     },
30699
30700     /**
30701      * Trigger rendering of this TreePanel
30702      */
30703     render : function(){
30704         if (this.innerCt) {
30705             return this; // stop it rendering more than once!!
30706         }
30707         
30708         this.innerCt = this.el.createChild({tag:"ul",
30709                cls:"x-tree-root-ct " +
30710                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30711
30712         if(this.containerScroll){
30713             Roo.dd.ScrollManager.register(this.el);
30714         }
30715         if((this.enableDD || this.enableDrop) && !this.dropZone){
30716            /**
30717             * The dropZone used by this tree if drop is enabled
30718             * @type Roo.tree.TreeDropZone
30719             */
30720              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30721                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30722            });
30723         }
30724         if((this.enableDD || this.enableDrag) && !this.dragZone){
30725            /**
30726             * The dragZone used by this tree if drag is enabled
30727             * @type Roo.tree.TreeDragZone
30728             */
30729             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30730                ddGroup: this.ddGroup || "TreeDD",
30731                scroll: this.ddScroll
30732            });
30733         }
30734         this.getSelectionModel().init(this);
30735         if (!this.root) {
30736             console.log("ROOT not set in tree");
30737             return;
30738         }
30739         this.root.render();
30740         if(!this.rootVisible){
30741             this.root.renderChildren();
30742         }
30743         return this;
30744     }
30745 });/*
30746  * Based on:
30747  * Ext JS Library 1.1.1
30748  * Copyright(c) 2006-2007, Ext JS, LLC.
30749  *
30750  * Originally Released Under LGPL - original licence link has changed is not relivant.
30751  *
30752  * Fork - LGPL
30753  * <script type="text/javascript">
30754  */
30755  
30756
30757 /**
30758  * @class Roo.tree.DefaultSelectionModel
30759  * @extends Roo.util.Observable
30760  * The default single selection for a TreePanel.
30761  */
30762 Roo.tree.DefaultSelectionModel = function(){
30763    this.selNode = null;
30764    
30765    this.addEvents({
30766        /**
30767         * @event selectionchange
30768         * Fires when the selected node changes
30769         * @param {DefaultSelectionModel} this
30770         * @param {TreeNode} node the new selection
30771         */
30772        "selectionchange" : true,
30773
30774        /**
30775         * @event beforeselect
30776         * Fires before the selected node changes, return false to cancel the change
30777         * @param {DefaultSelectionModel} this
30778         * @param {TreeNode} node the new selection
30779         * @param {TreeNode} node the old selection
30780         */
30781        "beforeselect" : true
30782    });
30783 };
30784
30785 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30786     init : function(tree){
30787         this.tree = tree;
30788         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30789         tree.on("click", this.onNodeClick, this);
30790     },
30791     
30792     onNodeClick : function(node, e){
30793         if (e.ctrlKey && this.selNode == node)  {
30794             this.unselect(node);
30795             return;
30796         }
30797         this.select(node);
30798     },
30799     
30800     /**
30801      * Select a node.
30802      * @param {TreeNode} node The node to select
30803      * @return {TreeNode} The selected node
30804      */
30805     select : function(node){
30806         var last = this.selNode;
30807         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30808             if(last){
30809                 last.ui.onSelectedChange(false);
30810             }
30811             this.selNode = node;
30812             node.ui.onSelectedChange(true);
30813             this.fireEvent("selectionchange", this, node, last);
30814         }
30815         return node;
30816     },
30817     
30818     /**
30819      * Deselect a node.
30820      * @param {TreeNode} node The node to unselect
30821      */
30822     unselect : function(node){
30823         if(this.selNode == node){
30824             this.clearSelections();
30825         }    
30826     },
30827     
30828     /**
30829      * Clear all selections
30830      */
30831     clearSelections : function(){
30832         var n = this.selNode;
30833         if(n){
30834             n.ui.onSelectedChange(false);
30835             this.selNode = null;
30836             this.fireEvent("selectionchange", this, null);
30837         }
30838         return n;
30839     },
30840     
30841     /**
30842      * Get the selected node
30843      * @return {TreeNode} The selected node
30844      */
30845     getSelectedNode : function(){
30846         return this.selNode;    
30847     },
30848     
30849     /**
30850      * Returns true if the node is selected
30851      * @param {TreeNode} node The node to check
30852      * @return {Boolean}
30853      */
30854     isSelected : function(node){
30855         return this.selNode == node;  
30856     },
30857
30858     /**
30859      * Selects the node above the selected node in the tree, intelligently walking the nodes
30860      * @return TreeNode The new selection
30861      */
30862     selectPrevious : function(){
30863         var s = this.selNode || this.lastSelNode;
30864         if(!s){
30865             return null;
30866         }
30867         var ps = s.previousSibling;
30868         if(ps){
30869             if(!ps.isExpanded() || ps.childNodes.length < 1){
30870                 return this.select(ps);
30871             } else{
30872                 var lc = ps.lastChild;
30873                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30874                     lc = lc.lastChild;
30875                 }
30876                 return this.select(lc);
30877             }
30878         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30879             return this.select(s.parentNode);
30880         }
30881         return null;
30882     },
30883
30884     /**
30885      * Selects the node above the selected node in the tree, intelligently walking the nodes
30886      * @return TreeNode The new selection
30887      */
30888     selectNext : function(){
30889         var s = this.selNode || this.lastSelNode;
30890         if(!s){
30891             return null;
30892         }
30893         if(s.firstChild && s.isExpanded()){
30894              return this.select(s.firstChild);
30895          }else if(s.nextSibling){
30896              return this.select(s.nextSibling);
30897          }else if(s.parentNode){
30898             var newS = null;
30899             s.parentNode.bubble(function(){
30900                 if(this.nextSibling){
30901                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30902                     return false;
30903                 }
30904             });
30905             return newS;
30906          }
30907         return null;
30908     },
30909
30910     onKeyDown : function(e){
30911         var s = this.selNode || this.lastSelNode;
30912         // undesirable, but required
30913         var sm = this;
30914         if(!s){
30915             return;
30916         }
30917         var k = e.getKey();
30918         switch(k){
30919              case e.DOWN:
30920                  e.stopEvent();
30921                  this.selectNext();
30922              break;
30923              case e.UP:
30924                  e.stopEvent();
30925                  this.selectPrevious();
30926              break;
30927              case e.RIGHT:
30928                  e.preventDefault();
30929                  if(s.hasChildNodes()){
30930                      if(!s.isExpanded()){
30931                          s.expand();
30932                      }else if(s.firstChild){
30933                          this.select(s.firstChild, e);
30934                      }
30935                  }
30936              break;
30937              case e.LEFT:
30938                  e.preventDefault();
30939                  if(s.hasChildNodes() && s.isExpanded()){
30940                      s.collapse();
30941                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
30942                      this.select(s.parentNode, e);
30943                  }
30944              break;
30945         };
30946     }
30947 });
30948
30949 /**
30950  * @class Roo.tree.MultiSelectionModel
30951  * @extends Roo.util.Observable
30952  * Multi selection for a TreePanel.
30953  */
30954 Roo.tree.MultiSelectionModel = function(){
30955    this.selNodes = [];
30956    this.selMap = {};
30957    this.addEvents({
30958        /**
30959         * @event selectionchange
30960         * Fires when the selected nodes change
30961         * @param {MultiSelectionModel} this
30962         * @param {Array} nodes Array of the selected nodes
30963         */
30964        "selectionchange" : true
30965    });
30966 };
30967
30968 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
30969     init : function(tree){
30970         this.tree = tree;
30971         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30972         tree.on("click", this.onNodeClick, this);
30973     },
30974     
30975     onNodeClick : function(node, e){
30976         this.select(node, e, e.ctrlKey);
30977     },
30978     
30979     /**
30980      * Select a node.
30981      * @param {TreeNode} node The node to select
30982      * @param {EventObject} e (optional) An event associated with the selection
30983      * @param {Boolean} keepExisting True to retain existing selections
30984      * @return {TreeNode} The selected node
30985      */
30986     select : function(node, e, keepExisting){
30987         if(keepExisting !== true){
30988             this.clearSelections(true);
30989         }
30990         if(this.isSelected(node)){
30991             this.lastSelNode = node;
30992             return node;
30993         }
30994         this.selNodes.push(node);
30995         this.selMap[node.id] = node;
30996         this.lastSelNode = node;
30997         node.ui.onSelectedChange(true);
30998         this.fireEvent("selectionchange", this, this.selNodes);
30999         return node;
31000     },
31001     
31002     /**
31003      * Deselect a node.
31004      * @param {TreeNode} node The node to unselect
31005      */
31006     unselect : function(node){
31007         if(this.selMap[node.id]){
31008             node.ui.onSelectedChange(false);
31009             var sn = this.selNodes;
31010             var index = -1;
31011             if(sn.indexOf){
31012                 index = sn.indexOf(node);
31013             }else{
31014                 for(var i = 0, len = sn.length; i < len; i++){
31015                     if(sn[i] == node){
31016                         index = i;
31017                         break;
31018                     }
31019                 }
31020             }
31021             if(index != -1){
31022                 this.selNodes.splice(index, 1);
31023             }
31024             delete this.selMap[node.id];
31025             this.fireEvent("selectionchange", this, this.selNodes);
31026         }
31027     },
31028     
31029     /**
31030      * Clear all selections
31031      */
31032     clearSelections : function(suppressEvent){
31033         var sn = this.selNodes;
31034         if(sn.length > 0){
31035             for(var i = 0, len = sn.length; i < len; i++){
31036                 sn[i].ui.onSelectedChange(false);
31037             }
31038             this.selNodes = [];
31039             this.selMap = {};
31040             if(suppressEvent !== true){
31041                 this.fireEvent("selectionchange", this, this.selNodes);
31042             }
31043         }
31044     },
31045     
31046     /**
31047      * Returns true if the node is selected
31048      * @param {TreeNode} node The node to check
31049      * @return {Boolean}
31050      */
31051     isSelected : function(node){
31052         return this.selMap[node.id] ? true : false;  
31053     },
31054     
31055     /**
31056      * Returns an array of the selected nodes
31057      * @return {Array}
31058      */
31059     getSelectedNodes : function(){
31060         return this.selNodes;    
31061     },
31062
31063     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31064
31065     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31066
31067     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31068 });/*
31069  * Based on:
31070  * Ext JS Library 1.1.1
31071  * Copyright(c) 2006-2007, Ext JS, LLC.
31072  *
31073  * Originally Released Under LGPL - original licence link has changed is not relivant.
31074  *
31075  * Fork - LGPL
31076  * <script type="text/javascript">
31077  */
31078  
31079 /**
31080  * @class Roo.tree.TreeNode
31081  * @extends Roo.data.Node
31082  * @cfg {String} text The text for this node
31083  * @cfg {Boolean} expanded true to start the node expanded
31084  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31085  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31086  * @cfg {Boolean} disabled true to start the node disabled
31087  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31088  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31089  * @cfg {String} cls A css class to be added to the node
31090  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31091  * @cfg {String} href URL of the link used for the node (defaults to #)
31092  * @cfg {String} hrefTarget target frame for the link
31093  * @cfg {String} qtip An Ext QuickTip for the node
31094  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31095  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31096  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31097  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31098  * (defaults to undefined with no checkbox rendered)
31099  * @constructor
31100  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31101  */
31102 Roo.tree.TreeNode = function(attributes){
31103     attributes = attributes || {};
31104     if(typeof attributes == "string"){
31105         attributes = {text: attributes};
31106     }
31107     this.childrenRendered = false;
31108     this.rendered = false;
31109     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31110     this.expanded = attributes.expanded === true;
31111     this.isTarget = attributes.isTarget !== false;
31112     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31113     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31114
31115     /**
31116      * Read-only. The text for this node. To change it use setText().
31117      * @type String
31118      */
31119     this.text = attributes.text;
31120     /**
31121      * True if this node is disabled.
31122      * @type Boolean
31123      */
31124     this.disabled = attributes.disabled === true;
31125
31126     this.addEvents({
31127         /**
31128         * @event textchange
31129         * Fires when the text for this node is changed
31130         * @param {Node} this This node
31131         * @param {String} text The new text
31132         * @param {String} oldText The old text
31133         */
31134         "textchange" : true,
31135         /**
31136         * @event beforeexpand
31137         * Fires before this node is expanded, return false to cancel.
31138         * @param {Node} this This node
31139         * @param {Boolean} deep
31140         * @param {Boolean} anim
31141         */
31142         "beforeexpand" : true,
31143         /**
31144         * @event beforecollapse
31145         * Fires before this node is collapsed, return false to cancel.
31146         * @param {Node} this This node
31147         * @param {Boolean} deep
31148         * @param {Boolean} anim
31149         */
31150         "beforecollapse" : true,
31151         /**
31152         * @event expand
31153         * Fires when this node is expanded
31154         * @param {Node} this This node
31155         */
31156         "expand" : true,
31157         /**
31158         * @event disabledchange
31159         * Fires when the disabled status of this node changes
31160         * @param {Node} this This node
31161         * @param {Boolean} disabled
31162         */
31163         "disabledchange" : true,
31164         /**
31165         * @event collapse
31166         * Fires when this node is collapsed
31167         * @param {Node} this This node
31168         */
31169         "collapse" : true,
31170         /**
31171         * @event beforeclick
31172         * Fires before click processing. Return false to cancel the default action.
31173         * @param {Node} this This node
31174         * @param {Roo.EventObject} e The event object
31175         */
31176         "beforeclick":true,
31177         /**
31178         * @event checkchange
31179         * Fires when a node with a checkbox's checked property changes
31180         * @param {Node} this This node
31181         * @param {Boolean} checked
31182         */
31183         "checkchange":true,
31184         /**
31185         * @event click
31186         * Fires when this node is clicked
31187         * @param {Node} this This node
31188         * @param {Roo.EventObject} e The event object
31189         */
31190         "click":true,
31191         /**
31192         * @event dblclick
31193         * Fires when this node is double clicked
31194         * @param {Node} this This node
31195         * @param {Roo.EventObject} e The event object
31196         */
31197         "dblclick":true,
31198         /**
31199         * @event contextmenu
31200         * Fires when this node is right clicked
31201         * @param {Node} this This node
31202         * @param {Roo.EventObject} e The event object
31203         */
31204         "contextmenu":true,
31205         /**
31206         * @event beforechildrenrendered
31207         * Fires right before the child nodes for this node are rendered
31208         * @param {Node} this This node
31209         */
31210         "beforechildrenrendered":true
31211     });
31212
31213     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31214
31215     /**
31216      * Read-only. The UI for this node
31217      * @type TreeNodeUI
31218      */
31219     this.ui = new uiClass(this);
31220 };
31221 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31222     preventHScroll: true,
31223     /**
31224      * Returns true if this node is expanded
31225      * @return {Boolean}
31226      */
31227     isExpanded : function(){
31228         return this.expanded;
31229     },
31230
31231     /**
31232      * Returns the UI object for this node
31233      * @return {TreeNodeUI}
31234      */
31235     getUI : function(){
31236         return this.ui;
31237     },
31238
31239     // private override
31240     setFirstChild : function(node){
31241         var of = this.firstChild;
31242         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31243         if(this.childrenRendered && of && node != of){
31244             of.renderIndent(true, true);
31245         }
31246         if(this.rendered){
31247             this.renderIndent(true, true);
31248         }
31249     },
31250
31251     // private override
31252     setLastChild : function(node){
31253         var ol = this.lastChild;
31254         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31255         if(this.childrenRendered && ol && node != ol){
31256             ol.renderIndent(true, true);
31257         }
31258         if(this.rendered){
31259             this.renderIndent(true, true);
31260         }
31261     },
31262
31263     // these methods are overridden to provide lazy rendering support
31264     // private override
31265     appendChild : function(){
31266         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31267         if(node && this.childrenRendered){
31268             node.render();
31269         }
31270         this.ui.updateExpandIcon();
31271         return node;
31272     },
31273
31274     // private override
31275     removeChild : function(node){
31276         this.ownerTree.getSelectionModel().unselect(node);
31277         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31278         // if it's been rendered remove dom node
31279         if(this.childrenRendered){
31280             node.ui.remove();
31281         }
31282         if(this.childNodes.length < 1){
31283             this.collapse(false, false);
31284         }else{
31285             this.ui.updateExpandIcon();
31286         }
31287         if(!this.firstChild) {
31288             this.childrenRendered = false;
31289         }
31290         return node;
31291     },
31292
31293     // private override
31294     insertBefore : function(node, refNode){
31295         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31296         if(newNode && refNode && this.childrenRendered){
31297             node.render();
31298         }
31299         this.ui.updateExpandIcon();
31300         return newNode;
31301     },
31302
31303     /**
31304      * Sets the text for this node
31305      * @param {String} text
31306      */
31307     setText : function(text){
31308         var oldText = this.text;
31309         this.text = text;
31310         this.attributes.text = text;
31311         if(this.rendered){ // event without subscribing
31312             this.ui.onTextChange(this, text, oldText);
31313         }
31314         this.fireEvent("textchange", this, text, oldText);
31315     },
31316
31317     /**
31318      * Triggers selection of this node
31319      */
31320     select : function(){
31321         this.getOwnerTree().getSelectionModel().select(this);
31322     },
31323
31324     /**
31325      * Triggers deselection of this node
31326      */
31327     unselect : function(){
31328         this.getOwnerTree().getSelectionModel().unselect(this);
31329     },
31330
31331     /**
31332      * Returns true if this node is selected
31333      * @return {Boolean}
31334      */
31335     isSelected : function(){
31336         return this.getOwnerTree().getSelectionModel().isSelected(this);
31337     },
31338
31339     /**
31340      * Expand this node.
31341      * @param {Boolean} deep (optional) True to expand all children as well
31342      * @param {Boolean} anim (optional) false to cancel the default animation
31343      * @param {Function} callback (optional) A callback to be called when
31344      * expanding this node completes (does not wait for deep expand to complete).
31345      * Called with 1 parameter, this node.
31346      */
31347     expand : function(deep, anim, callback){
31348         if(!this.expanded){
31349             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31350                 return;
31351             }
31352             if(!this.childrenRendered){
31353                 this.renderChildren();
31354             }
31355             this.expanded = true;
31356             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31357                 this.ui.animExpand(function(){
31358                     this.fireEvent("expand", this);
31359                     if(typeof callback == "function"){
31360                         callback(this);
31361                     }
31362                     if(deep === true){
31363                         this.expandChildNodes(true);
31364                     }
31365                 }.createDelegate(this));
31366                 return;
31367             }else{
31368                 this.ui.expand();
31369                 this.fireEvent("expand", this);
31370                 if(typeof callback == "function"){
31371                     callback(this);
31372                 }
31373             }
31374         }else{
31375            if(typeof callback == "function"){
31376                callback(this);
31377            }
31378         }
31379         if(deep === true){
31380             this.expandChildNodes(true);
31381         }
31382     },
31383
31384     isHiddenRoot : function(){
31385         return this.isRoot && !this.getOwnerTree().rootVisible;
31386     },
31387
31388     /**
31389      * Collapse this node.
31390      * @param {Boolean} deep (optional) True to collapse all children as well
31391      * @param {Boolean} anim (optional) false to cancel the default animation
31392      */
31393     collapse : function(deep, anim){
31394         if(this.expanded && !this.isHiddenRoot()){
31395             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31396                 return;
31397             }
31398             this.expanded = false;
31399             if((this.getOwnerTree().animate && anim !== false) || anim){
31400                 this.ui.animCollapse(function(){
31401                     this.fireEvent("collapse", this);
31402                     if(deep === true){
31403                         this.collapseChildNodes(true);
31404                     }
31405                 }.createDelegate(this));
31406                 return;
31407             }else{
31408                 this.ui.collapse();
31409                 this.fireEvent("collapse", this);
31410             }
31411         }
31412         if(deep === true){
31413             var cs = this.childNodes;
31414             for(var i = 0, len = cs.length; i < len; i++) {
31415                 cs[i].collapse(true, false);
31416             }
31417         }
31418     },
31419
31420     // private
31421     delayedExpand : function(delay){
31422         if(!this.expandProcId){
31423             this.expandProcId = this.expand.defer(delay, this);
31424         }
31425     },
31426
31427     // private
31428     cancelExpand : function(){
31429         if(this.expandProcId){
31430             clearTimeout(this.expandProcId);
31431         }
31432         this.expandProcId = false;
31433     },
31434
31435     /**
31436      * Toggles expanded/collapsed state of the node
31437      */
31438     toggle : function(){
31439         if(this.expanded){
31440             this.collapse();
31441         }else{
31442             this.expand();
31443         }
31444     },
31445
31446     /**
31447      * Ensures all parent nodes are expanded
31448      */
31449     ensureVisible : function(callback){
31450         var tree = this.getOwnerTree();
31451         tree.expandPath(this.parentNode.getPath(), false, function(){
31452             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31453             Roo.callback(callback);
31454         }.createDelegate(this));
31455     },
31456
31457     /**
31458      * Expand all child nodes
31459      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31460      */
31461     expandChildNodes : function(deep){
31462         var cs = this.childNodes;
31463         for(var i = 0, len = cs.length; i < len; i++) {
31464                 cs[i].expand(deep);
31465         }
31466     },
31467
31468     /**
31469      * Collapse all child nodes
31470      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31471      */
31472     collapseChildNodes : function(deep){
31473         var cs = this.childNodes;
31474         for(var i = 0, len = cs.length; i < len; i++) {
31475                 cs[i].collapse(deep);
31476         }
31477     },
31478
31479     /**
31480      * Disables this node
31481      */
31482     disable : function(){
31483         this.disabled = true;
31484         this.unselect();
31485         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31486             this.ui.onDisableChange(this, true);
31487         }
31488         this.fireEvent("disabledchange", this, true);
31489     },
31490
31491     /**
31492      * Enables this node
31493      */
31494     enable : function(){
31495         this.disabled = false;
31496         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31497             this.ui.onDisableChange(this, false);
31498         }
31499         this.fireEvent("disabledchange", this, false);
31500     },
31501
31502     // private
31503     renderChildren : function(suppressEvent){
31504         if(suppressEvent !== false){
31505             this.fireEvent("beforechildrenrendered", this);
31506         }
31507         var cs = this.childNodes;
31508         for(var i = 0, len = cs.length; i < len; i++){
31509             cs[i].render(true);
31510         }
31511         this.childrenRendered = true;
31512     },
31513
31514     // private
31515     sort : function(fn, scope){
31516         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31517         if(this.childrenRendered){
31518             var cs = this.childNodes;
31519             for(var i = 0, len = cs.length; i < len; i++){
31520                 cs[i].render(true);
31521             }
31522         }
31523     },
31524
31525     // private
31526     render : function(bulkRender){
31527         this.ui.render(bulkRender);
31528         if(!this.rendered){
31529             this.rendered = true;
31530             if(this.expanded){
31531                 this.expanded = false;
31532                 this.expand(false, false);
31533             }
31534         }
31535     },
31536
31537     // private
31538     renderIndent : function(deep, refresh){
31539         if(refresh){
31540             this.ui.childIndent = null;
31541         }
31542         this.ui.renderIndent();
31543         if(deep === true && this.childrenRendered){
31544             var cs = this.childNodes;
31545             for(var i = 0, len = cs.length; i < len; i++){
31546                 cs[i].renderIndent(true, refresh);
31547             }
31548         }
31549     }
31550 });/*
31551  * Based on:
31552  * Ext JS Library 1.1.1
31553  * Copyright(c) 2006-2007, Ext JS, LLC.
31554  *
31555  * Originally Released Under LGPL - original licence link has changed is not relivant.
31556  *
31557  * Fork - LGPL
31558  * <script type="text/javascript">
31559  */
31560  
31561 /**
31562  * @class Roo.tree.AsyncTreeNode
31563  * @extends Roo.tree.TreeNode
31564  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31565  * @constructor
31566  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31567  */
31568  Roo.tree.AsyncTreeNode = function(config){
31569     this.loaded = false;
31570     this.loading = false;
31571     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31572     /**
31573     * @event beforeload
31574     * Fires before this node is loaded, return false to cancel
31575     * @param {Node} this This node
31576     */
31577     this.addEvents({'beforeload':true, 'load': true});
31578     /**
31579     * @event load
31580     * Fires when this node is loaded
31581     * @param {Node} this This node
31582     */
31583     /**
31584      * The loader used by this node (defaults to using the tree's defined loader)
31585      * @type TreeLoader
31586      * @property loader
31587      */
31588 };
31589 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31590     expand : function(deep, anim, callback){
31591         if(this.loading){ // if an async load is already running, waiting til it's done
31592             var timer;
31593             var f = function(){
31594                 if(!this.loading){ // done loading
31595                     clearInterval(timer);
31596                     this.expand(deep, anim, callback);
31597                 }
31598             }.createDelegate(this);
31599             timer = setInterval(f, 200);
31600             return;
31601         }
31602         if(!this.loaded){
31603             if(this.fireEvent("beforeload", this) === false){
31604                 return;
31605             }
31606             this.loading = true;
31607             this.ui.beforeLoad(this);
31608             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31609             if(loader){
31610                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31611                 return;
31612             }
31613         }
31614         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31615     },
31616     
31617     /**
31618      * Returns true if this node is currently loading
31619      * @return {Boolean}
31620      */
31621     isLoading : function(){
31622         return this.loading;  
31623     },
31624     
31625     loadComplete : function(deep, anim, callback){
31626         this.loading = false;
31627         this.loaded = true;
31628         this.ui.afterLoad(this);
31629         this.fireEvent("load", this);
31630         this.expand(deep, anim, callback);
31631     },
31632     
31633     /**
31634      * Returns true if this node has been loaded
31635      * @return {Boolean}
31636      */
31637     isLoaded : function(){
31638         return this.loaded;
31639     },
31640     
31641     hasChildNodes : function(){
31642         if(!this.isLeaf() && !this.loaded){
31643             return true;
31644         }else{
31645             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31646         }
31647     },
31648
31649     /**
31650      * Trigger a reload for this node
31651      * @param {Function} callback
31652      */
31653     reload : function(callback){
31654         this.collapse(false, false);
31655         while(this.firstChild){
31656             this.removeChild(this.firstChild);
31657         }
31658         this.childrenRendered = false;
31659         this.loaded = false;
31660         if(this.isHiddenRoot()){
31661             this.expanded = false;
31662         }
31663         this.expand(false, false, callback);
31664     }
31665 });/*
31666  * Based on:
31667  * Ext JS Library 1.1.1
31668  * Copyright(c) 2006-2007, Ext JS, LLC.
31669  *
31670  * Originally Released Under LGPL - original licence link has changed is not relivant.
31671  *
31672  * Fork - LGPL
31673  * <script type="text/javascript">
31674  */
31675  
31676 /**
31677  * @class Roo.tree.TreeNodeUI
31678  * @constructor
31679  * @param {Object} node The node to render
31680  * The TreeNode UI implementation is separate from the
31681  * tree implementation. Unless you are customizing the tree UI,
31682  * you should never have to use this directly.
31683  */
31684 Roo.tree.TreeNodeUI = function(node){
31685     this.node = node;
31686     this.rendered = false;
31687     this.animating = false;
31688     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31689 };
31690
31691 Roo.tree.TreeNodeUI.prototype = {
31692     removeChild : function(node){
31693         if(this.rendered){
31694             this.ctNode.removeChild(node.ui.getEl());
31695         }
31696     },
31697
31698     beforeLoad : function(){
31699          this.addClass("x-tree-node-loading");
31700     },
31701
31702     afterLoad : function(){
31703          this.removeClass("x-tree-node-loading");
31704     },
31705
31706     onTextChange : function(node, text, oldText){
31707         if(this.rendered){
31708             this.textNode.innerHTML = text;
31709         }
31710     },
31711
31712     onDisableChange : function(node, state){
31713         this.disabled = state;
31714         if(state){
31715             this.addClass("x-tree-node-disabled");
31716         }else{
31717             this.removeClass("x-tree-node-disabled");
31718         }
31719     },
31720
31721     onSelectedChange : function(state){
31722         if(state){
31723             this.focus();
31724             this.addClass("x-tree-selected");
31725         }else{
31726             //this.blur();
31727             this.removeClass("x-tree-selected");
31728         }
31729     },
31730
31731     onMove : function(tree, node, oldParent, newParent, index, refNode){
31732         this.childIndent = null;
31733         if(this.rendered){
31734             var targetNode = newParent.ui.getContainer();
31735             if(!targetNode){//target not rendered
31736                 this.holder = document.createElement("div");
31737                 this.holder.appendChild(this.wrap);
31738                 return;
31739             }
31740             var insertBefore = refNode ? refNode.ui.getEl() : null;
31741             if(insertBefore){
31742                 targetNode.insertBefore(this.wrap, insertBefore);
31743             }else{
31744                 targetNode.appendChild(this.wrap);
31745             }
31746             this.node.renderIndent(true);
31747         }
31748     },
31749
31750     addClass : function(cls){
31751         if(this.elNode){
31752             Roo.fly(this.elNode).addClass(cls);
31753         }
31754     },
31755
31756     removeClass : function(cls){
31757         if(this.elNode){
31758             Roo.fly(this.elNode).removeClass(cls);
31759         }
31760     },
31761
31762     remove : function(){
31763         if(this.rendered){
31764             this.holder = document.createElement("div");
31765             this.holder.appendChild(this.wrap);
31766         }
31767     },
31768
31769     fireEvent : function(){
31770         return this.node.fireEvent.apply(this.node, arguments);
31771     },
31772
31773     initEvents : function(){
31774         this.node.on("move", this.onMove, this);
31775         var E = Roo.EventManager;
31776         var a = this.anchor;
31777
31778         var el = Roo.fly(a, '_treeui');
31779
31780         if(Roo.isOpera){ // opera render bug ignores the CSS
31781             el.setStyle("text-decoration", "none");
31782         }
31783
31784         el.on("click", this.onClick, this);
31785         el.on("dblclick", this.onDblClick, this);
31786
31787         if(this.checkbox){
31788             Roo.EventManager.on(this.checkbox,
31789                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31790         }
31791
31792         el.on("contextmenu", this.onContextMenu, this);
31793
31794         var icon = Roo.fly(this.iconNode);
31795         icon.on("click", this.onClick, this);
31796         icon.on("dblclick", this.onDblClick, this);
31797         icon.on("contextmenu", this.onContextMenu, this);
31798         E.on(this.ecNode, "click", this.ecClick, this, true);
31799
31800         if(this.node.disabled){
31801             this.addClass("x-tree-node-disabled");
31802         }
31803         if(this.node.hidden){
31804             this.addClass("x-tree-node-disabled");
31805         }
31806         var ot = this.node.getOwnerTree();
31807         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31808         if(dd && (!this.node.isRoot || ot.rootVisible)){
31809             Roo.dd.Registry.register(this.elNode, {
31810                 node: this.node,
31811                 handles: this.getDDHandles(),
31812                 isHandle: false
31813             });
31814         }
31815     },
31816
31817     getDDHandles : function(){
31818         return [this.iconNode, this.textNode];
31819     },
31820
31821     hide : function(){
31822         if(this.rendered){
31823             this.wrap.style.display = "none";
31824         }
31825     },
31826
31827     show : function(){
31828         if(this.rendered){
31829             this.wrap.style.display = "";
31830         }
31831     },
31832
31833     onContextMenu : function(e){
31834         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31835             e.preventDefault();
31836             this.focus();
31837             this.fireEvent("contextmenu", this.node, e);
31838         }
31839     },
31840
31841     onClick : function(e){
31842         if(this.dropping){
31843             e.stopEvent();
31844             return;
31845         }
31846         if(this.fireEvent("beforeclick", this.node, e) !== false){
31847             if(!this.disabled && this.node.attributes.href){
31848                 this.fireEvent("click", this.node, e);
31849                 return;
31850             }
31851             e.preventDefault();
31852             if(this.disabled){
31853                 return;
31854             }
31855
31856             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31857                 this.node.toggle();
31858             }
31859
31860             this.fireEvent("click", this.node, e);
31861         }else{
31862             e.stopEvent();
31863         }
31864     },
31865
31866     onDblClick : function(e){
31867         e.preventDefault();
31868         if(this.disabled){
31869             return;
31870         }
31871         if(this.checkbox){
31872             this.toggleCheck();
31873         }
31874         if(!this.animating && this.node.hasChildNodes()){
31875             this.node.toggle();
31876         }
31877         this.fireEvent("dblclick", this.node, e);
31878     },
31879
31880     onCheckChange : function(){
31881         var checked = this.checkbox.checked;
31882         this.node.attributes.checked = checked;
31883         this.fireEvent('checkchange', this.node, checked);
31884     },
31885
31886     ecClick : function(e){
31887         if(!this.animating && this.node.hasChildNodes()){
31888             this.node.toggle();
31889         }
31890     },
31891
31892     startDrop : function(){
31893         this.dropping = true;
31894     },
31895
31896     // delayed drop so the click event doesn't get fired on a drop
31897     endDrop : function(){
31898        setTimeout(function(){
31899            this.dropping = false;
31900        }.createDelegate(this), 50);
31901     },
31902
31903     expand : function(){
31904         this.updateExpandIcon();
31905         this.ctNode.style.display = "";
31906     },
31907
31908     focus : function(){
31909         if(!this.node.preventHScroll){
31910             try{this.anchor.focus();
31911             }catch(e){}
31912         }else if(!Roo.isIE){
31913             try{
31914                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31915                 var l = noscroll.scrollLeft;
31916                 this.anchor.focus();
31917                 noscroll.scrollLeft = l;
31918             }catch(e){}
31919         }
31920     },
31921
31922     toggleCheck : function(value){
31923         var cb = this.checkbox;
31924         if(cb){
31925             cb.checked = (value === undefined ? !cb.checked : value);
31926         }
31927     },
31928
31929     blur : function(){
31930         try{
31931             this.anchor.blur();
31932         }catch(e){}
31933     },
31934
31935     animExpand : function(callback){
31936         var ct = Roo.get(this.ctNode);
31937         ct.stopFx();
31938         if(!this.node.hasChildNodes()){
31939             this.updateExpandIcon();
31940             this.ctNode.style.display = "";
31941             Roo.callback(callback);
31942             return;
31943         }
31944         this.animating = true;
31945         this.updateExpandIcon();
31946
31947         ct.slideIn('t', {
31948            callback : function(){
31949                this.animating = false;
31950                Roo.callback(callback);
31951             },
31952             scope: this,
31953             duration: this.node.ownerTree.duration || .25
31954         });
31955     },
31956
31957     highlight : function(){
31958         var tree = this.node.getOwnerTree();
31959         Roo.fly(this.wrap).highlight(
31960             tree.hlColor || "C3DAF9",
31961             {endColor: tree.hlBaseColor}
31962         );
31963     },
31964
31965     collapse : function(){
31966         this.updateExpandIcon();
31967         this.ctNode.style.display = "none";
31968     },
31969
31970     animCollapse : function(callback){
31971         var ct = Roo.get(this.ctNode);
31972         ct.enableDisplayMode('block');
31973         ct.stopFx();
31974
31975         this.animating = true;
31976         this.updateExpandIcon();
31977
31978         ct.slideOut('t', {
31979             callback : function(){
31980                this.animating = false;
31981                Roo.callback(callback);
31982             },
31983             scope: this,
31984             duration: this.node.ownerTree.duration || .25
31985         });
31986     },
31987
31988     getContainer : function(){
31989         return this.ctNode;
31990     },
31991
31992     getEl : function(){
31993         return this.wrap;
31994     },
31995
31996     appendDDGhost : function(ghostNode){
31997         ghostNode.appendChild(this.elNode.cloneNode(true));
31998     },
31999
32000     getDDRepairXY : function(){
32001         return Roo.lib.Dom.getXY(this.iconNode);
32002     },
32003
32004     onRender : function(){
32005         this.render();
32006     },
32007
32008     render : function(bulkRender){
32009         var n = this.node, a = n.attributes;
32010         var targetNode = n.parentNode ?
32011               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32012
32013         if(!this.rendered){
32014             this.rendered = true;
32015
32016             this.renderElements(n, a, targetNode, bulkRender);
32017
32018             if(a.qtip){
32019                if(this.textNode.setAttributeNS){
32020                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32021                    if(a.qtipTitle){
32022                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32023                    }
32024                }else{
32025                    this.textNode.setAttribute("ext:qtip", a.qtip);
32026                    if(a.qtipTitle){
32027                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32028                    }
32029                }
32030             }else if(a.qtipCfg){
32031                 a.qtipCfg.target = Roo.id(this.textNode);
32032                 Roo.QuickTips.register(a.qtipCfg);
32033             }
32034             this.initEvents();
32035             if(!this.node.expanded){
32036                 this.updateExpandIcon();
32037             }
32038         }else{
32039             if(bulkRender === true) {
32040                 targetNode.appendChild(this.wrap);
32041             }
32042         }
32043     },
32044
32045     renderElements : function(n, a, targetNode, bulkRender){
32046         // add some indent caching, this helps performance when rendering a large tree
32047         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32048         var t = n.getOwnerTree();
32049         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32050         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32051         var cb = typeof a.checked == 'boolean';
32052         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32053         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32054             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32055             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32056             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32057             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32058             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32059              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32060                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32061             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32062             "</li>"];
32063
32064         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32065             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32066                                 n.nextSibling.ui.getEl(), buf.join(""));
32067         }else{
32068             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32069         }
32070
32071         this.elNode = this.wrap.childNodes[0];
32072         this.ctNode = this.wrap.childNodes[1];
32073         var cs = this.elNode.childNodes;
32074         this.indentNode = cs[0];
32075         this.ecNode = cs[1];
32076         this.iconNode = cs[2];
32077         var index = 3;
32078         if(cb){
32079             this.checkbox = cs[3];
32080             index++;
32081         }
32082         this.anchor = cs[index];
32083         this.textNode = cs[index].firstChild;
32084     },
32085
32086     getAnchor : function(){
32087         return this.anchor;
32088     },
32089
32090     getTextEl : function(){
32091         return this.textNode;
32092     },
32093
32094     getIconEl : function(){
32095         return this.iconNode;
32096     },
32097
32098     isChecked : function(){
32099         return this.checkbox ? this.checkbox.checked : false;
32100     },
32101
32102     updateExpandIcon : function(){
32103         if(this.rendered){
32104             var n = this.node, c1, c2;
32105             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32106             var hasChild = n.hasChildNodes();
32107             if(hasChild){
32108                 if(n.expanded){
32109                     cls += "-minus";
32110                     c1 = "x-tree-node-collapsed";
32111                     c2 = "x-tree-node-expanded";
32112                 }else{
32113                     cls += "-plus";
32114                     c1 = "x-tree-node-expanded";
32115                     c2 = "x-tree-node-collapsed";
32116                 }
32117                 if(this.wasLeaf){
32118                     this.removeClass("x-tree-node-leaf");
32119                     this.wasLeaf = false;
32120                 }
32121                 if(this.c1 != c1 || this.c2 != c2){
32122                     Roo.fly(this.elNode).replaceClass(c1, c2);
32123                     this.c1 = c1; this.c2 = c2;
32124                 }
32125             }else{
32126                 if(!this.wasLeaf){
32127                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32128                     delete this.c1;
32129                     delete this.c2;
32130                     this.wasLeaf = true;
32131                 }
32132             }
32133             var ecc = "x-tree-ec-icon "+cls;
32134             if(this.ecc != ecc){
32135                 this.ecNode.className = ecc;
32136                 this.ecc = ecc;
32137             }
32138         }
32139     },
32140
32141     getChildIndent : function(){
32142         if(!this.childIndent){
32143             var buf = [];
32144             var p = this.node;
32145             while(p){
32146                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32147                     if(!p.isLast()) {
32148                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32149                     } else {
32150                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32151                     }
32152                 }
32153                 p = p.parentNode;
32154             }
32155             this.childIndent = buf.join("");
32156         }
32157         return this.childIndent;
32158     },
32159
32160     renderIndent : function(){
32161         if(this.rendered){
32162             var indent = "";
32163             var p = this.node.parentNode;
32164             if(p){
32165                 indent = p.ui.getChildIndent();
32166             }
32167             if(this.indentMarkup != indent){ // don't rerender if not required
32168                 this.indentNode.innerHTML = indent;
32169                 this.indentMarkup = indent;
32170             }
32171             this.updateExpandIcon();
32172         }
32173     }
32174 };
32175
32176 Roo.tree.RootTreeNodeUI = function(){
32177     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32178 };
32179 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32180     render : function(){
32181         if(!this.rendered){
32182             var targetNode = this.node.ownerTree.innerCt.dom;
32183             this.node.expanded = true;
32184             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32185             this.wrap = this.ctNode = targetNode.firstChild;
32186         }
32187     },
32188     collapse : function(){
32189     },
32190     expand : function(){
32191     }
32192 });/*
32193  * Based on:
32194  * Ext JS Library 1.1.1
32195  * Copyright(c) 2006-2007, Ext JS, LLC.
32196  *
32197  * Originally Released Under LGPL - original licence link has changed is not relivant.
32198  *
32199  * Fork - LGPL
32200  * <script type="text/javascript">
32201  */
32202 /**
32203  * @class Roo.tree.TreeLoader
32204  * @extends Roo.util.Observable
32205  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32206  * nodes from a specified URL. The response must be a javascript Array definition
32207  * who's elements are node definition objects. eg:
32208  * <pre><code>
32209    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32210     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32211 </code></pre>
32212  * <br><br>
32213  * A server request is sent, and child nodes are loaded only when a node is expanded.
32214  * The loading node's id is passed to the server under the parameter name "node" to
32215  * enable the server to produce the correct child nodes.
32216  * <br><br>
32217  * To pass extra parameters, an event handler may be attached to the "beforeload"
32218  * event, and the parameters specified in the TreeLoader's baseParams property:
32219  * <pre><code>
32220     myTreeLoader.on("beforeload", function(treeLoader, node) {
32221         this.baseParams.category = node.attributes.category;
32222     }, this);
32223 </code></pre><
32224  * This would pass an HTTP parameter called "category" to the server containing
32225  * the value of the Node's "category" attribute.
32226  * @constructor
32227  * Creates a new Treeloader.
32228  * @param {Object} config A config object containing config properties.
32229  */
32230 Roo.tree.TreeLoader = function(config){
32231     this.baseParams = {};
32232     this.requestMethod = "POST";
32233     Roo.apply(this, config);
32234
32235     this.addEvents({
32236         /**
32237          * @event beforeload
32238          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32239          * @param {Object} This TreeLoader object.
32240          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32241          * @param {Object} callback The callback function specified in the {@link #load} call.
32242          */
32243         "beforeload" : true,
32244         /**
32245          * @event load
32246          * Fires when the node has been successfuly loaded.
32247          * @param {Object} This TreeLoader object.
32248          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32249          * @param {Object} response The response object containing the data from the server.
32250          */
32251         "load" : true,
32252         /**
32253          * @event loadexception
32254          * Fires if the network request failed.
32255          * @param {Object} This TreeLoader object.
32256          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32257          * @param {Object} response The response object containing the data from the server.
32258          */
32259         "loadexception" : true,
32260           /**
32261          * @event create
32262          * Fires before a node is created, enabling you to return custom Node types 
32263          * @param {Object} This TreeLoader object.
32264          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32265          */
32266         "create" : true
32267     });
32268
32269     Roo.tree.TreeLoader.superclass.constructor.call(this);
32270 };
32271
32272 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32273     /**
32274     * @cfg {String} dataUrl The URL from which to request a Json string which
32275     * specifies an array of node definition object representing the child nodes
32276     * to be loaded.
32277     */
32278     /**
32279     * @cfg {Object} baseParams (optional) An object containing properties which
32280     * specify HTTP parameters to be passed to each request for child nodes.
32281     */
32282     /**
32283     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32284     * created by this loader. If the attributes sent by the server have an attribute in this object,
32285     * they take priority.
32286     */
32287     /**
32288     * @cfg {Object} uiProviders (optional) An object containing properties which
32289     * 
32290     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32291     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32292     * <i>uiProvider</i> attribute of a returned child node is a string rather
32293     * than a reference to a TreeNodeUI implementation, this that string value
32294     * is used as a property name in the uiProviders object. You can define the provider named
32295     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32296     */
32297     uiProviders : {},
32298
32299     /**
32300     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32301     * child nodes before loading.
32302     */
32303     clearOnLoad : true,
32304
32305     /**
32306     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32307     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32308     * Grid query { data : [ .....] }
32309     */
32310     
32311     root : false,
32312      /**
32313     * @cfg {String} queryParam (optional) 
32314     * Name of the query as it will be passed on the querystring (defaults to 'node')
32315     * eg. the request will be ?node=[id]
32316     */
32317     
32318     
32319     queryParam: false,
32320     
32321     /**
32322      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32323      * This is called automatically when a node is expanded, but may be used to reload
32324      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32325      * @param {Roo.tree.TreeNode} node
32326      * @param {Function} callback
32327      */
32328     load : function(node, callback){
32329         if(this.clearOnLoad){
32330             while(node.firstChild){
32331                 node.removeChild(node.firstChild);
32332             }
32333         }
32334         if(node.attributes.children){ // preloaded json children
32335             var cs = node.attributes.children;
32336             for(var i = 0, len = cs.length; i < len; i++){
32337                 node.appendChild(this.createNode(cs[i]));
32338             }
32339             if(typeof callback == "function"){
32340                 callback();
32341             }
32342         }else if(this.dataUrl){
32343             this.requestData(node, callback);
32344         }
32345     },
32346
32347     getParams: function(node){
32348         var buf = [], bp = this.baseParams;
32349         for(var key in bp){
32350             if(typeof bp[key] != "function"){
32351                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32352             }
32353         }
32354         var n = this.queryParam === false ? 'node' : this.queryParam;
32355         buf.push(n + "=", encodeURIComponent(node.id));
32356         return buf.join("");
32357     },
32358
32359     requestData : function(node, callback){
32360         if(this.fireEvent("beforeload", this, node, callback) !== false){
32361             this.transId = Roo.Ajax.request({
32362                 method:this.requestMethod,
32363                 url: this.dataUrl||this.url,
32364                 success: this.handleResponse,
32365                 failure: this.handleFailure,
32366                 scope: this,
32367                 argument: {callback: callback, node: node},
32368                 params: this.getParams(node)
32369             });
32370         }else{
32371             // if the load is cancelled, make sure we notify
32372             // the node that we are done
32373             if(typeof callback == "function"){
32374                 callback();
32375             }
32376         }
32377     },
32378
32379     isLoading : function(){
32380         return this.transId ? true : false;
32381     },
32382
32383     abort : function(){
32384         if(this.isLoading()){
32385             Roo.Ajax.abort(this.transId);
32386         }
32387     },
32388
32389     /**
32390     * Override this function for custom TreeNode node implementation
32391     */
32392     createNode : function(attr){
32393         // apply baseAttrs, nice idea Corey!
32394         if(this.baseAttrs){
32395             Roo.applyIf(attr, this.baseAttrs);
32396         }
32397         if(this.applyLoader !== false){
32398             attr.loader = this;
32399         }
32400         // uiProvider = depreciated..
32401         
32402         if(typeof(attr.uiProvider) == 'string'){
32403            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32404                 /**  eval:var:attr */ eval(attr.uiProvider);
32405         }
32406         if(typeof(this.uiProviders['default']) != 'undefined') {
32407             attr.uiProvider = this.uiProviders['default'];
32408         }
32409         
32410         this.fireEvent('create', this, attr);
32411         
32412         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32413         return(attr.leaf ?
32414                         new Roo.tree.TreeNode(attr) :
32415                         new Roo.tree.AsyncTreeNode(attr));
32416     },
32417
32418     processResponse : function(response, node, callback){
32419         var json = response.responseText;
32420         try {
32421             
32422             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32423             if (this.root !== false) {
32424                 o = o[this.root];
32425             }
32426             
32427             for(var i = 0, len = o.length; i < len; i++){
32428                 var n = this.createNode(o[i]);
32429                 if(n){
32430                     node.appendChild(n);
32431                 }
32432             }
32433             if(typeof callback == "function"){
32434                 callback(this, node);
32435             }
32436         }catch(e){
32437             this.handleFailure(response);
32438         }
32439     },
32440
32441     handleResponse : function(response){
32442         this.transId = false;
32443         var a = response.argument;
32444         this.processResponse(response, a.node, a.callback);
32445         this.fireEvent("load", this, a.node, response);
32446     },
32447
32448     handleFailure : function(response){
32449         this.transId = false;
32450         var a = response.argument;
32451         this.fireEvent("loadexception", this, a.node, response);
32452         if(typeof a.callback == "function"){
32453             a.callback(this, a.node);
32454         }
32455     }
32456 });/*
32457  * Based on:
32458  * Ext JS Library 1.1.1
32459  * Copyright(c) 2006-2007, Ext JS, LLC.
32460  *
32461  * Originally Released Under LGPL - original licence link has changed is not relivant.
32462  *
32463  * Fork - LGPL
32464  * <script type="text/javascript">
32465  */
32466
32467 /**
32468 * @class Roo.tree.TreeFilter
32469 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32470 * @param {TreePanel} tree
32471 * @param {Object} config (optional)
32472  */
32473 Roo.tree.TreeFilter = function(tree, config){
32474     this.tree = tree;
32475     this.filtered = {};
32476     Roo.apply(this, config);
32477 };
32478
32479 Roo.tree.TreeFilter.prototype = {
32480     clearBlank:false,
32481     reverse:false,
32482     autoClear:false,
32483     remove:false,
32484
32485      /**
32486      * Filter the data by a specific attribute.
32487      * @param {String/RegExp} value Either string that the attribute value
32488      * should start with or a RegExp to test against the attribute
32489      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32490      * @param {TreeNode} startNode (optional) The node to start the filter at.
32491      */
32492     filter : function(value, attr, startNode){
32493         attr = attr || "text";
32494         var f;
32495         if(typeof value == "string"){
32496             var vlen = value.length;
32497             // auto clear empty filter
32498             if(vlen == 0 && this.clearBlank){
32499                 this.clear();
32500                 return;
32501             }
32502             value = value.toLowerCase();
32503             f = function(n){
32504                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32505             };
32506         }else if(value.exec){ // regex?
32507             f = function(n){
32508                 return value.test(n.attributes[attr]);
32509             };
32510         }else{
32511             throw 'Illegal filter type, must be string or regex';
32512         }
32513         this.filterBy(f, null, startNode);
32514         },
32515
32516     /**
32517      * Filter by a function. The passed function will be called with each
32518      * node in the tree (or from the startNode). If the function returns true, the node is kept
32519      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32520      * @param {Function} fn The filter function
32521      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32522      */
32523     filterBy : function(fn, scope, startNode){
32524         startNode = startNode || this.tree.root;
32525         if(this.autoClear){
32526             this.clear();
32527         }
32528         var af = this.filtered, rv = this.reverse;
32529         var f = function(n){
32530             if(n == startNode){
32531                 return true;
32532             }
32533             if(af[n.id]){
32534                 return false;
32535             }
32536             var m = fn.call(scope || n, n);
32537             if(!m || rv){
32538                 af[n.id] = n;
32539                 n.ui.hide();
32540                 return false;
32541             }
32542             return true;
32543         };
32544         startNode.cascade(f);
32545         if(this.remove){
32546            for(var id in af){
32547                if(typeof id != "function"){
32548                    var n = af[id];
32549                    if(n && n.parentNode){
32550                        n.parentNode.removeChild(n);
32551                    }
32552                }
32553            }
32554         }
32555     },
32556
32557     /**
32558      * Clears the current filter. Note: with the "remove" option
32559      * set a filter cannot be cleared.
32560      */
32561     clear : function(){
32562         var t = this.tree;
32563         var af = this.filtered;
32564         for(var id in af){
32565             if(typeof id != "function"){
32566                 var n = af[id];
32567                 if(n){
32568                     n.ui.show();
32569                 }
32570             }
32571         }
32572         this.filtered = {};
32573     }
32574 };
32575 /*
32576  * Based on:
32577  * Ext JS Library 1.1.1
32578  * Copyright(c) 2006-2007, Ext JS, LLC.
32579  *
32580  * Originally Released Under LGPL - original licence link has changed is not relivant.
32581  *
32582  * Fork - LGPL
32583  * <script type="text/javascript">
32584  */
32585  
32586
32587 /**
32588  * @class Roo.tree.TreeSorter
32589  * Provides sorting of nodes in a TreePanel
32590  * 
32591  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32592  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32593  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32594  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32595  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32596  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32597  * @constructor
32598  * @param {TreePanel} tree
32599  * @param {Object} config
32600  */
32601 Roo.tree.TreeSorter = function(tree, config){
32602     Roo.apply(this, config);
32603     tree.on("beforechildrenrendered", this.doSort, this);
32604     tree.on("append", this.updateSort, this);
32605     tree.on("insert", this.updateSort, this);
32606     
32607     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32608     var p = this.property || "text";
32609     var sortType = this.sortType;
32610     var fs = this.folderSort;
32611     var cs = this.caseSensitive === true;
32612     var leafAttr = this.leafAttr || 'leaf';
32613
32614     this.sortFn = function(n1, n2){
32615         if(fs){
32616             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32617                 return 1;
32618             }
32619             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32620                 return -1;
32621             }
32622         }
32623         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32624         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32625         if(v1 < v2){
32626                         return dsc ? +1 : -1;
32627                 }else if(v1 > v2){
32628                         return dsc ? -1 : +1;
32629         }else{
32630                 return 0;
32631         }
32632     };
32633 };
32634
32635 Roo.tree.TreeSorter.prototype = {
32636     doSort : function(node){
32637         node.sort(this.sortFn);
32638     },
32639     
32640     compareNodes : function(n1, n2){
32641         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32642     },
32643     
32644     updateSort : function(tree, node){
32645         if(node.childrenRendered){
32646             this.doSort.defer(1, this, [node]);
32647         }
32648     }
32649 };/*
32650  * Based on:
32651  * Ext JS Library 1.1.1
32652  * Copyright(c) 2006-2007, Ext JS, LLC.
32653  *
32654  * Originally Released Under LGPL - original licence link has changed is not relivant.
32655  *
32656  * Fork - LGPL
32657  * <script type="text/javascript">
32658  */
32659
32660 if(Roo.dd.DropZone){
32661     
32662 Roo.tree.TreeDropZone = function(tree, config){
32663     this.allowParentInsert = false;
32664     this.allowContainerDrop = false;
32665     this.appendOnly = false;
32666     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32667     this.tree = tree;
32668     this.lastInsertClass = "x-tree-no-status";
32669     this.dragOverData = {};
32670 };
32671
32672 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32673     ddGroup : "TreeDD",
32674     
32675     expandDelay : 1000,
32676     
32677     expandNode : function(node){
32678         if(node.hasChildNodes() && !node.isExpanded()){
32679             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32680         }
32681     },
32682     
32683     queueExpand : function(node){
32684         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32685     },
32686     
32687     cancelExpand : function(){
32688         if(this.expandProcId){
32689             clearTimeout(this.expandProcId);
32690             this.expandProcId = false;
32691         }
32692     },
32693     
32694     isValidDropPoint : function(n, pt, dd, e, data){
32695         if(!n || !data){ return false; }
32696         var targetNode = n.node;
32697         var dropNode = data.node;
32698         // default drop rules
32699         if(!(targetNode && targetNode.isTarget && pt)){
32700             return false;
32701         }
32702         if(pt == "append" && targetNode.allowChildren === false){
32703             return false;
32704         }
32705         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32706             return false;
32707         }
32708         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32709             return false;
32710         }
32711         // reuse the object
32712         var overEvent = this.dragOverData;
32713         overEvent.tree = this.tree;
32714         overEvent.target = targetNode;
32715         overEvent.data = data;
32716         overEvent.point = pt;
32717         overEvent.source = dd;
32718         overEvent.rawEvent = e;
32719         overEvent.dropNode = dropNode;
32720         overEvent.cancel = false;  
32721         var result = this.tree.fireEvent("nodedragover", overEvent);
32722         return overEvent.cancel === false && result !== false;
32723     },
32724     
32725     getDropPoint : function(e, n, dd){
32726         var tn = n.node;
32727         if(tn.isRoot){
32728             return tn.allowChildren !== false ? "append" : false; // always append for root
32729         }
32730         var dragEl = n.ddel;
32731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32732         var y = Roo.lib.Event.getPageY(e);
32733         var noAppend = tn.allowChildren === false || tn.isLeaf();
32734         if(this.appendOnly || tn.parentNode.allowChildren === false){
32735             return noAppend ? false : "append";
32736         }
32737         var noBelow = false;
32738         if(!this.allowParentInsert){
32739             noBelow = tn.hasChildNodes() && tn.isExpanded();
32740         }
32741         var q = (b - t) / (noAppend ? 2 : 3);
32742         if(y >= t && y < (t + q)){
32743             return "above";
32744         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32745             return "below";
32746         }else{
32747             return "append";
32748         }
32749     },
32750     
32751     onNodeEnter : function(n, dd, e, data){
32752         this.cancelExpand();
32753     },
32754     
32755     onNodeOver : function(n, dd, e, data){
32756         var pt = this.getDropPoint(e, n, dd);
32757         var node = n.node;
32758         
32759         // auto node expand check
32760         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32761             this.queueExpand(node);
32762         }else if(pt != "append"){
32763             this.cancelExpand();
32764         }
32765         
32766         // set the insert point style on the target node
32767         var returnCls = this.dropNotAllowed;
32768         if(this.isValidDropPoint(n, pt, dd, e, data)){
32769            if(pt){
32770                var el = n.ddel;
32771                var cls;
32772                if(pt == "above"){
32773                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32774                    cls = "x-tree-drag-insert-above";
32775                }else if(pt == "below"){
32776                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32777                    cls = "x-tree-drag-insert-below";
32778                }else{
32779                    returnCls = "x-tree-drop-ok-append";
32780                    cls = "x-tree-drag-append";
32781                }
32782                if(this.lastInsertClass != cls){
32783                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32784                    this.lastInsertClass = cls;
32785                }
32786            }
32787        }
32788        return returnCls;
32789     },
32790     
32791     onNodeOut : function(n, dd, e, data){
32792         this.cancelExpand();
32793         this.removeDropIndicators(n);
32794     },
32795     
32796     onNodeDrop : function(n, dd, e, data){
32797         var point = this.getDropPoint(e, n, dd);
32798         var targetNode = n.node;
32799         targetNode.ui.startDrop();
32800         if(!this.isValidDropPoint(n, point, dd, e, data)){
32801             targetNode.ui.endDrop();
32802             return false;
32803         }
32804         // first try to find the drop node
32805         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32806         var dropEvent = {
32807             tree : this.tree,
32808             target: targetNode,
32809             data: data,
32810             point: point,
32811             source: dd,
32812             rawEvent: e,
32813             dropNode: dropNode,
32814             cancel: !dropNode   
32815         };
32816         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32817         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32818             targetNode.ui.endDrop();
32819             return false;
32820         }
32821         // allow target changing
32822         targetNode = dropEvent.target;
32823         if(point == "append" && !targetNode.isExpanded()){
32824             targetNode.expand(false, null, function(){
32825                 this.completeDrop(dropEvent);
32826             }.createDelegate(this));
32827         }else{
32828             this.completeDrop(dropEvent);
32829         }
32830         return true;
32831     },
32832     
32833     completeDrop : function(de){
32834         var ns = de.dropNode, p = de.point, t = de.target;
32835         if(!(ns instanceof Array)){
32836             ns = [ns];
32837         }
32838         var n;
32839         for(var i = 0, len = ns.length; i < len; i++){
32840             n = ns[i];
32841             if(p == "above"){
32842                 t.parentNode.insertBefore(n, t);
32843             }else if(p == "below"){
32844                 t.parentNode.insertBefore(n, t.nextSibling);
32845             }else{
32846                 t.appendChild(n);
32847             }
32848         }
32849         n.ui.focus();
32850         if(this.tree.hlDrop){
32851             n.ui.highlight();
32852         }
32853         t.ui.endDrop();
32854         this.tree.fireEvent("nodedrop", de);
32855     },
32856     
32857     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32858         if(this.tree.hlDrop){
32859             dropNode.ui.focus();
32860             dropNode.ui.highlight();
32861         }
32862         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32863     },
32864     
32865     getTree : function(){
32866         return this.tree;
32867     },
32868     
32869     removeDropIndicators : function(n){
32870         if(n && n.ddel){
32871             var el = n.ddel;
32872             Roo.fly(el).removeClass([
32873                     "x-tree-drag-insert-above",
32874                     "x-tree-drag-insert-below",
32875                     "x-tree-drag-append"]);
32876             this.lastInsertClass = "_noclass";
32877         }
32878     },
32879     
32880     beforeDragDrop : function(target, e, id){
32881         this.cancelExpand();
32882         return true;
32883     },
32884     
32885     afterRepair : function(data){
32886         if(data && Roo.enableFx){
32887             data.node.ui.highlight();
32888         }
32889         this.hideProxy();
32890     }    
32891 });
32892
32893 }/*
32894  * Based on:
32895  * Ext JS Library 1.1.1
32896  * Copyright(c) 2006-2007, Ext JS, LLC.
32897  *
32898  * Originally Released Under LGPL - original licence link has changed is not relivant.
32899  *
32900  * Fork - LGPL
32901  * <script type="text/javascript">
32902  */
32903  
32904
32905 if(Roo.dd.DragZone){
32906 Roo.tree.TreeDragZone = function(tree, config){
32907     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32908     this.tree = tree;
32909 };
32910
32911 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32912     ddGroup : "TreeDD",
32913     
32914     onBeforeDrag : function(data, e){
32915         var n = data.node;
32916         return n && n.draggable && !n.disabled;
32917     },
32918     
32919     onInitDrag : function(e){
32920         var data = this.dragData;
32921         this.tree.getSelectionModel().select(data.node);
32922         this.proxy.update("");
32923         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
32924         this.tree.fireEvent("startdrag", this.tree, data.node, e);
32925     },
32926     
32927     getRepairXY : function(e, data){
32928         return data.node.ui.getDDRepairXY();
32929     },
32930     
32931     onEndDrag : function(data, e){
32932         this.tree.fireEvent("enddrag", this.tree, data.node, e);
32933     },
32934     
32935     onValidDrop : function(dd, e, id){
32936         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
32937         this.hideProxy();
32938     },
32939     
32940     beforeInvalidDrop : function(e, id){
32941         // this scrolls the original position back into view
32942         var sm = this.tree.getSelectionModel();
32943         sm.clearSelections();
32944         sm.select(this.dragData.node);
32945     }
32946 });
32947 }/*
32948  * Based on:
32949  * Ext JS Library 1.1.1
32950  * Copyright(c) 2006-2007, Ext JS, LLC.
32951  *
32952  * Originally Released Under LGPL - original licence link has changed is not relivant.
32953  *
32954  * Fork - LGPL
32955  * <script type="text/javascript">
32956  */
32957 /**
32958  * @class Roo.tree.TreeEditor
32959  * @extends Roo.Editor
32960  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
32961  * as the editor field.
32962  * @constructor
32963  * @param {TreePanel} tree
32964  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
32965  */
32966 Roo.tree.TreeEditor = function(tree, config){
32967     config = config || {};
32968     var field = config.events ? config : new Roo.form.TextField(config);
32969     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
32970
32971     this.tree = tree;
32972
32973     tree.on('beforeclick', this.beforeNodeClick, this);
32974     tree.getTreeEl().on('mousedown', this.hide, this);
32975     this.on('complete', this.updateNode, this);
32976     this.on('beforestartedit', this.fitToTree, this);
32977     this.on('startedit', this.bindScroll, this, {delay:10});
32978     this.on('specialkey', this.onSpecialKey, this);
32979 };
32980
32981 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
32982     /**
32983      * @cfg {String} alignment
32984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
32985      */
32986     alignment: "l-l",
32987     // inherit
32988     autoSize: false,
32989     /**
32990      * @cfg {Boolean} hideEl
32991      * True to hide the bound element while the editor is displayed (defaults to false)
32992      */
32993     hideEl : false,
32994     /**
32995      * @cfg {String} cls
32996      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
32997      */
32998     cls: "x-small-editor x-tree-editor",
32999     /**
33000      * @cfg {Boolean} shim
33001      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33002      */
33003     shim:false,
33004     // inherit
33005     shadow:"frame",
33006     /**
33007      * @cfg {Number} maxWidth
33008      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33009      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33010      * scroll and client offsets into account prior to each edit.
33011      */
33012     maxWidth: 250,
33013
33014     editDelay : 350,
33015
33016     // private
33017     fitToTree : function(ed, el){
33018         var td = this.tree.getTreeEl().dom, nd = el.dom;
33019         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33020             td.scrollLeft = nd.offsetLeft;
33021         }
33022         var w = Math.min(
33023                 this.maxWidth,
33024                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33025         this.setSize(w, '');
33026     },
33027
33028     // private
33029     triggerEdit : function(node){
33030         this.completeEdit();
33031         this.editNode = node;
33032         this.startEdit(node.ui.textNode, node.text);
33033     },
33034
33035     // private
33036     bindScroll : function(){
33037         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33038     },
33039
33040     // private
33041     beforeNodeClick : function(node, e){
33042         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33043         this.lastClick = new Date();
33044         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33045             e.stopEvent();
33046             this.triggerEdit(node);
33047             return false;
33048         }
33049     },
33050
33051     // private
33052     updateNode : function(ed, value){
33053         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33054         this.editNode.setText(value);
33055     },
33056
33057     // private
33058     onHide : function(){
33059         Roo.tree.TreeEditor.superclass.onHide.call(this);
33060         if(this.editNode){
33061             this.editNode.ui.focus();
33062         }
33063     },
33064
33065     // private
33066     onSpecialKey : function(field, e){
33067         var k = e.getKey();
33068         if(k == e.ESC){
33069             e.stopEvent();
33070             this.cancelEdit();
33071         }else if(k == e.ENTER && !e.hasModifier()){
33072             e.stopEvent();
33073             this.completeEdit();
33074         }
33075     }
33076 });//<Script type="text/javascript">
33077 /*
33078  * Based on:
33079  * Ext JS Library 1.1.1
33080  * Copyright(c) 2006-2007, Ext JS, LLC.
33081  *
33082  * Originally Released Under LGPL - original licence link has changed is not relivant.
33083  *
33084  * Fork - LGPL
33085  * <script type="text/javascript">
33086  */
33087  
33088 /**
33089  * Not documented??? - probably should be...
33090  */
33091
33092 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33093     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33094     
33095     renderElements : function(n, a, targetNode, bulkRender){
33096         //consel.log("renderElements?");
33097         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33098
33099         var t = n.getOwnerTree();
33100         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33101         
33102         var cols = t.columns;
33103         var bw = t.borderWidth;
33104         var c = cols[0];
33105         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33106          var cb = typeof a.checked == "boolean";
33107         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33108         var colcls = 'x-t-' + tid + '-c0';
33109         var buf = [
33110             '<li class="x-tree-node">',
33111             
33112                 
33113                 '<div class="x-tree-node-el ', a.cls,'">',
33114                     // extran...
33115                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33116                 
33117                 
33118                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33119                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33120                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33121                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33122                            (a.iconCls ? ' '+a.iconCls : ''),
33123                            '" unselectable="on" />',
33124                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33125                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33126                              
33127                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33128                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33129                             '<span unselectable="on" qtip="' + tx + '">',
33130                              tx,
33131                              '</span></a>' ,
33132                     '</div>',
33133                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33134                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33135                  ];
33136         
33137         for(var i = 1, len = cols.length; i < len; i++){
33138             c = cols[i];
33139             colcls = 'x-t-' + tid + '-c' +i;
33140             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33141             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33142                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33143                       "</div>");
33144          }
33145          
33146          buf.push(
33147             '</a>',
33148             '<div class="x-clear"></div></div>',
33149             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33150             "</li>");
33151         
33152         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33153             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33154                                 n.nextSibling.ui.getEl(), buf.join(""));
33155         }else{
33156             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33157         }
33158         var el = this.wrap.firstChild;
33159         this.elRow = el;
33160         this.elNode = el.firstChild;
33161         this.ranchor = el.childNodes[1];
33162         this.ctNode = this.wrap.childNodes[1];
33163         var cs = el.firstChild.childNodes;
33164         this.indentNode = cs[0];
33165         this.ecNode = cs[1];
33166         this.iconNode = cs[2];
33167         var index = 3;
33168         if(cb){
33169             this.checkbox = cs[3];
33170             index++;
33171         }
33172         this.anchor = cs[index];
33173         
33174         this.textNode = cs[index].firstChild;
33175         
33176         //el.on("click", this.onClick, this);
33177         //el.on("dblclick", this.onDblClick, this);
33178         
33179         
33180        // console.log(this);
33181     },
33182     initEvents : function(){
33183         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33184         
33185             
33186         var a = this.ranchor;
33187
33188         var el = Roo.get(a);
33189
33190         if(Roo.isOpera){ // opera render bug ignores the CSS
33191             el.setStyle("text-decoration", "none");
33192         }
33193
33194         el.on("click", this.onClick, this);
33195         el.on("dblclick", this.onDblClick, this);
33196         el.on("contextmenu", this.onContextMenu, this);
33197         
33198     },
33199     
33200     /*onSelectedChange : function(state){
33201         if(state){
33202             this.focus();
33203             this.addClass("x-tree-selected");
33204         }else{
33205             //this.blur();
33206             this.removeClass("x-tree-selected");
33207         }
33208     },*/
33209     addClass : function(cls){
33210         if(this.elRow){
33211             Roo.fly(this.elRow).addClass(cls);
33212         }
33213         
33214     },
33215     
33216     
33217     removeClass : function(cls){
33218         if(this.elRow){
33219             Roo.fly(this.elRow).removeClass(cls);
33220         }
33221     }
33222
33223     
33224     
33225 });//<Script type="text/javascript">
33226
33227 /*
33228  * Based on:
33229  * Ext JS Library 1.1.1
33230  * Copyright(c) 2006-2007, Ext JS, LLC.
33231  *
33232  * Originally Released Under LGPL - original licence link has changed is not relivant.
33233  *
33234  * Fork - LGPL
33235  * <script type="text/javascript">
33236  */
33237  
33238
33239 /**
33240  * @class Roo.tree.ColumnTree
33241  * @extends Roo.data.TreePanel
33242  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33243  * @cfg {int} borderWidth  compined right/left border allowance
33244  * @constructor
33245  * @param {String/HTMLElement/Element} el The container element
33246  * @param {Object} config
33247  */
33248 Roo.tree.ColumnTree =  function(el, config)
33249 {
33250    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33251    this.addEvents({
33252         /**
33253         * @event resize
33254         * Fire this event on a container when it resizes
33255         * @param {int} w Width
33256         * @param {int} h Height
33257         */
33258        "resize" : true
33259     });
33260     this.on('resize', this.onResize, this);
33261 };
33262
33263 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33264     //lines:false,
33265     
33266     
33267     borderWidth: Roo.isBorderBox ? 0 : 2, 
33268     headEls : false,
33269     
33270     render : function(){
33271         // add the header.....
33272        
33273         Roo.tree.ColumnTree.superclass.render.apply(this);
33274         
33275         this.el.addClass('x-column-tree');
33276         
33277         this.headers = this.el.createChild(
33278             {cls:'x-tree-headers'},this.innerCt.dom);
33279    
33280         var cols = this.columns, c;
33281         var totalWidth = 0;
33282         this.headEls = [];
33283         var  len = cols.length;
33284         for(var i = 0; i < len; i++){
33285              c = cols[i];
33286              totalWidth += c.width;
33287             this.headEls.push(this.headers.createChild({
33288                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33289                  cn: {
33290                      cls:'x-tree-hd-text',
33291                      html: c.header
33292                  },
33293                  style:'width:'+(c.width-this.borderWidth)+'px;'
33294              }));
33295         }
33296         this.headers.createChild({cls:'x-clear'});
33297         // prevent floats from wrapping when clipped
33298         this.headers.setWidth(totalWidth);
33299         //this.innerCt.setWidth(totalWidth);
33300         this.innerCt.setStyle({ overflow: 'auto' });
33301         this.onResize(this.width, this.height);
33302              
33303         
33304     },
33305     onResize : function(w,h)
33306     {
33307         this.height = h;
33308         this.width = w;
33309         // resize cols..
33310         this.innerCt.setWidth(this.width);
33311         this.innerCt.setHeight(this.height-20);
33312         
33313         // headers...
33314         var cols = this.columns, c;
33315         var totalWidth = 0;
33316         var expEl = false;
33317         var len = cols.length;
33318         for(var i = 0; i < len; i++){
33319             c = cols[i];
33320             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33321                 // it's the expander..
33322                 expEl  = this.headEls[i];
33323                 continue;
33324             }
33325             totalWidth += c.width;
33326             
33327         }
33328         if (expEl) {
33329             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33330         }
33331         this.headers.setWidth(w-20);
33332
33333         
33334         
33335         
33336     }
33337 });
33338 /*
33339  * Based on:
33340  * Ext JS Library 1.1.1
33341  * Copyright(c) 2006-2007, Ext JS, LLC.
33342  *
33343  * Originally Released Under LGPL - original licence link has changed is not relivant.
33344  *
33345  * Fork - LGPL
33346  * <script type="text/javascript">
33347  */
33348  
33349 /**
33350  * @class Roo.menu.Menu
33351  * @extends Roo.util.Observable
33352  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33353  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33354  * @constructor
33355  * Creates a new Menu
33356  * @param {Object} config Configuration options
33357  */
33358 Roo.menu.Menu = function(config){
33359     Roo.apply(this, config);
33360     this.id = this.id || Roo.id();
33361     this.addEvents({
33362         /**
33363          * @event beforeshow
33364          * Fires before this menu is displayed
33365          * @param {Roo.menu.Menu} this
33366          */
33367         beforeshow : true,
33368         /**
33369          * @event beforehide
33370          * Fires before this menu is hidden
33371          * @param {Roo.menu.Menu} this
33372          */
33373         beforehide : true,
33374         /**
33375          * @event show
33376          * Fires after this menu is displayed
33377          * @param {Roo.menu.Menu} this
33378          */
33379         show : true,
33380         /**
33381          * @event hide
33382          * Fires after this menu is hidden
33383          * @param {Roo.menu.Menu} this
33384          */
33385         hide : true,
33386         /**
33387          * @event click
33388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33389          * @param {Roo.menu.Menu} this
33390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33391          * @param {Roo.EventObject} e
33392          */
33393         click : true,
33394         /**
33395          * @event mouseover
33396          * Fires when the mouse is hovering over this menu
33397          * @param {Roo.menu.Menu} this
33398          * @param {Roo.EventObject} e
33399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33400          */
33401         mouseover : true,
33402         /**
33403          * @event mouseout
33404          * Fires when the mouse exits this menu
33405          * @param {Roo.menu.Menu} this
33406          * @param {Roo.EventObject} e
33407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33408          */
33409         mouseout : true,
33410         /**
33411          * @event itemclick
33412          * Fires when a menu item contained in this menu is clicked
33413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33414          * @param {Roo.EventObject} e
33415          */
33416         itemclick: true
33417     });
33418     if (this.registerMenu) {
33419         Roo.menu.MenuMgr.register(this);
33420     }
33421     
33422     var mis = this.items;
33423     this.items = new Roo.util.MixedCollection();
33424     if(mis){
33425         this.add.apply(this, mis);
33426     }
33427 };
33428
33429 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33430     /**
33431      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33432      */
33433     minWidth : 120,
33434     /**
33435      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33436      * for bottom-right shadow (defaults to "sides")
33437      */
33438     shadow : "sides",
33439     /**
33440      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33441      * this menu (defaults to "tl-tr?")
33442      */
33443     subMenuAlign : "tl-tr?",
33444     /**
33445      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33446      * relative to its element of origin (defaults to "tl-bl?")
33447      */
33448     defaultAlign : "tl-bl?",
33449     /**
33450      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33451      */
33452     allowOtherMenus : false,
33453     /**
33454      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33455      */
33456     registerMenu : true,
33457
33458     hidden:true,
33459
33460     // private
33461     render : function(){
33462         if(this.el){
33463             return;
33464         }
33465         var el = this.el = new Roo.Layer({
33466             cls: "x-menu",
33467             shadow:this.shadow,
33468             constrain: false,
33469             parentEl: this.parentEl || document.body,
33470             zindex:15000
33471         });
33472
33473         this.keyNav = new Roo.menu.MenuNav(this);
33474
33475         if(this.plain){
33476             el.addClass("x-menu-plain");
33477         }
33478         if(this.cls){
33479             el.addClass(this.cls);
33480         }
33481         // generic focus element
33482         this.focusEl = el.createChild({
33483             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33484         });
33485         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33486         ul.on("click", this.onClick, this);
33487         ul.on("mouseover", this.onMouseOver, this);
33488         ul.on("mouseout", this.onMouseOut, this);
33489         this.items.each(function(item){
33490             var li = document.createElement("li");
33491             li.className = "x-menu-list-item";
33492             ul.dom.appendChild(li);
33493             item.render(li, this);
33494         }, this);
33495         this.ul = ul;
33496         this.autoWidth();
33497     },
33498
33499     // private
33500     autoWidth : function(){
33501         var el = this.el, ul = this.ul;
33502         if(!el){
33503             return;
33504         }
33505         var w = this.width;
33506         if(w){
33507             el.setWidth(w);
33508         }else if(Roo.isIE){
33509             el.setWidth(this.minWidth);
33510             var t = el.dom.offsetWidth; // force recalc
33511             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33512         }
33513     },
33514
33515     // private
33516     delayAutoWidth : function(){
33517         if(this.rendered){
33518             if(!this.awTask){
33519                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33520             }
33521             this.awTask.delay(20);
33522         }
33523     },
33524
33525     // private
33526     findTargetItem : function(e){
33527         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33528         if(t && t.menuItemId){
33529             return this.items.get(t.menuItemId);
33530         }
33531     },
33532
33533     // private
33534     onClick : function(e){
33535         var t;
33536         if(t = this.findTargetItem(e)){
33537             t.onClick(e);
33538             this.fireEvent("click", this, t, e);
33539         }
33540     },
33541
33542     // private
33543     setActiveItem : function(item, autoExpand){
33544         if(item != this.activeItem){
33545             if(this.activeItem){
33546                 this.activeItem.deactivate();
33547             }
33548             this.activeItem = item;
33549             item.activate(autoExpand);
33550         }else if(autoExpand){
33551             item.expandMenu();
33552         }
33553     },
33554
33555     // private
33556     tryActivate : function(start, step){
33557         var items = this.items;
33558         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33559             var item = items.get(i);
33560             if(!item.disabled && item.canActivate){
33561                 this.setActiveItem(item, false);
33562                 return item;
33563             }
33564         }
33565         return false;
33566     },
33567
33568     // private
33569     onMouseOver : function(e){
33570         var t;
33571         if(t = this.findTargetItem(e)){
33572             if(t.canActivate && !t.disabled){
33573                 this.setActiveItem(t, true);
33574             }
33575         }
33576         this.fireEvent("mouseover", this, e, t);
33577     },
33578
33579     // private
33580     onMouseOut : function(e){
33581         var t;
33582         if(t = this.findTargetItem(e)){
33583             if(t == this.activeItem && t.shouldDeactivate(e)){
33584                 this.activeItem.deactivate();
33585                 delete this.activeItem;
33586             }
33587         }
33588         this.fireEvent("mouseout", this, e, t);
33589     },
33590
33591     /**
33592      * Read-only.  Returns true if the menu is currently displayed, else false.
33593      * @type Boolean
33594      */
33595     isVisible : function(){
33596         return this.el && !this.hidden;
33597     },
33598
33599     /**
33600      * Displays this menu relative to another element
33601      * @param {String/HTMLElement/Roo.Element} element The element to align to
33602      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33603      * the element (defaults to this.defaultAlign)
33604      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33605      */
33606     show : function(el, pos, parentMenu){
33607         this.parentMenu = parentMenu;
33608         if(!this.el){
33609             this.render();
33610         }
33611         this.fireEvent("beforeshow", this);
33612         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33613     },
33614
33615     /**
33616      * Displays this menu at a specific xy position
33617      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33618      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33619      */
33620     showAt : function(xy, parentMenu, /* private: */_e){
33621         this.parentMenu = parentMenu;
33622         if(!this.el){
33623             this.render();
33624         }
33625         if(_e !== false){
33626             this.fireEvent("beforeshow", this);
33627             xy = this.el.adjustForConstraints(xy);
33628         }
33629         this.el.setXY(xy);
33630         this.el.show();
33631         this.hidden = false;
33632         this.focus();
33633         this.fireEvent("show", this);
33634     },
33635
33636     focus : function(){
33637         if(!this.hidden){
33638             this.doFocus.defer(50, this);
33639         }
33640     },
33641
33642     doFocus : function(){
33643         if(!this.hidden){
33644             this.focusEl.focus();
33645         }
33646     },
33647
33648     /**
33649      * Hides this menu and optionally all parent menus
33650      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33651      */
33652     hide : function(deep){
33653         if(this.el && this.isVisible()){
33654             this.fireEvent("beforehide", this);
33655             if(this.activeItem){
33656                 this.activeItem.deactivate();
33657                 this.activeItem = null;
33658             }
33659             this.el.hide();
33660             this.hidden = true;
33661             this.fireEvent("hide", this);
33662         }
33663         if(deep === true && this.parentMenu){
33664             this.parentMenu.hide(true);
33665         }
33666     },
33667
33668     /**
33669      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33670      * Any of the following are valid:
33671      * <ul>
33672      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33673      * <li>An HTMLElement object which will be converted to a menu item</li>
33674      * <li>A menu item config object that will be created as a new menu item</li>
33675      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33676      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33677      * </ul>
33678      * Usage:
33679      * <pre><code>
33680 // Create the menu
33681 var menu = new Roo.menu.Menu();
33682
33683 // Create a menu item to add by reference
33684 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33685
33686 // Add a bunch of items at once using different methods.
33687 // Only the last item added will be returned.
33688 var item = menu.add(
33689     menuItem,                // add existing item by ref
33690     'Dynamic Item',          // new TextItem
33691     '-',                     // new separator
33692     { text: 'Config Item' }  // new item by config
33693 );
33694 </code></pre>
33695      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33696      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33697      */
33698     add : function(){
33699         var a = arguments, l = a.length, item;
33700         for(var i = 0; i < l; i++){
33701             var el = a[i];
33702             if(el.render){ // some kind of Item
33703                 item = this.addItem(el);
33704             }else if(typeof el == "string"){ // string
33705                 if(el == "separator" || el == "-"){
33706                     item = this.addSeparator();
33707                 }else{
33708                     item = this.addText(el);
33709                 }
33710             }else if(el.tagName || el.el){ // element
33711                 item = this.addElement(el);
33712             }else if(typeof el == "object"){ // must be menu item config?
33713                 item = this.addMenuItem(el);
33714             }
33715         }
33716         return item;
33717     },
33718
33719     /**
33720      * Returns this menu's underlying {@link Roo.Element} object
33721      * @return {Roo.Element} The element
33722      */
33723     getEl : function(){
33724         if(!this.el){
33725             this.render();
33726         }
33727         return this.el;
33728     },
33729
33730     /**
33731      * Adds a separator bar to the menu
33732      * @return {Roo.menu.Item} The menu item that was added
33733      */
33734     addSeparator : function(){
33735         return this.addItem(new Roo.menu.Separator());
33736     },
33737
33738     /**
33739      * Adds an {@link Roo.Element} object to the menu
33740      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33741      * @return {Roo.menu.Item} The menu item that was added
33742      */
33743     addElement : function(el){
33744         return this.addItem(new Roo.menu.BaseItem(el));
33745     },
33746
33747     /**
33748      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33749      * @param {Roo.menu.Item} item The menu item to add
33750      * @return {Roo.menu.Item} The menu item that was added
33751      */
33752     addItem : function(item){
33753         this.items.add(item);
33754         if(this.ul){
33755             var li = document.createElement("li");
33756             li.className = "x-menu-list-item";
33757             this.ul.dom.appendChild(li);
33758             item.render(li, this);
33759             this.delayAutoWidth();
33760         }
33761         return item;
33762     },
33763
33764     /**
33765      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33766      * @param {Object} config A MenuItem config object
33767      * @return {Roo.menu.Item} The menu item that was added
33768      */
33769     addMenuItem : function(config){
33770         if(!(config instanceof Roo.menu.Item)){
33771             if(typeof config.checked == "boolean"){ // must be check menu item config?
33772                 config = new Roo.menu.CheckItem(config);
33773             }else{
33774                 config = new Roo.menu.Item(config);
33775             }
33776         }
33777         return this.addItem(config);
33778     },
33779
33780     /**
33781      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33782      * @param {String} text The text to display in the menu item
33783      * @return {Roo.menu.Item} The menu item that was added
33784      */
33785     addText : function(text){
33786         return this.addItem(new Roo.menu.TextItem(text));
33787     },
33788
33789     /**
33790      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33791      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33792      * @param {Roo.menu.Item} item The menu item to add
33793      * @return {Roo.menu.Item} The menu item that was added
33794      */
33795     insert : function(index, item){
33796         this.items.insert(index, item);
33797         if(this.ul){
33798             var li = document.createElement("li");
33799             li.className = "x-menu-list-item";
33800             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33801             item.render(li, this);
33802             this.delayAutoWidth();
33803         }
33804         return item;
33805     },
33806
33807     /**
33808      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33809      * @param {Roo.menu.Item} item The menu item to remove
33810      */
33811     remove : function(item){
33812         this.items.removeKey(item.id);
33813         item.destroy();
33814     },
33815
33816     /**
33817      * Removes and destroys all items in the menu
33818      */
33819     removeAll : function(){
33820         var f;
33821         while(f = this.items.first()){
33822             this.remove(f);
33823         }
33824     }
33825 });
33826
33827 // MenuNav is a private utility class used internally by the Menu
33828 Roo.menu.MenuNav = function(menu){
33829     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33830     this.scope = this.menu = menu;
33831 };
33832
33833 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33834     doRelay : function(e, h){
33835         var k = e.getKey();
33836         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33837             this.menu.tryActivate(0, 1);
33838             return false;
33839         }
33840         return h.call(this.scope || this, e, this.menu);
33841     },
33842
33843     up : function(e, m){
33844         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33845             m.tryActivate(m.items.length-1, -1);
33846         }
33847     },
33848
33849     down : function(e, m){
33850         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33851             m.tryActivate(0, 1);
33852         }
33853     },
33854
33855     right : function(e, m){
33856         if(m.activeItem){
33857             m.activeItem.expandMenu(true);
33858         }
33859     },
33860
33861     left : function(e, m){
33862         m.hide();
33863         if(m.parentMenu && m.parentMenu.activeItem){
33864             m.parentMenu.activeItem.activate();
33865         }
33866     },
33867
33868     enter : function(e, m){
33869         if(m.activeItem){
33870             e.stopPropagation();
33871             m.activeItem.onClick(e);
33872             m.fireEvent("click", this, m.activeItem);
33873             return true;
33874         }
33875     }
33876 });/*
33877  * Based on:
33878  * Ext JS Library 1.1.1
33879  * Copyright(c) 2006-2007, Ext JS, LLC.
33880  *
33881  * Originally Released Under LGPL - original licence link has changed is not relivant.
33882  *
33883  * Fork - LGPL
33884  * <script type="text/javascript">
33885  */
33886  
33887 /**
33888  * @class Roo.menu.MenuMgr
33889  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33890  * @singleton
33891  */
33892 Roo.menu.MenuMgr = function(){
33893    var menus, active, groups = {}, attached = false, lastShow = new Date();
33894
33895    // private - called when first menu is created
33896    function init(){
33897        menus = {};
33898        active = new Roo.util.MixedCollection();
33899        Roo.get(document).addKeyListener(27, function(){
33900            if(active.length > 0){
33901                hideAll();
33902            }
33903        });
33904    }
33905
33906    // private
33907    function hideAll(){
33908        if(active && active.length > 0){
33909            var c = active.clone();
33910            c.each(function(m){
33911                m.hide();
33912            });
33913        }
33914    }
33915
33916    // private
33917    function onHide(m){
33918        active.remove(m);
33919        if(active.length < 1){
33920            Roo.get(document).un("mousedown", onMouseDown);
33921            attached = false;
33922        }
33923    }
33924
33925    // private
33926    function onShow(m){
33927        var last = active.last();
33928        lastShow = new Date();
33929        active.add(m);
33930        if(!attached){
33931            Roo.get(document).on("mousedown", onMouseDown);
33932            attached = true;
33933        }
33934        if(m.parentMenu){
33935           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
33936           m.parentMenu.activeChild = m;
33937        }else if(last && last.isVisible()){
33938           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
33939        }
33940    }
33941
33942    // private
33943    function onBeforeHide(m){
33944        if(m.activeChild){
33945            m.activeChild.hide();
33946        }
33947        if(m.autoHideTimer){
33948            clearTimeout(m.autoHideTimer);
33949            delete m.autoHideTimer;
33950        }
33951    }
33952
33953    // private
33954    function onBeforeShow(m){
33955        var pm = m.parentMenu;
33956        if(!pm && !m.allowOtherMenus){
33957            hideAll();
33958        }else if(pm && pm.activeChild && active != m){
33959            pm.activeChild.hide();
33960        }
33961    }
33962
33963    // private
33964    function onMouseDown(e){
33965        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
33966            hideAll();
33967        }
33968    }
33969
33970    // private
33971    function onBeforeCheck(mi, state){
33972        if(state){
33973            var g = groups[mi.group];
33974            for(var i = 0, l = g.length; i < l; i++){
33975                if(g[i] != mi){
33976                    g[i].setChecked(false);
33977                }
33978            }
33979        }
33980    }
33981
33982    return {
33983
33984        /**
33985         * Hides all menus that are currently visible
33986         */
33987        hideAll : function(){
33988             hideAll();  
33989        },
33990
33991        // private
33992        register : function(menu){
33993            if(!menus){
33994                init();
33995            }
33996            menus[menu.id] = menu;
33997            menu.on("beforehide", onBeforeHide);
33998            menu.on("hide", onHide);
33999            menu.on("beforeshow", onBeforeShow);
34000            menu.on("show", onShow);
34001            var g = menu.group;
34002            if(g && menu.events["checkchange"]){
34003                if(!groups[g]){
34004                    groups[g] = [];
34005                }
34006                groups[g].push(menu);
34007                menu.on("checkchange", onCheck);
34008            }
34009        },
34010
34011         /**
34012          * Returns a {@link Roo.menu.Menu} object
34013          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34014          * be used to generate and return a new Menu instance.
34015          */
34016        get : function(menu){
34017            if(typeof menu == "string"){ // menu id
34018                return menus[menu];
34019            }else if(menu.events){  // menu instance
34020                return menu;
34021            }else if(typeof menu.length == 'number'){ // array of menu items?
34022                return new Roo.menu.Menu({items:menu});
34023            }else{ // otherwise, must be a config
34024                return new Roo.menu.Menu(menu);
34025            }
34026        },
34027
34028        // private
34029        unregister : function(menu){
34030            delete menus[menu.id];
34031            menu.un("beforehide", onBeforeHide);
34032            menu.un("hide", onHide);
34033            menu.un("beforeshow", onBeforeShow);
34034            menu.un("show", onShow);
34035            var g = menu.group;
34036            if(g && menu.events["checkchange"]){
34037                groups[g].remove(menu);
34038                menu.un("checkchange", onCheck);
34039            }
34040        },
34041
34042        // private
34043        registerCheckable : function(menuItem){
34044            var g = menuItem.group;
34045            if(g){
34046                if(!groups[g]){
34047                    groups[g] = [];
34048                }
34049                groups[g].push(menuItem);
34050                menuItem.on("beforecheckchange", onBeforeCheck);
34051            }
34052        },
34053
34054        // private
34055        unregisterCheckable : function(menuItem){
34056            var g = menuItem.group;
34057            if(g){
34058                groups[g].remove(menuItem);
34059                menuItem.un("beforecheckchange", onBeforeCheck);
34060            }
34061        }
34062    };
34063 }();/*
34064  * Based on:
34065  * Ext JS Library 1.1.1
34066  * Copyright(c) 2006-2007, Ext JS, LLC.
34067  *
34068  * Originally Released Under LGPL - original licence link has changed is not relivant.
34069  *
34070  * Fork - LGPL
34071  * <script type="text/javascript">
34072  */
34073  
34074
34075 /**
34076  * @class Roo.menu.BaseItem
34077  * @extends Roo.Component
34078  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34079  * management and base configuration options shared by all menu components.
34080  * @constructor
34081  * Creates a new BaseItem
34082  * @param {Object} config Configuration options
34083  */
34084 Roo.menu.BaseItem = function(config){
34085     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34086
34087     this.addEvents({
34088         /**
34089          * @event click
34090          * Fires when this item is clicked
34091          * @param {Roo.menu.BaseItem} this
34092          * @param {Roo.EventObject} e
34093          */
34094         click: true,
34095         /**
34096          * @event activate
34097          * Fires when this item is activated
34098          * @param {Roo.menu.BaseItem} this
34099          */
34100         activate : true,
34101         /**
34102          * @event deactivate
34103          * Fires when this item is deactivated
34104          * @param {Roo.menu.BaseItem} this
34105          */
34106         deactivate : true
34107     });
34108
34109     if(this.handler){
34110         this.on("click", this.handler, this.scope, true);
34111     }
34112 };
34113
34114 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34115     /**
34116      * @cfg {Function} handler
34117      * A function that will handle the click event of this menu item (defaults to undefined)
34118      */
34119     /**
34120      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34121      */
34122     canActivate : false,
34123     /**
34124      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34125      */
34126     activeClass : "x-menu-item-active",
34127     /**
34128      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34129      */
34130     hideOnClick : true,
34131     /**
34132      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34133      */
34134     hideDelay : 100,
34135
34136     // private
34137     ctype: "Roo.menu.BaseItem",
34138
34139     // private
34140     actionMode : "container",
34141
34142     // private
34143     render : function(container, parentMenu){
34144         this.parentMenu = parentMenu;
34145         Roo.menu.BaseItem.superclass.render.call(this, container);
34146         this.container.menuItemId = this.id;
34147     },
34148
34149     // private
34150     onRender : function(container, position){
34151         this.el = Roo.get(this.el);
34152         container.dom.appendChild(this.el.dom);
34153     },
34154
34155     // private
34156     onClick : function(e){
34157         if(!this.disabled && this.fireEvent("click", this, e) !== false
34158                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34159             this.handleClick(e);
34160         }else{
34161             e.stopEvent();
34162         }
34163     },
34164
34165     // private
34166     activate : function(){
34167         if(this.disabled){
34168             return false;
34169         }
34170         var li = this.container;
34171         li.addClass(this.activeClass);
34172         this.region = li.getRegion().adjust(2, 2, -2, -2);
34173         this.fireEvent("activate", this);
34174         return true;
34175     },
34176
34177     // private
34178     deactivate : function(){
34179         this.container.removeClass(this.activeClass);
34180         this.fireEvent("deactivate", this);
34181     },
34182
34183     // private
34184     shouldDeactivate : function(e){
34185         return !this.region || !this.region.contains(e.getPoint());
34186     },
34187
34188     // private
34189     handleClick : function(e){
34190         if(this.hideOnClick){
34191             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34192         }
34193     },
34194
34195     // private
34196     expandMenu : function(autoActivate){
34197         // do nothing
34198     },
34199
34200     // private
34201     hideMenu : function(){
34202         // do nothing
34203     }
34204 });/*
34205  * Based on:
34206  * Ext JS Library 1.1.1
34207  * Copyright(c) 2006-2007, Ext JS, LLC.
34208  *
34209  * Originally Released Under LGPL - original licence link has changed is not relivant.
34210  *
34211  * Fork - LGPL
34212  * <script type="text/javascript">
34213  */
34214  
34215 /**
34216  * @class Roo.menu.Adapter
34217  * @extends Roo.menu.BaseItem
34218  * 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.
34219  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34220  * @constructor
34221  * Creates a new Adapter
34222  * @param {Object} config Configuration options
34223  */
34224 Roo.menu.Adapter = function(component, config){
34225     Roo.menu.Adapter.superclass.constructor.call(this, config);
34226     this.component = component;
34227 };
34228 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34229     // private
34230     canActivate : true,
34231
34232     // private
34233     onRender : function(container, position){
34234         this.component.render(container);
34235         this.el = this.component.getEl();
34236     },
34237
34238     // private
34239     activate : function(){
34240         if(this.disabled){
34241             return false;
34242         }
34243         this.component.focus();
34244         this.fireEvent("activate", this);
34245         return true;
34246     },
34247
34248     // private
34249     deactivate : function(){
34250         this.fireEvent("deactivate", this);
34251     },
34252
34253     // private
34254     disable : function(){
34255         this.component.disable();
34256         Roo.menu.Adapter.superclass.disable.call(this);
34257     },
34258
34259     // private
34260     enable : function(){
34261         this.component.enable();
34262         Roo.menu.Adapter.superclass.enable.call(this);
34263     }
34264 });/*
34265  * Based on:
34266  * Ext JS Library 1.1.1
34267  * Copyright(c) 2006-2007, Ext JS, LLC.
34268  *
34269  * Originally Released Under LGPL - original licence link has changed is not relivant.
34270  *
34271  * Fork - LGPL
34272  * <script type="text/javascript">
34273  */
34274
34275 /**
34276  * @class Roo.menu.TextItem
34277  * @extends Roo.menu.BaseItem
34278  * Adds a static text string to a menu, usually used as either a heading or group separator.
34279  * @constructor
34280  * Creates a new TextItem
34281  * @param {String} text The text to display
34282  */
34283 Roo.menu.TextItem = function(text){
34284     this.text = text;
34285     Roo.menu.TextItem.superclass.constructor.call(this);
34286 };
34287
34288 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34289     /**
34290      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34291      */
34292     hideOnClick : false,
34293     /**
34294      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34295      */
34296     itemCls : "x-menu-text",
34297
34298     // private
34299     onRender : function(){
34300         var s = document.createElement("span");
34301         s.className = this.itemCls;
34302         s.innerHTML = this.text;
34303         this.el = s;
34304         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34305     }
34306 });/*
34307  * Based on:
34308  * Ext JS Library 1.1.1
34309  * Copyright(c) 2006-2007, Ext JS, LLC.
34310  *
34311  * Originally Released Under LGPL - original licence link has changed is not relivant.
34312  *
34313  * Fork - LGPL
34314  * <script type="text/javascript">
34315  */
34316
34317 /**
34318  * @class Roo.menu.Separator
34319  * @extends Roo.menu.BaseItem
34320  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34321  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34322  * @constructor
34323  * @param {Object} config Configuration options
34324  */
34325 Roo.menu.Separator = function(config){
34326     Roo.menu.Separator.superclass.constructor.call(this, config);
34327 };
34328
34329 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34330     /**
34331      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34332      */
34333     itemCls : "x-menu-sep",
34334     /**
34335      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34336      */
34337     hideOnClick : false,
34338
34339     // private
34340     onRender : function(li){
34341         var s = document.createElement("span");
34342         s.className = this.itemCls;
34343         s.innerHTML = "&#160;";
34344         this.el = s;
34345         li.addClass("x-menu-sep-li");
34346         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34347     }
34348 });/*
34349  * Based on:
34350  * Ext JS Library 1.1.1
34351  * Copyright(c) 2006-2007, Ext JS, LLC.
34352  *
34353  * Originally Released Under LGPL - original licence link has changed is not relivant.
34354  *
34355  * Fork - LGPL
34356  * <script type="text/javascript">
34357  */
34358 /**
34359  * @class Roo.menu.Item
34360  * @extends Roo.menu.BaseItem
34361  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34362  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34363  * activation and click handling.
34364  * @constructor
34365  * Creates a new Item
34366  * @param {Object} config Configuration options
34367  */
34368 Roo.menu.Item = function(config){
34369     Roo.menu.Item.superclass.constructor.call(this, config);
34370     if(this.menu){
34371         this.menu = Roo.menu.MenuMgr.get(this.menu);
34372     }
34373 };
34374 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34375     /**
34376      * @cfg {String} icon
34377      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34378      */
34379     /**
34380      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34381      */
34382     itemCls : "x-menu-item",
34383     /**
34384      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34385      */
34386     canActivate : true,
34387     /**
34388      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34389      */
34390     showDelay: 200,
34391     // doc'd in BaseItem
34392     hideDelay: 200,
34393
34394     // private
34395     ctype: "Roo.menu.Item",
34396     
34397     // private
34398     onRender : function(container, position){
34399         var el = document.createElement("a");
34400         el.hideFocus = true;
34401         el.unselectable = "on";
34402         el.href = this.href || "#";
34403         if(this.hrefTarget){
34404             el.target = this.hrefTarget;
34405         }
34406         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34407         el.innerHTML = String.format(
34408                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34409                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34410         this.el = el;
34411         Roo.menu.Item.superclass.onRender.call(this, container, position);
34412     },
34413
34414     /**
34415      * Sets the text to display in this menu item
34416      * @param {String} text The text to display
34417      */
34418     setText : function(text){
34419         this.text = text;
34420         if(this.rendered){
34421             this.el.update(String.format(
34422                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34423                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34424             this.parentMenu.autoWidth();
34425         }
34426     },
34427
34428     // private
34429     handleClick : function(e){
34430         if(!this.href){ // if no link defined, stop the event automatically
34431             e.stopEvent();
34432         }
34433         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34434     },
34435
34436     // private
34437     activate : function(autoExpand){
34438         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34439             this.focus();
34440             if(autoExpand){
34441                 this.expandMenu();
34442             }
34443         }
34444         return true;
34445     },
34446
34447     // private
34448     shouldDeactivate : function(e){
34449         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34450             if(this.menu && this.menu.isVisible()){
34451                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34452             }
34453             return true;
34454         }
34455         return false;
34456     },
34457
34458     // private
34459     deactivate : function(){
34460         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34461         this.hideMenu();
34462     },
34463
34464     // private
34465     expandMenu : function(autoActivate){
34466         if(!this.disabled && this.menu){
34467             clearTimeout(this.hideTimer);
34468             delete this.hideTimer;
34469             if(!this.menu.isVisible() && !this.showTimer){
34470                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34471             }else if (this.menu.isVisible() && autoActivate){
34472                 this.menu.tryActivate(0, 1);
34473             }
34474         }
34475     },
34476
34477     // private
34478     deferExpand : function(autoActivate){
34479         delete this.showTimer;
34480         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34481         if(autoActivate){
34482             this.menu.tryActivate(0, 1);
34483         }
34484     },
34485
34486     // private
34487     hideMenu : function(){
34488         clearTimeout(this.showTimer);
34489         delete this.showTimer;
34490         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34491             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34492         }
34493     },
34494
34495     // private
34496     deferHide : function(){
34497         delete this.hideTimer;
34498         this.menu.hide();
34499     }
34500 });/*
34501  * Based on:
34502  * Ext JS Library 1.1.1
34503  * Copyright(c) 2006-2007, Ext JS, LLC.
34504  *
34505  * Originally Released Under LGPL - original licence link has changed is not relivant.
34506  *
34507  * Fork - LGPL
34508  * <script type="text/javascript">
34509  */
34510  
34511 /**
34512  * @class Roo.menu.CheckItem
34513  * @extends Roo.menu.Item
34514  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34515  * @constructor
34516  * Creates a new CheckItem
34517  * @param {Object} config Configuration options
34518  */
34519 Roo.menu.CheckItem = function(config){
34520     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34521     this.addEvents({
34522         /**
34523          * @event beforecheckchange
34524          * Fires before the checked value is set, providing an opportunity to cancel if needed
34525          * @param {Roo.menu.CheckItem} this
34526          * @param {Boolean} checked The new checked value that will be set
34527          */
34528         "beforecheckchange" : true,
34529         /**
34530          * @event checkchange
34531          * Fires after the checked value has been set
34532          * @param {Roo.menu.CheckItem} this
34533          * @param {Boolean} checked The checked value that was set
34534          */
34535         "checkchange" : true
34536     });
34537     if(this.checkHandler){
34538         this.on('checkchange', this.checkHandler, this.scope);
34539     }
34540 };
34541 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34542     /**
34543      * @cfg {String} group
34544      * All check items with the same group name will automatically be grouped into a single-select
34545      * radio button group (defaults to '')
34546      */
34547     /**
34548      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34549      */
34550     itemCls : "x-menu-item x-menu-check-item",
34551     /**
34552      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34553      */
34554     groupClass : "x-menu-group-item",
34555
34556     /**
34557      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34558      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34559      * initialized with checked = true will be rendered as checked.
34560      */
34561     checked: false,
34562
34563     // private
34564     ctype: "Roo.menu.CheckItem",
34565
34566     // private
34567     onRender : function(c){
34568         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34569         if(this.group){
34570             this.el.addClass(this.groupClass);
34571         }
34572         Roo.menu.MenuMgr.registerCheckable(this);
34573         if(this.checked){
34574             this.checked = false;
34575             this.setChecked(true, true);
34576         }
34577     },
34578
34579     // private
34580     destroy : function(){
34581         if(this.rendered){
34582             Roo.menu.MenuMgr.unregisterCheckable(this);
34583         }
34584         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34585     },
34586
34587     /**
34588      * Set the checked state of this item
34589      * @param {Boolean} checked The new checked value
34590      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34591      */
34592     setChecked : function(state, suppressEvent){
34593         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34594             if(this.container){
34595                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34596             }
34597             this.checked = state;
34598             if(suppressEvent !== true){
34599                 this.fireEvent("checkchange", this, state);
34600             }
34601         }
34602     },
34603
34604     // private
34605     handleClick : function(e){
34606        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34607            this.setChecked(!this.checked);
34608        }
34609        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34610     }
34611 });/*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621  
34622 /**
34623  * @class Roo.menu.DateItem
34624  * @extends Roo.menu.Adapter
34625  * A menu item that wraps the {@link Roo.DatPicker} component.
34626  * @constructor
34627  * Creates a new DateItem
34628  * @param {Object} config Configuration options
34629  */
34630 Roo.menu.DateItem = function(config){
34631     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34632     /** The Roo.DatePicker object @type Roo.DatePicker */
34633     this.picker = this.component;
34634     this.addEvents({select: true});
34635     
34636     this.picker.on("render", function(picker){
34637         picker.getEl().swallowEvent("click");
34638         picker.container.addClass("x-menu-date-item");
34639     });
34640
34641     this.picker.on("select", this.onSelect, this);
34642 };
34643
34644 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34645     // private
34646     onSelect : function(picker, date){
34647         this.fireEvent("select", this, date, picker);
34648         Roo.menu.DateItem.superclass.handleClick.call(this);
34649     }
34650 });/*
34651  * Based on:
34652  * Ext JS Library 1.1.1
34653  * Copyright(c) 2006-2007, Ext JS, LLC.
34654  *
34655  * Originally Released Under LGPL - original licence link has changed is not relivant.
34656  *
34657  * Fork - LGPL
34658  * <script type="text/javascript">
34659  */
34660  
34661 /**
34662  * @class Roo.menu.ColorItem
34663  * @extends Roo.menu.Adapter
34664  * A menu item that wraps the {@link Roo.ColorPalette} component.
34665  * @constructor
34666  * Creates a new ColorItem
34667  * @param {Object} config Configuration options
34668  */
34669 Roo.menu.ColorItem = function(config){
34670     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34671     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34672     this.palette = this.component;
34673     this.relayEvents(this.palette, ["select"]);
34674     if(this.selectHandler){
34675         this.on('select', this.selectHandler, this.scope);
34676     }
34677 };
34678 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34679  * Based on:
34680  * Ext JS Library 1.1.1
34681  * Copyright(c) 2006-2007, Ext JS, LLC.
34682  *
34683  * Originally Released Under LGPL - original licence link has changed is not relivant.
34684  *
34685  * Fork - LGPL
34686  * <script type="text/javascript">
34687  */
34688  
34689
34690 /**
34691  * @class Roo.menu.DateMenu
34692  * @extends Roo.menu.Menu
34693  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34694  * @constructor
34695  * Creates a new DateMenu
34696  * @param {Object} config Configuration options
34697  */
34698 Roo.menu.DateMenu = function(config){
34699     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34700     this.plain = true;
34701     var di = new Roo.menu.DateItem(config);
34702     this.add(di);
34703     /**
34704      * The {@link Roo.DatePicker} instance for this DateMenu
34705      * @type DatePicker
34706      */
34707     this.picker = di.picker;
34708     /**
34709      * @event select
34710      * @param {DatePicker} picker
34711      * @param {Date} date
34712      */
34713     this.relayEvents(di, ["select"]);
34714
34715     this.on('beforeshow', function(){
34716         if(this.picker){
34717             this.picker.hideMonthPicker(true);
34718         }
34719     }, this);
34720 };
34721 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34722     cls:'x-date-menu'
34723 });/*
34724  * Based on:
34725  * Ext JS Library 1.1.1
34726  * Copyright(c) 2006-2007, Ext JS, LLC.
34727  *
34728  * Originally Released Under LGPL - original licence link has changed is not relivant.
34729  *
34730  * Fork - LGPL
34731  * <script type="text/javascript">
34732  */
34733  
34734
34735 /**
34736  * @class Roo.menu.ColorMenu
34737  * @extends Roo.menu.Menu
34738  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34739  * @constructor
34740  * Creates a new ColorMenu
34741  * @param {Object} config Configuration options
34742  */
34743 Roo.menu.ColorMenu = function(config){
34744     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34745     this.plain = true;
34746     var ci = new Roo.menu.ColorItem(config);
34747     this.add(ci);
34748     /**
34749      * The {@link Roo.ColorPalette} instance for this ColorMenu
34750      * @type ColorPalette
34751      */
34752     this.palette = ci.palette;
34753     /**
34754      * @event select
34755      * @param {ColorPalette} palette
34756      * @param {String} color
34757      */
34758     this.relayEvents(ci, ["select"]);
34759 };
34760 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34761  * Based on:
34762  * Ext JS Library 1.1.1
34763  * Copyright(c) 2006-2007, Ext JS, LLC.
34764  *
34765  * Originally Released Under LGPL - original licence link has changed is not relivant.
34766  *
34767  * Fork - LGPL
34768  * <script type="text/javascript">
34769  */
34770  
34771 /**
34772  * @class Roo.form.Field
34773  * @extends Roo.BoxComponent
34774  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34775  * @constructor
34776  * Creates a new Field
34777  * @param {Object} config Configuration options
34778  */
34779 Roo.form.Field = function(config){
34780     Roo.form.Field.superclass.constructor.call(this, config);
34781 };
34782
34783 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34784     /**
34785      * @cfg {String} fieldLabel Label to use when rendering a form.
34786      */
34787        /**
34788      * @cfg {String} qtip Mouse over tip
34789      */
34790      
34791     /**
34792      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34793      */
34794     invalidClass : "x-form-invalid",
34795     /**
34796      * @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")
34797      */
34798     invalidText : "The value in this field is invalid",
34799     /**
34800      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34801      */
34802     focusClass : "x-form-focus",
34803     /**
34804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34805       automatic validation (defaults to "keyup").
34806      */
34807     validationEvent : "keyup",
34808     /**
34809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34810      */
34811     validateOnBlur : true,
34812     /**
34813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34814      */
34815     validationDelay : 250,
34816     /**
34817      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34818      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34819      */
34820     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34821     /**
34822      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34823      */
34824     fieldClass : "x-form-field",
34825     /**
34826      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34827      *<pre>
34828 Value         Description
34829 -----------   ----------------------------------------------------------------------
34830 qtip          Display a quick tip when the user hovers over the field
34831 title         Display a default browser title attribute popup
34832 under         Add a block div beneath the field containing the error text
34833 side          Add an error icon to the right of the field with a popup on hover
34834 [element id]  Add the error text directly to the innerHTML of the specified element
34835 </pre>
34836      */
34837     msgTarget : 'qtip',
34838     /**
34839      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34840      */
34841     msgFx : 'normal',
34842
34843     /**
34844      * @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.
34845      */
34846     readOnly : false,
34847
34848     /**
34849      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34850      */
34851     disabled : false,
34852
34853     /**
34854      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34855      */
34856     inputType : undefined,
34857     
34858     /**
34859      * @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).
34860          */
34861         tabIndex : undefined,
34862         
34863     // private
34864     isFormField : true,
34865
34866     // private
34867     hasFocus : false,
34868     /**
34869      * @property {Roo.Element} fieldEl
34870      * Element Containing the rendered Field (with label etc.)
34871      */
34872     /**
34873      * @cfg {Mixed} value A value to initialize this field with.
34874      */
34875     value : undefined,
34876
34877     /**
34878      * @cfg {String} name The field's HTML name attribute.
34879      */
34880     /**
34881      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34882      */
34883
34884         // private ??
34885         initComponent : function(){
34886         Roo.form.Field.superclass.initComponent.call(this);
34887         this.addEvents({
34888             /**
34889              * @event focus
34890              * Fires when this field receives input focus.
34891              * @param {Roo.form.Field} this
34892              */
34893             focus : true,
34894             /**
34895              * @event blur
34896              * Fires when this field loses input focus.
34897              * @param {Roo.form.Field} this
34898              */
34899             blur : true,
34900             /**
34901              * @event specialkey
34902              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34903              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34904              * @param {Roo.form.Field} this
34905              * @param {Roo.EventObject} e The event object
34906              */
34907             specialkey : true,
34908             /**
34909              * @event change
34910              * Fires just before the field blurs if the field value has changed.
34911              * @param {Roo.form.Field} this
34912              * @param {Mixed} newValue The new value
34913              * @param {Mixed} oldValue The original value
34914              */
34915             change : true,
34916             /**
34917              * @event invalid
34918              * Fires after the field has been marked as invalid.
34919              * @param {Roo.form.Field} this
34920              * @param {String} msg The validation message
34921              */
34922             invalid : true,
34923             /**
34924              * @event valid
34925              * Fires after the field has been validated with no errors.
34926              * @param {Roo.form.Field} this
34927              */
34928             valid : true
34929         });
34930     },
34931
34932     /**
34933      * Returns the name attribute of the field if available
34934      * @return {String} name The field name
34935      */
34936     getName: function(){
34937          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
34938     },
34939
34940     // private
34941     onRender : function(ct, position){
34942         Roo.form.Field.superclass.onRender.call(this, ct, position);
34943         if(!this.el){
34944             var cfg = this.getAutoCreate();
34945             if(!cfg.name){
34946                 cfg.name = this.name || this.id;
34947             }
34948             if(this.inputType){
34949                 cfg.type = this.inputType;
34950             }
34951             this.el = ct.createChild(cfg, position);
34952         }
34953         var type = this.el.dom.type;
34954         if(type){
34955             if(type == 'password'){
34956                 type = 'text';
34957             }
34958             this.el.addClass('x-form-'+type);
34959         }
34960         if(this.readOnly){
34961             this.el.dom.readOnly = true;
34962         }
34963         if(this.tabIndex !== undefined){
34964             this.el.dom.setAttribute('tabIndex', this.tabIndex);
34965         }
34966
34967         this.el.addClass([this.fieldClass, this.cls]);
34968         this.initValue();
34969     },
34970
34971     /**
34972      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
34973      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
34974      * @return {Roo.form.Field} this
34975      */
34976     applyTo : function(target){
34977         this.allowDomMove = false;
34978         this.el = Roo.get(target);
34979         this.render(this.el.dom.parentNode);
34980         return this;
34981     },
34982
34983     // private
34984     initValue : function(){
34985         if(this.value !== undefined){
34986             this.setValue(this.value);
34987         }else if(this.el.dom.value.length > 0){
34988             this.setValue(this.el.dom.value);
34989         }
34990     },
34991
34992     /**
34993      * Returns true if this field has been changed since it was originally loaded and is not disabled.
34994      */
34995     isDirty : function() {
34996         if(this.disabled) {
34997             return false;
34998         }
34999         return String(this.getValue()) !== String(this.originalValue);
35000     },
35001
35002     // private
35003     afterRender : function(){
35004         Roo.form.Field.superclass.afterRender.call(this);
35005         this.initEvents();
35006     },
35007
35008     // private
35009     fireKey : function(e){
35010         if(e.isNavKeyPress()){
35011             this.fireEvent("specialkey", this, e);
35012         }
35013     },
35014
35015     /**
35016      * Resets the current field value to the originally loaded value and clears any validation messages
35017      */
35018     reset : function(){
35019         this.setValue(this.originalValue);
35020         this.clearInvalid();
35021     },
35022
35023     // private
35024     initEvents : function(){
35025         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35026         this.el.on("focus", this.onFocus,  this);
35027         this.el.on("blur", this.onBlur,  this);
35028
35029         // reference to original value for reset
35030         this.originalValue = this.getValue();
35031     },
35032
35033     // private
35034     onFocus : function(){
35035         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35036             this.el.addClass(this.focusClass);
35037         }
35038         if(!this.hasFocus){
35039             this.hasFocus = true;
35040             this.startValue = this.getValue();
35041             this.fireEvent("focus", this);
35042         }
35043     },
35044
35045     beforeBlur : Roo.emptyFn,
35046
35047     // private
35048     onBlur : function(){
35049         this.beforeBlur();
35050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35051             this.el.removeClass(this.focusClass);
35052         }
35053         this.hasFocus = false;
35054         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35055             this.validate();
35056         }
35057         var v = this.getValue();
35058         if(String(v) !== String(this.startValue)){
35059             this.fireEvent('change', this, v, this.startValue);
35060         }
35061         this.fireEvent("blur", this);
35062     },
35063
35064     /**
35065      * Returns whether or not the field value is currently valid
35066      * @param {Boolean} preventMark True to disable marking the field invalid
35067      * @return {Boolean} True if the value is valid, else false
35068      */
35069     isValid : function(preventMark){
35070         if(this.disabled){
35071             return true;
35072         }
35073         var restore = this.preventMark;
35074         this.preventMark = preventMark === true;
35075         var v = this.validateValue(this.processValue(this.getRawValue()));
35076         this.preventMark = restore;
35077         return v;
35078     },
35079
35080     /**
35081      * Validates the field value
35082      * @return {Boolean} True if the value is valid, else false
35083      */
35084     validate : function(){
35085         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35086             this.clearInvalid();
35087             return true;
35088         }
35089         return false;
35090     },
35091
35092     processValue : function(value){
35093         return value;
35094     },
35095
35096     // private
35097     // Subclasses should provide the validation implementation by overriding this
35098     validateValue : function(value){
35099         return true;
35100     },
35101
35102     /**
35103      * Mark this field as invalid
35104      * @param {String} msg The validation message
35105      */
35106     markInvalid : function(msg){
35107         if(!this.rendered || this.preventMark){ // not rendered
35108             return;
35109         }
35110         this.el.addClass(this.invalidClass);
35111         msg = msg || this.invalidText;
35112         switch(this.msgTarget){
35113             case 'qtip':
35114                 this.el.dom.qtip = msg;
35115                 this.el.dom.qclass = 'x-form-invalid-tip';
35116                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35117                     Roo.QuickTips.enable();
35118                 }
35119                 break;
35120             case 'title':
35121                 this.el.dom.title = msg;
35122                 break;
35123             case 'under':
35124                 if(!this.errorEl){
35125                     var elp = this.el.findParent('.x-form-element', 5, true);
35126                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35127                     this.errorEl.setWidth(elp.getWidth(true)-20);
35128                 }
35129                 this.errorEl.update(msg);
35130                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35131                 break;
35132             case 'side':
35133                 if(!this.errorIcon){
35134                     var elp = this.el.findParent('.x-form-element', 5, true);
35135                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35136                 }
35137                 this.alignErrorIcon();
35138                 this.errorIcon.dom.qtip = msg;
35139                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35140                 this.errorIcon.show();
35141                 this.on('resize', this.alignErrorIcon, this);
35142                 break;
35143             default:
35144                 var t = Roo.getDom(this.msgTarget);
35145                 t.innerHTML = msg;
35146                 t.style.display = this.msgDisplay;
35147                 break;
35148         }
35149         this.fireEvent('invalid', this, msg);
35150     },
35151
35152     // private
35153     alignErrorIcon : function(){
35154         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35155     },
35156
35157     /**
35158      * Clear any invalid styles/messages for this field
35159      */
35160     clearInvalid : function(){
35161         if(!this.rendered || this.preventMark){ // not rendered
35162             return;
35163         }
35164         this.el.removeClass(this.invalidClass);
35165         switch(this.msgTarget){
35166             case 'qtip':
35167                 this.el.dom.qtip = '';
35168                 break;
35169             case 'title':
35170                 this.el.dom.title = '';
35171                 break;
35172             case 'under':
35173                 if(this.errorEl){
35174                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35175                 }
35176                 break;
35177             case 'side':
35178                 if(this.errorIcon){
35179                     this.errorIcon.dom.qtip = '';
35180                     this.errorIcon.hide();
35181                     this.un('resize', this.alignErrorIcon, this);
35182                 }
35183                 break;
35184             default:
35185                 var t = Roo.getDom(this.msgTarget);
35186                 t.innerHTML = '';
35187                 t.style.display = 'none';
35188                 break;
35189         }
35190         this.fireEvent('valid', this);
35191     },
35192
35193     /**
35194      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35195      * @return {Mixed} value The field value
35196      */
35197     getRawValue : function(){
35198         var v = this.el.getValue();
35199         if(v === this.emptyText){
35200             v = '';
35201         }
35202         return v;
35203     },
35204
35205     /**
35206      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35207      * @return {Mixed} value The field value
35208      */
35209     getValue : function(){
35210         var v = this.el.getValue();
35211         if(v === this.emptyText || v === undefined){
35212             v = '';
35213         }
35214         return v;
35215     },
35216
35217     /**
35218      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35219      * @param {Mixed} value The value to set
35220      */
35221     setRawValue : function(v){
35222         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35223     },
35224
35225     /**
35226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35227      * @param {Mixed} value The value to set
35228      */
35229     setValue : function(v){
35230         this.value = v;
35231         if(this.rendered){
35232             this.el.dom.value = (v === null || v === undefined ? '' : v);
35233             this.validate();
35234         }
35235     },
35236
35237     adjustSize : function(w, h){
35238         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35239         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35240         return s;
35241     },
35242
35243     adjustWidth : function(tag, w){
35244         tag = tag.toLowerCase();
35245         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35246             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35247                 if(tag == 'input'){
35248                     return w + 2;
35249                 }
35250                 if(tag = 'textarea'){
35251                     return w-2;
35252                 }
35253             }else if(Roo.isOpera){
35254                 if(tag == 'input'){
35255                     return w + 2;
35256                 }
35257                 if(tag = 'textarea'){
35258                     return w-2;
35259                 }
35260             }
35261         }
35262         return w;
35263     }
35264 });
35265
35266
35267 // anything other than normal should be considered experimental
35268 Roo.form.Field.msgFx = {
35269     normal : {
35270         show: function(msgEl, f){
35271             msgEl.setDisplayed('block');
35272         },
35273
35274         hide : function(msgEl, f){
35275             msgEl.setDisplayed(false).update('');
35276         }
35277     },
35278
35279     slide : {
35280         show: function(msgEl, f){
35281             msgEl.slideIn('t', {stopFx:true});
35282         },
35283
35284         hide : function(msgEl, f){
35285             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35286         }
35287     },
35288
35289     slideRight : {
35290         show: function(msgEl, f){
35291             msgEl.fixDisplay();
35292             msgEl.alignTo(f.el, 'tl-tr');
35293             msgEl.slideIn('l', {stopFx:true});
35294         },
35295
35296         hide : function(msgEl, f){
35297             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35298         }
35299     }
35300 };/*
35301  * Based on:
35302  * Ext JS Library 1.1.1
35303  * Copyright(c) 2006-2007, Ext JS, LLC.
35304  *
35305  * Originally Released Under LGPL - original licence link has changed is not relivant.
35306  *
35307  * Fork - LGPL
35308  * <script type="text/javascript">
35309  */
35310  
35311
35312 /**
35313  * @class Roo.form.TextField
35314  * @extends Roo.form.Field
35315  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35316  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35317  * @constructor
35318  * Creates a new TextField
35319  * @param {Object} config Configuration options
35320  */
35321 Roo.form.TextField = function(config){
35322     Roo.form.TextField.superclass.constructor.call(this, config);
35323     this.addEvents({
35324         /**
35325          * @event autosize
35326          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35327          * according to the default logic, but this event provides a hook for the developer to apply additional
35328          * logic at runtime to resize the field if needed.
35329              * @param {Roo.form.Field} this This text field
35330              * @param {Number} width The new field width
35331              */
35332         autosize : true
35333     });
35334 };
35335
35336 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35337     /**
35338      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35339      */
35340     grow : false,
35341     /**
35342      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35343      */
35344     growMin : 30,
35345     /**
35346      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35347      */
35348     growMax : 800,
35349     /**
35350      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35351      */
35352     vtype : null,
35353     /**
35354      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35355      */
35356     maskRe : null,
35357     /**
35358      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35359      */
35360     disableKeyFilter : false,
35361     /**
35362      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35363      */
35364     allowBlank : true,
35365     /**
35366      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35367      */
35368     minLength : 0,
35369     /**
35370      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35371      */
35372     maxLength : Number.MAX_VALUE,
35373     /**
35374      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35375      */
35376     minLengthText : "The minimum length for this field is {0}",
35377     /**
35378      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35379      */
35380     maxLengthText : "The maximum length for this field is {0}",
35381     /**
35382      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35383      */
35384     selectOnFocus : false,
35385     /**
35386      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35387      */
35388     blankText : "This field is required",
35389     /**
35390      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35391      * If available, this function will be called only after the basic validators all return true, and will be passed the
35392      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35393      */
35394     validator : null,
35395     /**
35396      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35397      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35398      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35399      */
35400     regex : null,
35401     /**
35402      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35403      */
35404     regexText : "",
35405     /**
35406      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35407      */
35408     emptyText : null,
35409     /**
35410      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35411      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35412      */
35413     emptyClass : 'x-form-empty-field',
35414
35415     // private
35416     initEvents : function(){
35417         Roo.form.TextField.superclass.initEvents.call(this);
35418         if(this.validationEvent == 'keyup'){
35419             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35420             this.el.on('keyup', this.filterValidation, this);
35421         }
35422         else if(this.validationEvent !== false){
35423             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35424         }
35425         if(this.selectOnFocus || this.emptyText){
35426             this.on("focus", this.preFocus, this);
35427             if(this.emptyText){
35428                 this.on('blur', this.postBlur, this);
35429                 this.applyEmptyText();
35430             }
35431         }
35432         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35433             this.el.on("keypress", this.filterKeys, this);
35434         }
35435         if(this.grow){
35436             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35437             this.el.on("click", this.autoSize,  this);
35438         }
35439     },
35440
35441     processValue : function(value){
35442         if(this.stripCharsRe){
35443             var newValue = value.replace(this.stripCharsRe, '');
35444             if(newValue !== value){
35445                 this.setRawValue(newValue);
35446                 return newValue;
35447             }
35448         }
35449         return value;
35450     },
35451
35452     filterValidation : function(e){
35453         if(!e.isNavKeyPress()){
35454             this.validationTask.delay(this.validationDelay);
35455         }
35456     },
35457
35458     // private
35459     onKeyUp : function(e){
35460         if(!e.isNavKeyPress()){
35461             this.autoSize();
35462         }
35463     },
35464
35465     /**
35466      * Resets the current field value to the originally-loaded value and clears any validation messages.
35467      * Also adds emptyText and emptyClass if the original value was blank.
35468      */
35469     reset : function(){
35470         Roo.form.TextField.superclass.reset.call(this);
35471         this.applyEmptyText();
35472     },
35473
35474     applyEmptyText : function(){
35475         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35476             this.setRawValue(this.emptyText);
35477             this.el.addClass(this.emptyClass);
35478         }
35479     },
35480
35481     // private
35482     preFocus : function(){
35483         if(this.emptyText){
35484             if(this.el.dom.value == this.emptyText){
35485                 this.setRawValue('');
35486             }
35487             this.el.removeClass(this.emptyClass);
35488         }
35489         if(this.selectOnFocus){
35490             this.el.dom.select();
35491         }
35492     },
35493
35494     // private
35495     postBlur : function(){
35496         this.applyEmptyText();
35497     },
35498
35499     // private
35500     filterKeys : function(e){
35501         var k = e.getKey();
35502         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35503             return;
35504         }
35505         var c = e.getCharCode(), cc = String.fromCharCode(c);
35506         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35507             return;
35508         }
35509         if(!this.maskRe.test(cc)){
35510             e.stopEvent();
35511         }
35512     },
35513
35514     setValue : function(v){
35515         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35516             this.el.removeClass(this.emptyClass);
35517         }
35518         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35519         this.applyEmptyText();
35520         this.autoSize();
35521     },
35522
35523     /**
35524      * Validates a value according to the field's validation rules and marks the field as invalid
35525      * if the validation fails
35526      * @param {Mixed} value The value to validate
35527      * @return {Boolean} True if the value is valid, else false
35528      */
35529     validateValue : function(value){
35530         if(value.length < 1 || value === this.emptyText){ // if it's blank
35531              if(this.allowBlank){
35532                 this.clearInvalid();
35533                 return true;
35534              }else{
35535                 this.markInvalid(this.blankText);
35536                 return false;
35537              }
35538         }
35539         if(value.length < this.minLength){
35540             this.markInvalid(String.format(this.minLengthText, this.minLength));
35541             return false;
35542         }
35543         if(value.length > this.maxLength){
35544             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35545             return false;
35546         }
35547         if(this.vtype){
35548             var vt = Roo.form.VTypes;
35549             if(!vt[this.vtype](value, this)){
35550                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35551                 return false;
35552             }
35553         }
35554         if(typeof this.validator == "function"){
35555             var msg = this.validator(value);
35556             if(msg !== true){
35557                 this.markInvalid(msg);
35558                 return false;
35559             }
35560         }
35561         if(this.regex && !this.regex.test(value)){
35562             this.markInvalid(this.regexText);
35563             return false;
35564         }
35565         return true;
35566     },
35567
35568     /**
35569      * Selects text in this field
35570      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35571      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35572      */
35573     selectText : function(start, end){
35574         var v = this.getRawValue();
35575         if(v.length > 0){
35576             start = start === undefined ? 0 : start;
35577             end = end === undefined ? v.length : end;
35578             var d = this.el.dom;
35579             if(d.setSelectionRange){
35580                 d.setSelectionRange(start, end);
35581             }else if(d.createTextRange){
35582                 var range = d.createTextRange();
35583                 range.moveStart("character", start);
35584                 range.moveEnd("character", v.length-end);
35585                 range.select();
35586             }
35587         }
35588     },
35589
35590     /**
35591      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35592      * This only takes effect if grow = true, and fires the autosize event.
35593      */
35594     autoSize : function(){
35595         if(!this.grow || !this.rendered){
35596             return;
35597         }
35598         if(!this.metrics){
35599             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35600         }
35601         var el = this.el;
35602         var v = el.dom.value;
35603         var d = document.createElement('div');
35604         d.appendChild(document.createTextNode(v));
35605         v = d.innerHTML;
35606         d = null;
35607         v += "&#160;";
35608         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35609         this.el.setWidth(w);
35610         this.fireEvent("autosize", this, w);
35611     }
35612 });/*
35613  * Based on:
35614  * Ext JS Library 1.1.1
35615  * Copyright(c) 2006-2007, Ext JS, LLC.
35616  *
35617  * Originally Released Under LGPL - original licence link has changed is not relivant.
35618  *
35619  * Fork - LGPL
35620  * <script type="text/javascript">
35621  */
35622  
35623 /**
35624  * @class Roo.form.Hidden
35625  * @extends Roo.form.TextField
35626  * Simple Hidden element used on forms 
35627  * 
35628  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35629  * 
35630  * @constructor
35631  * Creates a new Hidden form element.
35632  * @param {Object} config Configuration options
35633  */
35634
35635
35636
35637 // easy hidden field...
35638 Roo.form.Hidden = function(config){
35639     Roo.form.Hidden.superclass.constructor.call(this, config);
35640 };
35641   
35642 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35643     fieldLabel:      '',
35644     inputType:      'hidden',
35645     width:          50,
35646     allowBlank:     true,
35647     labelSeparator: '',
35648     hidden:         true,
35649     itemCls :       'x-form-item-display-none'
35650
35651
35652 });
35653
35654
35655 /*
35656  * Based on:
35657  * Ext JS Library 1.1.1
35658  * Copyright(c) 2006-2007, Ext JS, LLC.
35659  *
35660  * Originally Released Under LGPL - original licence link has changed is not relivant.
35661  *
35662  * Fork - LGPL
35663  * <script type="text/javascript">
35664  */
35665  
35666 /**
35667  * @class Roo.form.TriggerField
35668  * @extends Roo.form.TextField
35669  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35670  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35671  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35672  * for which you can provide a custom implementation.  For example:
35673  * <pre><code>
35674 var trigger = new Roo.form.TriggerField();
35675 trigger.onTriggerClick = myTriggerFn;
35676 trigger.applyTo('my-field');
35677 </code></pre>
35678  *
35679  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35680  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35681  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35682  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35683  * @constructor
35684  * Create a new TriggerField.
35685  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35686  * to the base TextField)
35687  */
35688 Roo.form.TriggerField = function(config){
35689     this.mimicing = false;
35690     Roo.form.TriggerField.superclass.constructor.call(this, config);
35691 };
35692
35693 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35694     /**
35695      * @cfg {String} triggerClass A CSS class to apply to the trigger
35696      */
35697     /**
35698      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35699      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35700      */
35701     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35702     /**
35703      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35704      */
35705     hideTrigger:false,
35706
35707     /** @cfg {Boolean} grow @hide */
35708     /** @cfg {Number} growMin @hide */
35709     /** @cfg {Number} growMax @hide */
35710
35711     /**
35712      * @hide 
35713      * @method
35714      */
35715     autoSize: Roo.emptyFn,
35716     // private
35717     monitorTab : true,
35718     // private
35719     deferHeight : true,
35720
35721     
35722     actionMode : 'wrap',
35723     // private
35724     onResize : function(w, h){
35725         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35726         if(typeof w == 'number'){
35727             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35728         }
35729     },
35730
35731     // private
35732     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35733
35734     // private
35735     getResizeEl : function(){
35736         return this.wrap;
35737     },
35738
35739     // private
35740     getPositionEl : function(){
35741         return this.wrap;
35742     },
35743
35744     // private
35745     alignErrorIcon : function(){
35746         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35747     },
35748
35749     // private
35750     onRender : function(ct, position){
35751         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35752         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35753         this.trigger = this.wrap.createChild(this.triggerConfig ||
35754                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35755         if(this.hideTrigger){
35756             this.trigger.setDisplayed(false);
35757         }
35758         this.initTrigger();
35759         if(!this.width){
35760             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35761         }
35762     },
35763
35764     // private
35765     initTrigger : function(){
35766         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35767         this.trigger.addClassOnOver('x-form-trigger-over');
35768         this.trigger.addClassOnClick('x-form-trigger-click');
35769     },
35770
35771     // private
35772     onDestroy : function(){
35773         if(this.trigger){
35774             this.trigger.removeAllListeners();
35775             this.trigger.remove();
35776         }
35777         if(this.wrap){
35778             this.wrap.remove();
35779         }
35780         Roo.form.TriggerField.superclass.onDestroy.call(this);
35781     },
35782
35783     // private
35784     onFocus : function(){
35785         Roo.form.TriggerField.superclass.onFocus.call(this);
35786         if(!this.mimicing){
35787             this.wrap.addClass('x-trigger-wrap-focus');
35788             this.mimicing = true;
35789             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35790             if(this.monitorTab){
35791                 this.el.on("keydown", this.checkTab, this);
35792             }
35793         }
35794     },
35795
35796     // private
35797     checkTab : function(e){
35798         if(e.getKey() == e.TAB){
35799             this.triggerBlur();
35800         }
35801     },
35802
35803     // private
35804     onBlur : function(){
35805         // do nothing
35806     },
35807
35808     // private
35809     mimicBlur : function(e, t){
35810         if(!this.wrap.contains(t) && this.validateBlur()){
35811             this.triggerBlur();
35812         }
35813     },
35814
35815     // private
35816     triggerBlur : function(){
35817         this.mimicing = false;
35818         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35819         if(this.monitorTab){
35820             this.el.un("keydown", this.checkTab, this);
35821         }
35822         this.wrap.removeClass('x-trigger-wrap-focus');
35823         Roo.form.TriggerField.superclass.onBlur.call(this);
35824     },
35825
35826     // private
35827     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35828     validateBlur : function(e, t){
35829         return true;
35830     },
35831
35832     // private
35833     onDisable : function(){
35834         Roo.form.TriggerField.superclass.onDisable.call(this);
35835         if(this.wrap){
35836             this.wrap.addClass('x-item-disabled');
35837         }
35838     },
35839
35840     // private
35841     onEnable : function(){
35842         Roo.form.TriggerField.superclass.onEnable.call(this);
35843         if(this.wrap){
35844             this.wrap.removeClass('x-item-disabled');
35845         }
35846     },
35847
35848     // private
35849     onShow : function(){
35850         var ae = this.getActionEl();
35851         
35852         if(ae){
35853             ae.dom.style.display = '';
35854             ae.dom.style.visibility = 'visible';
35855         }
35856     },
35857
35858     // private
35859     
35860     onHide : function(){
35861         var ae = this.getActionEl();
35862         ae.dom.style.display = 'none';
35863     },
35864
35865     /**
35866      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35867      * by an implementing function.
35868      * @method
35869      * @param {EventObject} e
35870      */
35871     onTriggerClick : Roo.emptyFn
35872 });
35873
35874 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35875 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35876 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35877 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35878     initComponent : function(){
35879         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35880
35881         this.triggerConfig = {
35882             tag:'span', cls:'x-form-twin-triggers', cn:[
35883             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35884             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35885         ]};
35886     },
35887
35888     getTrigger : function(index){
35889         return this.triggers[index];
35890     },
35891
35892     initTrigger : function(){
35893         var ts = this.trigger.select('.x-form-trigger', true);
35894         this.wrap.setStyle('overflow', 'hidden');
35895         var triggerField = this;
35896         ts.each(function(t, all, index){
35897             t.hide = function(){
35898                 var w = triggerField.wrap.getWidth();
35899                 this.dom.style.display = 'none';
35900                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35901             };
35902             t.show = function(){
35903                 var w = triggerField.wrap.getWidth();
35904                 this.dom.style.display = '';
35905                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35906             };
35907             var triggerIndex = 'Trigger'+(index+1);
35908
35909             if(this['hide'+triggerIndex]){
35910                 t.dom.style.display = 'none';
35911             }
35912             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35913             t.addClassOnOver('x-form-trigger-over');
35914             t.addClassOnClick('x-form-trigger-click');
35915         }, this);
35916         this.triggers = ts.elements;
35917     },
35918
35919     onTrigger1Click : Roo.emptyFn,
35920     onTrigger2Click : Roo.emptyFn
35921 });/*
35922  * Based on:
35923  * Ext JS Library 1.1.1
35924  * Copyright(c) 2006-2007, Ext JS, LLC.
35925  *
35926  * Originally Released Under LGPL - original licence link has changed is not relivant.
35927  *
35928  * Fork - LGPL
35929  * <script type="text/javascript">
35930  */
35931  
35932 /**
35933  * @class Roo.form.TextArea
35934  * @extends Roo.form.TextField
35935  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
35936  * support for auto-sizing.
35937  * @constructor
35938  * Creates a new TextArea
35939  * @param {Object} config Configuration options
35940  */
35941 Roo.form.TextArea = function(config){
35942     Roo.form.TextArea.superclass.constructor.call(this, config);
35943     // these are provided exchanges for backwards compat
35944     // minHeight/maxHeight were replaced by growMin/growMax to be
35945     // compatible with TextField growing config values
35946     if(this.minHeight !== undefined){
35947         this.growMin = this.minHeight;
35948     }
35949     if(this.maxHeight !== undefined){
35950         this.growMax = this.maxHeight;
35951     }
35952 };
35953
35954 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
35955     /**
35956      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
35957      */
35958     growMin : 60,
35959     /**
35960      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
35961      */
35962     growMax: 1000,
35963     /**
35964      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
35965      * in the field (equivalent to setting overflow: hidden, defaults to false)
35966      */
35967     preventScrollbars: false,
35968     /**
35969      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35970      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
35971      */
35972
35973     // private
35974     onRender : function(ct, position){
35975         if(!this.el){
35976             this.defaultAutoCreate = {
35977                 tag: "textarea",
35978                 style:"width:300px;height:60px;",
35979                 autocomplete: "off"
35980             };
35981         }
35982         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
35983         if(this.grow){
35984             this.textSizeEl = Roo.DomHelper.append(document.body, {
35985                 tag: "pre", cls: "x-form-grow-sizer"
35986             });
35987             if(this.preventScrollbars){
35988                 this.el.setStyle("overflow", "hidden");
35989             }
35990             this.el.setHeight(this.growMin);
35991         }
35992     },
35993
35994     onDestroy : function(){
35995         if(this.textSizeEl){
35996             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
35997         }
35998         Roo.form.TextArea.superclass.onDestroy.call(this);
35999     },
36000
36001     // private
36002     onKeyUp : function(e){
36003         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36004             this.autoSize();
36005         }
36006     },
36007
36008     /**
36009      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36010      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36011      */
36012     autoSize : function(){
36013         if(!this.grow || !this.textSizeEl){
36014             return;
36015         }
36016         var el = this.el;
36017         var v = el.dom.value;
36018         var ts = this.textSizeEl;
36019
36020         ts.innerHTML = '';
36021         ts.appendChild(document.createTextNode(v));
36022         v = ts.innerHTML;
36023
36024         Roo.fly(ts).setWidth(this.el.getWidth());
36025         if(v.length < 1){
36026             v = "&#160;&#160;";
36027         }else{
36028             if(Roo.isIE){
36029                 v = v.replace(/\n/g, '<p>&#160;</p>');
36030             }
36031             v += "&#160;\n&#160;";
36032         }
36033         ts.innerHTML = v;
36034         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36035         if(h != this.lastHeight){
36036             this.lastHeight = h;
36037             this.el.setHeight(h);
36038             this.fireEvent("autosize", this, h);
36039         }
36040     }
36041 });/*
36042  * Based on:
36043  * Ext JS Library 1.1.1
36044  * Copyright(c) 2006-2007, Ext JS, LLC.
36045  *
36046  * Originally Released Under LGPL - original licence link has changed is not relivant.
36047  *
36048  * Fork - LGPL
36049  * <script type="text/javascript">
36050  */
36051  
36052
36053 /**
36054  * @class Roo.form.NumberField
36055  * @extends Roo.form.TextField
36056  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36057  * @constructor
36058  * Creates a new NumberField
36059  * @param {Object} config Configuration options
36060  */
36061 Roo.form.NumberField = function(config){
36062     Roo.form.NumberField.superclass.constructor.call(this, config);
36063 };
36064
36065 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36066     /**
36067      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36068      */
36069     fieldClass: "x-form-field x-form-num-field",
36070     /**
36071      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36072      */
36073     allowDecimals : true,
36074     /**
36075      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36076      */
36077     decimalSeparator : ".",
36078     /**
36079      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36080      */
36081     decimalPrecision : 2,
36082     /**
36083      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36084      */
36085     allowNegative : true,
36086     /**
36087      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36088      */
36089     minValue : Number.NEGATIVE_INFINITY,
36090     /**
36091      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36092      */
36093     maxValue : Number.MAX_VALUE,
36094     /**
36095      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36096      */
36097     minText : "The minimum value for this field is {0}",
36098     /**
36099      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36100      */
36101     maxText : "The maximum value for this field is {0}",
36102     /**
36103      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36104      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36105      */
36106     nanText : "{0} is not a valid number",
36107
36108     // private
36109     initEvents : function(){
36110         Roo.form.NumberField.superclass.initEvents.call(this);
36111         var allowed = "0123456789";
36112         if(this.allowDecimals){
36113             allowed += this.decimalSeparator;
36114         }
36115         if(this.allowNegative){
36116             allowed += "-";
36117         }
36118         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36119         var keyPress = function(e){
36120             var k = e.getKey();
36121             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36122                 return;
36123             }
36124             var c = e.getCharCode();
36125             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36126                 e.stopEvent();
36127             }
36128         };
36129         this.el.on("keypress", keyPress, this);
36130     },
36131
36132     // private
36133     validateValue : function(value){
36134         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36135             return false;
36136         }
36137         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36138              return true;
36139         }
36140         var num = this.parseValue(value);
36141         if(isNaN(num)){
36142             this.markInvalid(String.format(this.nanText, value));
36143             return false;
36144         }
36145         if(num < this.minValue){
36146             this.markInvalid(String.format(this.minText, this.minValue));
36147             return false;
36148         }
36149         if(num > this.maxValue){
36150             this.markInvalid(String.format(this.maxText, this.maxValue));
36151             return false;
36152         }
36153         return true;
36154     },
36155
36156     getValue : function(){
36157         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36158     },
36159
36160     // private
36161     parseValue : function(value){
36162         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36163         return isNaN(value) ? '' : value;
36164     },
36165
36166     // private
36167     fixPrecision : function(value){
36168         var nan = isNaN(value);
36169         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36170             return nan ? '' : value;
36171         }
36172         return parseFloat(value).toFixed(this.decimalPrecision);
36173     },
36174
36175     setValue : function(v){
36176         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36177     },
36178
36179     // private
36180     decimalPrecisionFcn : function(v){
36181         return Math.floor(v);
36182     },
36183
36184     beforeBlur : function(){
36185         var v = this.parseValue(this.getRawValue());
36186         if(v){
36187             this.setValue(this.fixPrecision(v));
36188         }
36189     }
36190 });/*
36191  * Based on:
36192  * Ext JS Library 1.1.1
36193  * Copyright(c) 2006-2007, Ext JS, LLC.
36194  *
36195  * Originally Released Under LGPL - original licence link has changed is not relivant.
36196  *
36197  * Fork - LGPL
36198  * <script type="text/javascript">
36199  */
36200  
36201 /**
36202  * @class Roo.form.DateField
36203  * @extends Roo.form.TriggerField
36204  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36205 * @constructor
36206 * Create a new DateField
36207 * @param {Object} config
36208  */
36209 Roo.form.DateField = function(config){
36210     Roo.form.DateField.superclass.constructor.call(this, config);
36211     
36212       this.addEvents({
36213          
36214         /**
36215          * @event select
36216          * Fires when a date is selected
36217              * @param {Roo.form.DateField} combo This combo box
36218              * @param {Date} date The date selected
36219              */
36220         'select' : true
36221          
36222     });
36223     
36224     
36225     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36226     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36227     this.ddMatch = null;
36228     if(this.disabledDates){
36229         var dd = this.disabledDates;
36230         var re = "(?:";
36231         for(var i = 0; i < dd.length; i++){
36232             re += dd[i];
36233             if(i != dd.length-1) re += "|";
36234         }
36235         this.ddMatch = new RegExp(re + ")");
36236     }
36237 };
36238
36239 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36240     /**
36241      * @cfg {String} format
36242      * The default date format string which can be overriden for localization support.  The format must be
36243      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36244      */
36245     format : "m/d/y",
36246     /**
36247      * @cfg {String} altFormats
36248      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36249      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36250      */
36251     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36252     /**
36253      * @cfg {Array} disabledDays
36254      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36255      */
36256     disabledDays : null,
36257     /**
36258      * @cfg {String} disabledDaysText
36259      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36260      */
36261     disabledDaysText : "Disabled",
36262     /**
36263      * @cfg {Array} disabledDates
36264      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36265      * expression so they are very powerful. Some examples:
36266      * <ul>
36267      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36268      * <li>["03/08", "09/16"] would disable those days for every year</li>
36269      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36270      * <li>["03/../2006"] would disable every day in March 2006</li>
36271      * <li>["^03"] would disable every day in every March</li>
36272      * </ul>
36273      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36274      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36275      */
36276     disabledDates : null,
36277     /**
36278      * @cfg {String} disabledDatesText
36279      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36280      */
36281     disabledDatesText : "Disabled",
36282     /**
36283      * @cfg {Date/String} minValue
36284      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36285      * valid format (defaults to null).
36286      */
36287     minValue : null,
36288     /**
36289      * @cfg {Date/String} maxValue
36290      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36291      * valid format (defaults to null).
36292      */
36293     maxValue : null,
36294     /**
36295      * @cfg {String} minText
36296      * The error text to display when the date in the cell is before minValue (defaults to
36297      * 'The date in this field must be after {minValue}').
36298      */
36299     minText : "The date in this field must be equal to or after {0}",
36300     /**
36301      * @cfg {String} maxText
36302      * The error text to display when the date in the cell is after maxValue (defaults to
36303      * 'The date in this field must be before {maxValue}').
36304      */
36305     maxText : "The date in this field must be equal to or before {0}",
36306     /**
36307      * @cfg {String} invalidText
36308      * The error text to display when the date in the field is invalid (defaults to
36309      * '{value} is not a valid date - it must be in the format {format}').
36310      */
36311     invalidText : "{0} is not a valid date - it must be in the format {1}",
36312     /**
36313      * @cfg {String} triggerClass
36314      * An additional CSS class used to style the trigger button.  The trigger will always get the
36315      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36316      * which displays a calendar icon).
36317      */
36318     triggerClass : 'x-form-date-trigger',
36319     
36320
36321     /**
36322      * @cfg {bool} useIso
36323      * if enabled, then the date field will use a hidden field to store the 
36324      * real value as iso formated date. default (false)
36325      */ 
36326     useIso : false,
36327     /**
36328      * @cfg {String/Object} autoCreate
36329      * A DomHelper element spec, or true for a default element spec (defaults to
36330      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36331      */ 
36332     // private
36333     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36334     
36335     // private
36336     hiddenField: false,
36337     
36338     onRender : function(ct, position)
36339     {
36340         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36341         if (this.useIso) {
36342             this.el.dom.removeAttribute('name'); 
36343             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36344                     'before', true);
36345             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36346             // prevent input submission
36347             this.hiddenName = this.name;
36348         }
36349             
36350             
36351     },
36352     
36353     // private
36354     validateValue : function(value)
36355     {
36356         value = this.formatDate(value);
36357         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36358             return false;
36359         }
36360         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36361              return true;
36362         }
36363         var svalue = value;
36364         value = this.parseDate(value);
36365         if(!value){
36366             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36367             return false;
36368         }
36369         var time = value.getTime();
36370         if(this.minValue && time < this.minValue.getTime()){
36371             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36372             return false;
36373         }
36374         if(this.maxValue && time > this.maxValue.getTime()){
36375             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36376             return false;
36377         }
36378         if(this.disabledDays){
36379             var day = value.getDay();
36380             for(var i = 0; i < this.disabledDays.length; i++) {
36381                 if(day === this.disabledDays[i]){
36382                     this.markInvalid(this.disabledDaysText);
36383                     return false;
36384                 }
36385             }
36386         }
36387         var fvalue = this.formatDate(value);
36388         if(this.ddMatch && this.ddMatch.test(fvalue)){
36389             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36390             return false;
36391         }
36392         return true;
36393     },
36394
36395     // private
36396     // Provides logic to override the default TriggerField.validateBlur which just returns true
36397     validateBlur : function(){
36398         return !this.menu || !this.menu.isVisible();
36399     },
36400
36401     /**
36402      * Returns the current date value of the date field.
36403      * @return {Date} The date value
36404      */
36405     getValue : function(){
36406         
36407         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36408     },
36409
36410     /**
36411      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36412      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36413      * (the default format used is "m/d/y").
36414      * <br />Usage:
36415      * <pre><code>
36416 //All of these calls set the same date value (May 4, 2006)
36417
36418 //Pass a date object:
36419 var dt = new Date('5/4/06');
36420 dateField.setValue(dt);
36421
36422 //Pass a date string (default format):
36423 dateField.setValue('5/4/06');
36424
36425 //Pass a date string (custom format):
36426 dateField.format = 'Y-m-d';
36427 dateField.setValue('2006-5-4');
36428 </code></pre>
36429      * @param {String/Date} date The date or valid date string
36430      */
36431     setValue : function(date){
36432         if (this.hiddenField) {
36433             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36434         }
36435         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36436     },
36437
36438     // private
36439     parseDate : function(value){
36440         if(!value || value instanceof Date){
36441             return value;
36442         }
36443         var v = Date.parseDate(value, this.format);
36444         if(!v && this.altFormats){
36445             if(!this.altFormatsArray){
36446                 this.altFormatsArray = this.altFormats.split("|");
36447             }
36448             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36449                 v = Date.parseDate(value, this.altFormatsArray[i]);
36450             }
36451         }
36452         return v;
36453     },
36454
36455     // private
36456     formatDate : function(date, fmt){
36457         return (!date || !(date instanceof Date)) ?
36458                date : date.dateFormat(fmt || this.format);
36459     },
36460
36461     // private
36462     menuListeners : {
36463         select: function(m, d){
36464             this.setValue(d);
36465             this.fireEvent('select', this, d);
36466         },
36467         show : function(){ // retain focus styling
36468             this.onFocus();
36469         },
36470         hide : function(){
36471             this.focus.defer(10, this);
36472             var ml = this.menuListeners;
36473             this.menu.un("select", ml.select,  this);
36474             this.menu.un("show", ml.show,  this);
36475             this.menu.un("hide", ml.hide,  this);
36476         }
36477     },
36478
36479     // private
36480     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36481     onTriggerClick : function(){
36482         if(this.disabled){
36483             return;
36484         }
36485         if(this.menu == null){
36486             this.menu = new Roo.menu.DateMenu();
36487         }
36488         Roo.apply(this.menu.picker,  {
36489             showClear: this.allowBlank,
36490             minDate : this.minValue,
36491             maxDate : this.maxValue,
36492             disabledDatesRE : this.ddMatch,
36493             disabledDatesText : this.disabledDatesText,
36494             disabledDays : this.disabledDays,
36495             disabledDaysText : this.disabledDaysText,
36496             format : this.format,
36497             minText : String.format(this.minText, this.formatDate(this.minValue)),
36498             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36499         });
36500         this.menu.on(Roo.apply({}, this.menuListeners, {
36501             scope:this
36502         }));
36503         this.menu.picker.setValue(this.getValue() || new Date());
36504         this.menu.show(this.el, "tl-bl?");
36505     },
36506
36507     beforeBlur : function(){
36508         var v = this.parseDate(this.getRawValue());
36509         if(v){
36510             this.setValue(v);
36511         }
36512     }
36513
36514     /** @cfg {Boolean} grow @hide */
36515     /** @cfg {Number} growMin @hide */
36516     /** @cfg {Number} growMax @hide */
36517     /**
36518      * @hide
36519      * @method autoSize
36520      */
36521 });/*
36522  * Based on:
36523  * Ext JS Library 1.1.1
36524  * Copyright(c) 2006-2007, Ext JS, LLC.
36525  *
36526  * Originally Released Under LGPL - original licence link has changed is not relivant.
36527  *
36528  * Fork - LGPL
36529  * <script type="text/javascript">
36530  */
36531  
36532
36533 /**
36534  * @class Roo.form.ComboBox
36535  * @extends Roo.form.TriggerField
36536  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36537  * @constructor
36538  * Create a new ComboBox.
36539  * @param {Object} config Configuration options
36540  */
36541 Roo.form.ComboBox = function(config){
36542     Roo.form.ComboBox.superclass.constructor.call(this, config);
36543     this.addEvents({
36544         /**
36545          * @event expand
36546          * Fires when the dropdown list is expanded
36547              * @param {Roo.form.ComboBox} combo This combo box
36548              */
36549         'expand' : true,
36550         /**
36551          * @event collapse
36552          * Fires when the dropdown list is collapsed
36553              * @param {Roo.form.ComboBox} combo This combo box
36554              */
36555         'collapse' : true,
36556         /**
36557          * @event beforeselect
36558          * Fires before a list item is selected. Return false to cancel the selection.
36559              * @param {Roo.form.ComboBox} combo This combo box
36560              * @param {Roo.data.Record} record The data record returned from the underlying store
36561              * @param {Number} index The index of the selected item in the dropdown list
36562              */
36563         'beforeselect' : true,
36564         /**
36565          * @event select
36566          * Fires when a list item is selected
36567              * @param {Roo.form.ComboBox} combo This combo box
36568              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36569              * @param {Number} index The index of the selected item in the dropdown list
36570              */
36571         'select' : true,
36572         /**
36573          * @event beforequery
36574          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36575          * The event object passed has these properties:
36576              * @param {Roo.form.ComboBox} combo This combo box
36577              * @param {String} query The query
36578              * @param {Boolean} forceAll true to force "all" query
36579              * @param {Boolean} cancel true to cancel the query
36580              * @param {Object} e The query event object
36581              */
36582         'beforequery': true
36583     });
36584     if(this.transform){
36585         this.allowDomMove = false;
36586         var s = Roo.getDom(this.transform);
36587         if(!this.hiddenName){
36588             this.hiddenName = s.name;
36589         }
36590         if(!this.store){
36591             this.mode = 'local';
36592             var d = [], opts = s.options;
36593             for(var i = 0, len = opts.length;i < len; i++){
36594                 var o = opts[i];
36595                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36596                 if(o.selected) {
36597                     this.value = value;
36598                 }
36599                 d.push([value, o.text]);
36600             }
36601             this.store = new Roo.data.SimpleStore({
36602                 'id': 0,
36603                 fields: ['value', 'text'],
36604                 data : d
36605             });
36606             this.valueField = 'value';
36607             this.displayField = 'text';
36608         }
36609         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36610         if(!this.lazyRender){
36611             this.target = true;
36612             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36613             s.parentNode.removeChild(s); // remove it
36614             this.render(this.el.parentNode);
36615         }else{
36616             s.parentNode.removeChild(s); // remove it
36617         }
36618
36619     }
36620     if (this.store) {
36621         this.store = Roo.factory(this.store, Roo.data);
36622     }
36623     
36624     this.selectedIndex = -1;
36625     if(this.mode == 'local'){
36626         if(config.queryDelay === undefined){
36627             this.queryDelay = 10;
36628         }
36629         if(config.minChars === undefined){
36630             this.minChars = 0;
36631         }
36632     }
36633 };
36634
36635 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36636     /**
36637      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36638      */
36639     /**
36640      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36641      * rendering into an Roo.Editor, defaults to false)
36642      */
36643     /**
36644      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36645      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36646      */
36647     /**
36648      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36649      */
36650     /**
36651      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36652      * the dropdown list (defaults to undefined, with no header element)
36653      */
36654
36655      /**
36656      * @cfg {String/Roo.Template} tpl The template to use to render the output
36657      */
36658      
36659     // private
36660     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36661     /**
36662      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36663      */
36664     listWidth: undefined,
36665     /**
36666      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36667      * mode = 'remote' or 'text' if mode = 'local')
36668      */
36669     displayField: undefined,
36670     /**
36671      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36672      * mode = 'remote' or 'value' if mode = 'local'). 
36673      * Note: use of a valueField requires the user make a selection
36674      * in order for a value to be mapped.
36675      */
36676     valueField: undefined,
36677     /**
36678      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36679      * field's data value (defaults to the underlying DOM element's name)
36680      */
36681     hiddenName: undefined,
36682     /**
36683      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36684      */
36685     listClass: '',
36686     /**
36687      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36688      */
36689     selectedClass: 'x-combo-selected',
36690     /**
36691      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36692      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36693      * which displays a downward arrow icon).
36694      */
36695     triggerClass : 'x-form-arrow-trigger',
36696     /**
36697      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36698      */
36699     shadow:'sides',
36700     /**
36701      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36702      * anchor positions (defaults to 'tl-bl')
36703      */
36704     listAlign: 'tl-bl?',
36705     /**
36706      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36707      */
36708     maxHeight: 300,
36709     /**
36710      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36711      * query specified by the allQuery config option (defaults to 'query')
36712      */
36713     triggerAction: 'query',
36714     /**
36715      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36716      * (defaults to 4, does not apply if editable = false)
36717      */
36718     minChars : 4,
36719     /**
36720      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36721      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36722      */
36723     typeAhead: false,
36724     /**
36725      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36726      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36727      */
36728     queryDelay: 500,
36729     /**
36730      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36731      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36732      */
36733     pageSize: 0,
36734     /**
36735      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36736      * when editable = true (defaults to false)
36737      */
36738     selectOnFocus:false,
36739     /**
36740      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36741      */
36742     queryParam: 'query',
36743     /**
36744      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36745      * when mode = 'remote' (defaults to 'Loading...')
36746      */
36747     loadingText: 'Loading...',
36748     /**
36749      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36750      */
36751     resizable: false,
36752     /**
36753      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36754      */
36755     handleHeight : 8,
36756     /**
36757      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36758      * traditional select (defaults to true)
36759      */
36760     editable: true,
36761     /**
36762      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36763      */
36764     allQuery: '',
36765     /**
36766      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36767      */
36768     mode: 'remote',
36769     /**
36770      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36771      * listWidth has a higher value)
36772      */
36773     minListWidth : 70,
36774     /**
36775      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36776      * allow the user to set arbitrary text into the field (defaults to false)
36777      */
36778     forceSelection:false,
36779     /**
36780      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36781      * if typeAhead = true (defaults to 250)
36782      */
36783     typeAheadDelay : 250,
36784     /**
36785      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36786      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36787      */
36788     valueNotFoundText : undefined,
36789     /**
36790      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36791      */
36792     blockFocus : false,
36793     
36794     /**
36795      * @cfg {bool} disableClear Disable showing of clear button.
36796      */
36797     disableClear : false,
36798     
36799     // private
36800     onRender : function(ct, position){
36801         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36802         if(this.hiddenName){
36803             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36804                     'before', true);
36805             this.hiddenField.value =
36806                 this.hiddenValue !== undefined ? this.hiddenValue :
36807                 this.value !== undefined ? this.value : '';
36808
36809             // prevent input submission
36810             this.el.dom.removeAttribute('name');
36811         }
36812         if(Roo.isGecko){
36813             this.el.dom.setAttribute('autocomplete', 'off');
36814         }
36815
36816         var cls = 'x-combo-list';
36817
36818         this.list = new Roo.Layer({
36819             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36820         });
36821
36822         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36823         this.list.setWidth(lw);
36824         this.list.swallowEvent('mousewheel');
36825         this.assetHeight = 0;
36826
36827         if(this.title){
36828             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36829             this.assetHeight += this.header.getHeight();
36830         }
36831
36832         this.innerList = this.list.createChild({cls:cls+'-inner'});
36833         this.innerList.on('mouseover', this.onViewOver, this);
36834         this.innerList.on('mousemove', this.onViewMove, this);
36835         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36836         
36837         if(this.allowBlank && !this.pageSize && !this.disableClear){
36838             this.footer = this.list.createChild({cls:cls+'-ft'});
36839             this.pageTb = new Roo.Toolbar(this.footer);
36840            
36841         }
36842         if(this.pageSize){
36843             this.footer = this.list.createChild({cls:cls+'-ft'});
36844             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36845                     {pageSize: this.pageSize});
36846             
36847         }
36848         
36849         if (this.pageTb && this.allowBlank && !this.disableClear) {
36850             var _this = this;
36851             this.pageTb.add(new Roo.Toolbar.Fill(), {
36852                 cls: 'x-btn-icon x-btn-clear',
36853                 text: '&#160;',
36854                 handler: function()
36855                 {
36856                     _this.collapse();
36857                     _this.clearValue();
36858                     _this.onSelect(false, -1);
36859                 }
36860             });
36861         }
36862         if (this.footer) {
36863             this.assetHeight += this.footer.getHeight();
36864         }
36865         
36866
36867         if(!this.tpl){
36868             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36869         }
36870
36871         this.view = new Roo.View(this.innerList, this.tpl, {
36872             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36873         });
36874
36875         this.view.on('click', this.onViewClick, this);
36876
36877         this.store.on('beforeload', this.onBeforeLoad, this);
36878         this.store.on('load', this.onLoad, this);
36879         this.store.on('loadexception', this.collapse, this);
36880
36881         if(this.resizable){
36882             this.resizer = new Roo.Resizable(this.list,  {
36883                pinned:true, handles:'se'
36884             });
36885             this.resizer.on('resize', function(r, w, h){
36886                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36887                 this.listWidth = w;
36888                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36889                 this.restrictHeight();
36890             }, this);
36891             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36892         }
36893         if(!this.editable){
36894             this.editable = true;
36895             this.setEditable(false);
36896         }
36897     },
36898
36899     // private
36900     initEvents : function(){
36901         Roo.form.ComboBox.superclass.initEvents.call(this);
36902
36903         this.keyNav = new Roo.KeyNav(this.el, {
36904             "up" : function(e){
36905                 this.inKeyMode = true;
36906                 this.selectPrev();
36907             },
36908
36909             "down" : function(e){
36910                 if(!this.isExpanded()){
36911                     this.onTriggerClick();
36912                 }else{
36913                     this.inKeyMode = true;
36914                     this.selectNext();
36915                 }
36916             },
36917
36918             "enter" : function(e){
36919                 this.onViewClick();
36920                 //return true;
36921             },
36922
36923             "esc" : function(e){
36924                 this.collapse();
36925             },
36926
36927             "tab" : function(e){
36928                 this.onViewClick(false);
36929                 return true;
36930             },
36931
36932             scope : this,
36933
36934             doRelay : function(foo, bar, hname){
36935                 if(hname == 'down' || this.scope.isExpanded()){
36936                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
36937                 }
36938                 return true;
36939             },
36940
36941             forceKeyDown: true
36942         });
36943         this.queryDelay = Math.max(this.queryDelay || 10,
36944                 this.mode == 'local' ? 10 : 250);
36945         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
36946         if(this.typeAhead){
36947             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
36948         }
36949         if(this.editable !== false){
36950             this.el.on("keyup", this.onKeyUp, this);
36951         }
36952         if(this.forceSelection){
36953             this.on('blur', this.doForce, this);
36954         }
36955     },
36956
36957     onDestroy : function(){
36958         if(this.view){
36959             this.view.setStore(null);
36960             this.view.el.removeAllListeners();
36961             this.view.el.remove();
36962             this.view.purgeListeners();
36963         }
36964         if(this.list){
36965             this.list.destroy();
36966         }
36967         if(this.store){
36968             this.store.un('beforeload', this.onBeforeLoad, this);
36969             this.store.un('load', this.onLoad, this);
36970             this.store.un('loadexception', this.collapse, this);
36971         }
36972         Roo.form.ComboBox.superclass.onDestroy.call(this);
36973     },
36974
36975     // private
36976     fireKey : function(e){
36977         if(e.isNavKeyPress() && !this.list.isVisible()){
36978             this.fireEvent("specialkey", this, e);
36979         }
36980     },
36981
36982     // private
36983     onResize: function(w, h){
36984         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
36985         if(this.list && this.listWidth === undefined){
36986             var lw = Math.max(w, this.minListWidth);
36987             this.list.setWidth(lw);
36988             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36989         }
36990     },
36991
36992     /**
36993      * Allow or prevent the user from directly editing the field text.  If false is passed,
36994      * the user will only be able to select from the items defined in the dropdown list.  This method
36995      * is the runtime equivalent of setting the 'editable' config option at config time.
36996      * @param {Boolean} value True to allow the user to directly edit the field text
36997      */
36998     setEditable : function(value){
36999         if(value == this.editable){
37000             return;
37001         }
37002         this.editable = value;
37003         if(!value){
37004             this.el.dom.setAttribute('readOnly', true);
37005             this.el.on('mousedown', this.onTriggerClick,  this);
37006             this.el.addClass('x-combo-noedit');
37007         }else{
37008             this.el.dom.setAttribute('readOnly', false);
37009             this.el.un('mousedown', this.onTriggerClick,  this);
37010             this.el.removeClass('x-combo-noedit');
37011         }
37012     },
37013
37014     // private
37015     onBeforeLoad : function(){
37016         if(!this.hasFocus){
37017             return;
37018         }
37019         this.innerList.update(this.loadingText ?
37020                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37021         this.restrictHeight();
37022         this.selectedIndex = -1;
37023     },
37024
37025     // private
37026     onLoad : function(){
37027         if(!this.hasFocus){
37028             return;
37029         }
37030         if(this.store.getCount() > 0){
37031             this.expand();
37032             this.restrictHeight();
37033             if(this.lastQuery == this.allQuery){
37034                 if(this.editable){
37035                     this.el.dom.select();
37036                 }
37037                 if(!this.selectByValue(this.value, true)){
37038                     this.select(0, true);
37039                 }
37040             }else{
37041                 this.selectNext();
37042                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37043                     this.taTask.delay(this.typeAheadDelay);
37044                 }
37045             }
37046         }else{
37047             this.onEmptyResults();
37048         }
37049         //this.el.focus();
37050     },
37051
37052     // private
37053     onTypeAhead : function(){
37054         if(this.store.getCount() > 0){
37055             var r = this.store.getAt(0);
37056             var newValue = r.data[this.displayField];
37057             var len = newValue.length;
37058             var selStart = this.getRawValue().length;
37059             if(selStart != len){
37060                 this.setRawValue(newValue);
37061                 this.selectText(selStart, newValue.length);
37062             }
37063         }
37064     },
37065
37066     // private
37067     onSelect : function(record, index){
37068         if(this.fireEvent('beforeselect', this, record, index) !== false){
37069             this.setFromData(index > -1 ? record.data : false);
37070             this.collapse();
37071             this.fireEvent('select', this, record, index);
37072         }
37073     },
37074
37075     /**
37076      * Returns the currently selected field value or empty string if no value is set.
37077      * @return {String} value The selected value
37078      */
37079     getValue : function(){
37080         if(this.valueField){
37081             return typeof this.value != 'undefined' ? this.value : '';
37082         }else{
37083             return Roo.form.ComboBox.superclass.getValue.call(this);
37084         }
37085     },
37086
37087     /**
37088      * Clears any text/value currently set in the field
37089      */
37090     clearValue : function(){
37091         if(this.hiddenField){
37092             this.hiddenField.value = '';
37093         }
37094         this.value = '';
37095         this.setRawValue('');
37096         this.lastSelectionText = '';
37097         this.applyEmptyText();
37098     },
37099
37100     /**
37101      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37102      * will be displayed in the field.  If the value does not match the data value of an existing item,
37103      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37104      * Otherwise the field will be blank (although the value will still be set).
37105      * @param {String} value The value to match
37106      */
37107     setValue : function(v){
37108         var text = v;
37109         if(this.valueField){
37110             var r = this.findRecord(this.valueField, v);
37111             if(r){
37112                 text = r.data[this.displayField];
37113             }else if(this.valueNotFoundText !== undefined){
37114                 text = this.valueNotFoundText;
37115             }
37116         }
37117         this.lastSelectionText = text;
37118         if(this.hiddenField){
37119             this.hiddenField.value = v;
37120         }
37121         Roo.form.ComboBox.superclass.setValue.call(this, text);
37122         this.value = v;
37123     },
37124     /**
37125      * @property {Object} the last set data for the element
37126      */
37127     
37128     lastData : false,
37129     /**
37130      * Sets the value of the field based on a object which is related to the record format for the store.
37131      * @param {Object} value the value to set as. or false on reset?
37132      */
37133     setFromData : function(o){
37134         var dv = ''; // display value
37135         var vv = ''; // value value..
37136         this.lastData = o;
37137         if (this.displayField) {
37138             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37139         } else {
37140             // this is an error condition!!!
37141             console.log('no value field set for '+ this.name);
37142         }
37143         
37144         if(this.valueField){
37145             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37146         }
37147         if(this.hiddenField){
37148             this.hiddenField.value = vv;
37149             
37150             this.lastSelectionText = dv;
37151             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37152             this.value = vv;
37153             return;
37154         }
37155         // no hidden field.. - we store the value in 'value', but still display
37156         // display field!!!!
37157         this.lastSelectionText = dv;
37158         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37159         this.value = vv;
37160         
37161         
37162     },
37163     // private
37164     findRecord : function(prop, value){
37165         var record;
37166         if(this.store.getCount() > 0){
37167             this.store.each(function(r){
37168                 if(r.data[prop] == value){
37169                     record = r;
37170                     return false;
37171                 }
37172             });
37173         }
37174         return record;
37175     },
37176
37177     // private
37178     onViewMove : function(e, t){
37179         this.inKeyMode = false;
37180     },
37181
37182     // private
37183     onViewOver : function(e, t){
37184         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37185             return;
37186         }
37187         var item = this.view.findItemFromChild(t);
37188         if(item){
37189             var index = this.view.indexOf(item);
37190             this.select(index, false);
37191         }
37192     },
37193
37194     // private
37195     onViewClick : function(doFocus){
37196         var index = this.view.getSelectedIndexes()[0];
37197         var r = this.store.getAt(index);
37198         if(r){
37199             this.onSelect(r, index);
37200         }
37201         if(doFocus !== false && !this.blockFocus){
37202             this.el.focus();
37203         }
37204     },
37205
37206     // private
37207     restrictHeight : function(){
37208         this.innerList.dom.style.height = '';
37209         var inner = this.innerList.dom;
37210         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37211         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37212         this.list.beginUpdate();
37213         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37214         this.list.alignTo(this.el, this.listAlign);
37215         this.list.endUpdate();
37216     },
37217
37218     // private
37219     onEmptyResults : function(){
37220         this.collapse();
37221     },
37222
37223     /**
37224      * Returns true if the dropdown list is expanded, else false.
37225      */
37226     isExpanded : function(){
37227         return this.list.isVisible();
37228     },
37229
37230     /**
37231      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37232      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37233      * @param {String} value The data value of the item to select
37234      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37235      * selected item if it is not currently in view (defaults to true)
37236      * @return {Boolean} True if the value matched an item in the list, else false
37237      */
37238     selectByValue : function(v, scrollIntoView){
37239         if(v !== undefined && v !== null){
37240             var r = this.findRecord(this.valueField || this.displayField, v);
37241             if(r){
37242                 this.select(this.store.indexOf(r), scrollIntoView);
37243                 return true;
37244             }
37245         }
37246         return false;
37247     },
37248
37249     /**
37250      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37251      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37252      * @param {Number} index The zero-based index of the list item to select
37253      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37254      * selected item if it is not currently in view (defaults to true)
37255      */
37256     select : function(index, scrollIntoView){
37257         this.selectedIndex = index;
37258         this.view.select(index);
37259         if(scrollIntoView !== false){
37260             var el = this.view.getNode(index);
37261             if(el){
37262                 this.innerList.scrollChildIntoView(el, false);
37263             }
37264         }
37265     },
37266
37267     // private
37268     selectNext : function(){
37269         var ct = this.store.getCount();
37270         if(ct > 0){
37271             if(this.selectedIndex == -1){
37272                 this.select(0);
37273             }else if(this.selectedIndex < ct-1){
37274                 this.select(this.selectedIndex+1);
37275             }
37276         }
37277     },
37278
37279     // private
37280     selectPrev : function(){
37281         var ct = this.store.getCount();
37282         if(ct > 0){
37283             if(this.selectedIndex == -1){
37284                 this.select(0);
37285             }else if(this.selectedIndex != 0){
37286                 this.select(this.selectedIndex-1);
37287             }
37288         }
37289     },
37290
37291     // private
37292     onKeyUp : function(e){
37293         if(this.editable !== false && !e.isSpecialKey()){
37294             this.lastKey = e.getKey();
37295             this.dqTask.delay(this.queryDelay);
37296         }
37297     },
37298
37299     // private
37300     validateBlur : function(){
37301         return !this.list || !this.list.isVisible();   
37302     },
37303
37304     // private
37305     initQuery : function(){
37306         this.doQuery(this.getRawValue());
37307     },
37308
37309     // private
37310     doForce : function(){
37311         if(this.el.dom.value.length > 0){
37312             this.el.dom.value =
37313                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37314             this.applyEmptyText();
37315         }
37316     },
37317
37318     /**
37319      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37320      * query allowing the query action to be canceled if needed.
37321      * @param {String} query The SQL query to execute
37322      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37323      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37324      * saved in the current store (defaults to false)
37325      */
37326     doQuery : function(q, forceAll){
37327         if(q === undefined || q === null){
37328             q = '';
37329         }
37330         var qe = {
37331             query: q,
37332             forceAll: forceAll,
37333             combo: this,
37334             cancel:false
37335         };
37336         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37337             return false;
37338         }
37339         q = qe.query;
37340         forceAll = qe.forceAll;
37341         if(forceAll === true || (q.length >= this.minChars)){
37342             if(this.lastQuery != q){
37343                 this.lastQuery = q;
37344                 if(this.mode == 'local'){
37345                     this.selectedIndex = -1;
37346                     if(forceAll){
37347                         this.store.clearFilter();
37348                     }else{
37349                         this.store.filter(this.displayField, q);
37350                     }
37351                     this.onLoad();
37352                 }else{
37353                     this.store.baseParams[this.queryParam] = q;
37354                     this.store.load({
37355                         params: this.getParams(q)
37356                     });
37357                     this.expand();
37358                 }
37359             }else{
37360                 this.selectedIndex = -1;
37361                 this.onLoad();   
37362             }
37363         }
37364     },
37365
37366     // private
37367     getParams : function(q){
37368         var p = {};
37369         //p[this.queryParam] = q;
37370         if(this.pageSize){
37371             p.start = 0;
37372             p.limit = this.pageSize;
37373         }
37374         return p;
37375     },
37376
37377     /**
37378      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37379      */
37380     collapse : function(){
37381         if(!this.isExpanded()){
37382             return;
37383         }
37384         this.list.hide();
37385         Roo.get(document).un('mousedown', this.collapseIf, this);
37386         Roo.get(document).un('mousewheel', this.collapseIf, this);
37387         this.fireEvent('collapse', this);
37388     },
37389
37390     // private
37391     collapseIf : function(e){
37392         if(!e.within(this.wrap) && !e.within(this.list)){
37393             this.collapse();
37394         }
37395     },
37396
37397     /**
37398      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37399      */
37400     expand : function(){
37401         if(this.isExpanded() || !this.hasFocus){
37402             return;
37403         }
37404         this.list.alignTo(this.el, this.listAlign);
37405         this.list.show();
37406         Roo.get(document).on('mousedown', this.collapseIf, this);
37407         Roo.get(document).on('mousewheel', this.collapseIf, this);
37408         this.fireEvent('expand', this);
37409     },
37410
37411     // private
37412     // Implements the default empty TriggerField.onTriggerClick function
37413     onTriggerClick : function(){
37414         if(this.disabled){
37415             return;
37416         }
37417         if(this.isExpanded()){
37418             this.collapse();
37419             if (!this.blockFocus) {
37420                 this.el.focus();
37421             }
37422             
37423         }else {
37424             this.hasFocus = true;
37425             if(this.triggerAction == 'all') {
37426                 this.doQuery(this.allQuery, true);
37427             } else {
37428                 this.doQuery(this.getRawValue());
37429             }
37430             if (!this.blockFocus) {
37431                 this.el.focus();
37432             }
37433         }
37434     }
37435
37436     /** 
37437     * @cfg {Boolean} grow 
37438     * @hide 
37439     */
37440     /** 
37441     * @cfg {Number} growMin 
37442     * @hide 
37443     */
37444     /** 
37445     * @cfg {Number} growMax 
37446     * @hide 
37447     */
37448     /**
37449      * @hide
37450      * @method autoSize
37451      */
37452 });/*
37453  * Based on:
37454  * Ext JS Library 1.1.1
37455  * Copyright(c) 2006-2007, Ext JS, LLC.
37456  *
37457  * Originally Released Under LGPL - original licence link has changed is not relivant.
37458  *
37459  * Fork - LGPL
37460  * <script type="text/javascript">
37461  */
37462 /**
37463  * @class Roo.form.Checkbox
37464  * @extends Roo.form.Field
37465  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37466  * @constructor
37467  * Creates a new Checkbox
37468  * @param {Object} config Configuration options
37469  */
37470 Roo.form.Checkbox = function(config){
37471     Roo.form.Checkbox.superclass.constructor.call(this, config);
37472     this.addEvents({
37473         /**
37474          * @event check
37475          * Fires when the checkbox is checked or unchecked.
37476              * @param {Roo.form.Checkbox} this This checkbox
37477              * @param {Boolean} checked The new checked value
37478              */
37479         check : true
37480     });
37481 };
37482
37483 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37484     /**
37485      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37486      */
37487     focusClass : undefined,
37488     /**
37489      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37490      */
37491     fieldClass: "x-form-field",
37492     /**
37493      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37494      */
37495     checked: false,
37496     /**
37497      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37498      * {tag: "input", type: "checkbox", autocomplete: "off"})
37499      */
37500     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37501     /**
37502      * @cfg {String} boxLabel The text that appears beside the checkbox
37503      */
37504     boxLabel : "",
37505     /**
37506      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37507      */  
37508     inputValue : '1',
37509     /**
37510      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37511      */
37512      valueOff: '0', // value when not checked..
37513
37514     actionMode : 'viewEl', 
37515     //
37516     // private
37517     itemCls : 'x-menu-check-item x-form-item',
37518     groupClass : 'x-menu-group-item',
37519     inputType : 'hidden',
37520     
37521     
37522     inSetChecked: false, // check that we are not calling self...
37523     
37524     inputElement: false, // real input element?
37525     basedOn: false, // ????
37526     
37527     isFormField: true, // not sure where this is needed!!!!
37528
37529     onResize : function(){
37530         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37531         if(!this.boxLabel){
37532             this.el.alignTo(this.wrap, 'c-c');
37533         }
37534     },
37535
37536     initEvents : function(){
37537         Roo.form.Checkbox.superclass.initEvents.call(this);
37538         this.el.on("click", this.onClick,  this);
37539         this.el.on("change", this.onClick,  this);
37540     },
37541
37542
37543     getResizeEl : function(){
37544         return this.wrap;
37545     },
37546
37547     getPositionEl : function(){
37548         return this.wrap;
37549     },
37550
37551     // private
37552     onRender : function(ct, position){
37553         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37554         /*
37555         if(this.inputValue !== undefined){
37556             this.el.dom.value = this.inputValue;
37557         }
37558         */
37559         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37560         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37561         var viewEl = this.wrap.createChild({ 
37562             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37563         this.viewEl = viewEl;   
37564         this.wrap.on('click', this.onClick,  this); 
37565         
37566         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37567         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37568         
37569         
37570         
37571         if(this.boxLabel){
37572             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37573         //    viewEl.on('click', this.onClick,  this); 
37574         }
37575         //if(this.checked){
37576             this.setChecked(this.checked);
37577         //}else{
37578             //this.checked = this.el.dom;
37579         //}
37580
37581     },
37582
37583     // private
37584     initValue : Roo.emptyFn,
37585
37586     /**
37587      * Returns the checked state of the checkbox.
37588      * @return {Boolean} True if checked, else false
37589      */
37590     getValue : function(){
37591         if(this.el){
37592             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37593         }
37594         return this.valueOff;
37595         
37596     },
37597
37598         // private
37599     onClick : function(){ 
37600         this.setChecked(!this.checked);
37601
37602         //if(this.el.dom.checked != this.checked){
37603         //    this.setValue(this.el.dom.checked);
37604        // }
37605     },
37606
37607     /**
37608      * Sets the checked state of the checkbox.
37609      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37610      */
37611     setValue : function(v,suppressEvent){
37612         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37613         //if(this.el && this.el.dom){
37614         //    this.el.dom.checked = this.checked;
37615         //    this.el.dom.defaultChecked = this.checked;
37616         //}
37617         this.setChecked(v === this.inputValue);
37618         //this.fireEvent("check", this, this.checked);
37619     },
37620     // private..
37621     setChecked : function(state,suppressEvent)
37622     {
37623         if (this.inSetChecked) {
37624             this.checked = state;
37625             return;
37626         }
37627         
37628     
37629         if(this.wrap){
37630             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37631         }
37632         this.checked = state;
37633         if(suppressEvent !== true){
37634             this.fireEvent('checkchange', this, state);
37635         }
37636         this.inSetChecked = true;
37637         this.el.dom.value = state ? this.inputValue : this.valueOff;
37638         this.inSetChecked = false;
37639         
37640     },
37641     // handle setting of hidden value by some other method!!?!?
37642     setFromHidden: function()
37643     {
37644         if(!this.el){
37645             return;
37646         }
37647         //console.log("SET FROM HIDDEN");
37648         //alert('setFrom hidden');
37649         this.setValue(this.el.dom.value);
37650     },
37651     
37652     onDestroy : function()
37653     {
37654         if(this.viewEl){
37655             Roo.get(this.viewEl).remove();
37656         }
37657          
37658         Roo.form.Checkbox.superclass.onDestroy.call(this);
37659     }
37660
37661 });/*
37662  * Based on:
37663  * Ext JS Library 1.1.1
37664  * Copyright(c) 2006-2007, Ext JS, LLC.
37665  *
37666  * Originally Released Under LGPL - original licence link has changed is not relivant.
37667  *
37668  * Fork - LGPL
37669  * <script type="text/javascript">
37670  */
37671  
37672 /**
37673  * @class Roo.form.Radio
37674  * @extends Roo.form.Checkbox
37675  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37676  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37677  * @constructor
37678  * Creates a new Radio
37679  * @param {Object} config Configuration options
37680  */
37681 Roo.form.Radio = function(){
37682     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37683 };
37684 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37685     inputType: 'radio',
37686
37687     /**
37688      * If this radio is part of a group, it will return the selected value
37689      * @return {String}
37690      */
37691     getGroupValue : function(){
37692         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37693     }
37694 });//<script type="text/javascript">
37695
37696 /*
37697  * Ext JS Library 1.1.1
37698  * Copyright(c) 2006-2007, Ext JS, LLC.
37699  * licensing@extjs.com
37700  * 
37701  * http://www.extjs.com/license
37702  */
37703  
37704  /*
37705   * 
37706   * Known bugs:
37707   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37708   * - IE ? - no idea how much works there.
37709   * 
37710   * 
37711   * 
37712   */
37713  
37714
37715 /**
37716  * @class Ext.form.HtmlEditor
37717  * @extends Ext.form.Field
37718  * Provides a lightweight HTML Editor component.
37719  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37720  * 
37721  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37722  * supported by this editor.</b><br/><br/>
37723  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37724  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37725  */
37726 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37727       /**
37728      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37729      */
37730     toolbars : false,
37731     /**
37732      * @cfg {String} createLinkText The default text for the create link prompt
37733      */
37734     createLinkText : 'Please enter the URL for the link:',
37735     /**
37736      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37737      */
37738     defaultLinkValue : 'http:/'+'/',
37739    
37740     
37741     // id of frame..
37742     frameId: false,
37743     
37744     // private properties
37745     validationEvent : false,
37746     deferHeight: true,
37747     initialized : false,
37748     activated : false,
37749     sourceEditMode : false,
37750     onFocus : Roo.emptyFn,
37751     iframePad:3,
37752     hideMode:'offsets',
37753     defaultAutoCreate : {
37754         tag: "textarea",
37755         style:"width:500px;height:300px;",
37756         autocomplete: "off"
37757     },
37758
37759     // private
37760     initComponent : function(){
37761         this.addEvents({
37762             /**
37763              * @event initialize
37764              * Fires when the editor is fully initialized (including the iframe)
37765              * @param {HtmlEditor} this
37766              */
37767             initialize: true,
37768             /**
37769              * @event activate
37770              * Fires when the editor is first receives the focus. Any insertion must wait
37771              * until after this event.
37772              * @param {HtmlEditor} this
37773              */
37774             activate: true,
37775              /**
37776              * @event beforesync
37777              * Fires before the textarea is updated with content from the editor iframe. Return false
37778              * to cancel the sync.
37779              * @param {HtmlEditor} this
37780              * @param {String} html
37781              */
37782             beforesync: true,
37783              /**
37784              * @event beforepush
37785              * Fires before the iframe editor is updated with content from the textarea. Return false
37786              * to cancel the push.
37787              * @param {HtmlEditor} this
37788              * @param {String} html
37789              */
37790             beforepush: true,
37791              /**
37792              * @event sync
37793              * Fires when the textarea is updated with content from the editor iframe.
37794              * @param {HtmlEditor} this
37795              * @param {String} html
37796              */
37797             sync: true,
37798              /**
37799              * @event push
37800              * Fires when the iframe editor is updated with content from the textarea.
37801              * @param {HtmlEditor} this
37802              * @param {String} html
37803              */
37804             push: true,
37805              /**
37806              * @event editmodechange
37807              * Fires when the editor switches edit modes
37808              * @param {HtmlEditor} this
37809              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37810              */
37811             editmodechange: true,
37812             /**
37813              * @event editorevent
37814              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37815              * @param {HtmlEditor} this
37816              */
37817             editorevent: true
37818         })
37819     },
37820
37821     /**
37822      * Protected method that will not generally be called directly. It
37823      * is called when the editor creates its toolbar. Override this method if you need to
37824      * add custom toolbar buttons.
37825      * @param {HtmlEditor} editor
37826      */
37827     createToolbar : function(editor){
37828         if (!editor.toolbars || !editor.toolbars.length) {
37829             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37830         }
37831         
37832         for (var i =0 ; i < editor.toolbars.length;i++) {
37833             editor.toolbars[i].init(editor);
37834         }
37835          
37836         
37837     },
37838
37839     /**
37840      * Protected method that will not generally be called directly. It
37841      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37842      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37843      */
37844     getDocMarkup : function(){
37845         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37846     },
37847
37848     // private
37849     onRender : function(ct, position){
37850         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37851         this.el.dom.style.border = '0 none';
37852         this.el.dom.setAttribute('tabIndex', -1);
37853         this.el.addClass('x-hidden');
37854         if(Roo.isIE){ // fix IE 1px bogus margin
37855             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37856         }
37857         this.wrap = this.el.wrap({
37858             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37859         });
37860
37861         this.frameId = Roo.id();
37862         this.createToolbar(this);
37863         
37864         
37865         
37866         
37867       
37868         
37869         var iframe = this.wrap.createChild({
37870             tag: 'iframe',
37871             id: this.frameId,
37872             name: this.frameId,
37873             frameBorder : 'no',
37874             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37875         });
37876         
37877        // console.log(iframe);
37878         //this.wrap.dom.appendChild(iframe);
37879
37880         this.iframe = iframe.dom;
37881
37882          this.assignDocWin();
37883         
37884         this.doc.designMode = 'on';
37885        
37886         this.doc.open();
37887         this.doc.write(this.getDocMarkup());
37888         this.doc.close();
37889
37890         
37891         var task = { // must defer to wait for browser to be ready
37892             run : function(){
37893                 //console.log("run task?" + this.doc.readyState);
37894                 this.assignDocWin();
37895                 if(this.doc.body || this.doc.readyState == 'complete'){
37896                     try {
37897                         
37898                        
37899                         this.doc.designMode="on";
37900                     } catch (e) {
37901                         return;
37902                     }
37903                     Roo.TaskMgr.stop(task);
37904                     this.initEditor.defer(10, this);
37905                 }
37906             },
37907             interval : 10,
37908             duration:10000,
37909             scope: this
37910         };
37911         Roo.TaskMgr.start(task);
37912
37913         if(!this.width){
37914             this.setSize(this.el.getSize());
37915         }
37916     },
37917
37918     // private
37919     onResize : function(w, h){
37920         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
37921         if(this.el && this.iframe){
37922             if(typeof w == 'number'){
37923                 var aw = w - this.wrap.getFrameWidth('lr');
37924                 this.el.setWidth(this.adjustWidth('textarea', aw));
37925                 this.iframe.style.width = aw + 'px';
37926             }
37927             if(typeof h == 'number'){
37928                 var tbh = 0;
37929                 for (var i =0; i < this.toolbars.length;i++) {
37930                     // fixme - ask toolbars for heights?
37931                     tbh += this.toolbars[i].tb.el.getHeight();
37932                 }
37933                 
37934                 
37935                 
37936                 
37937                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
37938                 this.el.setHeight(this.adjustWidth('textarea', ah));
37939                 this.iframe.style.height = ah + 'px';
37940                 if(this.doc){
37941                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
37942                 }
37943             }
37944         }
37945     },
37946
37947     /**
37948      * Toggles the editor between standard and source edit mode.
37949      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
37950      */
37951     toggleSourceEdit : function(sourceEditMode){
37952         
37953         this.sourceEditMode = sourceEditMode === true;
37954         
37955         if(this.sourceEditMode){
37956           
37957             this.syncValue();
37958             this.iframe.className = 'x-hidden';
37959             this.el.removeClass('x-hidden');
37960             this.el.dom.removeAttribute('tabIndex');
37961             this.el.focus();
37962         }else{
37963              
37964             this.pushValue();
37965             this.iframe.className = '';
37966             this.el.addClass('x-hidden');
37967             this.el.dom.setAttribute('tabIndex', -1);
37968             this.deferFocus();
37969         }
37970         this.setSize(this.wrap.getSize());
37971         this.fireEvent('editmodechange', this, this.sourceEditMode);
37972     },
37973
37974     // private used internally
37975     createLink : function(){
37976         var url = prompt(this.createLinkText, this.defaultLinkValue);
37977         if(url && url != 'http:/'+'/'){
37978             this.relayCmd('createlink', url);
37979         }
37980     },
37981
37982     // private (for BoxComponent)
37983     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37984
37985     // private (for BoxComponent)
37986     getResizeEl : function(){
37987         return this.wrap;
37988     },
37989
37990     // private (for BoxComponent)
37991     getPositionEl : function(){
37992         return this.wrap;
37993     },
37994
37995     // private
37996     initEvents : function(){
37997         this.originalValue = this.getValue();
37998     },
37999
38000     /**
38001      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38002      * @method
38003      */
38004     markInvalid : Roo.emptyFn,
38005     /**
38006      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38007      * @method
38008      */
38009     clearInvalid : Roo.emptyFn,
38010
38011     setValue : function(v){
38012         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38013         this.pushValue();
38014     },
38015
38016     /**
38017      * Protected method that will not generally be called directly. If you need/want
38018      * custom HTML cleanup, this is the method you should override.
38019      * @param {String} html The HTML to be cleaned
38020      * return {String} The cleaned HTML
38021      */
38022     cleanHtml : function(html){
38023         html = String(html);
38024         if(html.length > 5){
38025             if(Roo.isSafari){ // strip safari nonsense
38026                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38027             }
38028         }
38029         if(html == '&nbsp;'){
38030             html = '';
38031         }
38032         return html;
38033     },
38034
38035     /**
38036      * Protected method that will not generally be called directly. Syncs the contents
38037      * of the editor iframe with the textarea.
38038      */
38039     syncValue : function(){
38040         if(this.initialized){
38041             var bd = (this.doc.body || this.doc.documentElement);
38042             var html = bd.innerHTML;
38043             if(Roo.isSafari){
38044                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38045                 var m = bs.match(/text-align:(.*?);/i);
38046                 if(m && m[1]){
38047                     html = '<div style="'+m[0]+'">' + html + '</div>';
38048                 }
38049             }
38050             html = this.cleanHtml(html);
38051             if(this.fireEvent('beforesync', this, html) !== false){
38052                 this.el.dom.value = html;
38053                 this.fireEvent('sync', this, html);
38054             }
38055         }
38056     },
38057
38058     /**
38059      * Protected method that will not generally be called directly. Pushes the value of the textarea
38060      * into the iframe editor.
38061      */
38062     pushValue : function(){
38063         if(this.initialized){
38064             var v = this.el.dom.value;
38065             if(v.length < 1){
38066                 v = '&#160;';
38067             }
38068             if(this.fireEvent('beforepush', this, v) !== false){
38069                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38070                 this.fireEvent('push', this, v);
38071             }
38072         }
38073     },
38074
38075     // private
38076     deferFocus : function(){
38077         this.focus.defer(10, this);
38078     },
38079
38080     // doc'ed in Field
38081     focus : function(){
38082         if(this.win && !this.sourceEditMode){
38083             this.win.focus();
38084         }else{
38085             this.el.focus();
38086         }
38087     },
38088     
38089     assignDocWin: function()
38090     {
38091         var iframe = this.iframe;
38092         
38093          if(Roo.isIE){
38094             this.doc = iframe.contentWindow.document;
38095             this.win = iframe.contentWindow;
38096         } else {
38097             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38098             this.win = Roo.get(this.frameId).dom.contentWindow;
38099         }
38100     },
38101     
38102     // private
38103     initEditor : function(){
38104         //console.log("INIT EDITOR");
38105         this.assignDocWin();
38106         
38107         
38108         
38109         this.doc.designMode="on";
38110         this.doc.open();
38111         this.doc.write(this.getDocMarkup());
38112         this.doc.close();
38113         
38114         var dbody = (this.doc.body || this.doc.documentElement);
38115         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38116         // this copies styles from the containing element into thsi one..
38117         // not sure why we need all of this..
38118         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38119         ss['background-attachment'] = 'fixed'; // w3c
38120         dbody.bgProperties = 'fixed'; // ie
38121         Roo.DomHelper.applyStyles(dbody, ss);
38122         Roo.EventManager.on(this.doc, {
38123             'mousedown': this.onEditorEvent,
38124             'dblclick': this.onEditorEvent,
38125             'click': this.onEditorEvent,
38126             'keyup': this.onEditorEvent,
38127             buffer:100,
38128             scope: this
38129         });
38130         if(Roo.isGecko){
38131             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38132         }
38133         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38134             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38135         }
38136         this.initialized = true;
38137
38138         this.fireEvent('initialize', this);
38139         this.pushValue();
38140     },
38141
38142     // private
38143     onDestroy : function(){
38144         
38145         
38146         
38147         if(this.rendered){
38148             
38149             for (var i =0; i < this.toolbars.length;i++) {
38150                 // fixme - ask toolbars for heights?
38151                 this.toolbars[i].onDestroy();
38152             }
38153             
38154             this.wrap.dom.innerHTML = '';
38155             this.wrap.remove();
38156         }
38157     },
38158
38159     // private
38160     onFirstFocus : function(){
38161         
38162         this.assignDocWin();
38163         
38164         
38165         this.activated = true;
38166         for (var i =0; i < this.toolbars.length;i++) {
38167             this.toolbars[i].onFirstFocus();
38168         }
38169        
38170         if(Roo.isGecko){ // prevent silly gecko errors
38171             this.win.focus();
38172             var s = this.win.getSelection();
38173             if(!s.focusNode || s.focusNode.nodeType != 3){
38174                 var r = s.getRangeAt(0);
38175                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38176                 r.collapse(true);
38177                 this.deferFocus();
38178             }
38179             try{
38180                 this.execCmd('useCSS', true);
38181                 this.execCmd('styleWithCSS', false);
38182             }catch(e){}
38183         }
38184         this.fireEvent('activate', this);
38185     },
38186
38187     // private
38188     adjustFont: function(btn){
38189         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38190         //if(Roo.isSafari){ // safari
38191         //    adjust *= 2;
38192        // }
38193         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38194         if(Roo.isSafari){ // safari
38195             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38196             v =  (v < 10) ? 10 : v;
38197             v =  (v > 48) ? 48 : v;
38198             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38199             
38200         }
38201         
38202         
38203         v = Math.max(1, v+adjust);
38204         
38205         this.execCmd('FontSize', v  );
38206     },
38207
38208     onEditorEvent : function(e){
38209         this.fireEvent('editorevent', this, e);
38210       //  this.updateToolbar();
38211         this.syncValue();
38212     },
38213
38214     insertTag : function(tg)
38215     {
38216         // could be a bit smarter... -> wrap the current selected tRoo..
38217         
38218         this.execCmd("formatblock",   tg);
38219         
38220     },
38221     
38222     insertText : function(txt)
38223     {
38224         
38225         
38226         range = this.createRange();
38227         range.deleteContents();
38228                //alert(Sender.getAttribute('label'));
38229                
38230         range.insertNode(this.doc.createTextNode(txt));
38231     } ,
38232     
38233     // private
38234     relayBtnCmd : function(btn){
38235         this.relayCmd(btn.cmd);
38236     },
38237
38238     /**
38239      * Executes a Midas editor command on the editor document and performs necessary focus and
38240      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38241      * @param {String} cmd The Midas command
38242      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38243      */
38244     relayCmd : function(cmd, value){
38245         this.win.focus();
38246         this.execCmd(cmd, value);
38247         this.fireEvent('editorevent', this);
38248         //this.updateToolbar();
38249         this.deferFocus();
38250     },
38251
38252     /**
38253      * Executes a Midas editor command directly on the editor document.
38254      * For visual commands, you should use {@link #relayCmd} instead.
38255      * <b>This should only be called after the editor is initialized.</b>
38256      * @param {String} cmd The Midas command
38257      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38258      */
38259     execCmd : function(cmd, value){
38260         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38261         this.syncValue();
38262     },
38263
38264     // private
38265     applyCommand : function(e){
38266         if(e.ctrlKey){
38267             var c = e.getCharCode(), cmd;
38268             if(c > 0){
38269                 c = String.fromCharCode(c);
38270                 switch(c){
38271                     case 'b':
38272                         cmd = 'bold';
38273                     break;
38274                     case 'i':
38275                         cmd = 'italic';
38276                     break;
38277                     case 'u':
38278                         cmd = 'underline';
38279                     break;
38280                 }
38281                 if(cmd){
38282                     this.win.focus();
38283                     this.execCmd(cmd);
38284                     this.deferFocus();
38285                     e.preventDefault();
38286                 }
38287             }
38288         }
38289     },
38290
38291     /**
38292      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38293      * to insert tRoo.
38294      * @param {String} text
38295      */
38296     insertAtCursor : function(text){
38297         if(!this.activated){
38298             return;
38299         }
38300         if(Roo.isIE){
38301             this.win.focus();
38302             var r = this.doc.selection.createRange();
38303             if(r){
38304                 r.collapse(true);
38305                 r.pasteHTML(text);
38306                 this.syncValue();
38307                 this.deferFocus();
38308             }
38309         }else if(Roo.isGecko || Roo.isOpera){
38310             this.win.focus();
38311             this.execCmd('InsertHTML', text);
38312             this.deferFocus();
38313         }else if(Roo.isSafari){
38314             this.execCmd('InsertText', text);
38315             this.deferFocus();
38316         }
38317     },
38318
38319     // private
38320     fixKeys : function(){ // load time branching for fastest keydown performance
38321         if(Roo.isIE){
38322             return function(e){
38323                 var k = e.getKey(), r;
38324                 if(k == e.TAB){
38325                     e.stopEvent();
38326                     r = this.doc.selection.createRange();
38327                     if(r){
38328                         r.collapse(true);
38329                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38330                         this.deferFocus();
38331                     }
38332                 }else if(k == e.ENTER){
38333                     r = this.doc.selection.createRange();
38334                     if(r){
38335                         var target = r.parentElement();
38336                         if(!target || target.tagName.toLowerCase() != 'li'){
38337                             e.stopEvent();
38338                             r.pasteHTML('<br />');
38339                             r.collapse(false);
38340                             r.select();
38341                         }
38342                     }
38343                 }
38344             };
38345         }else if(Roo.isOpera){
38346             return function(e){
38347                 var k = e.getKey();
38348                 if(k == e.TAB){
38349                     e.stopEvent();
38350                     this.win.focus();
38351                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38352                     this.deferFocus();
38353                 }
38354             };
38355         }else if(Roo.isSafari){
38356             return function(e){
38357                 var k = e.getKey();
38358                 if(k == e.TAB){
38359                     e.stopEvent();
38360                     this.execCmd('InsertText','\t');
38361                     this.deferFocus();
38362                 }
38363              };
38364         }
38365     }(),
38366     
38367     getAllAncestors: function()
38368     {
38369         var p = this.getSelectedNode();
38370         var a = [];
38371         if (!p) {
38372             a.push(p); // push blank onto stack..
38373             p = this.getParentElement();
38374         }
38375         
38376         
38377         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38378             a.push(p);
38379             p = p.parentNode;
38380         }
38381         a.push(this.doc.body);
38382         return a;
38383     },
38384     lastSel : false,
38385     lastSelNode : false,
38386     
38387     
38388     getSelection : function() 
38389     {
38390         this.assignDocWin();
38391         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38392     },
38393     
38394     getSelectedNode: function() 
38395     {
38396         // this may only work on Gecko!!!
38397         
38398         // should we cache this!!!!
38399         
38400         
38401         
38402          
38403         var range = this.createRange(this.getSelection());
38404         
38405         if (Roo.isIE) {
38406             var parent = range.parentElement();
38407             while (true) {
38408                 var testRange = range.duplicate();
38409                 testRange.moveToElementText(parent);
38410                 if (testRange.inRange(range)) {
38411                     break;
38412                 }
38413                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38414                     break;
38415                 }
38416                 parent = parent.parentElement;
38417             }
38418             return parent;
38419         }
38420         
38421         
38422         var ar = range.endContainer.childNodes;
38423         if (!ar.length) {
38424             ar = range.commonAncestorContainer.childNodes;
38425             //alert(ar.length);
38426         }
38427         var nodes = [];
38428         var other_nodes = [];
38429         var has_other_nodes = false;
38430         for (var i=0;i<ar.length;i++) {
38431             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38432                 continue;
38433             }
38434             // fullly contained node.
38435             
38436             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38437                 nodes.push(ar[i]);
38438                 continue;
38439             }
38440             
38441             // probably selected..
38442             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38443                 other_nodes.push(ar[i]);
38444                 continue;
38445             }
38446             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38447                 continue;
38448             }
38449             
38450             
38451             has_other_nodes = true;
38452         }
38453         if (!nodes.length && other_nodes.length) {
38454             nodes= other_nodes;
38455         }
38456         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38457             return false;
38458         }
38459         
38460         return nodes[0];
38461     },
38462     createRange: function(sel)
38463     {
38464         // this has strange effects when using with 
38465         // top toolbar - not sure if it's a great idea.
38466         //this.editor.contentWindow.focus();
38467         if (typeof sel != "undefined") {
38468             try {
38469                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38470             } catch(e) {
38471                 return this.doc.createRange();
38472             }
38473         } else {
38474             return this.doc.createRange();
38475         }
38476     },
38477     getParentElement: function()
38478     {
38479         
38480         this.assignDocWin();
38481         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38482         
38483         var range = this.createRange(sel);
38484          
38485         try {
38486             var p = range.commonAncestorContainer;
38487             while (p.nodeType == 3) { // text node
38488                 p = p.parentNode;
38489             }
38490             return p;
38491         } catch (e) {
38492             return null;
38493         }
38494     
38495     },
38496     
38497     
38498     
38499     // BC Hacks - cause I cant work out what i was trying to do..
38500     rangeIntersectsNode : function(range, node)
38501     {
38502         var nodeRange = node.ownerDocument.createRange();
38503         try {
38504             nodeRange.selectNode(node);
38505         }
38506         catch (e) {
38507             nodeRange.selectNodeContents(node);
38508         }
38509
38510         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38511                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38512     },
38513     rangeCompareNode : function(range, node) {
38514         var nodeRange = node.ownerDocument.createRange();
38515         try {
38516             nodeRange.selectNode(node);
38517         } catch (e) {
38518             nodeRange.selectNodeContents(node);
38519         }
38520         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38521         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38522
38523         if (nodeIsBefore && !nodeIsAfter)
38524             return 0;
38525         if (!nodeIsBefore && nodeIsAfter)
38526             return 1;
38527         if (nodeIsBefore && nodeIsAfter)
38528             return 2;
38529
38530         return 3;
38531     }
38532
38533     
38534     
38535     // hide stuff that is not compatible
38536     /**
38537      * @event blur
38538      * @hide
38539      */
38540     /**
38541      * @event change
38542      * @hide
38543      */
38544     /**
38545      * @event focus
38546      * @hide
38547      */
38548     /**
38549      * @event specialkey
38550      * @hide
38551      */
38552     /**
38553      * @cfg {String} fieldClass @hide
38554      */
38555     /**
38556      * @cfg {String} focusClass @hide
38557      */
38558     /**
38559      * @cfg {String} autoCreate @hide
38560      */
38561     /**
38562      * @cfg {String} inputType @hide
38563      */
38564     /**
38565      * @cfg {String} invalidClass @hide
38566      */
38567     /**
38568      * @cfg {String} invalidText @hide
38569      */
38570     /**
38571      * @cfg {String} msgFx @hide
38572      */
38573     /**
38574      * @cfg {String} validateOnBlur @hide
38575      */
38576 });// <script type="text/javascript">
38577 /*
38578  * Based on
38579  * Ext JS Library 1.1.1
38580  * Copyright(c) 2006-2007, Ext JS, LLC.
38581  *  
38582  
38583  */
38584
38585 /**
38586  * @class Roo.form.HtmlEditorToolbar1
38587  * Basic Toolbar
38588  * 
38589  * Usage:
38590  *
38591  new Roo.form.HtmlEditor({
38592     ....
38593     toolbars : [
38594         new Roo.form.HtmlEditorToolbar1({
38595             disable : { fonts: 1 , format: 1, ..., ... , ...],
38596             btns : [ .... ]
38597         })
38598     }
38599      
38600  * 
38601  * @cfg {Object} disable List of elements to disable..
38602  * @cfg {Array} btns List of additional buttons.
38603  * 
38604  * 
38605  * NEEDS Extra CSS? 
38606  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38607  */
38608  
38609 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38610 {
38611     
38612     Roo.apply(this, config);
38613     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38614     // dont call parent... till later.
38615 }
38616
38617 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38618     
38619     tb: false,
38620     
38621     rendered: false,
38622     
38623     editor : false,
38624     /**
38625      * @cfg {Object} disable  List of toolbar elements to disable
38626          
38627      */
38628     disable : false,
38629       /**
38630      * @cfg {Array} fontFamilies An array of available font families
38631      */
38632     fontFamilies : [
38633         'Arial',
38634         'Courier New',
38635         'Tahoma',
38636         'Times New Roman',
38637         'Verdana'
38638     ],
38639     
38640     specialChars : [
38641            "&#169;",
38642           "&#174;",     
38643           "&#8482;",    
38644           "&#163;" ,    
38645          // "&#8212;",    
38646           "&#8230;",    
38647           "&#247;" ,    
38648         //  "&#225;" ,     ?? a acute?
38649            "&#8364;"    , //Euro
38650        //   "&#8220;"    ,
38651         //  "&#8221;"    ,
38652         //  "&#8226;"    ,
38653           "&#176;"  //   , // degrees
38654
38655          // "&#233;"     , // e ecute
38656          // "&#250;"     , // u ecute?
38657     ],
38658     inputElements : [ 
38659             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38660             "input:submit", "input:button", "select", "textarea", "label" ],
38661     formats : [
38662         ["p"] ,  
38663         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38664         ["pre"],[ "code"], 
38665         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38666     ],
38667      /**
38668      * @cfg {String} defaultFont default font to use.
38669      */
38670     defaultFont: 'tahoma',
38671    
38672     fontSelect : false,
38673     
38674     
38675     formatCombo : false,
38676     
38677     init : function(editor)
38678     {
38679         this.editor = editor;
38680         
38681         
38682         var fid = editor.frameId;
38683         var etb = this;
38684         function btn(id, toggle, handler){
38685             var xid = fid + '-'+ id ;
38686             return {
38687                 id : xid,
38688                 cmd : id,
38689                 cls : 'x-btn-icon x-edit-'+id,
38690                 enableToggle:toggle !== false,
38691                 scope: editor, // was editor...
38692                 handler:handler||editor.relayBtnCmd,
38693                 clickEvent:'mousedown',
38694                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38695                 tabIndex:-1
38696             };
38697         }
38698         
38699         
38700         
38701         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38702         this.tb = tb;
38703          // stop form submits
38704         tb.el.on('click', function(e){
38705             e.preventDefault(); // what does this do?
38706         });
38707
38708         if(!this.disable.font && !Roo.isSafari){
38709             /* why no safari for fonts
38710             editor.fontSelect = tb.el.createChild({
38711                 tag:'select',
38712                 tabIndex: -1,
38713                 cls:'x-font-select',
38714                 html: editor.createFontOptions()
38715             });
38716             editor.fontSelect.on('change', function(){
38717                 var font = editor.fontSelect.dom.value;
38718                 editor.relayCmd('fontname', font);
38719                 editor.deferFocus();
38720             }, editor);
38721             tb.add(
38722                 editor.fontSelect.dom,
38723                 '-'
38724             );
38725             */
38726         };
38727         if(!this.disable.formats){
38728             this.formatCombo = new Roo.form.ComboBox({
38729                 store: new Roo.data.SimpleStore({
38730                     id : 'tag',
38731                     fields: ['tag'],
38732                     data : this.formats // from states.js
38733                 }),
38734                 blockFocus : true,
38735                 //autoCreate : {tag: "div",  size: "20"},
38736                 displayField:'tag',
38737                 typeAhead: false,
38738                 mode: 'local',
38739                 editable : false,
38740                 triggerAction: 'all',
38741                 emptyText:'Add tag',
38742                 selectOnFocus:true,
38743                 width:135,
38744                 listeners : {
38745                     'select': function(c, r, i) {
38746                         editor.insertTag(r.get('tag'));
38747                         editor.focus();
38748                     }
38749                 }
38750
38751             });
38752             tb.addField(this.formatCombo);
38753             
38754         }
38755         
38756         if(!this.disable.format){
38757             tb.add(
38758                 btn('bold'),
38759                 btn('italic'),
38760                 btn('underline')
38761             );
38762         };
38763         if(!this.disable.fontSize){
38764             tb.add(
38765                 '-',
38766                 
38767                 
38768                 btn('increasefontsize', false, editor.adjustFont),
38769                 btn('decreasefontsize', false, editor.adjustFont)
38770             );
38771         };
38772         
38773         
38774         if(this.disable.colors){
38775             tb.add(
38776                 '-', {
38777                     id:editor.frameId +'-forecolor',
38778                     cls:'x-btn-icon x-edit-forecolor',
38779                     clickEvent:'mousedown',
38780                     tooltip: this.buttonTips['forecolor'] || undefined,
38781                     tabIndex:-1,
38782                     menu : new Roo.menu.ColorMenu({
38783                         allowReselect: true,
38784                         focus: Roo.emptyFn,
38785                         value:'000000',
38786                         plain:true,
38787                         selectHandler: function(cp, color){
38788                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38789                             editor.deferFocus();
38790                         },
38791                         scope: editor,
38792                         clickEvent:'mousedown'
38793                     })
38794                 }, {
38795                     id:editor.frameId +'backcolor',
38796                     cls:'x-btn-icon x-edit-backcolor',
38797                     clickEvent:'mousedown',
38798                     tooltip: this.buttonTips['backcolor'] || undefined,
38799                     tabIndex:-1,
38800                     menu : new Roo.menu.ColorMenu({
38801                         focus: Roo.emptyFn,
38802                         value:'FFFFFF',
38803                         plain:true,
38804                         allowReselect: true,
38805                         selectHandler: function(cp, color){
38806                             if(Roo.isGecko){
38807                                 editor.execCmd('useCSS', false);
38808                                 editor.execCmd('hilitecolor', color);
38809                                 editor.execCmd('useCSS', true);
38810                                 editor.deferFocus();
38811                             }else{
38812                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38813                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38814                                 editor.deferFocus();
38815                             }
38816                         },
38817                         scope:editor,
38818                         clickEvent:'mousedown'
38819                     })
38820                 }
38821             );
38822         };
38823         // now add all the items...
38824         
38825
38826         if(!this.disable.alignments){
38827             tb.add(
38828                 '-',
38829                 btn('justifyleft'),
38830                 btn('justifycenter'),
38831                 btn('justifyright')
38832             );
38833         };
38834
38835         //if(!Roo.isSafari){
38836             if(!this.disable.links){
38837                 tb.add(
38838                     '-',
38839                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38840                 );
38841             };
38842
38843             if(!this.disable.lists){
38844                 tb.add(
38845                     '-',
38846                     btn('insertorderedlist'),
38847                     btn('insertunorderedlist')
38848                 );
38849             }
38850             if(!this.disable.sourceEdit){
38851                 tb.add(
38852                     '-',
38853                     btn('sourceedit', true, function(btn){
38854                         this.toggleSourceEdit(btn.pressed);
38855                     })
38856                 );
38857             }
38858         //}
38859         
38860         var smenu = { };
38861         // special menu.. - needs to be tidied up..
38862         if (!this.disable.special) {
38863             smenu = {
38864                 text: "&#169;",
38865                 cls: 'x-edit-none',
38866                 menu : {
38867                     items : []
38868                    }
38869             };
38870             for (var i =0; i < this.specialChars.length; i++) {
38871                 smenu.menu.items.push({
38872                     
38873                     text: this.specialChars[i],
38874                     handler: function(a,b) {
38875                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38876                     },
38877                     tabIndex:-1
38878                 });
38879             }
38880             
38881             
38882             tb.add(smenu);
38883             
38884             
38885         }
38886         if (this.btns) {
38887             for(var i =0; i< this.btns.length;i++) {
38888                 var b = this.btns[i];
38889                 b.cls =  'x-edit-none';
38890                 b.scope = editor;
38891                 tb.add(b);
38892             }
38893         
38894         }
38895         
38896         
38897         
38898         // disable everything...
38899         
38900         this.tb.items.each(function(item){
38901            if(item.id != editor.frameId+ '-sourceedit'){
38902                 item.disable();
38903             }
38904         });
38905         this.rendered = true;
38906         
38907         // the all the btns;
38908         editor.on('editorevent', this.updateToolbar, this);
38909         // other toolbars need to implement this..
38910         //editor.on('editmodechange', this.updateToolbar, this);
38911     },
38912     
38913     
38914     
38915     /**
38916      * Protected method that will not generally be called directly. It triggers
38917      * a toolbar update by reading the markup state of the current selection in the editor.
38918      */
38919     updateToolbar: function(){
38920
38921         if(!this.editor.activated){
38922             this.editor.onFirstFocus();
38923             return;
38924         }
38925
38926         var btns = this.tb.items.map, 
38927             doc = this.editor.doc,
38928             frameId = this.editor.frameId;
38929
38930         if(!this.disable.font && !Roo.isSafari){
38931             /*
38932             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
38933             if(name != this.fontSelect.dom.value){
38934                 this.fontSelect.dom.value = name;
38935             }
38936             */
38937         }
38938         if(!this.disable.format){
38939             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
38940             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
38941             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
38942         }
38943         if(!this.disable.alignments){
38944             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
38945             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
38946             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
38947         }
38948         if(!Roo.isSafari && !this.disable.lists){
38949             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
38950             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
38951         }
38952         
38953         var ans = this.editor.getAllAncestors();
38954         if (this.formatCombo) {
38955             
38956             
38957             var store = this.formatCombo.store;
38958             this.formatCombo.setValue("");
38959             for (var i =0; i < ans.length;i++) {
38960                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
38961                     // select it..
38962                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
38963                     break;
38964                 }
38965             }
38966         }
38967         
38968         
38969         
38970         // hides menus... - so this cant be on a menu...
38971         Roo.menu.MenuMgr.hideAll();
38972
38973         //this.editorsyncValue();
38974     },
38975    
38976     
38977     createFontOptions : function(){
38978         var buf = [], fs = this.fontFamilies, ff, lc;
38979         for(var i = 0, len = fs.length; i< len; i++){
38980             ff = fs[i];
38981             lc = ff.toLowerCase();
38982             buf.push(
38983                 '<option value="',lc,'" style="font-family:',ff,';"',
38984                     (this.defaultFont == lc ? ' selected="true">' : '>'),
38985                     ff,
38986                 '</option>'
38987             );
38988         }
38989         return buf.join('');
38990     },
38991     
38992     toggleSourceEdit : function(sourceEditMode){
38993         if(sourceEditMode === undefined){
38994             sourceEditMode = !this.sourceEditMode;
38995         }
38996         this.sourceEditMode = sourceEditMode === true;
38997         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
38998         // just toggle the button?
38999         if(btn.pressed !== this.editor.sourceEditMode){
39000             btn.toggle(this.editor.sourceEditMode);
39001             return;
39002         }
39003         
39004         if(this.sourceEditMode){
39005             this.tb.items.each(function(item){
39006                 if(item.cmd != 'sourceedit'){
39007                     item.disable();
39008                 }
39009             });
39010           
39011         }else{
39012             if(this.initialized){
39013                 this.tb.items.each(function(item){
39014                     item.enable();
39015                 });
39016             }
39017             
39018         }
39019         // tell the editor that it's been pressed..
39020         this.editor.toggleSourceEdit(sourceEditMode);
39021        
39022     },
39023      /**
39024      * Object collection of toolbar tooltips for the buttons in the editor. The key
39025      * is the command id associated with that button and the value is a valid QuickTips object.
39026      * For example:
39027 <pre><code>
39028 {
39029     bold : {
39030         title: 'Bold (Ctrl+B)',
39031         text: 'Make the selected text bold.',
39032         cls: 'x-html-editor-tip'
39033     },
39034     italic : {
39035         title: 'Italic (Ctrl+I)',
39036         text: 'Make the selected text italic.',
39037         cls: 'x-html-editor-tip'
39038     },
39039     ...
39040 </code></pre>
39041     * @type Object
39042      */
39043     buttonTips : {
39044         bold : {
39045             title: 'Bold (Ctrl+B)',
39046             text: 'Make the selected text bold.',
39047             cls: 'x-html-editor-tip'
39048         },
39049         italic : {
39050             title: 'Italic (Ctrl+I)',
39051             text: 'Make the selected text italic.',
39052             cls: 'x-html-editor-tip'
39053         },
39054         underline : {
39055             title: 'Underline (Ctrl+U)',
39056             text: 'Underline the selected text.',
39057             cls: 'x-html-editor-tip'
39058         },
39059         increasefontsize : {
39060             title: 'Grow Text',
39061             text: 'Increase the font size.',
39062             cls: 'x-html-editor-tip'
39063         },
39064         decreasefontsize : {
39065             title: 'Shrink Text',
39066             text: 'Decrease the font size.',
39067             cls: 'x-html-editor-tip'
39068         },
39069         backcolor : {
39070             title: 'Text Highlight Color',
39071             text: 'Change the background color of the selected text.',
39072             cls: 'x-html-editor-tip'
39073         },
39074         forecolor : {
39075             title: 'Font Color',
39076             text: 'Change the color of the selected text.',
39077             cls: 'x-html-editor-tip'
39078         },
39079         justifyleft : {
39080             title: 'Align Text Left',
39081             text: 'Align text to the left.',
39082             cls: 'x-html-editor-tip'
39083         },
39084         justifycenter : {
39085             title: 'Center Text',
39086             text: 'Center text in the editor.',
39087             cls: 'x-html-editor-tip'
39088         },
39089         justifyright : {
39090             title: 'Align Text Right',
39091             text: 'Align text to the right.',
39092             cls: 'x-html-editor-tip'
39093         },
39094         insertunorderedlist : {
39095             title: 'Bullet List',
39096             text: 'Start a bulleted list.',
39097             cls: 'x-html-editor-tip'
39098         },
39099         insertorderedlist : {
39100             title: 'Numbered List',
39101             text: 'Start a numbered list.',
39102             cls: 'x-html-editor-tip'
39103         },
39104         createlink : {
39105             title: 'Hyperlink',
39106             text: 'Make the selected text a hyperlink.',
39107             cls: 'x-html-editor-tip'
39108         },
39109         sourceedit : {
39110             title: 'Source Edit',
39111             text: 'Switch to source editing mode.',
39112             cls: 'x-html-editor-tip'
39113         }
39114     },
39115     // private
39116     onDestroy : function(){
39117         if(this.rendered){
39118             
39119             this.tb.items.each(function(item){
39120                 if(item.menu){
39121                     item.menu.removeAll();
39122                     if(item.menu.el){
39123                         item.menu.el.destroy();
39124                     }
39125                 }
39126                 item.destroy();
39127             });
39128              
39129         }
39130     },
39131     onFirstFocus: function() {
39132         this.tb.items.each(function(item){
39133            item.enable();
39134         });
39135     }
39136 });
39137
39138
39139
39140
39141 // <script type="text/javascript">
39142 /*
39143  * Based on
39144  * Ext JS Library 1.1.1
39145  * Copyright(c) 2006-2007, Ext JS, LLC.
39146  *  
39147  
39148  */
39149
39150  
39151 /**
39152  * @class Roo.form.HtmlEditor.ToolbarContext
39153  * Context Toolbar
39154  * 
39155  * Usage:
39156  *
39157  new Roo.form.HtmlEditor({
39158     ....
39159     toolbars : [
39160         new Roo.form.HtmlEditor.ToolbarStandard(),
39161         new Roo.form.HtmlEditor.ToolbarContext()
39162         })
39163     }
39164      
39165  * 
39166  * @config : {Object} disable List of elements to disable.. (not done yet.)
39167  * 
39168  * 
39169  */
39170
39171 Roo.form.HtmlEditor.ToolbarContext = function(config)
39172 {
39173     
39174     Roo.apply(this, config);
39175     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39176     // dont call parent... till later.
39177 }
39178 Roo.form.HtmlEditor.ToolbarContext.types = {
39179     'IMG' : {
39180         width : {
39181             title: "Width",
39182             width: 40
39183         },
39184         height:  {
39185             title: "Height",
39186             width: 40
39187         },
39188         align: {
39189             title: "Align",
39190             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39191             width : 80
39192             
39193         },
39194         border: {
39195             title: "Border",
39196             width: 40
39197         },
39198         alt: {
39199             title: "Alt",
39200             width: 120
39201         },
39202         src : {
39203             title: "Src",
39204             width: 220
39205         }
39206         
39207     },
39208     'A' : {
39209         name : {
39210             title: "Name",
39211             width: 50
39212         },
39213         href:  {
39214             title: "Href",
39215             width: 220
39216         } // border?
39217         
39218     },
39219     'TABLE' : {
39220         rows : {
39221             title: "Rows",
39222             width: 20
39223         },
39224         cols : {
39225             title: "Cols",
39226             width: 20
39227         },
39228         width : {
39229             title: "Width",
39230             width: 40
39231         },
39232         height : {
39233             title: "Height",
39234             width: 40
39235         },
39236         border : {
39237             title: "Border",
39238             width: 20
39239         }
39240     },
39241     'TD' : {
39242         width : {
39243             title: "Width",
39244             width: 40
39245         },
39246         height : {
39247             title: "Height",
39248             width: 40
39249         },   
39250         align: {
39251             title: "Align",
39252             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39253             width: 40
39254         },
39255         valign: {
39256             title: "Valign",
39257             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39258             width: 40
39259         },
39260         colspan: {
39261             title: "Colspan",
39262             width: 20
39263             
39264         }
39265     },
39266     'INPUT' : {
39267         name : {
39268             title: "name",
39269             width: 120
39270         },
39271         value : {
39272             title: "Value",
39273             width: 120
39274         },
39275         width : {
39276             title: "Width",
39277             width: 40
39278         }
39279     },
39280     'LABEL' : {
39281         'for' : {
39282             title: "For",
39283             width: 120
39284         }
39285     },
39286     'TEXTAREA' : {
39287           name : {
39288             title: "name",
39289             width: 120
39290         },
39291         rows : {
39292             title: "Rows",
39293             width: 20
39294         },
39295         cols : {
39296             title: "Cols",
39297             width: 20
39298         }
39299     },
39300     'SELECT' : {
39301         name : {
39302             title: "name",
39303             width: 120
39304         },
39305         selectoptions : {
39306             title: "Options",
39307             width: 200
39308         }
39309     },
39310     'BODY' : {
39311         title : {
39312             title: "title",
39313             width: 120,
39314             disabled : true
39315         }
39316     }
39317 };
39318
39319
39320
39321 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39322     
39323     tb: false,
39324     
39325     rendered: false,
39326     
39327     editor : false,
39328     /**
39329      * @cfg {Object} disable  List of toolbar elements to disable
39330          
39331      */
39332     disable : false,
39333     
39334     
39335     
39336     toolbars : false,
39337     
39338     init : function(editor)
39339     {
39340         this.editor = editor;
39341         
39342         
39343         var fid = editor.frameId;
39344         var etb = this;
39345         function btn(id, toggle, handler){
39346             var xid = fid + '-'+ id ;
39347             return {
39348                 id : xid,
39349                 cmd : id,
39350                 cls : 'x-btn-icon x-edit-'+id,
39351                 enableToggle:toggle !== false,
39352                 scope: editor, // was editor...
39353                 handler:handler||editor.relayBtnCmd,
39354                 clickEvent:'mousedown',
39355                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39356                 tabIndex:-1
39357             };
39358         }
39359         // create a new element.
39360         var wdiv = editor.wrap.createChild({
39361                 tag: 'div'
39362             }, editor.wrap.dom.firstChild.nextSibling, true);
39363         
39364         // can we do this more than once??
39365         
39366          // stop form submits
39367       
39368  
39369         // disable everything...
39370         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39371         this.toolbars = {};
39372            
39373         for (var i in  ty) {
39374             this.toolbars[i] = this.buildToolbar(ty[i],i);
39375         }
39376         this.tb = this.toolbars.BODY;
39377         this.tb.el.show();
39378         
39379          
39380         this.rendered = true;
39381         
39382         // the all the btns;
39383         editor.on('editorevent', this.updateToolbar, this);
39384         // other toolbars need to implement this..
39385         //editor.on('editmodechange', this.updateToolbar, this);
39386     },
39387     
39388     
39389     
39390     /**
39391      * Protected method that will not generally be called directly. It triggers
39392      * a toolbar update by reading the markup state of the current selection in the editor.
39393      */
39394     updateToolbar: function(){
39395
39396         if(!this.editor.activated){
39397             this.editor.onFirstFocus();
39398             return;
39399         }
39400
39401         
39402         var ans = this.editor.getAllAncestors();
39403         
39404         // pick
39405         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39406         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39407         sel = sel ? sel : this.editor.doc.body;
39408         sel = sel.tagName.length ? sel : this.editor.doc.body;
39409         var tn = sel.tagName.toUpperCase();
39410         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39411         tn = sel.tagName.toUpperCase();
39412         if (this.tb.name  == tn) {
39413             return; // no change
39414         }
39415         this.tb.el.hide();
39416         ///console.log("show: " + tn);
39417         this.tb =  this.toolbars[tn];
39418         this.tb.el.show();
39419         this.tb.fields.each(function(e) {
39420             e.setValue(sel.getAttribute(e.name));
39421         });
39422         this.tb.selectedNode = sel;
39423         
39424         
39425         Roo.menu.MenuMgr.hideAll();
39426
39427         //this.editorsyncValue();
39428     },
39429    
39430        
39431     // private
39432     onDestroy : function(){
39433         if(this.rendered){
39434             
39435             this.tb.items.each(function(item){
39436                 if(item.menu){
39437                     item.menu.removeAll();
39438                     if(item.menu.el){
39439                         item.menu.el.destroy();
39440                     }
39441                 }
39442                 item.destroy();
39443             });
39444              
39445         }
39446     },
39447     onFirstFocus: function() {
39448         // need to do this for all the toolbars..
39449         this.tb.items.each(function(item){
39450            item.enable();
39451         });
39452     },
39453     buildToolbar: function(tlist, nm)
39454     {
39455         var editor = this.editor;
39456          // create a new element.
39457         var wdiv = editor.wrap.createChild({
39458                 tag: 'div'
39459             }, editor.wrap.dom.firstChild.nextSibling, true);
39460         
39461        
39462         var tb = new Roo.Toolbar(wdiv);
39463         tb.add(nm+ ":&nbsp;");
39464         for (var i in tlist) {
39465             var item = tlist[i];
39466             tb.add(item.title + ":&nbsp;");
39467             if (item.opts) {
39468                 // fixme
39469                 
39470               
39471                 tb.addField( new Roo.form.ComboBox({
39472                     store: new Roo.data.SimpleStore({
39473                         id : 'val',
39474                         fields: ['val'],
39475                         data : item.opts // from states.js
39476                     }),
39477                     name : i,
39478                     displayField:'val',
39479                     typeAhead: false,
39480                     mode: 'local',
39481                     editable : false,
39482                     triggerAction: 'all',
39483                     emptyText:'Select',
39484                     selectOnFocus:true,
39485                     width: item.width ? item.width  : 130,
39486                     listeners : {
39487                         'select': function(c, r, i) {
39488                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39489                         }
39490                     }
39491
39492                 }));
39493                 continue;
39494                     
39495                 
39496                 
39497                 
39498                 
39499                 tb.addField( new Roo.form.TextField({
39500                     name: i,
39501                     width: 100,
39502                     //allowBlank:false,
39503                     value: ''
39504                 }));
39505                 continue;
39506             }
39507             tb.addField( new Roo.form.TextField({
39508                 name: i,
39509                 width: item.width,
39510                 //allowBlank:true,
39511                 value: '',
39512                 listeners: {
39513                     'change' : function(f, nv, ov) {
39514                         tb.selectedNode.setAttribute(f.name, nv);
39515                     }
39516                 }
39517             }));
39518              
39519         }
39520         tb.el.on('click', function(e){
39521             e.preventDefault(); // what does this do?
39522         });
39523         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39524         tb.el.hide();
39525         tb.name = nm;
39526         // dont need to disable them... as they will get hidden
39527         return tb;
39528          
39529         
39530     }
39531     
39532     
39533     
39534     
39535 });
39536
39537
39538
39539
39540
39541 /*
39542  * Based on:
39543  * Ext JS Library 1.1.1
39544  * Copyright(c) 2006-2007, Ext JS, LLC.
39545  *
39546  * Originally Released Under LGPL - original licence link has changed is not relivant.
39547  *
39548  * Fork - LGPL
39549  * <script type="text/javascript">
39550  */
39551  
39552 /**
39553  * @class Roo.form.BasicForm
39554  * @extends Roo.util.Observable
39555  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39556  * @constructor
39557  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39558  * @param {Object} config Configuration options
39559  */
39560 Roo.form.BasicForm = function(el, config){
39561     Roo.apply(this, config);
39562     /*
39563      * The Roo.form.Field items in this form.
39564      * @type MixedCollection
39565      */
39566     this.items = new Roo.util.MixedCollection(false, function(o){
39567         return o.id || (o.id = Roo.id());
39568     });
39569     this.addEvents({
39570         /**
39571          * @event beforeaction
39572          * Fires before any action is performed. Return false to cancel the action.
39573          * @param {Form} this
39574          * @param {Action} action The action to be performed
39575          */
39576         beforeaction: true,
39577         /**
39578          * @event actionfailed
39579          * Fires when an action fails.
39580          * @param {Form} this
39581          * @param {Action} action The action that failed
39582          */
39583         actionfailed : true,
39584         /**
39585          * @event actioncomplete
39586          * Fires when an action is completed.
39587          * @param {Form} this
39588          * @param {Action} action The action that completed
39589          */
39590         actioncomplete : true
39591     });
39592     if(el){
39593         this.initEl(el);
39594     }
39595     Roo.form.BasicForm.superclass.constructor.call(this);
39596 };
39597
39598 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39599     /**
39600      * @cfg {String} method
39601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39602      */
39603     /**
39604      * @cfg {DataReader} reader
39605      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39606      * This is optional as there is built-in support for processing JSON.
39607      */
39608     /**
39609      * @cfg {DataReader} errorReader
39610      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39611      * This is completely optional as there is built-in support for processing JSON.
39612      */
39613     /**
39614      * @cfg {String} url
39615      * The URL to use for form actions if one isn't supplied in the action options.
39616      */
39617     /**
39618      * @cfg {Boolean} fileUpload
39619      * Set to true if this form is a file upload.
39620      */
39621     /**
39622      * @cfg {Object} baseParams
39623      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39624      */
39625     /**
39626      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39627      */
39628     timeout: 30,
39629
39630     // private
39631     activeAction : null,
39632
39633     /**
39634      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39635      * or setValues() data instead of when the form was first created.
39636      */
39637     trackResetOnLoad : false,
39638
39639     /**
39640      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39641      * element by passing it or its id or mask the form itself by passing in true.
39642      * @type Mixed
39643      */
39644     waitMsgTarget : undefined,
39645
39646     // private
39647     initEl : function(el){
39648         this.el = Roo.get(el);
39649         this.id = this.el.id || Roo.id();
39650         this.el.on('submit', this.onSubmit, this);
39651         this.el.addClass('x-form');
39652     },
39653
39654     // private
39655     onSubmit : function(e){
39656         e.stopEvent();
39657     },
39658
39659     /**
39660      * Returns true if client-side validation on the form is successful.
39661      * @return Boolean
39662      */
39663     isValid : function(){
39664         var valid = true;
39665         this.items.each(function(f){
39666            if(!f.validate()){
39667                valid = false;
39668            }
39669         });
39670         return valid;
39671     },
39672
39673     /**
39674      * Returns true if any fields in this form have changed since their original load.
39675      * @return Boolean
39676      */
39677     isDirty : function(){
39678         var dirty = false;
39679         this.items.each(function(f){
39680            if(f.isDirty()){
39681                dirty = true;
39682                return false;
39683            }
39684         });
39685         return dirty;
39686     },
39687
39688     /**
39689      * Performs a predefined action (submit or load) or custom actions you define on this form.
39690      * @param {String} actionName The name of the action type
39691      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39692      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39693      * accept other config options):
39694      * <pre>
39695 Property          Type             Description
39696 ----------------  ---------------  ----------------------------------------------------------------------------------
39697 url               String           The url for the action (defaults to the form's url)
39698 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39699 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39700 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39701                                    validate the form on the client (defaults to false)
39702      * </pre>
39703      * @return {BasicForm} this
39704      */
39705     doAction : function(action, options){
39706         if(typeof action == 'string'){
39707             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39708         }
39709         if(this.fireEvent('beforeaction', this, action) !== false){
39710             this.beforeAction(action);
39711             action.run.defer(100, action);
39712         }
39713         return this;
39714     },
39715
39716     /**
39717      * Shortcut to do a submit action.
39718      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39719      * @return {BasicForm} this
39720      */
39721     submit : function(options){
39722         this.doAction('submit', options);
39723         return this;
39724     },
39725
39726     /**
39727      * Shortcut to do a load action.
39728      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39729      * @return {BasicForm} this
39730      */
39731     load : function(options){
39732         this.doAction('load', options);
39733         return this;
39734     },
39735
39736     /**
39737      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39738      * @param {Record} record The record to edit
39739      * @return {BasicForm} this
39740      */
39741     updateRecord : function(record){
39742         record.beginEdit();
39743         var fs = record.fields;
39744         fs.each(function(f){
39745             var field = this.findField(f.name);
39746             if(field){
39747                 record.set(f.name, field.getValue());
39748             }
39749         }, this);
39750         record.endEdit();
39751         return this;
39752     },
39753
39754     /**
39755      * Loads an Roo.data.Record into this form.
39756      * @param {Record} record The record to load
39757      * @return {BasicForm} this
39758      */
39759     loadRecord : function(record){
39760         this.setValues(record.data);
39761         return this;
39762     },
39763
39764     // private
39765     beforeAction : function(action){
39766         var o = action.options;
39767         if(o.waitMsg){
39768             if(this.waitMsgTarget === true){
39769                 this.el.mask(o.waitMsg, 'x-mask-loading');
39770             }else if(this.waitMsgTarget){
39771                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39772                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39773             }else{
39774                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39775             }
39776         }
39777     },
39778
39779     // private
39780     afterAction : function(action, success){
39781         this.activeAction = null;
39782         var o = action.options;
39783         if(o.waitMsg){
39784             if(this.waitMsgTarget === true){
39785                 this.el.unmask();
39786             }else if(this.waitMsgTarget){
39787                 this.waitMsgTarget.unmask();
39788             }else{
39789                 Roo.MessageBox.updateProgress(1);
39790                 Roo.MessageBox.hide();
39791             }
39792         }
39793         if(success){
39794             if(o.reset){
39795                 this.reset();
39796             }
39797             Roo.callback(o.success, o.scope, [this, action]);
39798             this.fireEvent('actioncomplete', this, action);
39799         }else{
39800             Roo.callback(o.failure, o.scope, [this, action]);
39801             this.fireEvent('actionfailed', this, action);
39802         }
39803     },
39804
39805     /**
39806      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39807      * @param {String} id The value to search for
39808      * @return Field
39809      */
39810     findField : function(id){
39811         var field = this.items.get(id);
39812         if(!field){
39813             this.items.each(function(f){
39814                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39815                     field = f;
39816                     return false;
39817                 }
39818             });
39819         }
39820         return field || null;
39821     },
39822
39823
39824     /**
39825      * Mark fields in this form invalid in bulk.
39826      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39827      * @return {BasicForm} this
39828      */
39829     markInvalid : function(errors){
39830         if(errors instanceof Array){
39831             for(var i = 0, len = errors.length; i < len; i++){
39832                 var fieldError = errors[i];
39833                 var f = this.findField(fieldError.id);
39834                 if(f){
39835                     f.markInvalid(fieldError.msg);
39836                 }
39837             }
39838         }else{
39839             var field, id;
39840             for(id in errors){
39841                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39842                     field.markInvalid(errors[id]);
39843                 }
39844             }
39845         }
39846         return this;
39847     },
39848
39849     /**
39850      * Set values for fields in this form in bulk.
39851      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
39852      * @return {BasicForm} this
39853      */
39854     setValues : function(values){
39855         if(values instanceof Array){ // array of objects
39856             for(var i = 0, len = values.length; i < len; i++){
39857                 var v = values[i];
39858                 var f = this.findField(v.id);
39859                 if(f){
39860                     f.setValue(v.value);
39861                     if(this.trackResetOnLoad){
39862                         f.originalValue = f.getValue();
39863                     }
39864                 }
39865             }
39866         }else{ // object hash
39867             var field, id;
39868             for(id in values){
39869                 if(typeof values[id] != 'function' && (field = this.findField(id))){
39870                     
39871                     if (field.setFromData && 
39872                         field.valueField && 
39873                         field.displayField &&
39874                         // combos' with local stores can 
39875                         // be queried via setValue()
39876                         // to set their value..
39877                         (field.store && !field.store.isLocal)
39878                         ) {
39879                         // it's a combo
39880                         var sd = { };
39881                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
39882                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
39883                         field.setFromData(sd);
39884                         
39885                     } else {
39886                         field.setValue(values[id]);
39887                     }
39888                     
39889                     
39890                     if(this.trackResetOnLoad){
39891                         field.originalValue = field.getValue();
39892                     }
39893                 }
39894             }
39895         }
39896         return this;
39897     },
39898
39899     /**
39900      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
39901      * they are returned as an array.
39902      * @param {Boolean} asString
39903      * @return {Object}
39904      */
39905     getValues : function(asString){
39906         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
39907         if(asString === true){
39908             return fs;
39909         }
39910         return Roo.urlDecode(fs);
39911     },
39912
39913     /**
39914      * Clears all invalid messages in this form.
39915      * @return {BasicForm} this
39916      */
39917     clearInvalid : function(){
39918         this.items.each(function(f){
39919            f.clearInvalid();
39920         });
39921         return this;
39922     },
39923
39924     /**
39925      * Resets this form.
39926      * @return {BasicForm} this
39927      */
39928     reset : function(){
39929         this.items.each(function(f){
39930             f.reset();
39931         });
39932         return this;
39933     },
39934
39935     /**
39936      * Add Roo.form components to this form.
39937      * @param {Field} field1
39938      * @param {Field} field2 (optional)
39939      * @param {Field} etc (optional)
39940      * @return {BasicForm} this
39941      */
39942     add : function(){
39943         this.items.addAll(Array.prototype.slice.call(arguments, 0));
39944         return this;
39945     },
39946
39947
39948     /**
39949      * Removes a field from the items collection (does NOT remove its markup).
39950      * @param {Field} field
39951      * @return {BasicForm} this
39952      */
39953     remove : function(field){
39954         this.items.remove(field);
39955         return this;
39956     },
39957
39958     /**
39959      * Looks at the fields in this form, checks them for an id attribute,
39960      * and calls applyTo on the existing dom element with that id.
39961      * @return {BasicForm} this
39962      */
39963     render : function(){
39964         this.items.each(function(f){
39965             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
39966                 f.applyTo(f.id);
39967             }
39968         });
39969         return this;
39970     },
39971
39972     /**
39973      * Calls {@link Ext#apply} for all fields in this form with the passed object.
39974      * @param {Object} values
39975      * @return {BasicForm} this
39976      */
39977     applyToFields : function(o){
39978         this.items.each(function(f){
39979            Roo.apply(f, o);
39980         });
39981         return this;
39982     },
39983
39984     /**
39985      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
39986      * @param {Object} values
39987      * @return {BasicForm} this
39988      */
39989     applyIfToFields : function(o){
39990         this.items.each(function(f){
39991            Roo.applyIf(f, o);
39992         });
39993         return this;
39994     }
39995 });
39996
39997 // back compat
39998 Roo.BasicForm = Roo.form.BasicForm;/*
39999  * Based on:
40000  * Ext JS Library 1.1.1
40001  * Copyright(c) 2006-2007, Ext JS, LLC.
40002  *
40003  * Originally Released Under LGPL - original licence link has changed is not relivant.
40004  *
40005  * Fork - LGPL
40006  * <script type="text/javascript">
40007  */
40008
40009 /**
40010  * @class Roo.form.Form
40011  * @extends Roo.form.BasicForm
40012  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40013  * @constructor
40014  * @param {Object} config Configuration options
40015  */
40016 Roo.form.Form = function(config){
40017     var xitems =  [];
40018     if (config.items) {
40019         xitems = config.items;
40020         delete config.items;
40021     }
40022     
40023     
40024     Roo.form.Form.superclass.constructor.call(this, null, config);
40025     this.url = this.url || this.action;
40026     if(!this.root){
40027         this.root = new Roo.form.Layout(Roo.applyIf({
40028             id: Roo.id()
40029         }, config));
40030     }
40031     this.active = this.root;
40032     /**
40033      * Array of all the buttons that have been added to this form via {@link addButton}
40034      * @type Array
40035      */
40036     this.buttons = [];
40037     this.allItems = [];
40038     this.addEvents({
40039         /**
40040          * @event clientvalidation
40041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40042          * @param {Form} this
40043          * @param {Boolean} valid true if the form has passed client-side validation
40044          */
40045         clientvalidation: true,
40046         /**
40047          * @event rendered
40048          * Fires when the form is rendered
40049          * @param {Roo.form.Form} form
40050          */
40051         rendered : true
40052     });
40053     
40054     Roo.each(xitems, this.addxtype, this);
40055     
40056     
40057     
40058 };
40059
40060 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40061     /**
40062      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40063      */
40064     /**
40065      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40066      */
40067     /**
40068      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40069      */
40070     buttonAlign:'center',
40071
40072     /**
40073      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40074      */
40075     minButtonWidth:75,
40076
40077     /**
40078      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40079      * This property cascades to child containers if not set.
40080      */
40081     labelAlign:'left',
40082
40083     /**
40084      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40085      * fires a looping event with that state. This is required to bind buttons to the valid
40086      * state using the config value formBind:true on the button.
40087      */
40088     monitorValid : false,
40089
40090     /**
40091      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40092      */
40093     monitorPoll : 200,
40094
40095     /**
40096      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40097      * fields are added and the column is closed. If no fields are passed the column remains open
40098      * until end() is called.
40099      * @param {Object} config The config to pass to the column
40100      * @param {Field} field1 (optional)
40101      * @param {Field} field2 (optional)
40102      * @param {Field} etc (optional)
40103      * @return Column The column container object
40104      */
40105     column : function(c){
40106         var col = new Roo.form.Column(c);
40107         this.start(col);
40108         if(arguments.length > 1){ // duplicate code required because of Opera
40109             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40110             this.end();
40111         }
40112         return col;
40113     },
40114
40115     /**
40116      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40117      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40118      * until end() is called.
40119      * @param {Object} config The config to pass to the fieldset
40120      * @param {Field} field1 (optional)
40121      * @param {Field} field2 (optional)
40122      * @param {Field} etc (optional)
40123      * @return FieldSet The fieldset container object
40124      */
40125     fieldset : function(c){
40126         var fs = new Roo.form.FieldSet(c);
40127         this.start(fs);
40128         if(arguments.length > 1){ // duplicate code required because of Opera
40129             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40130             this.end();
40131         }
40132         return fs;
40133     },
40134
40135     /**
40136      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40137      * fields are added and the container is closed. If no fields are passed the container remains open
40138      * until end() is called.
40139      * @param {Object} config The config to pass to the Layout
40140      * @param {Field} field1 (optional)
40141      * @param {Field} field2 (optional)
40142      * @param {Field} etc (optional)
40143      * @return Layout The container object
40144      */
40145     container : function(c){
40146         var l = new Roo.form.Layout(c);
40147         this.start(l);
40148         if(arguments.length > 1){ // duplicate code required because of Opera
40149             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40150             this.end();
40151         }
40152         return l;
40153     },
40154
40155     /**
40156      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40157      * @param {Object} container A Roo.form.Layout or subclass of Layout
40158      * @return {Form} this
40159      */
40160     start : function(c){
40161         // cascade label info
40162         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40163         this.active.stack.push(c);
40164         c.ownerCt = this.active;
40165         this.active = c;
40166         return this;
40167     },
40168
40169     /**
40170      * Closes the current open container
40171      * @return {Form} this
40172      */
40173     end : function(){
40174         if(this.active == this.root){
40175             return this;
40176         }
40177         this.active = this.active.ownerCt;
40178         return this;
40179     },
40180
40181     /**
40182      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40183      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40184      * as the label of the field.
40185      * @param {Field} field1
40186      * @param {Field} field2 (optional)
40187      * @param {Field} etc. (optional)
40188      * @return {Form} this
40189      */
40190     add : function(){
40191         this.active.stack.push.apply(this.active.stack, arguments);
40192         this.allItems.push.apply(this.allItems,arguments);
40193         var r = [];
40194         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40195             if(a[i].isFormField){
40196                 r.push(a[i]);
40197             }
40198         }
40199         if(r.length > 0){
40200             Roo.form.Form.superclass.add.apply(this, r);
40201         }
40202         return this;
40203     },
40204      /**
40205      * Find any element that has been added to a form, using it's ID or name
40206      * This can include framesets, columns etc. along with regular fields..
40207      * @param {String} id - id or name to find.
40208      
40209      * @return {Element} e - or false if nothing found.
40210      */
40211     findbyId : function(id)
40212     {
40213         var ret = false;
40214         if (!id) {
40215             return ret;
40216         }
40217         Ext.each(this.allItems, function(f){
40218             if (f.id == id || f.name == id ){
40219                 ret = f;
40220                 return false;
40221             }
40222         });
40223         return ret;
40224     },
40225
40226     
40227     
40228     /**
40229      * Render this form into the passed container. This should only be called once!
40230      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40231      * @return {Form} this
40232      */
40233     render : function(ct){
40234         ct = Roo.get(ct);
40235         var o = this.autoCreate || {
40236             tag: 'form',
40237             method : this.method || 'POST',
40238             id : this.id || Roo.id()
40239         };
40240         this.initEl(ct.createChild(o));
40241
40242         this.root.render(this.el);
40243
40244         this.items.each(function(f){
40245             f.render('x-form-el-'+f.id);
40246         });
40247
40248         if(this.buttons.length > 0){
40249             // tables are required to maintain order and for correct IE layout
40250             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40251                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40252                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40253             }}, null, true);
40254             var tr = tb.getElementsByTagName('tr')[0];
40255             for(var i = 0, len = this.buttons.length; i < len; i++) {
40256                 var b = this.buttons[i];
40257                 var td = document.createElement('td');
40258                 td.className = 'x-form-btn-td';
40259                 b.render(tr.appendChild(td));
40260             }
40261         }
40262         if(this.monitorValid){ // initialize after render
40263             this.startMonitoring();
40264         }
40265         this.fireEvent('rendered', this);
40266         return this;
40267     },
40268
40269     /**
40270      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40271      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40272      * object or a valid Roo.DomHelper element config
40273      * @param {Function} handler The function called when the button is clicked
40274      * @param {Object} scope (optional) The scope of the handler function
40275      * @return {Roo.Button}
40276      */
40277     addButton : function(config, handler, scope){
40278         var bc = {
40279             handler: handler,
40280             scope: scope,
40281             minWidth: this.minButtonWidth,
40282             hideParent:true
40283         };
40284         if(typeof config == "string"){
40285             bc.text = config;
40286         }else{
40287             Roo.apply(bc, config);
40288         }
40289         var btn = new Roo.Button(null, bc);
40290         this.buttons.push(btn);
40291         return btn;
40292     },
40293
40294      /**
40295      * Adds a series of form elements (using the xtype property as the factory method.
40296      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40297      * @param {Object} config 
40298      */
40299     
40300     addxtype : function()
40301     {
40302         var ar = Array.prototype.slice.call(arguments, 0);
40303         var ret = false;
40304         for(var i = 0; i < ar.length; i++) {
40305             if (!ar[i]) {
40306                 continue; // skip -- if this happends something invalid got sent, we 
40307                 // should ignore it, as basically that interface element will not show up
40308                 // and that should be pretty obvious!!
40309             }
40310             
40311             if (Roo.form[ar[i].xtype]) {
40312                 ar[i].form = this;
40313                 var fe = Roo.factory(ar[i], Roo.form);
40314                 if (!ret) {
40315                     ret = fe;
40316                 }
40317                 fe.form = this;
40318                 if (fe.store) {
40319                     fe.store.form = this;
40320                 }
40321                 if (fe.isLayout) {  
40322                          
40323                     this.start(fe);
40324                     this.allItems.push(fe);
40325                     if (fe.items && fe.addxtype) {
40326                         fe.addxtype.apply(fe, fe.items);
40327                         delete fe.items;
40328                     }
40329                      this.end();
40330                     continue;
40331                 }
40332                 
40333                 
40334                  
40335                 this.add(fe);
40336               //  console.log('adding ' + ar[i].xtype);
40337             }
40338             if (ar[i].xtype == 'Button') {  
40339                 //console.log('adding button');
40340                 //console.log(ar[i]);
40341                 this.addButton(ar[i]);
40342                 this.allItems.push(fe);
40343                 continue;
40344             }
40345             
40346             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40347                 alert('end is not supported on xtype any more, use items');
40348             //    this.end();
40349             //    //console.log('adding end');
40350             }
40351             
40352         }
40353         return ret;
40354     },
40355     
40356     /**
40357      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40358      * option "monitorValid"
40359      */
40360     startMonitoring : function(){
40361         if(!this.bound){
40362             this.bound = true;
40363             Roo.TaskMgr.start({
40364                 run : this.bindHandler,
40365                 interval : this.monitorPoll || 200,
40366                 scope: this
40367             });
40368         }
40369     },
40370
40371     /**
40372      * Stops monitoring of the valid state of this form
40373      */
40374     stopMonitoring : function(){
40375         this.bound = false;
40376     },
40377
40378     // private
40379     bindHandler : function(){
40380         if(!this.bound){
40381             return false; // stops binding
40382         }
40383         var valid = true;
40384         this.items.each(function(f){
40385             if(!f.isValid(true)){
40386                 valid = false;
40387                 return false;
40388             }
40389         });
40390         for(var i = 0, len = this.buttons.length; i < len; i++){
40391             var btn = this.buttons[i];
40392             if(btn.formBind === true && btn.disabled === valid){
40393                 btn.setDisabled(!valid);
40394             }
40395         }
40396         this.fireEvent('clientvalidation', this, valid);
40397     }
40398     
40399     
40400     
40401     
40402     
40403     
40404     
40405     
40406 });
40407
40408
40409 // back compat
40410 Roo.Form = Roo.form.Form;
40411 /*
40412  * Based on:
40413  * Ext JS Library 1.1.1
40414  * Copyright(c) 2006-2007, Ext JS, LLC.
40415  *
40416  * Originally Released Under LGPL - original licence link has changed is not relivant.
40417  *
40418  * Fork - LGPL
40419  * <script type="text/javascript">
40420  */
40421  
40422  /**
40423  * @class Roo.form.Action
40424  * Internal Class used to handle form actions
40425  * @constructor
40426  * @param {Roo.form.BasicForm} el The form element or its id
40427  * @param {Object} config Configuration options
40428  */
40429  
40430  
40431 // define the action interface
40432 Roo.form.Action = function(form, options){
40433     this.form = form;
40434     this.options = options || {};
40435 };
40436 /**
40437  * Client Validation Failed
40438  * @const 
40439  */
40440 Roo.form.Action.CLIENT_INVALID = 'client';
40441 /**
40442  * Server Validation Failed
40443  * @const 
40444  */
40445  Roo.form.Action.SERVER_INVALID = 'server';
40446  /**
40447  * Connect to Server Failed
40448  * @const 
40449  */
40450 Roo.form.Action.CONNECT_FAILURE = 'connect';
40451 /**
40452  * Reading Data from Server Failed
40453  * @const 
40454  */
40455 Roo.form.Action.LOAD_FAILURE = 'load';
40456
40457 Roo.form.Action.prototype = {
40458     type : 'default',
40459     failureType : undefined,
40460     response : undefined,
40461     result : undefined,
40462
40463     // interface method
40464     run : function(options){
40465
40466     },
40467
40468     // interface method
40469     success : function(response){
40470
40471     },
40472
40473     // interface method
40474     handleResponse : function(response){
40475
40476     },
40477
40478     // default connection failure
40479     failure : function(response){
40480         this.response = response;
40481         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40482         this.form.afterAction(this, false);
40483     },
40484
40485     processResponse : function(response){
40486         this.response = response;
40487         if(!response.responseText){
40488             return true;
40489         }
40490         this.result = this.handleResponse(response);
40491         return this.result;
40492     },
40493
40494     // utility functions used internally
40495     getUrl : function(appendParams){
40496         var url = this.options.url || this.form.url || this.form.el.dom.action;
40497         if(appendParams){
40498             var p = this.getParams();
40499             if(p){
40500                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40501             }
40502         }
40503         return url;
40504     },
40505
40506     getMethod : function(){
40507         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40508     },
40509
40510     getParams : function(){
40511         var bp = this.form.baseParams;
40512         var p = this.options.params;
40513         if(p){
40514             if(typeof p == "object"){
40515                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40516             }else if(typeof p == 'string' && bp){
40517                 p += '&' + Roo.urlEncode(bp);
40518             }
40519         }else if(bp){
40520             p = Roo.urlEncode(bp);
40521         }
40522         return p;
40523     },
40524
40525     createCallback : function(){
40526         return {
40527             success: this.success,
40528             failure: this.failure,
40529             scope: this,
40530             timeout: (this.form.timeout*1000),
40531             upload: this.form.fileUpload ? this.success : undefined
40532         };
40533     }
40534 };
40535
40536 Roo.form.Action.Submit = function(form, options){
40537     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40538 };
40539
40540 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40541     type : 'submit',
40542
40543     run : function(){
40544         var o = this.options;
40545         var method = this.getMethod();
40546         var isPost = method == 'POST';
40547         if(o.clientValidation === false || this.form.isValid()){
40548             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40549                 form:this.form.el.dom,
40550                 url:this.getUrl(!isPost),
40551                 method: method,
40552                 params:isPost ? this.getParams() : null,
40553                 isUpload: this.form.fileUpload
40554             }));
40555
40556         }else if (o.clientValidation !== false){ // client validation failed
40557             this.failureType = Roo.form.Action.CLIENT_INVALID;
40558             this.form.afterAction(this, false);
40559         }
40560     },
40561
40562     success : function(response){
40563         var result = this.processResponse(response);
40564         if(result === true || result.success){
40565             this.form.afterAction(this, true);
40566             return;
40567         }
40568         if(result.errors){
40569             this.form.markInvalid(result.errors);
40570             this.failureType = Roo.form.Action.SERVER_INVALID;
40571         }
40572         this.form.afterAction(this, false);
40573     },
40574
40575     handleResponse : function(response){
40576         if(this.form.errorReader){
40577             var rs = this.form.errorReader.read(response);
40578             var errors = [];
40579             if(rs.records){
40580                 for(var i = 0, len = rs.records.length; i < len; i++) {
40581                     var r = rs.records[i];
40582                     errors[i] = r.data;
40583                 }
40584             }
40585             if(errors.length < 1){
40586                 errors = null;
40587             }
40588             return {
40589                 success : rs.success,
40590                 errors : errors
40591             };
40592         }
40593         var ret = false;
40594         try {
40595             ret = Roo.decode(response.responseText);
40596         } catch (e) {
40597             ret = {
40598                 success: false,
40599                 errorMsg: "Failed to read server message: " + response.responseText,
40600                 errors : []
40601             };
40602         }
40603         return ret;
40604         
40605     }
40606 });
40607
40608
40609 Roo.form.Action.Load = function(form, options){
40610     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40611     this.reader = this.form.reader;
40612 };
40613
40614 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40615     type : 'load',
40616
40617     run : function(){
40618         Roo.Ajax.request(Roo.apply(
40619                 this.createCallback(), {
40620                     method:this.getMethod(),
40621                     url:this.getUrl(false),
40622                     params:this.getParams()
40623         }));
40624     },
40625
40626     success : function(response){
40627         var result = this.processResponse(response);
40628         if(result === true || !result.success || !result.data){
40629             this.failureType = Roo.form.Action.LOAD_FAILURE;
40630             this.form.afterAction(this, false);
40631             return;
40632         }
40633         this.form.clearInvalid();
40634         this.form.setValues(result.data);
40635         this.form.afterAction(this, true);
40636     },
40637
40638     handleResponse : function(response){
40639         if(this.form.reader){
40640             var rs = this.form.reader.read(response);
40641             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40642             return {
40643                 success : rs.success,
40644                 data : data
40645             };
40646         }
40647         return Roo.decode(response.responseText);
40648     }
40649 });
40650
40651 Roo.form.Action.ACTION_TYPES = {
40652     'load' : Roo.form.Action.Load,
40653     'submit' : Roo.form.Action.Submit
40654 };/*
40655  * Based on:
40656  * Ext JS Library 1.1.1
40657  * Copyright(c) 2006-2007, Ext JS, LLC.
40658  *
40659  * Originally Released Under LGPL - original licence link has changed is not relivant.
40660  *
40661  * Fork - LGPL
40662  * <script type="text/javascript">
40663  */
40664  
40665 /**
40666  * @class Roo.form.Layout
40667  * @extends Roo.Component
40668  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40669  * @constructor
40670  * @param {Object} config Configuration options
40671  */
40672 Roo.form.Layout = function(config){
40673     var xitems = [];
40674     if (config.items) {
40675         xitems = config.items;
40676         delete config.items;
40677     }
40678     Roo.form.Layout.superclass.constructor.call(this, config);
40679     this.stack = [];
40680     Roo.each(xitems, this.addxtype, this);
40681      
40682 };
40683
40684 Roo.extend(Roo.form.Layout, Roo.Component, {
40685     /**
40686      * @cfg {String/Object} autoCreate
40687      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40688      */
40689     /**
40690      * @cfg {String/Object/Function} style
40691      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40692      * a function which returns such a specification.
40693      */
40694     /**
40695      * @cfg {String} labelAlign
40696      * Valid values are "left," "top" and "right" (defaults to "left")
40697      */
40698     /**
40699      * @cfg {Number} labelWidth
40700      * Fixed width in pixels of all field labels (defaults to undefined)
40701      */
40702     /**
40703      * @cfg {Boolean} clear
40704      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40705      */
40706     clear : true,
40707     /**
40708      * @cfg {String} labelSeparator
40709      * The separator to use after field labels (defaults to ':')
40710      */
40711     labelSeparator : ':',
40712     /**
40713      * @cfg {Boolean} hideLabels
40714      * True to suppress the display of field labels in this layout (defaults to false)
40715      */
40716     hideLabels : false,
40717
40718     // private
40719     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40720     
40721     isLayout : true,
40722     
40723     // private
40724     onRender : function(ct, position){
40725         if(this.el){ // from markup
40726             this.el = Roo.get(this.el);
40727         }else {  // generate
40728             var cfg = this.getAutoCreate();
40729             this.el = ct.createChild(cfg, position);
40730         }
40731         if(this.style){
40732             this.el.applyStyles(this.style);
40733         }
40734         if(this.labelAlign){
40735             this.el.addClass('x-form-label-'+this.labelAlign);
40736         }
40737         if(this.hideLabels){
40738             this.labelStyle = "display:none";
40739             this.elementStyle = "padding-left:0;";
40740         }else{
40741             if(typeof this.labelWidth == 'number'){
40742                 this.labelStyle = "width:"+this.labelWidth+"px;";
40743                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40744             }
40745             if(this.labelAlign == 'top'){
40746                 this.labelStyle = "width:auto;";
40747                 this.elementStyle = "padding-left:0;";
40748             }
40749         }
40750         var stack = this.stack;
40751         var slen = stack.length;
40752         if(slen > 0){
40753             if(!this.fieldTpl){
40754                 var t = new Roo.Template(
40755                     '<div class="x-form-item {5}">',
40756                         '<label for="{0}" style="{2}">{1}{4}</label>',
40757                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40758                         '</div>',
40759                     '</div><div class="x-form-clear-left"></div>'
40760                 );
40761                 t.disableFormats = true;
40762                 t.compile();
40763                 Roo.form.Layout.prototype.fieldTpl = t;
40764             }
40765             for(var i = 0; i < slen; i++) {
40766                 if(stack[i].isFormField){
40767                     this.renderField(stack[i]);
40768                 }else{
40769                     this.renderComponent(stack[i]);
40770                 }
40771             }
40772         }
40773         if(this.clear){
40774             this.el.createChild({cls:'x-form-clear'});
40775         }
40776     },
40777
40778     // private
40779     renderField : function(f){
40780         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40781                f.id, //0
40782                f.fieldLabel, //1
40783                f.labelStyle||this.labelStyle||'', //2
40784                this.elementStyle||'', //3
40785                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40786                f.itemCls||this.itemCls||''  //5
40787        ], true).getPrevSibling());
40788     },
40789
40790     // private
40791     renderComponent : function(c){
40792         c.render(c.isLayout ? this.el : this.el.createChild());    
40793     },
40794     /**
40795      * Adds a object form elements (using the xtype property as the factory method.)
40796      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40797      * @param {Object} config 
40798      */
40799     addxtype : function(o)
40800     {
40801         // create the lement.
40802         o.form = this.form;
40803         var fe = Roo.factory(o, Roo.form);
40804         this.form.allItems.push(fe);
40805         this.stack.push(fe);
40806         
40807         if (fe.isFormField) {
40808             this.form.items.add(fe);
40809         }
40810          
40811         return fe;
40812     }
40813 });
40814
40815 /**
40816  * @class Roo.form.Column
40817  * @extends Roo.form.Layout
40818  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
40819  * @constructor
40820  * @param {Object} config Configuration options
40821  */
40822 Roo.form.Column = function(config){
40823     Roo.form.Column.superclass.constructor.call(this, config);
40824 };
40825
40826 Roo.extend(Roo.form.Column, Roo.form.Layout, {
40827     /**
40828      * @cfg {Number/String} width
40829      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40830      */
40831     /**
40832      * @cfg {String/Object} autoCreate
40833      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
40834      */
40835
40836     // private
40837     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
40838
40839     // private
40840     onRender : function(ct, position){
40841         Roo.form.Column.superclass.onRender.call(this, ct, position);
40842         if(this.width){
40843             this.el.setWidth(this.width);
40844         }
40845     }
40846 });
40847
40848
40849 /**
40850  * @class Roo.form.Row
40851  * @extends Roo.form.Layout
40852  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
40853  * @constructor
40854  * @param {Object} config Configuration options
40855  */
40856
40857  
40858 Roo.form.Row = function(config){
40859     Roo.form.Row.superclass.constructor.call(this, config);
40860 };
40861  
40862 Roo.extend(Roo.form.Row, Roo.form.Layout, {
40863       /**
40864      * @cfg {Number/String} width
40865      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40866      */
40867     /**
40868      * @cfg {Number/String} height
40869      * The fixed height of the column in pixels or CSS value (defaults to "auto")
40870      */
40871     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
40872     
40873     padWidth : 20,
40874     // private
40875     onRender : function(ct, position){
40876         //console.log('row render');
40877         if(!this.rowTpl){
40878             var t = new Roo.Template(
40879                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
40880                     '<label for="{0}" style="{2}">{1}{4}</label>',
40881                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40882                     '</div>',
40883                 '</div>'
40884             );
40885             t.disableFormats = true;
40886             t.compile();
40887             Roo.form.Layout.prototype.rowTpl = t;
40888         }
40889         this.fieldTpl = this.rowTpl;
40890         
40891         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
40892         var labelWidth = 100;
40893         
40894         if ((this.labelAlign != 'top')) {
40895             if (typeof this.labelWidth == 'number') {
40896                 labelWidth = this.labelWidth
40897             }
40898             this.padWidth =  20 + labelWidth;
40899             
40900         }
40901         
40902         Roo.form.Column.superclass.onRender.call(this, ct, position);
40903         if(this.width){
40904             this.el.setWidth(this.width);
40905         }
40906         if(this.height){
40907             this.el.setHeight(this.height);
40908         }
40909     },
40910     
40911     // private
40912     renderField : function(f){
40913         f.fieldEl = this.fieldTpl.append(this.el, [
40914                f.id, f.fieldLabel,
40915                f.labelStyle||this.labelStyle||'',
40916                this.elementStyle||'',
40917                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
40918                f.itemCls||this.itemCls||'',
40919                f.width ? f.width + this.padWidth : 160 + this.padWidth
40920        ],true);
40921     }
40922 });
40923  
40924
40925 /**
40926  * @class Roo.form.FieldSet
40927  * @extends Roo.form.Layout
40928  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
40929  * @constructor
40930  * @param {Object} config Configuration options
40931  */
40932 Roo.form.FieldSet = function(config){
40933     Roo.form.FieldSet.superclass.constructor.call(this, config);
40934 };
40935
40936 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
40937     /**
40938      * @cfg {String} legend
40939      * The text to display as the legend for the FieldSet (defaults to '')
40940      */
40941     /**
40942      * @cfg {String/Object} autoCreate
40943      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
40944      */
40945
40946     // private
40947     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
40948
40949     // private
40950     onRender : function(ct, position){
40951         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
40952         if(this.legend){
40953             this.setLegend(this.legend);
40954         }
40955     },
40956
40957     // private
40958     setLegend : function(text){
40959         if(this.rendered){
40960             this.el.child('legend').update(text);
40961         }
40962     }
40963 });/*
40964  * Based on:
40965  * Ext JS Library 1.1.1
40966  * Copyright(c) 2006-2007, Ext JS, LLC.
40967  *
40968  * Originally Released Under LGPL - original licence link has changed is not relivant.
40969  *
40970  * Fork - LGPL
40971  * <script type="text/javascript">
40972  */
40973 /**
40974  * @class Roo.form.VTypes
40975  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
40976  * @singleton
40977  */
40978 Roo.form.VTypes = function(){
40979     // closure these in so they are only created once.
40980     var alpha = /^[a-zA-Z_]+$/;
40981     var alphanum = /^[a-zA-Z0-9_]+$/;
40982     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
40983     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
40984
40985     // All these messages and functions are configurable
40986     return {
40987         /**
40988          * The function used to validate email addresses
40989          * @param {String} value The email address
40990          */
40991         'email' : function(v){
40992             return email.test(v);
40993         },
40994         /**
40995          * The error text to display when the email validation function returns false
40996          * @type String
40997          */
40998         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
40999         /**
41000          * The keystroke filter mask to be applied on email input
41001          * @type RegExp
41002          */
41003         'emailMask' : /[a-z0-9_\.\-@]/i,
41004
41005         /**
41006          * The function used to validate URLs
41007          * @param {String} value The URL
41008          */
41009         'url' : function(v){
41010             return url.test(v);
41011         },
41012         /**
41013          * The error text to display when the url validation function returns false
41014          * @type String
41015          */
41016         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41017         
41018         /**
41019          * The function used to validate alpha values
41020          * @param {String} value The value
41021          */
41022         'alpha' : function(v){
41023             return alpha.test(v);
41024         },
41025         /**
41026          * The error text to display when the alpha validation function returns false
41027          * @type String
41028          */
41029         'alphaText' : 'This field should only contain letters and _',
41030         /**
41031          * The keystroke filter mask to be applied on alpha input
41032          * @type RegExp
41033          */
41034         'alphaMask' : /[a-z_]/i,
41035
41036         /**
41037          * The function used to validate alphanumeric values
41038          * @param {String} value The value
41039          */
41040         'alphanum' : function(v){
41041             return alphanum.test(v);
41042         },
41043         /**
41044          * The error text to display when the alphanumeric validation function returns false
41045          * @type String
41046          */
41047         'alphanumText' : 'This field should only contain letters, numbers and _',
41048         /**
41049          * The keystroke filter mask to be applied on alphanumeric input
41050          * @type RegExp
41051          */
41052         'alphanumMask' : /[a-z0-9_]/i
41053     };
41054 }();//<script type="text/javascript">
41055
41056 /**
41057  * @class Roo.form.FCKeditor
41058  * @extends Roo.form.TextArea
41059  * Wrapper around the FCKEditor http://www.fckeditor.net
41060  * @constructor
41061  * Creates a new FCKeditor
41062  * @param {Object} config Configuration options
41063  */
41064 Roo.form.FCKeditor = function(config){
41065     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41066     this.addEvents({
41067          /**
41068          * @event editorinit
41069          * Fired when the editor is initialized - you can add extra handlers here..
41070          * @param {FCKeditor} this
41071          * @param {Object} the FCK object.
41072          */
41073         editorinit : true
41074     });
41075     
41076     
41077 };
41078 Roo.form.FCKeditor.editors = { };
41079 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41080 {
41081     //defaultAutoCreate : {
41082     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41083     //},
41084     // private
41085     /**
41086      * @cfg {Object} fck options - see fck manual for details.
41087      */
41088     fckconfig : false,
41089     
41090     /**
41091      * @cfg {Object} fck toolbar set (Basic or Default)
41092      */
41093     toolbarSet : 'Basic',
41094     /**
41095      * @cfg {Object} fck BasePath
41096      */ 
41097     basePath : '/fckeditor/',
41098     
41099     
41100     frame : false,
41101     
41102     value : '',
41103     
41104    
41105     onRender : function(ct, position)
41106     {
41107         if(!this.el){
41108             this.defaultAutoCreate = {
41109                 tag: "textarea",
41110                 style:"width:300px;height:60px;",
41111                 autocomplete: "off"
41112             };
41113         }
41114         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41115         /*
41116         if(this.grow){
41117             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41118             if(this.preventScrollbars){
41119                 this.el.setStyle("overflow", "hidden");
41120             }
41121             this.el.setHeight(this.growMin);
41122         }
41123         */
41124         //console.log('onrender' + this.getId() );
41125         Roo.form.FCKeditor.editors[this.getId()] = this;
41126          
41127
41128         this.replaceTextarea() ;
41129         
41130     },
41131     
41132     getEditor : function() {
41133         return this.fckEditor;
41134     },
41135     /**
41136      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41137      * @param {Mixed} value The value to set
41138      */
41139     
41140     
41141     setValue : function(value)
41142     {
41143         //console.log('setValue: ' + value);
41144         
41145         if(typeof(value) == 'undefined') { // not sure why this is happending...
41146             return;
41147         }
41148         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41149         
41150         //if(!this.el || !this.getEditor()) {
41151         //    this.value = value;
41152             //this.setValue.defer(100,this,[value]);    
41153         //    return;
41154         //} 
41155         
41156         if(!this.getEditor()) {
41157             return;
41158         }
41159         
41160         this.getEditor().SetData(value);
41161         
41162         //
41163
41164     },
41165
41166     /**
41167      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41168      * @return {Mixed} value The field value
41169      */
41170     getValue : function()
41171     {
41172         
41173         if (this.frame && this.frame.dom.style.display == 'none') {
41174             return Roo.form.FCKeditor.superclass.getValue.call(this);
41175         }
41176         
41177         if(!this.el || !this.getEditor()) {
41178            
41179            // this.getValue.defer(100,this); 
41180             return this.value;
41181         }
41182        
41183         
41184         var value=this.getEditor().GetData();
41185         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41186         return Roo.form.FCKeditor.superclass.getValue.call(this);
41187         
41188
41189     },
41190
41191     /**
41192      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41193      * @return {Mixed} value The field value
41194      */
41195     getRawValue : function()
41196     {
41197         if (this.frame && this.frame.dom.style.display == 'none') {
41198             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41199         }
41200         
41201         if(!this.el || !this.getEditor()) {
41202             //this.getRawValue.defer(100,this); 
41203             return this.value;
41204             return;
41205         }
41206         
41207         
41208         
41209         var value=this.getEditor().GetData();
41210         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41211         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41212          
41213     },
41214     
41215     setSize : function(w,h) {
41216         
41217         
41218         
41219         //if (this.frame && this.frame.dom.style.display == 'none') {
41220         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41221         //    return;
41222         //}
41223         //if(!this.el || !this.getEditor()) {
41224         //    this.setSize.defer(100,this, [w,h]); 
41225         //    return;
41226         //}
41227         
41228         
41229         
41230         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41231         
41232         this.frame.dom.setAttribute('width', w);
41233         this.frame.dom.setAttribute('height', h);
41234         this.frame.setSize(w,h);
41235         
41236     },
41237     
41238     toggleSourceEdit : function(value) {
41239         
41240       
41241          
41242         this.el.dom.style.display = value ? '' : 'none';
41243         this.frame.dom.style.display = value ?  'none' : '';
41244         
41245     },
41246     
41247     
41248     focus: function(tag)
41249     {
41250         if (this.frame.dom.style.display == 'none') {
41251             return Roo.form.FCKeditor.superclass.focus.call(this);
41252         }
41253         if(!this.el || !this.getEditor()) {
41254             this.focus.defer(100,this, [tag]); 
41255             return;
41256         }
41257         
41258         
41259         
41260         
41261         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41262         this.getEditor().Focus();
41263         if (tgs.length) {
41264             if (!this.getEditor().Selection.GetSelection()) {
41265                 this.focus.defer(100,this, [tag]); 
41266                 return;
41267             }
41268             
41269             
41270             var r = this.getEditor().EditorDocument.createRange();
41271             r.setStart(tgs[0],0);
41272             r.setEnd(tgs[0],0);
41273             this.getEditor().Selection.GetSelection().removeAllRanges();
41274             this.getEditor().Selection.GetSelection().addRange(r);
41275             this.getEditor().Focus();
41276         }
41277         
41278     },
41279     
41280     
41281     
41282     replaceTextarea : function()
41283     {
41284         if ( document.getElementById( this.getId() + '___Frame' ) )
41285             return ;
41286         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41287         //{
41288             // We must check the elements firstly using the Id and then the name.
41289         var oTextarea = document.getElementById( this.getId() );
41290         
41291         var colElementsByName = document.getElementsByName( this.getId() ) ;
41292          
41293         oTextarea.style.display = 'none' ;
41294
41295         if ( oTextarea.tabIndex ) {            
41296             this.TabIndex = oTextarea.tabIndex ;
41297         }
41298         
41299         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41300         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41301         this.frame = Roo.get(this.getId() + '___Frame')
41302     },
41303     
41304     _getConfigHtml : function()
41305     {
41306         var sConfig = '' ;
41307
41308         for ( var o in this.fckconfig ) {
41309             sConfig += sConfig.length > 0  ? '&amp;' : '';
41310             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41311         }
41312
41313         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41314     },
41315     
41316     
41317     _getIFrameHtml : function()
41318     {
41319         var sFile = 'fckeditor.html' ;
41320         /* no idea what this is about..
41321         try
41322         {
41323             if ( (/fcksource=true/i).test( window.top.location.search ) )
41324                 sFile = 'fckeditor.original.html' ;
41325         }
41326         catch (e) { 
41327         */
41328
41329         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41330         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41331         
41332         
41333         var html = '<iframe id="' + this.getId() +
41334             '___Frame" src="' + sLink +
41335             '" width="' + this.width +
41336             '" height="' + this.height + '"' +
41337             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41338             ' frameborder="0" scrolling="no"></iframe>' ;
41339
41340         return html ;
41341     },
41342     
41343     _insertHtmlBefore : function( html, element )
41344     {
41345         if ( element.insertAdjacentHTML )       {
41346             // IE
41347             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41348         } else { // Gecko
41349             var oRange = document.createRange() ;
41350             oRange.setStartBefore( element ) ;
41351             var oFragment = oRange.createContextualFragment( html );
41352             element.parentNode.insertBefore( oFragment, element ) ;
41353         }
41354     }
41355     
41356     
41357   
41358     
41359     
41360     
41361     
41362
41363 });
41364
41365 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41366
41367 function FCKeditor_OnComplete(editorInstance){
41368     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41369     f.fckEditor = editorInstance;
41370     //console.log("loaded");
41371     f.fireEvent('editorinit', f, editorInstance);
41372
41373   
41374
41375  
41376
41377
41378
41379
41380
41381
41382
41383
41384
41385
41386
41387
41388
41389
41390
41391 //<script type="text/javascript">
41392 /**
41393  * @class Roo.form.GridField
41394  * @extends Roo.form.Field
41395  * Embed a grid (or editable grid into a form)
41396  * STATUS ALPHA
41397  * @constructor
41398  * Creates a new GridField
41399  * @param {Object} config Configuration options
41400  */
41401 Roo.form.GridField = function(config){
41402     Roo.form.GridField.superclass.constructor.call(this, config);
41403      
41404 };
41405
41406 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41407     /**
41408      * @cfg {Number} width  - used to restrict width of grid..
41409      */
41410     width : 100,
41411     /**
41412      * @cfg {Number} height - used to restrict height of grid..
41413      */
41414     height : 50,
41415      /**
41416      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41417      */
41418     xgrid : false, 
41419     /**
41420      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41421      * {tag: "input", type: "checkbox", autocomplete: "off"})
41422      */
41423    // defaultAutoCreate : { tag: 'div' },
41424     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41425     /**
41426      * @cfg {String} addTitle Text to include for adding a title.
41427      */
41428     addTitle : false,
41429     //
41430     onResize : function(){
41431         Roo.form.Field.superclass.onResize.apply(this, arguments);
41432     },
41433
41434     initEvents : function(){
41435         // Roo.form.Checkbox.superclass.initEvents.call(this);
41436         // has no events...
41437        
41438     },
41439
41440
41441     getResizeEl : function(){
41442         return this.wrap;
41443     },
41444
41445     getPositionEl : function(){
41446         return this.wrap;
41447     },
41448
41449     // private
41450     onRender : function(ct, position){
41451         
41452         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41453         var style = this.style;
41454         delete this.style;
41455         
41456         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41457         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41458         this.viewEl = this.wrap.createChild({ tag: 'div' });
41459         if (style) {
41460             this.viewEl.applyStyles(style);
41461         }
41462         if (this.width) {
41463             this.viewEl.setWidth(this.width);
41464         }
41465         if (this.height) {
41466             this.viewEl.setHeight(this.height);
41467         }
41468         //if(this.inputValue !== undefined){
41469         //this.setValue(this.value);
41470         
41471         
41472         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41473         
41474         
41475         this.grid.render();
41476         this.grid.getDataSource().on('remove', this.refreshValue, this);
41477         this.grid.getDataSource().on('update', this.refreshValue, this);
41478         this.grid.on('afteredit', this.refreshValue, this);
41479  
41480     },
41481      
41482     
41483     /**
41484      * Sets the value of the item. 
41485      * @param {String} either an object  or a string..
41486      */
41487     setValue : function(v){
41488         //this.value = v;
41489         v = v || []; // empty set..
41490         // this does not seem smart - it really only affects memoryproxy grids..
41491         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41492             var ds = this.grid.getDataSource();
41493             // assumes a json reader..
41494             var data = {}
41495             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41496             ds.loadData( data);
41497         }
41498         Roo.form.GridField.superclass.setValue.call(this, v);
41499         this.refreshValue();
41500         // should load data in the grid really....
41501     },
41502     
41503     // private
41504     refreshValue: function() {
41505          var val = [];
41506         this.grid.getDataSource().each(function(r) {
41507             val.push(r.data);
41508         });
41509         this.el.dom.value = Roo.encode(val);
41510     }
41511     
41512      
41513     
41514     
41515 });//<script type="text/javasscript">
41516  
41517
41518 /**
41519  * @class Roo.DDView
41520  * A DnD enabled version of Roo.View.
41521  * @param {Element/String} container The Element in which to create the View.
41522  * @param {String} tpl The template string used to create the markup for each element of the View
41523  * @param {Object} config The configuration properties. These include all the config options of
41524  * {@link Roo.View} plus some specific to this class.<br>
41525  * <p>
41526  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41527  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41528  * <p>
41529  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41530 .x-view-drag-insert-above {
41531         border-top:1px dotted #3366cc;
41532 }
41533 .x-view-drag-insert-below {
41534         border-bottom:1px dotted #3366cc;
41535 }
41536 </code></pre>
41537  * 
41538  */
41539  
41540 Roo.DDView = function(container, tpl, config) {
41541     Roo.DDView.superclass.constructor.apply(this, arguments);
41542     this.getEl().setStyle("outline", "0px none");
41543     this.getEl().unselectable();
41544     if (this.dragGroup) {
41545                 this.setDraggable(this.dragGroup.split(","));
41546     }
41547     if (this.dropGroup) {
41548                 this.setDroppable(this.dropGroup.split(","));
41549     }
41550     if (this.deletable) {
41551         this.setDeletable();
41552     }
41553     this.isDirtyFlag = false;
41554         this.addEvents({
41555                 "drop" : true
41556         });
41557 };
41558
41559 Roo.extend(Roo.DDView, Roo.View, {
41560 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41561 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41562 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41563 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41564
41565         isFormField: true,
41566
41567         reset: Roo.emptyFn,
41568         
41569         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41570
41571         validate: function() {
41572                 return true;
41573         },
41574         
41575         destroy: function() {
41576                 this.purgeListeners();
41577                 this.getEl.removeAllListeners();
41578                 this.getEl().remove();
41579                 if (this.dragZone) {
41580                         if (this.dragZone.destroy) {
41581                                 this.dragZone.destroy();
41582                         }
41583                 }
41584                 if (this.dropZone) {
41585                         if (this.dropZone.destroy) {
41586                                 this.dropZone.destroy();
41587                         }
41588                 }
41589         },
41590
41591 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41592         getName: function() {
41593                 return this.name;
41594         },
41595
41596 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41597         setValue: function(v) {
41598                 if (!this.store) {
41599                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41600                 }
41601                 var data = {};
41602                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41603                 this.store.proxy = new Roo.data.MemoryProxy(data);
41604                 this.store.load();
41605         },
41606
41607 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41608         getValue: function() {
41609                 var result = '(';
41610                 this.store.each(function(rec) {
41611                         result += rec.id + ',';
41612                 });
41613                 return result.substr(0, result.length - 1) + ')';
41614         },
41615         
41616         getIds: function() {
41617                 var i = 0, result = new Array(this.store.getCount());
41618                 this.store.each(function(rec) {
41619                         result[i++] = rec.id;
41620                 });
41621                 return result;
41622         },
41623         
41624         isDirty: function() {
41625                 return this.isDirtyFlag;
41626         },
41627
41628 /**
41629  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41630  *      whole Element becomes the target, and this causes the drop gesture to append.
41631  */
41632     getTargetFromEvent : function(e) {
41633                 var target = e.getTarget();
41634                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41635                 target = target.parentNode;
41636                 }
41637                 if (!target) {
41638                         target = this.el.dom.lastChild || this.el.dom;
41639                 }
41640                 return target;
41641     },
41642
41643 /**
41644  *      Create the drag data which consists of an object which has the property "ddel" as
41645  *      the drag proxy element. 
41646  */
41647     getDragData : function(e) {
41648         var target = this.findItemFromChild(e.getTarget());
41649                 if(target) {
41650                         this.handleSelection(e);
41651                         var selNodes = this.getSelectedNodes();
41652             var dragData = {
41653                 source: this,
41654                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41655                 nodes: selNodes,
41656                 records: []
41657                         };
41658                         var selectedIndices = this.getSelectedIndexes();
41659                         for (var i = 0; i < selectedIndices.length; i++) {
41660                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41661                         }
41662                         if (selNodes.length == 1) {
41663                                 dragData.ddel = target.cloneNode(true); // the div element
41664                         } else {
41665                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41666                                 div.className = 'multi-proxy';
41667                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41668                                         div.appendChild(selNodes[i].cloneNode(true));
41669                                 }
41670                                 dragData.ddel = div;
41671                         }
41672             //console.log(dragData)
41673             //console.log(dragData.ddel.innerHTML)
41674                         return dragData;
41675                 }
41676         //console.log('nodragData')
41677                 return false;
41678     },
41679     
41680 /**     Specify to which ddGroup items in this DDView may be dragged. */
41681     setDraggable: function(ddGroup) {
41682         if (ddGroup instanceof Array) {
41683                 Roo.each(ddGroup, this.setDraggable, this);
41684                 return;
41685         }
41686         if (this.dragZone) {
41687                 this.dragZone.addToGroup(ddGroup);
41688         } else {
41689                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41690                                 containerScroll: true,
41691                                 ddGroup: ddGroup 
41692
41693                         });
41694 //                      Draggability implies selection. DragZone's mousedown selects the element.
41695                         if (!this.multiSelect) { this.singleSelect = true; }
41696
41697 //                      Wire the DragZone's handlers up to methods in *this*
41698                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41699                 }
41700     },
41701
41702 /**     Specify from which ddGroup this DDView accepts drops. */
41703     setDroppable: function(ddGroup) {
41704         if (ddGroup instanceof Array) {
41705                 Roo.each(ddGroup, this.setDroppable, this);
41706                 return;
41707         }
41708         if (this.dropZone) {
41709                 this.dropZone.addToGroup(ddGroup);
41710         } else {
41711                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41712                                 containerScroll: true,
41713                                 ddGroup: ddGroup
41714                         });
41715
41716 //                      Wire the DropZone's handlers up to methods in *this*
41717                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41718                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41719                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41720                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41721                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41722                 }
41723     },
41724
41725 /**     Decide whether to drop above or below a View node. */
41726     getDropPoint : function(e, n, dd){
41727         if (n == this.el.dom) { return "above"; }
41728                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41729                 var c = t + (b - t) / 2;
41730                 var y = Roo.lib.Event.getPageY(e);
41731                 if(y <= c) {
41732                         return "above";
41733                 }else{
41734                         return "below";
41735                 }
41736     },
41737
41738     onNodeEnter : function(n, dd, e, data){
41739                 return false;
41740     },
41741     
41742     onNodeOver : function(n, dd, e, data){
41743                 var pt = this.getDropPoint(e, n, dd);
41744                 // set the insert point style on the target node
41745                 var dragElClass = this.dropNotAllowed;
41746                 if (pt) {
41747                         var targetElClass;
41748                         if (pt == "above"){
41749                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41750                                 targetElClass = "x-view-drag-insert-above";
41751                         } else {
41752                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41753                                 targetElClass = "x-view-drag-insert-below";
41754                         }
41755                         if (this.lastInsertClass != targetElClass){
41756                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41757                                 this.lastInsertClass = targetElClass;
41758                         }
41759                 }
41760                 return dragElClass;
41761         },
41762
41763     onNodeOut : function(n, dd, e, data){
41764                 this.removeDropIndicators(n);
41765     },
41766
41767     onNodeDrop : function(n, dd, e, data){
41768         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41769                 return false;
41770         }
41771         var pt = this.getDropPoint(e, n, dd);
41772                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41773                 if (pt == "below") { insertAt++; }
41774                 for (var i = 0; i < data.records.length; i++) {
41775                         var r = data.records[i];
41776                         var dup = this.store.getById(r.id);
41777                         if (dup && (dd != this.dragZone)) {
41778                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41779                         } else {
41780                                 if (data.copy) {
41781                                         this.store.insert(insertAt++, r.copy());
41782                                 } else {
41783                                         data.source.isDirtyFlag = true;
41784                                         r.store.remove(r);
41785                                         this.store.insert(insertAt++, r);
41786                                 }
41787                                 this.isDirtyFlag = true;
41788                         }
41789                 }
41790                 this.dragZone.cachedTarget = null;
41791                 return true;
41792     },
41793
41794     removeDropIndicators : function(n){
41795                 if(n){
41796                         Roo.fly(n).removeClass([
41797                                 "x-view-drag-insert-above",
41798                                 "x-view-drag-insert-below"]);
41799                         this.lastInsertClass = "_noclass";
41800                 }
41801     },
41802
41803 /**
41804  *      Utility method. Add a delete option to the DDView's context menu.
41805  *      @param {String} imageUrl The URL of the "delete" icon image.
41806  */
41807         setDeletable: function(imageUrl) {
41808                 if (!this.singleSelect && !this.multiSelect) {
41809                         this.singleSelect = true;
41810                 }
41811                 var c = this.getContextMenu();
41812                 this.contextMenu.on("itemclick", function(item) {
41813                         switch (item.id) {
41814                                 case "delete":
41815                                         this.remove(this.getSelectedIndexes());
41816                                         break;
41817                         }
41818                 }, this);
41819                 this.contextMenu.add({
41820                         icon: imageUrl,
41821                         id: "delete",
41822                         text: 'Delete'
41823                 });
41824         },
41825         
41826 /**     Return the context menu for this DDView. */
41827         getContextMenu: function() {
41828                 if (!this.contextMenu) {
41829 //                      Create the View's context menu
41830                         this.contextMenu = new Roo.menu.Menu({
41831                                 id: this.id + "-contextmenu"
41832                         });
41833                         this.el.on("contextmenu", this.showContextMenu, this);
41834                 }
41835                 return this.contextMenu;
41836         },
41837         
41838         disableContextMenu: function() {
41839                 if (this.contextMenu) {
41840                         this.el.un("contextmenu", this.showContextMenu, this);
41841                 }
41842         },
41843
41844         showContextMenu: function(e, item) {
41845         item = this.findItemFromChild(e.getTarget());
41846                 if (item) {
41847                         e.stopEvent();
41848                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
41849                         this.contextMenu.showAt(e.getXY());
41850             }
41851     },
41852
41853 /**
41854  *      Remove {@link Roo.data.Record}s at the specified indices.
41855  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
41856  */
41857     remove: function(selectedIndices) {
41858                 selectedIndices = [].concat(selectedIndices);
41859                 for (var i = 0; i < selectedIndices.length; i++) {
41860                         var rec = this.store.getAt(selectedIndices[i]);
41861                         this.store.remove(rec);
41862                 }
41863     },
41864
41865 /**
41866  *      Double click fires the event, but also, if this is draggable, and there is only one other
41867  *      related DropZone, it transfers the selected node.
41868  */
41869     onDblClick : function(e){
41870         var item = this.findItemFromChild(e.getTarget());
41871         if(item){
41872             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
41873                 return false;
41874             }
41875             if (this.dragGroup) {
41876                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
41877                     while (targets.indexOf(this.dropZone) > -1) {
41878                             targets.remove(this.dropZone);
41879                                 }
41880                     if (targets.length == 1) {
41881                                         this.dragZone.cachedTarget = null;
41882                         var el = Roo.get(targets[0].getEl());
41883                         var box = el.getBox(true);
41884                         targets[0].onNodeDrop(el.dom, {
41885                                 target: el.dom,
41886                                 xy: [box.x, box.y + box.height - 1]
41887                         }, null, this.getDragData(e));
41888                     }
41889                 }
41890         }
41891     },
41892     
41893     handleSelection: function(e) {
41894                 this.dragZone.cachedTarget = null;
41895         var item = this.findItemFromChild(e.getTarget());
41896         if (!item) {
41897                 this.clearSelections(true);
41898                 return;
41899         }
41900                 if (item && (this.multiSelect || this.singleSelect)){
41901                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
41902                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
41903                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
41904                                 this.unselect(item);
41905                         } else {
41906                                 this.select(item, this.multiSelect && e.ctrlKey);
41907                                 this.lastSelection = item;
41908                         }
41909                 }
41910     },
41911
41912     onItemClick : function(item, index, e){
41913                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
41914                         return false;
41915                 }
41916                 return true;
41917     },
41918
41919     unselect : function(nodeInfo, suppressEvent){
41920                 var node = this.getNode(nodeInfo);
41921                 if(node && this.isSelected(node)){
41922                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
41923                                 Roo.fly(node).removeClass(this.selectedClass);
41924                                 this.selections.remove(node);
41925                                 if(!suppressEvent){
41926                                         this.fireEvent("selectionchange", this, this.selections);
41927                                 }
41928                         }
41929                 }
41930     }
41931 });
41932 /*
41933  * Based on:
41934  * Ext JS Library 1.1.1
41935  * Copyright(c) 2006-2007, Ext JS, LLC.
41936  *
41937  * Originally Released Under LGPL - original licence link has changed is not relivant.
41938  *
41939  * Fork - LGPL
41940  * <script type="text/javascript">
41941  */
41942  
41943 /**
41944  * @class Roo.LayoutManager
41945  * @extends Roo.util.Observable
41946  * Base class for layout managers.
41947  */
41948 Roo.LayoutManager = function(container, config){
41949     Roo.LayoutManager.superclass.constructor.call(this);
41950     this.el = Roo.get(container);
41951     // ie scrollbar fix
41952     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
41953         document.body.scroll = "no";
41954     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
41955         this.el.position('relative');
41956     }
41957     this.id = this.el.id;
41958     this.el.addClass("x-layout-container");
41959     /** false to disable window resize monitoring @type Boolean */
41960     this.monitorWindowResize = true;
41961     this.regions = {};
41962     this.addEvents({
41963         /**
41964          * @event layout
41965          * Fires when a layout is performed. 
41966          * @param {Roo.LayoutManager} this
41967          */
41968         "layout" : true,
41969         /**
41970          * @event regionresized
41971          * Fires when the user resizes a region. 
41972          * @param {Roo.LayoutRegion} region The resized region
41973          * @param {Number} newSize The new size (width for east/west, height for north/south)
41974          */
41975         "regionresized" : true,
41976         /**
41977          * @event regioncollapsed
41978          * Fires when a region is collapsed. 
41979          * @param {Roo.LayoutRegion} region The collapsed region
41980          */
41981         "regioncollapsed" : true,
41982         /**
41983          * @event regionexpanded
41984          * Fires when a region is expanded.  
41985          * @param {Roo.LayoutRegion} region The expanded region
41986          */
41987         "regionexpanded" : true
41988     });
41989     this.updating = false;
41990     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
41991 };
41992
41993 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
41994     /**
41995      * Returns true if this layout is currently being updated
41996      * @return {Boolean}
41997      */
41998     isUpdating : function(){
41999         return this.updating; 
42000     },
42001     
42002     /**
42003      * Suspend the LayoutManager from doing auto-layouts while
42004      * making multiple add or remove calls
42005      */
42006     beginUpdate : function(){
42007         this.updating = true;    
42008     },
42009     
42010     /**
42011      * Restore auto-layouts and optionally disable the manager from performing a layout
42012      * @param {Boolean} noLayout true to disable a layout update 
42013      */
42014     endUpdate : function(noLayout){
42015         this.updating = false;
42016         if(!noLayout){
42017             this.layout();
42018         }    
42019     },
42020     
42021     layout: function(){
42022         
42023     },
42024     
42025     onRegionResized : function(region, newSize){
42026         this.fireEvent("regionresized", region, newSize);
42027         this.layout();
42028     },
42029     
42030     onRegionCollapsed : function(region){
42031         this.fireEvent("regioncollapsed", region);
42032     },
42033     
42034     onRegionExpanded : function(region){
42035         this.fireEvent("regionexpanded", region);
42036     },
42037         
42038     /**
42039      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42040      * performs box-model adjustments.
42041      * @return {Object} The size as an object {width: (the width), height: (the height)}
42042      */
42043     getViewSize : function(){
42044         var size;
42045         if(this.el.dom != document.body){
42046             size = this.el.getSize();
42047         }else{
42048             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42049         }
42050         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42051         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42052         return size;
42053     },
42054     
42055     /**
42056      * Returns the Element this layout is bound to.
42057      * @return {Roo.Element}
42058      */
42059     getEl : function(){
42060         return this.el;
42061     },
42062     
42063     /**
42064      * Returns the specified region.
42065      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42066      * @return {Roo.LayoutRegion}
42067      */
42068     getRegion : function(target){
42069         return this.regions[target.toLowerCase()];
42070     },
42071     
42072     onWindowResize : function(){
42073         if(this.monitorWindowResize){
42074             this.layout();
42075         }
42076     }
42077 });/*
42078  * Based on:
42079  * Ext JS Library 1.1.1
42080  * Copyright(c) 2006-2007, Ext JS, LLC.
42081  *
42082  * Originally Released Under LGPL - original licence link has changed is not relivant.
42083  *
42084  * Fork - LGPL
42085  * <script type="text/javascript">
42086  */
42087 /**
42088  * @class Roo.BorderLayout
42089  * @extends Roo.LayoutManager
42090  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42091  * please see: <br><br>
42092  * <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>
42093  * <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>
42094  * Example:
42095  <pre><code>
42096  var layout = new Roo.BorderLayout(document.body, {
42097     north: {
42098         initialSize: 25,
42099         titlebar: false
42100     },
42101     west: {
42102         split:true,
42103         initialSize: 200,
42104         minSize: 175,
42105         maxSize: 400,
42106         titlebar: true,
42107         collapsible: true
42108     },
42109     east: {
42110         split:true,
42111         initialSize: 202,
42112         minSize: 175,
42113         maxSize: 400,
42114         titlebar: true,
42115         collapsible: true
42116     },
42117     south: {
42118         split:true,
42119         initialSize: 100,
42120         minSize: 100,
42121         maxSize: 200,
42122         titlebar: true,
42123         collapsible: true
42124     },
42125     center: {
42126         titlebar: true,
42127         autoScroll:true,
42128         resizeTabs: true,
42129         minTabWidth: 50,
42130         preferredTabWidth: 150
42131     }
42132 });
42133
42134 // shorthand
42135 var CP = Roo.ContentPanel;
42136
42137 layout.beginUpdate();
42138 layout.add("north", new CP("north", "North"));
42139 layout.add("south", new CP("south", {title: "South", closable: true}));
42140 layout.add("west", new CP("west", {title: "West"}));
42141 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42142 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42143 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42144 layout.getRegion("center").showPanel("center1");
42145 layout.endUpdate();
42146 </code></pre>
42147
42148 <b>The container the layout is rendered into can be either the body element or any other element.
42149 If it is not the body element, the container needs to either be an absolute positioned element,
42150 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42151 the container size if it is not the body element.</b>
42152
42153 * @constructor
42154 * Create a new BorderLayout
42155 * @param {String/HTMLElement/Element} container The container this layout is bound to
42156 * @param {Object} config Configuration options
42157  */
42158 Roo.BorderLayout = function(container, config){
42159     config = config || {};
42160     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42161     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42162     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42163         var target = this.factory.validRegions[i];
42164         if(config[target]){
42165             this.addRegion(target, config[target]);
42166         }
42167     }
42168 };
42169
42170 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42171     /**
42172      * Creates and adds a new region if it doesn't already exist.
42173      * @param {String} target The target region key (north, south, east, west or center).
42174      * @param {Object} config The regions config object
42175      * @return {BorderLayoutRegion} The new region
42176      */
42177     addRegion : function(target, config){
42178         if(!this.regions[target]){
42179             var r = this.factory.create(target, this, config);
42180             this.bindRegion(target, r);
42181         }
42182         return this.regions[target];
42183     },
42184
42185     // private (kinda)
42186     bindRegion : function(name, r){
42187         this.regions[name] = r;
42188         r.on("visibilitychange", this.layout, this);
42189         r.on("paneladded", this.layout, this);
42190         r.on("panelremoved", this.layout, this);
42191         r.on("invalidated", this.layout, this);
42192         r.on("resized", this.onRegionResized, this);
42193         r.on("collapsed", this.onRegionCollapsed, this);
42194         r.on("expanded", this.onRegionExpanded, this);
42195     },
42196
42197     /**
42198      * Performs a layout update.
42199      */
42200     layout : function(){
42201         if(this.updating) return;
42202         var size = this.getViewSize();
42203         var w = size.width;
42204         var h = size.height;
42205         var centerW = w;
42206         var centerH = h;
42207         var centerY = 0;
42208         var centerX = 0;
42209         //var x = 0, y = 0;
42210
42211         var rs = this.regions;
42212         var north = rs["north"];
42213         var south = rs["south"]; 
42214         var west = rs["west"];
42215         var east = rs["east"];
42216         var center = rs["center"];
42217         //if(this.hideOnLayout){ // not supported anymore
42218             //c.el.setStyle("display", "none");
42219         //}
42220         if(north && north.isVisible()){
42221             var b = north.getBox();
42222             var m = north.getMargins();
42223             b.width = w - (m.left+m.right);
42224             b.x = m.left;
42225             b.y = m.top;
42226             centerY = b.height + b.y + m.bottom;
42227             centerH -= centerY;
42228             north.updateBox(this.safeBox(b));
42229         }
42230         if(south && south.isVisible()){
42231             var b = south.getBox();
42232             var m = south.getMargins();
42233             b.width = w - (m.left+m.right);
42234             b.x = m.left;
42235             var totalHeight = (b.height + m.top + m.bottom);
42236             b.y = h - totalHeight + m.top;
42237             centerH -= totalHeight;
42238             south.updateBox(this.safeBox(b));
42239         }
42240         if(west && west.isVisible()){
42241             var b = west.getBox();
42242             var m = west.getMargins();
42243             b.height = centerH - (m.top+m.bottom);
42244             b.x = m.left;
42245             b.y = centerY + m.top;
42246             var totalWidth = (b.width + m.left + m.right);
42247             centerX += totalWidth;
42248             centerW -= totalWidth;
42249             west.updateBox(this.safeBox(b));
42250         }
42251         if(east && east.isVisible()){
42252             var b = east.getBox();
42253             var m = east.getMargins();
42254             b.height = centerH - (m.top+m.bottom);
42255             var totalWidth = (b.width + m.left + m.right);
42256             b.x = w - totalWidth + m.left;
42257             b.y = centerY + m.top;
42258             centerW -= totalWidth;
42259             east.updateBox(this.safeBox(b));
42260         }
42261         if(center){
42262             var m = center.getMargins();
42263             var centerBox = {
42264                 x: centerX + m.left,
42265                 y: centerY + m.top,
42266                 width: centerW - (m.left+m.right),
42267                 height: centerH - (m.top+m.bottom)
42268             };
42269             //if(this.hideOnLayout){
42270                 //center.el.setStyle("display", "block");
42271             //}
42272             center.updateBox(this.safeBox(centerBox));
42273         }
42274         this.el.repaint();
42275         this.fireEvent("layout", this);
42276     },
42277
42278     // private
42279     safeBox : function(box){
42280         box.width = Math.max(0, box.width);
42281         box.height = Math.max(0, box.height);
42282         return box;
42283     },
42284
42285     /**
42286      * Adds a ContentPanel (or subclass) to this layout.
42287      * @param {String} target The target region key (north, south, east, west or center).
42288      * @param {Roo.ContentPanel} panel The panel to add
42289      * @return {Roo.ContentPanel} The added panel
42290      */
42291     add : function(target, panel){
42292          
42293         target = target.toLowerCase();
42294         return this.regions[target].add(panel);
42295     },
42296
42297     /**
42298      * Remove a ContentPanel (or subclass) to this layout.
42299      * @param {String} target The target region key (north, south, east, west or center).
42300      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42301      * @return {Roo.ContentPanel} The removed panel
42302      */
42303     remove : function(target, panel){
42304         target = target.toLowerCase();
42305         return this.regions[target].remove(panel);
42306     },
42307
42308     /**
42309      * Searches all regions for a panel with the specified id
42310      * @param {String} panelId
42311      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42312      */
42313     findPanel : function(panelId){
42314         var rs = this.regions;
42315         for(var target in rs){
42316             if(typeof rs[target] != "function"){
42317                 var p = rs[target].getPanel(panelId);
42318                 if(p){
42319                     return p;
42320                 }
42321             }
42322         }
42323         return null;
42324     },
42325
42326     /**
42327      * Searches all regions for a panel with the specified id and activates (shows) it.
42328      * @param {String/ContentPanel} panelId The panels id or the panel itself
42329      * @return {Roo.ContentPanel} The shown panel or null
42330      */
42331     showPanel : function(panelId) {
42332       var rs = this.regions;
42333       for(var target in rs){
42334          var r = rs[target];
42335          if(typeof r != "function"){
42336             if(r.hasPanel(panelId)){
42337                return r.showPanel(panelId);
42338             }
42339          }
42340       }
42341       return null;
42342    },
42343
42344    /**
42345      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42346      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42347      */
42348     restoreState : function(provider){
42349         if(!provider){
42350             provider = Roo.state.Manager;
42351         }
42352         var sm = new Roo.LayoutStateManager();
42353         sm.init(this, provider);
42354     },
42355
42356     /**
42357      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42358      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42359      * a valid ContentPanel config object.  Example:
42360      * <pre><code>
42361 // Create the main layout
42362 var layout = new Roo.BorderLayout('main-ct', {
42363     west: {
42364         split:true,
42365         minSize: 175,
42366         titlebar: true
42367     },
42368     center: {
42369         title:'Components'
42370     }
42371 }, 'main-ct');
42372
42373 // Create and add multiple ContentPanels at once via configs
42374 layout.batchAdd({
42375    west: {
42376        id: 'source-files',
42377        autoCreate:true,
42378        title:'Ext Source Files',
42379        autoScroll:true,
42380        fitToFrame:true
42381    },
42382    center : {
42383        el: cview,
42384        autoScroll:true,
42385        fitToFrame:true,
42386        toolbar: tb,
42387        resizeEl:'cbody'
42388    }
42389 });
42390 </code></pre>
42391      * @param {Object} regions An object containing ContentPanel configs by region name
42392      */
42393     batchAdd : function(regions){
42394         this.beginUpdate();
42395         for(var rname in regions){
42396             var lr = this.regions[rname];
42397             if(lr){
42398                 this.addTypedPanels(lr, regions[rname]);
42399             }
42400         }
42401         this.endUpdate();
42402     },
42403
42404     // private
42405     addTypedPanels : function(lr, ps){
42406         if(typeof ps == 'string'){
42407             lr.add(new Roo.ContentPanel(ps));
42408         }
42409         else if(ps instanceof Array){
42410             for(var i =0, len = ps.length; i < len; i++){
42411                 this.addTypedPanels(lr, ps[i]);
42412             }
42413         }
42414         else if(!ps.events){ // raw config?
42415             var el = ps.el;
42416             delete ps.el; // prevent conflict
42417             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42418         }
42419         else {  // panel object assumed!
42420             lr.add(ps);
42421         }
42422     },
42423     /**
42424      * Adds a xtype elements to the layout.
42425      * <pre><code>
42426
42427 layout.addxtype({
42428        xtype : 'ContentPanel',
42429        region: 'west',
42430        items: [ .... ]
42431    }
42432 );
42433
42434 layout.addxtype({
42435         xtype : 'NestedLayoutPanel',
42436         region: 'west',
42437         layout: {
42438            center: { },
42439            west: { }   
42440         },
42441         items : [ ... list of content panels or nested layout panels.. ]
42442    }
42443 );
42444 </code></pre>
42445      * @param {Object} cfg Xtype definition of item to add.
42446      */
42447     addxtype : function(cfg)
42448     {
42449         // basically accepts a pannel...
42450         // can accept a layout region..!?!?
42451        // console.log('BorderLayout add ' + cfg.xtype)
42452         
42453         if (!cfg.xtype.match(/Panel$/)) {
42454             return false;
42455         }
42456         var ret = false;
42457         var region = cfg.region;
42458         delete cfg.region;
42459         
42460           
42461         var xitems = [];
42462         if (cfg.items) {
42463             xitems = cfg.items;
42464             delete cfg.items;
42465         }
42466         
42467         
42468         switch(cfg.xtype) 
42469         {
42470             case 'ContentPanel':  // ContentPanel (el, cfg)
42471                 if(cfg.autoCreate) {
42472                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42473                 } else {
42474                     var el = this.el.createChild();
42475                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42476                 }
42477                 
42478                 this.add(region, ret);
42479                 break;
42480             
42481             
42482             case 'TreePanel': // our new panel!
42483                 cfg.el = this.el.createChild();
42484                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42485                 this.add(region, ret);
42486                 break;
42487             
42488             case 'NestedLayoutPanel': 
42489                 // create a new Layout (which is  a Border Layout...
42490                 var el = this.el.createChild();
42491                 var clayout = cfg.layout;
42492                 delete cfg.layout;
42493                 clayout.items   = clayout.items  || [];
42494                 // replace this exitems with the clayout ones..
42495                 xitems = clayout.items;
42496                  
42497                 
42498                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42499                     cfg.background = false;
42500                 }
42501                 var layout = new Roo.BorderLayout(el, clayout);
42502                 
42503                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42504                 //console.log('adding nested layout panel '  + cfg.toSource());
42505                 this.add(region, ret);
42506                 
42507                 break;
42508                 
42509             case 'GridPanel': 
42510             
42511                 // needs grid and region
42512                 
42513                 //var el = this.getRegion(region).el.createChild();
42514                 var el = this.el.createChild();
42515                 // create the grid first...
42516                 
42517                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42518                 delete cfg.grid;
42519                 if (region == 'center' && this.active ) {
42520                     cfg.background = false;
42521                 }
42522                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42523                 
42524                 this.add(region, ret);
42525                 if (cfg.background) {
42526                     ret.on('activate', function(gp) {
42527                         if (!gp.grid.rendered) {
42528                             gp.grid.render();
42529                         }
42530                     });
42531                 } else {
42532                     grid.render();
42533                 }
42534                 break;
42535            
42536                
42537                 
42538                 
42539             default: 
42540                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42541                 return;
42542              // GridPanel (grid, cfg)
42543             
42544         }
42545         this.beginUpdate();
42546         // add children..
42547         Roo.each(xitems, function(i)  {
42548             ret.addxtype(i);
42549         });
42550         this.endUpdate();
42551         return ret;
42552         
42553     }
42554 });
42555
42556 /**
42557  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42558  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42559  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42560  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42561  * <pre><code>
42562 // shorthand
42563 var CP = Roo.ContentPanel;
42564
42565 var layout = Roo.BorderLayout.create({
42566     north: {
42567         initialSize: 25,
42568         titlebar: false,
42569         panels: [new CP("north", "North")]
42570     },
42571     west: {
42572         split:true,
42573         initialSize: 200,
42574         minSize: 175,
42575         maxSize: 400,
42576         titlebar: true,
42577         collapsible: true,
42578         panels: [new CP("west", {title: "West"})]
42579     },
42580     east: {
42581         split:true,
42582         initialSize: 202,
42583         minSize: 175,
42584         maxSize: 400,
42585         titlebar: true,
42586         collapsible: true,
42587         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42588     },
42589     south: {
42590         split:true,
42591         initialSize: 100,
42592         minSize: 100,
42593         maxSize: 200,
42594         titlebar: true,
42595         collapsible: true,
42596         panels: [new CP("south", {title: "South", closable: true})]
42597     },
42598     center: {
42599         titlebar: true,
42600         autoScroll:true,
42601         resizeTabs: true,
42602         minTabWidth: 50,
42603         preferredTabWidth: 150,
42604         panels: [
42605             new CP("center1", {title: "Close Me", closable: true}),
42606             new CP("center2", {title: "Center Panel", closable: false})
42607         ]
42608     }
42609 }, document.body);
42610
42611 layout.getRegion("center").showPanel("center1");
42612 </code></pre>
42613  * @param config
42614  * @param targetEl
42615  */
42616 Roo.BorderLayout.create = function(config, targetEl){
42617     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42618     layout.beginUpdate();
42619     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42620     for(var j = 0, jlen = regions.length; j < jlen; j++){
42621         var lr = regions[j];
42622         if(layout.regions[lr] && config[lr].panels){
42623             var r = layout.regions[lr];
42624             var ps = config[lr].panels;
42625             layout.addTypedPanels(r, ps);
42626         }
42627     }
42628     layout.endUpdate();
42629     return layout;
42630 };
42631
42632 // private
42633 Roo.BorderLayout.RegionFactory = {
42634     // private
42635     validRegions : ["north","south","east","west","center"],
42636
42637     // private
42638     create : function(target, mgr, config){
42639         target = target.toLowerCase();
42640         if(config.lightweight || config.basic){
42641             return new Roo.BasicLayoutRegion(mgr, config, target);
42642         }
42643         switch(target){
42644             case "north":
42645                 return new Roo.NorthLayoutRegion(mgr, config);
42646             case "south":
42647                 return new Roo.SouthLayoutRegion(mgr, config);
42648             case "east":
42649                 return new Roo.EastLayoutRegion(mgr, config);
42650             case "west":
42651                 return new Roo.WestLayoutRegion(mgr, config);
42652             case "center":
42653                 return new Roo.CenterLayoutRegion(mgr, config);
42654         }
42655         throw 'Layout region "'+target+'" not supported.';
42656     }
42657 };/*
42658  * Based on:
42659  * Ext JS Library 1.1.1
42660  * Copyright(c) 2006-2007, Ext JS, LLC.
42661  *
42662  * Originally Released Under LGPL - original licence link has changed is not relivant.
42663  *
42664  * Fork - LGPL
42665  * <script type="text/javascript">
42666  */
42667  
42668 /**
42669  * @class Roo.BasicLayoutRegion
42670  * @extends Roo.util.Observable
42671  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42672  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42673  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42674  */
42675 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42676     this.mgr = mgr;
42677     this.position  = pos;
42678     this.events = {
42679         /**
42680          * @scope Roo.BasicLayoutRegion
42681          */
42682         
42683         /**
42684          * @event beforeremove
42685          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42686          * @param {Roo.LayoutRegion} this
42687          * @param {Roo.ContentPanel} panel The panel
42688          * @param {Object} e The cancel event object
42689          */
42690         "beforeremove" : true,
42691         /**
42692          * @event invalidated
42693          * Fires when the layout for this region is changed.
42694          * @param {Roo.LayoutRegion} this
42695          */
42696         "invalidated" : true,
42697         /**
42698          * @event visibilitychange
42699          * Fires when this region is shown or hidden 
42700          * @param {Roo.LayoutRegion} this
42701          * @param {Boolean} visibility true or false
42702          */
42703         "visibilitychange" : true,
42704         /**
42705          * @event paneladded
42706          * Fires when a panel is added. 
42707          * @param {Roo.LayoutRegion} this
42708          * @param {Roo.ContentPanel} panel The panel
42709          */
42710         "paneladded" : true,
42711         /**
42712          * @event panelremoved
42713          * Fires when a panel is removed. 
42714          * @param {Roo.LayoutRegion} this
42715          * @param {Roo.ContentPanel} panel The panel
42716          */
42717         "panelremoved" : true,
42718         /**
42719          * @event collapsed
42720          * Fires when this region is collapsed.
42721          * @param {Roo.LayoutRegion} this
42722          */
42723         "collapsed" : true,
42724         /**
42725          * @event expanded
42726          * Fires when this region is expanded.
42727          * @param {Roo.LayoutRegion} this
42728          */
42729         "expanded" : true,
42730         /**
42731          * @event slideshow
42732          * Fires when this region is slid into view.
42733          * @param {Roo.LayoutRegion} this
42734          */
42735         "slideshow" : true,
42736         /**
42737          * @event slidehide
42738          * Fires when this region slides out of view. 
42739          * @param {Roo.LayoutRegion} this
42740          */
42741         "slidehide" : true,
42742         /**
42743          * @event panelactivated
42744          * Fires when a panel is activated. 
42745          * @param {Roo.LayoutRegion} this
42746          * @param {Roo.ContentPanel} panel The activated panel
42747          */
42748         "panelactivated" : true,
42749         /**
42750          * @event resized
42751          * Fires when the user resizes this region. 
42752          * @param {Roo.LayoutRegion} this
42753          * @param {Number} newSize The new size (width for east/west, height for north/south)
42754          */
42755         "resized" : true
42756     };
42757     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42758     this.panels = new Roo.util.MixedCollection();
42759     this.panels.getKey = this.getPanelId.createDelegate(this);
42760     this.box = null;
42761     this.activePanel = null;
42762     // ensure listeners are added...
42763     
42764     if (config.listeners || config.events) {
42765         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42766             listeners : config.listeners || {},
42767             events : config.events || {}
42768         });
42769     }
42770     
42771     if(skipConfig !== true){
42772         this.applyConfig(config);
42773     }
42774 };
42775
42776 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42777     getPanelId : function(p){
42778         return p.getId();
42779     },
42780     
42781     applyConfig : function(config){
42782         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42783         this.config = config;
42784         
42785     },
42786     
42787     /**
42788      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42789      * the width, for horizontal (north, south) the height.
42790      * @param {Number} newSize The new width or height
42791      */
42792     resizeTo : function(newSize){
42793         var el = this.el ? this.el :
42794                  (this.activePanel ? this.activePanel.getEl() : null);
42795         if(el){
42796             switch(this.position){
42797                 case "east":
42798                 case "west":
42799                     el.setWidth(newSize);
42800                     this.fireEvent("resized", this, newSize);
42801                 break;
42802                 case "north":
42803                 case "south":
42804                     el.setHeight(newSize);
42805                     this.fireEvent("resized", this, newSize);
42806                 break;                
42807             }
42808         }
42809     },
42810     
42811     getBox : function(){
42812         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42813     },
42814     
42815     getMargins : function(){
42816         return this.margins;
42817     },
42818     
42819     updateBox : function(box){
42820         this.box = box;
42821         var el = this.activePanel.getEl();
42822         el.dom.style.left = box.x + "px";
42823         el.dom.style.top = box.y + "px";
42824         this.activePanel.setSize(box.width, box.height);
42825     },
42826     
42827     /**
42828      * Returns the container element for this region.
42829      * @return {Roo.Element}
42830      */
42831     getEl : function(){
42832         return this.activePanel;
42833     },
42834     
42835     /**
42836      * Returns true if this region is currently visible.
42837      * @return {Boolean}
42838      */
42839     isVisible : function(){
42840         return this.activePanel ? true : false;
42841     },
42842     
42843     setActivePanel : function(panel){
42844         panel = this.getPanel(panel);
42845         if(this.activePanel && this.activePanel != panel){
42846             this.activePanel.setActiveState(false);
42847             this.activePanel.getEl().setLeftTop(-10000,-10000);
42848         }
42849         this.activePanel = panel;
42850         panel.setActiveState(true);
42851         if(this.box){
42852             panel.setSize(this.box.width, this.box.height);
42853         }
42854         this.fireEvent("panelactivated", this, panel);
42855         this.fireEvent("invalidated");
42856     },
42857     
42858     /**
42859      * Show the specified panel.
42860      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42861      * @return {Roo.ContentPanel} The shown panel or null
42862      */
42863     showPanel : function(panel){
42864         if(panel = this.getPanel(panel)){
42865             this.setActivePanel(panel);
42866         }
42867         return panel;
42868     },
42869     
42870     /**
42871      * Get the active panel for this region.
42872      * @return {Roo.ContentPanel} The active panel or null
42873      */
42874     getActivePanel : function(){
42875         return this.activePanel;
42876     },
42877     
42878     /**
42879      * Add the passed ContentPanel(s)
42880      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42881      * @return {Roo.ContentPanel} The panel added (if only one was added)
42882      */
42883     add : function(panel){
42884         if(arguments.length > 1){
42885             for(var i = 0, len = arguments.length; i < len; i++) {
42886                 this.add(arguments[i]);
42887             }
42888             return null;
42889         }
42890         if(this.hasPanel(panel)){
42891             this.showPanel(panel);
42892             return panel;
42893         }
42894         var el = panel.getEl();
42895         if(el.dom.parentNode != this.mgr.el.dom){
42896             this.mgr.el.dom.appendChild(el.dom);
42897         }
42898         if(panel.setRegion){
42899             panel.setRegion(this);
42900         }
42901         this.panels.add(panel);
42902         el.setStyle("position", "absolute");
42903         if(!panel.background){
42904             this.setActivePanel(panel);
42905             if(this.config.initialSize && this.panels.getCount()==1){
42906                 this.resizeTo(this.config.initialSize);
42907             }
42908         }
42909         this.fireEvent("paneladded", this, panel);
42910         return panel;
42911     },
42912     
42913     /**
42914      * Returns true if the panel is in this region.
42915      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42916      * @return {Boolean}
42917      */
42918     hasPanel : function(panel){
42919         if(typeof panel == "object"){ // must be panel obj
42920             panel = panel.getId();
42921         }
42922         return this.getPanel(panel) ? true : false;
42923     },
42924     
42925     /**
42926      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42927      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42928      * @param {Boolean} preservePanel Overrides the config preservePanel option
42929      * @return {Roo.ContentPanel} The panel that was removed
42930      */
42931     remove : function(panel, preservePanel){
42932         panel = this.getPanel(panel);
42933         if(!panel){
42934             return null;
42935         }
42936         var e = {};
42937         this.fireEvent("beforeremove", this, panel, e);
42938         if(e.cancel === true){
42939             return null;
42940         }
42941         var panelId = panel.getId();
42942         this.panels.removeKey(panelId);
42943         return panel;
42944     },
42945     
42946     /**
42947      * Returns the panel specified or null if it's not in this region.
42948      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42949      * @return {Roo.ContentPanel}
42950      */
42951     getPanel : function(id){
42952         if(typeof id == "object"){ // must be panel obj
42953             return id;
42954         }
42955         return this.panels.get(id);
42956     },
42957     
42958     /**
42959      * Returns this regions position (north/south/east/west/center).
42960      * @return {String} 
42961      */
42962     getPosition: function(){
42963         return this.position;    
42964     }
42965 });/*
42966  * Based on:
42967  * Ext JS Library 1.1.1
42968  * Copyright(c) 2006-2007, Ext JS, LLC.
42969  *
42970  * Originally Released Under LGPL - original licence link has changed is not relivant.
42971  *
42972  * Fork - LGPL
42973  * <script type="text/javascript">
42974  */
42975  
42976 /**
42977  * @class Roo.LayoutRegion
42978  * @extends Roo.BasicLayoutRegion
42979  * This class represents a region in a layout manager.
42980  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
42981  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
42982  * @cfg {Boolean} floatable False to disable floating (defaults to true)
42983  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
42984  * @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})
42985  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
42986  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
42987  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
42988  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
42989  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
42990  * @cfg {String} title The title for the region (overrides panel titles)
42991  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
42992  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
42993  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
42994  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
42995  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
42996  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
42997  * the space available, similar to FireFox 1.5 tabs (defaults to false)
42998  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
42999  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43000  * @cfg {Boolean} showPin True to show a pin button
43001 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43002 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43003 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43004 * @cfg {Number} width  For East/West panels
43005 * @cfg {Number} height For North/South panels
43006 * @cfg {Boolean} split To show the splitter
43007  */
43008 Roo.LayoutRegion = function(mgr, config, pos){
43009     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43010     var dh = Roo.DomHelper;
43011     /** This region's container element 
43012     * @type Roo.Element */
43013     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43014     /** This region's title element 
43015     * @type Roo.Element */
43016
43017     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43018         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43019         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43020     ]}, true);
43021     this.titleEl.enableDisplayMode();
43022     /** This region's title text element 
43023     * @type HTMLElement */
43024     this.titleTextEl = this.titleEl.dom.firstChild;
43025     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43026     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43027     this.closeBtn.enableDisplayMode();
43028     this.closeBtn.on("click", this.closeClicked, this);
43029     this.closeBtn.hide();
43030
43031     this.createBody(config);
43032     this.visible = true;
43033     this.collapsed = false;
43034
43035     if(config.hideWhenEmpty){
43036         this.hide();
43037         this.on("paneladded", this.validateVisibility, this);
43038         this.on("panelremoved", this.validateVisibility, this);
43039     }
43040     this.applyConfig(config);
43041 };
43042
43043 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43044
43045     createBody : function(){
43046         /** This region's body element 
43047         * @type Roo.Element */
43048         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43049     },
43050
43051     applyConfig : function(c){
43052         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43053             var dh = Roo.DomHelper;
43054             if(c.titlebar !== false){
43055                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43056                 this.collapseBtn.on("click", this.collapse, this);
43057                 this.collapseBtn.enableDisplayMode();
43058
43059                 if(c.showPin === true || this.showPin){
43060                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43061                     this.stickBtn.enableDisplayMode();
43062                     this.stickBtn.on("click", this.expand, this);
43063                     this.stickBtn.hide();
43064                 }
43065             }
43066             /** This region's collapsed element
43067             * @type Roo.Element */
43068             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43069                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43070             ]}, true);
43071             if(c.floatable !== false){
43072                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43073                this.collapsedEl.on("click", this.collapseClick, this);
43074             }
43075
43076             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43077                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43078                    id: "message", unselectable: "on", style:{"float":"left"}});
43079                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43080              }
43081             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43082             this.expandBtn.on("click", this.expand, this);
43083         }
43084         if(this.collapseBtn){
43085             this.collapseBtn.setVisible(c.collapsible == true);
43086         }
43087         this.cmargins = c.cmargins || this.cmargins ||
43088                          (this.position == "west" || this.position == "east" ?
43089                              {top: 0, left: 2, right:2, bottom: 0} :
43090                              {top: 2, left: 0, right:0, bottom: 2});
43091         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43092         this.bottomTabs = c.tabPosition != "top";
43093         this.autoScroll = c.autoScroll || false;
43094         if(this.autoScroll){
43095             this.bodyEl.setStyle("overflow", "auto");
43096         }else{
43097             this.bodyEl.setStyle("overflow", "hidden");
43098         }
43099         //if(c.titlebar !== false){
43100             if((!c.titlebar && !c.title) || c.titlebar === false){
43101                 this.titleEl.hide();
43102             }else{
43103                 this.titleEl.show();
43104                 if(c.title){
43105                     this.titleTextEl.innerHTML = c.title;
43106                 }
43107             }
43108         //}
43109         this.duration = c.duration || .30;
43110         this.slideDuration = c.slideDuration || .45;
43111         this.config = c;
43112         if(c.collapsed){
43113             this.collapse(true);
43114         }
43115         if(c.hidden){
43116             this.hide();
43117         }
43118     },
43119     /**
43120      * Returns true if this region is currently visible.
43121      * @return {Boolean}
43122      */
43123     isVisible : function(){
43124         return this.visible;
43125     },
43126
43127     /**
43128      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43129      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43130      */
43131     setCollapsedTitle : function(title){
43132         title = title || "&#160;";
43133         if(this.collapsedTitleTextEl){
43134             this.collapsedTitleTextEl.innerHTML = title;
43135         }
43136     },
43137
43138     getBox : function(){
43139         var b;
43140         if(!this.collapsed){
43141             b = this.el.getBox(false, true);
43142         }else{
43143             b = this.collapsedEl.getBox(false, true);
43144         }
43145         return b;
43146     },
43147
43148     getMargins : function(){
43149         return this.collapsed ? this.cmargins : this.margins;
43150     },
43151
43152     highlight : function(){
43153         this.el.addClass("x-layout-panel-dragover");
43154     },
43155
43156     unhighlight : function(){
43157         this.el.removeClass("x-layout-panel-dragover");
43158     },
43159
43160     updateBox : function(box){
43161         this.box = box;
43162         if(!this.collapsed){
43163             this.el.dom.style.left = box.x + "px";
43164             this.el.dom.style.top = box.y + "px";
43165             this.updateBody(box.width, box.height);
43166         }else{
43167             this.collapsedEl.dom.style.left = box.x + "px";
43168             this.collapsedEl.dom.style.top = box.y + "px";
43169             this.collapsedEl.setSize(box.width, box.height);
43170         }
43171         if(this.tabs){
43172             this.tabs.autoSizeTabs();
43173         }
43174     },
43175
43176     updateBody : function(w, h){
43177         if(w !== null){
43178             this.el.setWidth(w);
43179             w -= this.el.getBorderWidth("rl");
43180             if(this.config.adjustments){
43181                 w += this.config.adjustments[0];
43182             }
43183         }
43184         if(h !== null){
43185             this.el.setHeight(h);
43186             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43187             h -= this.el.getBorderWidth("tb");
43188             if(this.config.adjustments){
43189                 h += this.config.adjustments[1];
43190             }
43191             this.bodyEl.setHeight(h);
43192             if(this.tabs){
43193                 h = this.tabs.syncHeight(h);
43194             }
43195         }
43196         if(this.panelSize){
43197             w = w !== null ? w : this.panelSize.width;
43198             h = h !== null ? h : this.panelSize.height;
43199         }
43200         if(this.activePanel){
43201             var el = this.activePanel.getEl();
43202             w = w !== null ? w : el.getWidth();
43203             h = h !== null ? h : el.getHeight();
43204             this.panelSize = {width: w, height: h};
43205             this.activePanel.setSize(w, h);
43206         }
43207         if(Roo.isIE && this.tabs){
43208             this.tabs.el.repaint();
43209         }
43210     },
43211
43212     /**
43213      * Returns the container element for this region.
43214      * @return {Roo.Element}
43215      */
43216     getEl : function(){
43217         return this.el;
43218     },
43219
43220     /**
43221      * Hides this region.
43222      */
43223     hide : function(){
43224         if(!this.collapsed){
43225             this.el.dom.style.left = "-2000px";
43226             this.el.hide();
43227         }else{
43228             this.collapsedEl.dom.style.left = "-2000px";
43229             this.collapsedEl.hide();
43230         }
43231         this.visible = false;
43232         this.fireEvent("visibilitychange", this, false);
43233     },
43234
43235     /**
43236      * Shows this region if it was previously hidden.
43237      */
43238     show : function(){
43239         if(!this.collapsed){
43240             this.el.show();
43241         }else{
43242             this.collapsedEl.show();
43243         }
43244         this.visible = true;
43245         this.fireEvent("visibilitychange", this, true);
43246     },
43247
43248     closeClicked : function(){
43249         if(this.activePanel){
43250             this.remove(this.activePanel);
43251         }
43252     },
43253
43254     collapseClick : function(e){
43255         if(this.isSlid){
43256            e.stopPropagation();
43257            this.slideIn();
43258         }else{
43259            e.stopPropagation();
43260            this.slideOut();
43261         }
43262     },
43263
43264     /**
43265      * Collapses this region.
43266      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43267      */
43268     collapse : function(skipAnim){
43269         if(this.collapsed) return;
43270         this.collapsed = true;
43271         if(this.split){
43272             this.split.el.hide();
43273         }
43274         if(this.config.animate && skipAnim !== true){
43275             this.fireEvent("invalidated", this);
43276             this.animateCollapse();
43277         }else{
43278             this.el.setLocation(-20000,-20000);
43279             this.el.hide();
43280             this.collapsedEl.show();
43281             this.fireEvent("collapsed", this);
43282             this.fireEvent("invalidated", this);
43283         }
43284     },
43285
43286     animateCollapse : function(){
43287         // overridden
43288     },
43289
43290     /**
43291      * Expands this region if it was previously collapsed.
43292      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43293      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43294      */
43295     expand : function(e, skipAnim){
43296         if(e) e.stopPropagation();
43297         if(!this.collapsed || this.el.hasActiveFx()) return;
43298         if(this.isSlid){
43299             this.afterSlideIn();
43300             skipAnim = true;
43301         }
43302         this.collapsed = false;
43303         if(this.config.animate && skipAnim !== true){
43304             this.animateExpand();
43305         }else{
43306             this.el.show();
43307             if(this.split){
43308                 this.split.el.show();
43309             }
43310             this.collapsedEl.setLocation(-2000,-2000);
43311             this.collapsedEl.hide();
43312             this.fireEvent("invalidated", this);
43313             this.fireEvent("expanded", this);
43314         }
43315     },
43316
43317     animateExpand : function(){
43318         // overridden
43319     },
43320
43321     initTabs : function(){
43322         this.bodyEl.setStyle("overflow", "hidden");
43323         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43324             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43325             disableTooltips: this.config.disableTabTips
43326         });
43327         if(this.config.hideTabs){
43328             ts.stripWrap.setDisplayed(false);
43329         }
43330         this.tabs = ts;
43331         ts.resizeTabs = this.config.resizeTabs === true;
43332         ts.minTabWidth = this.config.minTabWidth || 40;
43333         ts.maxTabWidth = this.config.maxTabWidth || 250;
43334         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43335         ts.monitorResize = false;
43336         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43337         ts.bodyEl.addClass('x-layout-tabs-body');
43338         this.panels.each(this.initPanelAsTab, this);
43339     },
43340
43341     initPanelAsTab : function(panel){
43342         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43343                     this.config.closeOnTab && panel.isClosable());
43344         if(panel.tabTip !== undefined){
43345             ti.setTooltip(panel.tabTip);
43346         }
43347         ti.on("activate", function(){
43348               this.setActivePanel(panel);
43349         }, this);
43350         if(this.config.closeOnTab){
43351             ti.on("beforeclose", function(t, e){
43352                 e.cancel = true;
43353                 this.remove(panel);
43354             }, this);
43355         }
43356         return ti;
43357     },
43358
43359     updatePanelTitle : function(panel, title){
43360         if(this.activePanel == panel){
43361             this.updateTitle(title);
43362         }
43363         if(this.tabs){
43364             var ti = this.tabs.getTab(panel.getEl().id);
43365             ti.setText(title);
43366             if(panel.tabTip !== undefined){
43367                 ti.setTooltip(panel.tabTip);
43368             }
43369         }
43370     },
43371
43372     updateTitle : function(title){
43373         if(this.titleTextEl && !this.config.title){
43374             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43375         }
43376     },
43377
43378     setActivePanel : function(panel){
43379         panel = this.getPanel(panel);
43380         if(this.activePanel && this.activePanel != panel){
43381             this.activePanel.setActiveState(false);
43382         }
43383         this.activePanel = panel;
43384         panel.setActiveState(true);
43385         if(this.panelSize){
43386             panel.setSize(this.panelSize.width, this.panelSize.height);
43387         }
43388         if(this.closeBtn){
43389             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43390         }
43391         this.updateTitle(panel.getTitle());
43392         if(this.tabs){
43393             this.fireEvent("invalidated", this);
43394         }
43395         this.fireEvent("panelactivated", this, panel);
43396     },
43397
43398     /**
43399      * Shows the specified panel.
43400      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43401      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43402      */
43403     showPanel : function(panel){
43404         if(panel = this.getPanel(panel)){
43405             if(this.tabs){
43406                 var tab = this.tabs.getTab(panel.getEl().id);
43407                 if(tab.isHidden()){
43408                     this.tabs.unhideTab(tab.id);
43409                 }
43410                 tab.activate();
43411             }else{
43412                 this.setActivePanel(panel);
43413             }
43414         }
43415         return panel;
43416     },
43417
43418     /**
43419      * Get the active panel for this region.
43420      * @return {Roo.ContentPanel} The active panel or null
43421      */
43422     getActivePanel : function(){
43423         return this.activePanel;
43424     },
43425
43426     validateVisibility : function(){
43427         if(this.panels.getCount() < 1){
43428             this.updateTitle("&#160;");
43429             this.closeBtn.hide();
43430             this.hide();
43431         }else{
43432             if(!this.isVisible()){
43433                 this.show();
43434             }
43435         }
43436     },
43437
43438     /**
43439      * Adds the passed ContentPanel(s) to this region.
43440      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43441      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43442      */
43443     add : function(panel){
43444         if(arguments.length > 1){
43445             for(var i = 0, len = arguments.length; i < len; i++) {
43446                 this.add(arguments[i]);
43447             }
43448             return null;
43449         }
43450         if(this.hasPanel(panel)){
43451             this.showPanel(panel);
43452             return panel;
43453         }
43454         panel.setRegion(this);
43455         this.panels.add(panel);
43456         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43457             this.bodyEl.dom.appendChild(panel.getEl().dom);
43458             if(panel.background !== true){
43459                 this.setActivePanel(panel);
43460             }
43461             this.fireEvent("paneladded", this, panel);
43462             return panel;
43463         }
43464         if(!this.tabs){
43465             this.initTabs();
43466         }else{
43467             this.initPanelAsTab(panel);
43468         }
43469         if(panel.background !== true){
43470             this.tabs.activate(panel.getEl().id);
43471         }
43472         this.fireEvent("paneladded", this, panel);
43473         return panel;
43474     },
43475
43476     /**
43477      * Hides the tab for the specified panel.
43478      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43479      */
43480     hidePanel : function(panel){
43481         if(this.tabs && (panel = this.getPanel(panel))){
43482             this.tabs.hideTab(panel.getEl().id);
43483         }
43484     },
43485
43486     /**
43487      * Unhides the tab for a previously hidden panel.
43488      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43489      */
43490     unhidePanel : function(panel){
43491         if(this.tabs && (panel = this.getPanel(panel))){
43492             this.tabs.unhideTab(panel.getEl().id);
43493         }
43494     },
43495
43496     clearPanels : function(){
43497         while(this.panels.getCount() > 0){
43498              this.remove(this.panels.first());
43499         }
43500     },
43501
43502     /**
43503      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43504      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43505      * @param {Boolean} preservePanel Overrides the config preservePanel option
43506      * @return {Roo.ContentPanel} The panel that was removed
43507      */
43508     remove : function(panel, preservePanel){
43509         panel = this.getPanel(panel);
43510         if(!panel){
43511             return null;
43512         }
43513         var e = {};
43514         this.fireEvent("beforeremove", this, panel, e);
43515         if(e.cancel === true){
43516             return null;
43517         }
43518         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43519         var panelId = panel.getId();
43520         this.panels.removeKey(panelId);
43521         if(preservePanel){
43522             document.body.appendChild(panel.getEl().dom);
43523         }
43524         if(this.tabs){
43525             this.tabs.removeTab(panel.getEl().id);
43526         }else if (!preservePanel){
43527             this.bodyEl.dom.removeChild(panel.getEl().dom);
43528         }
43529         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43530             var p = this.panels.first();
43531             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43532             tempEl.appendChild(p.getEl().dom);
43533             this.bodyEl.update("");
43534             this.bodyEl.dom.appendChild(p.getEl().dom);
43535             tempEl = null;
43536             this.updateTitle(p.getTitle());
43537             this.tabs = null;
43538             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43539             this.setActivePanel(p);
43540         }
43541         panel.setRegion(null);
43542         if(this.activePanel == panel){
43543             this.activePanel = null;
43544         }
43545         if(this.config.autoDestroy !== false && preservePanel !== true){
43546             try{panel.destroy();}catch(e){}
43547         }
43548         this.fireEvent("panelremoved", this, panel);
43549         return panel;
43550     },
43551
43552     /**
43553      * Returns the TabPanel component used by this region
43554      * @return {Roo.TabPanel}
43555      */
43556     getTabs : function(){
43557         return this.tabs;
43558     },
43559
43560     createTool : function(parentEl, className){
43561         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43562             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43563         btn.addClassOnOver("x-layout-tools-button-over");
43564         return btn;
43565     }
43566 });/*
43567  * Based on:
43568  * Ext JS Library 1.1.1
43569  * Copyright(c) 2006-2007, Ext JS, LLC.
43570  *
43571  * Originally Released Under LGPL - original licence link has changed is not relivant.
43572  *
43573  * Fork - LGPL
43574  * <script type="text/javascript">
43575  */
43576  
43577
43578
43579 /**
43580  * @class Roo.SplitLayoutRegion
43581  * @extends Roo.LayoutRegion
43582  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43583  */
43584 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43585     this.cursor = cursor;
43586     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43587 };
43588
43589 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43590     splitTip : "Drag to resize.",
43591     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43592     useSplitTips : false,
43593
43594     applyConfig : function(config){
43595         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43596         if(config.split){
43597             if(!this.split){
43598                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43599                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43600                 /** The SplitBar for this region 
43601                 * @type Roo.SplitBar */
43602                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43603                 this.split.on("moved", this.onSplitMove, this);
43604                 this.split.useShim = config.useShim === true;
43605                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43606                 if(this.useSplitTips){
43607                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43608                 }
43609                 if(config.collapsible){
43610                     this.split.el.on("dblclick", this.collapse,  this);
43611                 }
43612             }
43613             if(typeof config.minSize != "undefined"){
43614                 this.split.minSize = config.minSize;
43615             }
43616             if(typeof config.maxSize != "undefined"){
43617                 this.split.maxSize = config.maxSize;
43618             }
43619             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43620                 this.hideSplitter();
43621             }
43622         }
43623     },
43624
43625     getHMaxSize : function(){
43626          var cmax = this.config.maxSize || 10000;
43627          var center = this.mgr.getRegion("center");
43628          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43629     },
43630
43631     getVMaxSize : function(){
43632          var cmax = this.config.maxSize || 10000;
43633          var center = this.mgr.getRegion("center");
43634          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43635     },
43636
43637     onSplitMove : function(split, newSize){
43638         this.fireEvent("resized", this, newSize);
43639     },
43640     
43641     /** 
43642      * Returns the {@link Roo.SplitBar} for this region.
43643      * @return {Roo.SplitBar}
43644      */
43645     getSplitBar : function(){
43646         return this.split;
43647     },
43648     
43649     hide : function(){
43650         this.hideSplitter();
43651         Roo.SplitLayoutRegion.superclass.hide.call(this);
43652     },
43653
43654     hideSplitter : function(){
43655         if(this.split){
43656             this.split.el.setLocation(-2000,-2000);
43657             this.split.el.hide();
43658         }
43659     },
43660
43661     show : function(){
43662         if(this.split){
43663             this.split.el.show();
43664         }
43665         Roo.SplitLayoutRegion.superclass.show.call(this);
43666     },
43667     
43668     beforeSlide: function(){
43669         if(Roo.isGecko){// firefox overflow auto bug workaround
43670             this.bodyEl.clip();
43671             if(this.tabs) this.tabs.bodyEl.clip();
43672             if(this.activePanel){
43673                 this.activePanel.getEl().clip();
43674                 
43675                 if(this.activePanel.beforeSlide){
43676                     this.activePanel.beforeSlide();
43677                 }
43678             }
43679         }
43680     },
43681     
43682     afterSlide : function(){
43683         if(Roo.isGecko){// firefox overflow auto bug workaround
43684             this.bodyEl.unclip();
43685             if(this.tabs) this.tabs.bodyEl.unclip();
43686             if(this.activePanel){
43687                 this.activePanel.getEl().unclip();
43688                 if(this.activePanel.afterSlide){
43689                     this.activePanel.afterSlide();
43690                 }
43691             }
43692         }
43693     },
43694
43695     initAutoHide : function(){
43696         if(this.autoHide !== false){
43697             if(!this.autoHideHd){
43698                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43699                 this.autoHideHd = {
43700                     "mouseout": function(e){
43701                         if(!e.within(this.el, true)){
43702                             st.delay(500);
43703                         }
43704                     },
43705                     "mouseover" : function(e){
43706                         st.cancel();
43707                     },
43708                     scope : this
43709                 };
43710             }
43711             this.el.on(this.autoHideHd);
43712         }
43713     },
43714
43715     clearAutoHide : function(){
43716         if(this.autoHide !== false){
43717             this.el.un("mouseout", this.autoHideHd.mouseout);
43718             this.el.un("mouseover", this.autoHideHd.mouseover);
43719         }
43720     },
43721
43722     clearMonitor : function(){
43723         Roo.get(document).un("click", this.slideInIf, this);
43724     },
43725
43726     // these names are backwards but not changed for compat
43727     slideOut : function(){
43728         if(this.isSlid || this.el.hasActiveFx()){
43729             return;
43730         }
43731         this.isSlid = true;
43732         if(this.collapseBtn){
43733             this.collapseBtn.hide();
43734         }
43735         this.closeBtnState = this.closeBtn.getStyle('display');
43736         this.closeBtn.hide();
43737         if(this.stickBtn){
43738             this.stickBtn.show();
43739         }
43740         this.el.show();
43741         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43742         this.beforeSlide();
43743         this.el.setStyle("z-index", 10001);
43744         this.el.slideIn(this.getSlideAnchor(), {
43745             callback: function(){
43746                 this.afterSlide();
43747                 this.initAutoHide();
43748                 Roo.get(document).on("click", this.slideInIf, this);
43749                 this.fireEvent("slideshow", this);
43750             },
43751             scope: this,
43752             block: true
43753         });
43754     },
43755
43756     afterSlideIn : function(){
43757         this.clearAutoHide();
43758         this.isSlid = false;
43759         this.clearMonitor();
43760         this.el.setStyle("z-index", "");
43761         if(this.collapseBtn){
43762             this.collapseBtn.show();
43763         }
43764         this.closeBtn.setStyle('display', this.closeBtnState);
43765         if(this.stickBtn){
43766             this.stickBtn.hide();
43767         }
43768         this.fireEvent("slidehide", this);
43769     },
43770
43771     slideIn : function(cb){
43772         if(!this.isSlid || this.el.hasActiveFx()){
43773             Roo.callback(cb);
43774             return;
43775         }
43776         this.isSlid = false;
43777         this.beforeSlide();
43778         this.el.slideOut(this.getSlideAnchor(), {
43779             callback: function(){
43780                 this.el.setLeftTop(-10000, -10000);
43781                 this.afterSlide();
43782                 this.afterSlideIn();
43783                 Roo.callback(cb);
43784             },
43785             scope: this,
43786             block: true
43787         });
43788     },
43789     
43790     slideInIf : function(e){
43791         if(!e.within(this.el)){
43792             this.slideIn();
43793         }
43794     },
43795
43796     animateCollapse : function(){
43797         this.beforeSlide();
43798         this.el.setStyle("z-index", 20000);
43799         var anchor = this.getSlideAnchor();
43800         this.el.slideOut(anchor, {
43801             callback : function(){
43802                 this.el.setStyle("z-index", "");
43803                 this.collapsedEl.slideIn(anchor, {duration:.3});
43804                 this.afterSlide();
43805                 this.el.setLocation(-10000,-10000);
43806                 this.el.hide();
43807                 this.fireEvent("collapsed", this);
43808             },
43809             scope: this,
43810             block: true
43811         });
43812     },
43813
43814     animateExpand : function(){
43815         this.beforeSlide();
43816         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43817         this.el.setStyle("z-index", 20000);
43818         this.collapsedEl.hide({
43819             duration:.1
43820         });
43821         this.el.slideIn(this.getSlideAnchor(), {
43822             callback : function(){
43823                 this.el.setStyle("z-index", "");
43824                 this.afterSlide();
43825                 if(this.split){
43826                     this.split.el.show();
43827                 }
43828                 this.fireEvent("invalidated", this);
43829                 this.fireEvent("expanded", this);
43830             },
43831             scope: this,
43832             block: true
43833         });
43834     },
43835
43836     anchors : {
43837         "west" : "left",
43838         "east" : "right",
43839         "north" : "top",
43840         "south" : "bottom"
43841     },
43842
43843     sanchors : {
43844         "west" : "l",
43845         "east" : "r",
43846         "north" : "t",
43847         "south" : "b"
43848     },
43849
43850     canchors : {
43851         "west" : "tl-tr",
43852         "east" : "tr-tl",
43853         "north" : "tl-bl",
43854         "south" : "bl-tl"
43855     },
43856
43857     getAnchor : function(){
43858         return this.anchors[this.position];
43859     },
43860
43861     getCollapseAnchor : function(){
43862         return this.canchors[this.position];
43863     },
43864
43865     getSlideAnchor : function(){
43866         return this.sanchors[this.position];
43867     },
43868
43869     getAlignAdj : function(){
43870         var cm = this.cmargins;
43871         switch(this.position){
43872             case "west":
43873                 return [0, 0];
43874             break;
43875             case "east":
43876                 return [0, 0];
43877             break;
43878             case "north":
43879                 return [0, 0];
43880             break;
43881             case "south":
43882                 return [0, 0];
43883             break;
43884         }
43885     },
43886
43887     getExpandAdj : function(){
43888         var c = this.collapsedEl, cm = this.cmargins;
43889         switch(this.position){
43890             case "west":
43891                 return [-(cm.right+c.getWidth()+cm.left), 0];
43892             break;
43893             case "east":
43894                 return [cm.right+c.getWidth()+cm.left, 0];
43895             break;
43896             case "north":
43897                 return [0, -(cm.top+cm.bottom+c.getHeight())];
43898             break;
43899             case "south":
43900                 return [0, cm.top+cm.bottom+c.getHeight()];
43901             break;
43902         }
43903     }
43904 });/*
43905  * Based on:
43906  * Ext JS Library 1.1.1
43907  * Copyright(c) 2006-2007, Ext JS, LLC.
43908  *
43909  * Originally Released Under LGPL - original licence link has changed is not relivant.
43910  *
43911  * Fork - LGPL
43912  * <script type="text/javascript">
43913  */
43914 /*
43915  * These classes are private internal classes
43916  */
43917 Roo.CenterLayoutRegion = function(mgr, config){
43918     Roo.LayoutRegion.call(this, mgr, config, "center");
43919     this.visible = true;
43920     this.minWidth = config.minWidth || 20;
43921     this.minHeight = config.minHeight || 20;
43922 };
43923
43924 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
43925     hide : function(){
43926         // center panel can't be hidden
43927     },
43928     
43929     show : function(){
43930         // center panel can't be hidden
43931     },
43932     
43933     getMinWidth: function(){
43934         return this.minWidth;
43935     },
43936     
43937     getMinHeight: function(){
43938         return this.minHeight;
43939     }
43940 });
43941
43942
43943 Roo.NorthLayoutRegion = function(mgr, config){
43944     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
43945     if(this.split){
43946         this.split.placement = Roo.SplitBar.TOP;
43947         this.split.orientation = Roo.SplitBar.VERTICAL;
43948         this.split.el.addClass("x-layout-split-v");
43949     }
43950     var size = config.initialSize || config.height;
43951     if(typeof size != "undefined"){
43952         this.el.setHeight(size);
43953     }
43954 };
43955 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
43956     orientation: Roo.SplitBar.VERTICAL,
43957     getBox : function(){
43958         if(this.collapsed){
43959             return this.collapsedEl.getBox();
43960         }
43961         var box = this.el.getBox();
43962         if(this.split){
43963             box.height += this.split.el.getHeight();
43964         }
43965         return box;
43966     },
43967     
43968     updateBox : function(box){
43969         if(this.split && !this.collapsed){
43970             box.height -= this.split.el.getHeight();
43971             this.split.el.setLeft(box.x);
43972             this.split.el.setTop(box.y+box.height);
43973             this.split.el.setWidth(box.width);
43974         }
43975         if(this.collapsed){
43976             this.updateBody(box.width, null);
43977         }
43978         Roo.LayoutRegion.prototype.updateBox.call(this, box);
43979     }
43980 });
43981
43982 Roo.SouthLayoutRegion = function(mgr, config){
43983     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
43984     if(this.split){
43985         this.split.placement = Roo.SplitBar.BOTTOM;
43986         this.split.orientation = Roo.SplitBar.VERTICAL;
43987         this.split.el.addClass("x-layout-split-v");
43988     }
43989     var size = config.initialSize || config.height;
43990     if(typeof size != "undefined"){
43991         this.el.setHeight(size);
43992     }
43993 };
43994 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
43995     orientation: Roo.SplitBar.VERTICAL,
43996     getBox : function(){
43997         if(this.collapsed){
43998             return this.collapsedEl.getBox();
43999         }
44000         var box = this.el.getBox();
44001         if(this.split){
44002             var sh = this.split.el.getHeight();
44003             box.height += sh;
44004             box.y -= sh;
44005         }
44006         return box;
44007     },
44008     
44009     updateBox : function(box){
44010         if(this.split && !this.collapsed){
44011             var sh = this.split.el.getHeight();
44012             box.height -= sh;
44013             box.y += sh;
44014             this.split.el.setLeft(box.x);
44015             this.split.el.setTop(box.y-sh);
44016             this.split.el.setWidth(box.width);
44017         }
44018         if(this.collapsed){
44019             this.updateBody(box.width, null);
44020         }
44021         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44022     }
44023 });
44024
44025 Roo.EastLayoutRegion = function(mgr, config){
44026     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44027     if(this.split){
44028         this.split.placement = Roo.SplitBar.RIGHT;
44029         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44030         this.split.el.addClass("x-layout-split-h");
44031     }
44032     var size = config.initialSize || config.width;
44033     if(typeof size != "undefined"){
44034         this.el.setWidth(size);
44035     }
44036 };
44037 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44038     orientation: Roo.SplitBar.HORIZONTAL,
44039     getBox : function(){
44040         if(this.collapsed){
44041             return this.collapsedEl.getBox();
44042         }
44043         var box = this.el.getBox();
44044         if(this.split){
44045             var sw = this.split.el.getWidth();
44046             box.width += sw;
44047             box.x -= sw;
44048         }
44049         return box;
44050     },
44051
44052     updateBox : function(box){
44053         if(this.split && !this.collapsed){
44054             var sw = this.split.el.getWidth();
44055             box.width -= sw;
44056             this.split.el.setLeft(box.x);
44057             this.split.el.setTop(box.y);
44058             this.split.el.setHeight(box.height);
44059             box.x += sw;
44060         }
44061         if(this.collapsed){
44062             this.updateBody(null, box.height);
44063         }
44064         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44065     }
44066 });
44067
44068 Roo.WestLayoutRegion = function(mgr, config){
44069     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44070     if(this.split){
44071         this.split.placement = Roo.SplitBar.LEFT;
44072         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44073         this.split.el.addClass("x-layout-split-h");
44074     }
44075     var size = config.initialSize || config.width;
44076     if(typeof size != "undefined"){
44077         this.el.setWidth(size);
44078     }
44079 };
44080 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44081     orientation: Roo.SplitBar.HORIZONTAL,
44082     getBox : function(){
44083         if(this.collapsed){
44084             return this.collapsedEl.getBox();
44085         }
44086         var box = this.el.getBox();
44087         if(this.split){
44088             box.width += this.split.el.getWidth();
44089         }
44090         return box;
44091     },
44092     
44093     updateBox : function(box){
44094         if(this.split && !this.collapsed){
44095             var sw = this.split.el.getWidth();
44096             box.width -= sw;
44097             this.split.el.setLeft(box.x+box.width);
44098             this.split.el.setTop(box.y);
44099             this.split.el.setHeight(box.height);
44100         }
44101         if(this.collapsed){
44102             this.updateBody(null, box.height);
44103         }
44104         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44105     }
44106 });
44107 /*
44108  * Based on:
44109  * Ext JS Library 1.1.1
44110  * Copyright(c) 2006-2007, Ext JS, LLC.
44111  *
44112  * Originally Released Under LGPL - original licence link has changed is not relivant.
44113  *
44114  * Fork - LGPL
44115  * <script type="text/javascript">
44116  */
44117  
44118  
44119 /*
44120  * Private internal class for reading and applying state
44121  */
44122 Roo.LayoutStateManager = function(layout){
44123      // default empty state
44124      this.state = {
44125         north: {},
44126         south: {},
44127         east: {},
44128         west: {}       
44129     };
44130 };
44131
44132 Roo.LayoutStateManager.prototype = {
44133     init : function(layout, provider){
44134         this.provider = provider;
44135         var state = provider.get(layout.id+"-layout-state");
44136         if(state){
44137             var wasUpdating = layout.isUpdating();
44138             if(!wasUpdating){
44139                 layout.beginUpdate();
44140             }
44141             for(var key in state){
44142                 if(typeof state[key] != "function"){
44143                     var rstate = state[key];
44144                     var r = layout.getRegion(key);
44145                     if(r && rstate){
44146                         if(rstate.size){
44147                             r.resizeTo(rstate.size);
44148                         }
44149                         if(rstate.collapsed == true){
44150                             r.collapse(true);
44151                         }else{
44152                             r.expand(null, true);
44153                         }
44154                     }
44155                 }
44156             }
44157             if(!wasUpdating){
44158                 layout.endUpdate();
44159             }
44160             this.state = state; 
44161         }
44162         this.layout = layout;
44163         layout.on("regionresized", this.onRegionResized, this);
44164         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44165         layout.on("regionexpanded", this.onRegionExpanded, this);
44166     },
44167     
44168     storeState : function(){
44169         this.provider.set(this.layout.id+"-layout-state", this.state);
44170     },
44171     
44172     onRegionResized : function(region, newSize){
44173         this.state[region.getPosition()].size = newSize;
44174         this.storeState();
44175     },
44176     
44177     onRegionCollapsed : function(region){
44178         this.state[region.getPosition()].collapsed = true;
44179         this.storeState();
44180     },
44181     
44182     onRegionExpanded : function(region){
44183         this.state[region.getPosition()].collapsed = false;
44184         this.storeState();
44185     }
44186 };/*
44187  * Based on:
44188  * Ext JS Library 1.1.1
44189  * Copyright(c) 2006-2007, Ext JS, LLC.
44190  *
44191  * Originally Released Under LGPL - original licence link has changed is not relivant.
44192  *
44193  * Fork - LGPL
44194  * <script type="text/javascript">
44195  */
44196 /**
44197  * @class Roo.ContentPanel
44198  * @extends Roo.util.Observable
44199  * A basic ContentPanel element.
44200  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44201  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44202  * @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
44203  * @cfg {Boolean} closable True if the panel can be closed/removed
44204  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44205  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44206  * @cfg {Toolbar} toolbar A toolbar for this panel
44207  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44208  * @cfg {String} title The title for this panel
44209  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44210  * @cfg {String} url Calls {@link #setUrl} with this value
44211  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44212  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44213  * @constructor
44214  * Create a new ContentPanel.
44215  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44216  * @param {String/Object} config A string to set only the title or a config object
44217  * @param {String} content (optional) Set the HTML content for this panel
44218  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44219  */
44220 Roo.ContentPanel = function(el, config, content){
44221     
44222      
44223     /*
44224     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44225         config = el;
44226         el = Roo.id();
44227     }
44228     if (config && config.parentLayout) { 
44229         el = config.parentLayout.el.createChild(); 
44230     }
44231     */
44232     if(el.autoCreate){ // xtype is available if this is called from factory
44233         config = el;
44234         el = Roo.id();
44235     }
44236     this.el = Roo.get(el);
44237     if(!this.el && config && config.autoCreate){
44238         if(typeof config.autoCreate == "object"){
44239             if(!config.autoCreate.id){
44240                 config.autoCreate.id = config.id||el;
44241             }
44242             this.el = Roo.DomHelper.append(document.body,
44243                         config.autoCreate, true);
44244         }else{
44245             this.el = Roo.DomHelper.append(document.body,
44246                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44247         }
44248     }
44249     this.closable = false;
44250     this.loaded = false;
44251     this.active = false;
44252     if(typeof config == "string"){
44253         this.title = config;
44254     }else{
44255         Roo.apply(this, config);
44256     }
44257     
44258     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44259         this.wrapEl = this.el.wrap();    
44260         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44261         
44262     }
44263     
44264     
44265     
44266     if(this.resizeEl){
44267         this.resizeEl = Roo.get(this.resizeEl, true);
44268     }else{
44269         this.resizeEl = this.el;
44270     }
44271     this.addEvents({
44272         /**
44273          * @event activate
44274          * Fires when this panel is activated. 
44275          * @param {Roo.ContentPanel} this
44276          */
44277         "activate" : true,
44278         /**
44279          * @event deactivate
44280          * Fires when this panel is activated. 
44281          * @param {Roo.ContentPanel} this
44282          */
44283         "deactivate" : true,
44284
44285         /**
44286          * @event resize
44287          * Fires when this panel is resized if fitToFrame is true.
44288          * @param {Roo.ContentPanel} this
44289          * @param {Number} width The width after any component adjustments
44290          * @param {Number} height The height after any component adjustments
44291          */
44292         "resize" : true
44293     });
44294     if(this.autoScroll){
44295         this.resizeEl.setStyle("overflow", "auto");
44296     }
44297     content = content || this.content;
44298     if(content){
44299         this.setContent(content);
44300     }
44301     if(config && config.url){
44302         this.setUrl(this.url, this.params, this.loadOnce);
44303     }
44304     
44305     
44306     
44307     Roo.ContentPanel.superclass.constructor.call(this);
44308 };
44309
44310 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44311     tabTip:'',
44312     setRegion : function(region){
44313         this.region = region;
44314         if(region){
44315            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44316         }else{
44317            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44318         } 
44319     },
44320     
44321     /**
44322      * Returns the toolbar for this Panel if one was configured. 
44323      * @return {Roo.Toolbar} 
44324      */
44325     getToolbar : function(){
44326         return this.toolbar;
44327     },
44328     
44329     setActiveState : function(active){
44330         this.active = active;
44331         if(!active){
44332             this.fireEvent("deactivate", this);
44333         }else{
44334             this.fireEvent("activate", this);
44335         }
44336     },
44337     /**
44338      * Updates this panel's element
44339      * @param {String} content The new content
44340      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44341     */
44342     setContent : function(content, loadScripts){
44343         this.el.update(content, loadScripts);
44344     },
44345
44346     ignoreResize : function(w, h){
44347         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44348             return true;
44349         }else{
44350             this.lastSize = {width: w, height: h};
44351             return false;
44352         }
44353     },
44354     /**
44355      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44356      * @return {Roo.UpdateManager} The UpdateManager
44357      */
44358     getUpdateManager : function(){
44359         return this.el.getUpdateManager();
44360     },
44361      /**
44362      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44363      * @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:
44364 <pre><code>
44365 panel.load({
44366     url: "your-url.php",
44367     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44368     callback: yourFunction,
44369     scope: yourObject, //(optional scope)
44370     discardUrl: false,
44371     nocache: false,
44372     text: "Loading...",
44373     timeout: 30,
44374     scripts: false
44375 });
44376 </code></pre>
44377      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44378      * 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.
44379      * @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}
44380      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44381      * @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.
44382      * @return {Roo.ContentPanel} this
44383      */
44384     load : function(){
44385         var um = this.el.getUpdateManager();
44386         um.update.apply(um, arguments);
44387         return this;
44388     },
44389
44390
44391     /**
44392      * 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.
44393      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44394      * @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)
44395      * @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)
44396      * @return {Roo.UpdateManager} The UpdateManager
44397      */
44398     setUrl : function(url, params, loadOnce){
44399         if(this.refreshDelegate){
44400             this.removeListener("activate", this.refreshDelegate);
44401         }
44402         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44403         this.on("activate", this.refreshDelegate);
44404         return this.el.getUpdateManager();
44405     },
44406     
44407     _handleRefresh : function(url, params, loadOnce){
44408         if(!loadOnce || !this.loaded){
44409             var updater = this.el.getUpdateManager();
44410             updater.update(url, params, this._setLoaded.createDelegate(this));
44411         }
44412     },
44413     
44414     _setLoaded : function(){
44415         this.loaded = true;
44416     }, 
44417     
44418     /**
44419      * Returns this panel's id
44420      * @return {String} 
44421      */
44422     getId : function(){
44423         return this.el.id;
44424     },
44425     
44426     /** 
44427      * Returns this panel's element - used by regiosn to add.
44428      * @return {Roo.Element} 
44429      */
44430     getEl : function(){
44431         return this.wrapEl || this.el;
44432     },
44433     
44434     adjustForComponents : function(width, height){
44435         if(this.resizeEl != this.el){
44436             width -= this.el.getFrameWidth('lr');
44437             height -= this.el.getFrameWidth('tb');
44438         }
44439         if(this.toolbar){
44440             var te = this.toolbar.getEl();
44441             height -= te.getHeight();
44442             te.setWidth(width);
44443         }
44444         if(this.adjustments){
44445             width += this.adjustments[0];
44446             height += this.adjustments[1];
44447         }
44448         return {"width": width, "height": height};
44449     },
44450     
44451     setSize : function(width, height){
44452         if(this.fitToFrame && !this.ignoreResize(width, height)){
44453             if(this.fitContainer && this.resizeEl != this.el){
44454                 this.el.setSize(width, height);
44455             }
44456             var size = this.adjustForComponents(width, height);
44457             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44458             this.fireEvent('resize', this, size.width, size.height);
44459         }
44460     },
44461     
44462     /**
44463      * Returns this panel's title
44464      * @return {String} 
44465      */
44466     getTitle : function(){
44467         return this.title;
44468     },
44469     
44470     /**
44471      * Set this panel's title
44472      * @param {String} title
44473      */
44474     setTitle : function(title){
44475         this.title = title;
44476         if(this.region){
44477             this.region.updatePanelTitle(this, title);
44478         }
44479     },
44480     
44481     /**
44482      * Returns true is this panel was configured to be closable
44483      * @return {Boolean} 
44484      */
44485     isClosable : function(){
44486         return this.closable;
44487     },
44488     
44489     beforeSlide : function(){
44490         this.el.clip();
44491         this.resizeEl.clip();
44492     },
44493     
44494     afterSlide : function(){
44495         this.el.unclip();
44496         this.resizeEl.unclip();
44497     },
44498     
44499     /**
44500      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44501      *   Will fail silently if the {@link #setUrl} method has not been called.
44502      *   This does not activate the panel, just updates its content.
44503      */
44504     refresh : function(){
44505         if(this.refreshDelegate){
44506            this.loaded = false;
44507            this.refreshDelegate();
44508         }
44509     },
44510     
44511     /**
44512      * Destroys this panel
44513      */
44514     destroy : function(){
44515         this.el.removeAllListeners();
44516         var tempEl = document.createElement("span");
44517         tempEl.appendChild(this.el.dom);
44518         tempEl.innerHTML = "";
44519         this.el.remove();
44520         this.el = null;
44521     },
44522     
44523       /**
44524      * Adds a xtype elements to the panel - currently only supports Forms.
44525      * <pre><code>
44526
44527 layout.addxtype({
44528        xtype : 'Form',
44529        items: [ .... ]
44530    }
44531 );
44532
44533 </code></pre>
44534      * @param {Object} cfg Xtype definition of item to add.
44535      */
44536     
44537     addxtype : function(cfg) {
44538         // add form..
44539         if (!cfg.xtype.match(/^Form$/)) {
44540             return false;
44541         }
44542         var el = this.el.createChild();
44543
44544         this.form = new  Roo.form.Form(cfg);
44545         
44546         
44547         if ( this.form.allItems.length) this.form.render(el.dom);
44548         return this.form;
44549         
44550     }
44551 });
44552
44553 /**
44554  * @class Roo.GridPanel
44555  * @extends Roo.ContentPanel
44556  * @constructor
44557  * Create a new GridPanel.
44558  * @param {Roo.grid.Grid} grid The grid for this panel
44559  * @param {String/Object} config A string to set only the panel's title, or a config object
44560  */
44561 Roo.GridPanel = function(grid, config){
44562     
44563   
44564     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44565         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44566         
44567     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44568     
44569     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44570     
44571     if(this.toolbar){
44572         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44573     }
44574     // xtype created footer. - not sure if will work as we normally have to render first..
44575     if (this.footer && !this.footer.el && this.footer.xtype) {
44576         
44577         this.footer.container = this.grid.getView().getFooterPanel(true);
44578         this.footer.dataSource = this.grid.dataSource;
44579         this.footer = Roo.factory(this.footer, Roo);
44580         
44581     }
44582     
44583     grid.monitorWindowResize = false; // turn off autosizing
44584     grid.autoHeight = false;
44585     grid.autoWidth = false;
44586     this.grid = grid;
44587     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44588 };
44589
44590 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44591     getId : function(){
44592         return this.grid.id;
44593     },
44594     
44595     /**
44596      * Returns the grid for this panel
44597      * @return {Roo.grid.Grid} 
44598      */
44599     getGrid : function(){
44600         return this.grid;    
44601     },
44602     
44603     setSize : function(width, height){
44604         if(!this.ignoreResize(width, height)){
44605             var grid = this.grid;
44606             var size = this.adjustForComponents(width, height);
44607             grid.getGridEl().setSize(size.width, size.height);
44608             grid.autoSize();
44609         }
44610     },
44611     
44612     beforeSlide : function(){
44613         this.grid.getView().scroller.clip();
44614     },
44615     
44616     afterSlide : function(){
44617         this.grid.getView().scroller.unclip();
44618     },
44619     
44620     destroy : function(){
44621         this.grid.destroy();
44622         delete this.grid;
44623         Roo.GridPanel.superclass.destroy.call(this); 
44624     }
44625 });
44626
44627
44628 /**
44629  * @class Roo.NestedLayoutPanel
44630  * @extends Roo.ContentPanel
44631  * @constructor
44632  * Create a new NestedLayoutPanel.
44633  * 
44634  * 
44635  * @param {Roo.BorderLayout} layout The layout for this panel
44636  * @param {String/Object} config A string to set only the title or a config object
44637  */
44638 Roo.NestedLayoutPanel = function(layout, config)
44639 {
44640     // construct with only one argument..
44641     /* FIXME - implement nicer consturctors
44642     if (layout.layout) {
44643         config = layout;
44644         layout = config.layout;
44645         delete config.layout;
44646     }
44647     if (layout.xtype && !layout.getEl) {
44648         // then layout needs constructing..
44649         layout = Roo.factory(layout, Roo);
44650     }
44651     */
44652     
44653     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44654     
44655     layout.monitorWindowResize = false; // turn off autosizing
44656     this.layout = layout;
44657     this.layout.getEl().addClass("x-layout-nested-layout");
44658     
44659     
44660     
44661 };
44662
44663 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44664
44665     setSize : function(width, height){
44666         if(!this.ignoreResize(width, height)){
44667             var size = this.adjustForComponents(width, height);
44668             var el = this.layout.getEl();
44669             el.setSize(size.width, size.height);
44670             var touch = el.dom.offsetWidth;
44671             this.layout.layout();
44672             // ie requires a double layout on the first pass
44673             if(Roo.isIE && !this.initialized){
44674                 this.initialized = true;
44675                 this.layout.layout();
44676             }
44677         }
44678     },
44679     
44680     // activate all subpanels if not currently active..
44681     
44682     setActiveState : function(active){
44683         this.active = active;
44684         if(!active){
44685             this.fireEvent("deactivate", this);
44686             return;
44687         }
44688         
44689         this.fireEvent("activate", this);
44690         // not sure if this should happen before or after..
44691         if (!this.layout) {
44692             return; // should not happen..
44693         }
44694         var reg = false;
44695         for (var r in this.layout.regions) {
44696             reg = this.layout.getRegion(r);
44697             if (reg.getActivePanel()) {
44698                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44699                 reg.setActivePanel(reg.getActivePanel());
44700                 continue;
44701             }
44702             if (!reg.panels.length) {
44703                 continue;
44704             }
44705             reg.showPanel(reg.getPanel(0));
44706         }
44707         
44708         
44709         
44710         
44711     },
44712     
44713     /**
44714      * Returns the nested BorderLayout for this panel
44715      * @return {Roo.BorderLayout} 
44716      */
44717     getLayout : function(){
44718         return this.layout;
44719     },
44720     
44721      /**
44722      * Adds a xtype elements to the layout of the nested panel
44723      * <pre><code>
44724
44725 panel.addxtype({
44726        xtype : 'ContentPanel',
44727        region: 'west',
44728        items: [ .... ]
44729    }
44730 );
44731
44732 panel.addxtype({
44733         xtype : 'NestedLayoutPanel',
44734         region: 'west',
44735         layout: {
44736            center: { },
44737            west: { }   
44738         },
44739         items : [ ... list of content panels or nested layout panels.. ]
44740    }
44741 );
44742 </code></pre>
44743      * @param {Object} cfg Xtype definition of item to add.
44744      */
44745     addxtype : function(cfg) {
44746         return this.layout.addxtype(cfg);
44747     
44748     }
44749 });
44750
44751 Roo.ScrollPanel = function(el, config, content){
44752     config = config || {};
44753     config.fitToFrame = true;
44754     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44755     
44756     this.el.dom.style.overflow = "hidden";
44757     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44758     this.el.removeClass("x-layout-inactive-content");
44759     this.el.on("mousewheel", this.onWheel, this);
44760
44761     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44762     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44763     up.unselectable(); down.unselectable();
44764     up.on("click", this.scrollUp, this);
44765     down.on("click", this.scrollDown, this);
44766     up.addClassOnOver("x-scroller-btn-over");
44767     down.addClassOnOver("x-scroller-btn-over");
44768     up.addClassOnClick("x-scroller-btn-click");
44769     down.addClassOnClick("x-scroller-btn-click");
44770     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44771
44772     this.resizeEl = this.el;
44773     this.el = wrap; this.up = up; this.down = down;
44774 };
44775
44776 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44777     increment : 100,
44778     wheelIncrement : 5,
44779     scrollUp : function(){
44780         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44781     },
44782
44783     scrollDown : function(){
44784         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44785     },
44786
44787     afterScroll : function(){
44788         var el = this.resizeEl;
44789         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44790         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44791         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44792     },
44793
44794     setSize : function(){
44795         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44796         this.afterScroll();
44797     },
44798
44799     onWheel : function(e){
44800         var d = e.getWheelDelta();
44801         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44802         this.afterScroll();
44803         e.stopEvent();
44804     },
44805
44806     setContent : function(content, loadScripts){
44807         this.resizeEl.update(content, loadScripts);
44808     }
44809
44810 });
44811
44812
44813
44814
44815
44816
44817
44818
44819
44820 /**
44821  * @class Roo.TreePanel
44822  * @extends Roo.ContentPanel
44823  * @constructor
44824  * Create a new TreePanel.
44825  * @param {String/Object} config A string to set only the panel's title, or a config object
44826  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
44827  */
44828 Roo.TreePanel = function(config){
44829     var el = config.el;
44830     var tree = config.tree;
44831     delete config.tree; 
44832     delete config.el; // hopefull!
44833     Roo.TreePanel.superclass.constructor.call(this, el, config);
44834     var treeEl = el.createChild();
44835     this.tree = new Roo.tree.TreePanel(treeEl , tree);
44836     //console.log(tree);
44837     this.on('activate', function()
44838     {
44839         if (this.tree.rendered) {
44840             return;
44841         }
44842         //console.log('render tree');
44843         this.tree.render();
44844     });
44845     
44846     this.on('resize',  function (cp, w, h) {
44847             this.tree.innerCt.setWidth(w);
44848             this.tree.innerCt.setHeight(h);
44849             this.tree.innerCt.setStyle('overflow-y', 'auto');
44850     });
44851
44852         
44853     
44854 };
44855
44856 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
44857
44858
44859
44860
44861
44862
44863
44864
44865
44866
44867
44868 /*
44869  * Based on:
44870  * Ext JS Library 1.1.1
44871  * Copyright(c) 2006-2007, Ext JS, LLC.
44872  *
44873  * Originally Released Under LGPL - original licence link has changed is not relivant.
44874  *
44875  * Fork - LGPL
44876  * <script type="text/javascript">
44877  */
44878  
44879
44880 /**
44881  * @class Roo.ReaderLayout
44882  * @extends Roo.BorderLayout
44883  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
44884  * center region containing two nested regions (a top one for a list view and one for item preview below),
44885  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
44886  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
44887  * expedites the setup of the overall layout and regions for this common application style.
44888  * Example:
44889  <pre><code>
44890 var reader = new Roo.ReaderLayout();
44891 var CP = Roo.ContentPanel;  // shortcut for adding
44892
44893 reader.beginUpdate();
44894 reader.add("north", new CP("north", "North"));
44895 reader.add("west", new CP("west", {title: "West"}));
44896 reader.add("east", new CP("east", {title: "East"}));
44897
44898 reader.regions.listView.add(new CP("listView", "List"));
44899 reader.regions.preview.add(new CP("preview", "Preview"));
44900 reader.endUpdate();
44901 </code></pre>
44902 * @constructor
44903 * Create a new ReaderLayout
44904 * @param {Object} config Configuration options
44905 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
44906 * document.body if omitted)
44907 */
44908 Roo.ReaderLayout = function(config, renderTo){
44909     var c = config || {size:{}};
44910     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
44911         north: c.north !== false ? Roo.apply({
44912             split:false,
44913             initialSize: 32,
44914             titlebar: false
44915         }, c.north) : false,
44916         west: c.west !== false ? Roo.apply({
44917             split:true,
44918             initialSize: 200,
44919             minSize: 175,
44920             maxSize: 400,
44921             titlebar: true,
44922             collapsible: true,
44923             animate: true,
44924             margins:{left:5,right:0,bottom:5,top:5},
44925             cmargins:{left:5,right:5,bottom:5,top:5}
44926         }, c.west) : false,
44927         east: c.east !== false ? Roo.apply({
44928             split:true,
44929             initialSize: 200,
44930             minSize: 175,
44931             maxSize: 400,
44932             titlebar: true,
44933             collapsible: true,
44934             animate: true,
44935             margins:{left:0,right:5,bottom:5,top:5},
44936             cmargins:{left:5,right:5,bottom:5,top:5}
44937         }, c.east) : false,
44938         center: Roo.apply({
44939             tabPosition: 'top',
44940             autoScroll:false,
44941             closeOnTab: true,
44942             titlebar:false,
44943             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
44944         }, c.center)
44945     });
44946
44947     this.el.addClass('x-reader');
44948
44949     this.beginUpdate();
44950
44951     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
44952         south: c.preview !== false ? Roo.apply({
44953             split:true,
44954             initialSize: 200,
44955             minSize: 100,
44956             autoScroll:true,
44957             collapsible:true,
44958             titlebar: true,
44959             cmargins:{top:5,left:0, right:0, bottom:0}
44960         }, c.preview) : false,
44961         center: Roo.apply({
44962             autoScroll:false,
44963             titlebar:false,
44964             minHeight:200
44965         }, c.listView)
44966     });
44967     this.add('center', new Roo.NestedLayoutPanel(inner,
44968             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
44969
44970     this.endUpdate();
44971
44972     this.regions.preview = inner.getRegion('south');
44973     this.regions.listView = inner.getRegion('center');
44974 };
44975
44976 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
44977  * Based on:
44978  * Ext JS Library 1.1.1
44979  * Copyright(c) 2006-2007, Ext JS, LLC.
44980  *
44981  * Originally Released Under LGPL - original licence link has changed is not relivant.
44982  *
44983  * Fork - LGPL
44984  * <script type="text/javascript">
44985  */
44986  
44987 /**
44988  * @class Roo.grid.Grid
44989  * @extends Roo.util.Observable
44990  * This class represents the primary interface of a component based grid control.
44991  * <br><br>Usage:<pre><code>
44992  var grid = new Roo.grid.Grid("my-container-id", {
44993      ds: myDataStore,
44994      cm: myColModel,
44995      selModel: mySelectionModel,
44996      autoSizeColumns: true,
44997      monitorWindowResize: false,
44998      trackMouseOver: true
44999  });
45000  // set any options
45001  grid.render();
45002  * </code></pre>
45003  * <b>Common Problems:</b><br/>
45004  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45005  * element will correct this<br/>
45006  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45007  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45008  * are unpredictable.<br/>
45009  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45010  * grid to calculate dimensions/offsets.<br/>
45011   * @constructor
45012  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45013  * The container MUST have some type of size defined for the grid to fill. The container will be
45014  * automatically set to position relative if it isn't already.
45015  * @param {Object} config A config object that sets properties on this grid.
45016  */
45017 Roo.grid.Grid = function(container, config){
45018         // initialize the container
45019         this.container = Roo.get(container);
45020         this.container.update("");
45021         this.container.setStyle("overflow", "hidden");
45022     this.container.addClass('x-grid-container');
45023
45024     this.id = this.container.id;
45025
45026     Roo.apply(this, config);
45027     // check and correct shorthanded configs
45028     if(this.ds){
45029         this.dataSource = this.ds;
45030         delete this.ds;
45031     }
45032     if(this.cm){
45033         this.colModel = this.cm;
45034         delete this.cm;
45035     }
45036     if(this.sm){
45037         this.selModel = this.sm;
45038         delete this.sm;
45039     }
45040
45041     if (this.selModel) {
45042         this.selModel = Roo.factory(this.selModel, Roo.grid);
45043         this.sm = this.selModel;
45044         this.sm.xmodule = this.xmodule || false;
45045     }
45046     if (typeof(this.colModel.config) == 'undefined') {
45047         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45048         this.cm = this.colModel;
45049         this.cm.xmodule = this.xmodule || false;
45050     }
45051     if (this.dataSource) {
45052         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45053         this.ds = this.dataSource;
45054         this.ds.xmodule = this.xmodule || false;
45055         
45056     }
45057     
45058     
45059     
45060     if(this.width){
45061         this.container.setWidth(this.width);
45062     }
45063
45064     if(this.height){
45065         this.container.setHeight(this.height);
45066     }
45067     /** @private */
45068         this.addEvents({
45069             // raw events
45070             /**
45071              * @event click
45072              * The raw click event for the entire grid.
45073              * @param {Roo.EventObject} e
45074              */
45075             "click" : true,
45076             /**
45077              * @event dblclick
45078              * The raw dblclick event for the entire grid.
45079              * @param {Roo.EventObject} e
45080              */
45081             "dblclick" : true,
45082             /**
45083              * @event contextmenu
45084              * The raw contextmenu event for the entire grid.
45085              * @param {Roo.EventObject} e
45086              */
45087             "contextmenu" : true,
45088             /**
45089              * @event mousedown
45090              * The raw mousedown event for the entire grid.
45091              * @param {Roo.EventObject} e
45092              */
45093             "mousedown" : true,
45094             /**
45095              * @event mouseup
45096              * The raw mouseup event for the entire grid.
45097              * @param {Roo.EventObject} e
45098              */
45099             "mouseup" : true,
45100             /**
45101              * @event mouseover
45102              * The raw mouseover event for the entire grid.
45103              * @param {Roo.EventObject} e
45104              */
45105             "mouseover" : true,
45106             /**
45107              * @event mouseout
45108              * The raw mouseout event for the entire grid.
45109              * @param {Roo.EventObject} e
45110              */
45111             "mouseout" : true,
45112             /**
45113              * @event keypress
45114              * The raw keypress event for the entire grid.
45115              * @param {Roo.EventObject} e
45116              */
45117             "keypress" : true,
45118             /**
45119              * @event keydown
45120              * The raw keydown event for the entire grid.
45121              * @param {Roo.EventObject} e
45122              */
45123             "keydown" : true,
45124
45125             // custom events
45126
45127             /**
45128              * @event cellclick
45129              * Fires when a cell is clicked
45130              * @param {Grid} this
45131              * @param {Number} rowIndex
45132              * @param {Number} columnIndex
45133              * @param {Roo.EventObject} e
45134              */
45135             "cellclick" : true,
45136             /**
45137              * @event celldblclick
45138              * Fires when a cell is double clicked
45139              * @param {Grid} this
45140              * @param {Number} rowIndex
45141              * @param {Number} columnIndex
45142              * @param {Roo.EventObject} e
45143              */
45144             "celldblclick" : true,
45145             /**
45146              * @event rowclick
45147              * Fires when a row is clicked
45148              * @param {Grid} this
45149              * @param {Number} rowIndex
45150              * @param {Roo.EventObject} e
45151              */
45152             "rowclick" : true,
45153             /**
45154              * @event rowdblclick
45155              * Fires when a row is double clicked
45156              * @param {Grid} this
45157              * @param {Number} rowIndex
45158              * @param {Roo.EventObject} e
45159              */
45160             "rowdblclick" : true,
45161             /**
45162              * @event headerclick
45163              * Fires when a header is clicked
45164              * @param {Grid} this
45165              * @param {Number} columnIndex
45166              * @param {Roo.EventObject} e
45167              */
45168             "headerclick" : true,
45169             /**
45170              * @event headerdblclick
45171              * Fires when a header cell is double clicked
45172              * @param {Grid} this
45173              * @param {Number} columnIndex
45174              * @param {Roo.EventObject} e
45175              */
45176             "headerdblclick" : true,
45177             /**
45178              * @event rowcontextmenu
45179              * Fires when a row is right clicked
45180              * @param {Grid} this
45181              * @param {Number} rowIndex
45182              * @param {Roo.EventObject} e
45183              */
45184             "rowcontextmenu" : true,
45185             /**
45186          * @event cellcontextmenu
45187          * Fires when a cell is right clicked
45188          * @param {Grid} this
45189          * @param {Number} rowIndex
45190          * @param {Number} cellIndex
45191          * @param {Roo.EventObject} e
45192          */
45193          "cellcontextmenu" : true,
45194             /**
45195              * @event headercontextmenu
45196              * Fires when a header is right clicked
45197              * @param {Grid} this
45198              * @param {Number} columnIndex
45199              * @param {Roo.EventObject} e
45200              */
45201             "headercontextmenu" : true,
45202             /**
45203              * @event bodyscroll
45204              * Fires when the body element is scrolled
45205              * @param {Number} scrollLeft
45206              * @param {Number} scrollTop
45207              */
45208             "bodyscroll" : true,
45209             /**
45210              * @event columnresize
45211              * Fires when the user resizes a column
45212              * @param {Number} columnIndex
45213              * @param {Number} newSize
45214              */
45215             "columnresize" : true,
45216             /**
45217              * @event columnmove
45218              * Fires when the user moves a column
45219              * @param {Number} oldIndex
45220              * @param {Number} newIndex
45221              */
45222             "columnmove" : true,
45223             /**
45224              * @event startdrag
45225              * Fires when row(s) start being dragged
45226              * @param {Grid} this
45227              * @param {Roo.GridDD} dd The drag drop object
45228              * @param {event} e The raw browser event
45229              */
45230             "startdrag" : true,
45231             /**
45232              * @event enddrag
45233              * Fires when a drag operation is complete
45234              * @param {Grid} this
45235              * @param {Roo.GridDD} dd The drag drop object
45236              * @param {event} e The raw browser event
45237              */
45238             "enddrag" : true,
45239             /**
45240              * @event dragdrop
45241              * Fires when dragged row(s) are dropped on a valid DD target
45242              * @param {Grid} this
45243              * @param {Roo.GridDD} dd The drag drop object
45244              * @param {String} targetId The target drag drop object
45245              * @param {event} e The raw browser event
45246              */
45247             "dragdrop" : true,
45248             /**
45249              * @event dragover
45250              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45251              * @param {Grid} this
45252              * @param {Roo.GridDD} dd The drag drop object
45253              * @param {String} targetId The target drag drop object
45254              * @param {event} e The raw browser event
45255              */
45256             "dragover" : true,
45257             /**
45258              * @event dragenter
45259              *  Fires when the dragged row(s) first cross another DD target while being dragged
45260              * @param {Grid} this
45261              * @param {Roo.GridDD} dd The drag drop object
45262              * @param {String} targetId The target drag drop object
45263              * @param {event} e The raw browser event
45264              */
45265             "dragenter" : true,
45266             /**
45267              * @event dragout
45268              * Fires when the dragged row(s) leave another DD target while being dragged
45269              * @param {Grid} this
45270              * @param {Roo.GridDD} dd The drag drop object
45271              * @param {String} targetId The target drag drop object
45272              * @param {event} e The raw browser event
45273              */
45274             "dragout" : true,
45275         /**
45276          * @event render
45277          * Fires when the grid is rendered
45278          * @param {Grid} grid
45279          */
45280         render : true
45281     });
45282
45283     Roo.grid.Grid.superclass.constructor.call(this);
45284 };
45285 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45286     /**
45287      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45288          */
45289         minColumnWidth : 25,
45290
45291     /**
45292          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45293          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45294          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45295          */
45296         autoSizeColumns : false,
45297
45298         /**
45299          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45300          */
45301         autoSizeHeaders : true,
45302
45303         /**
45304          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45305          */
45306         monitorWindowResize : true,
45307
45308         /**
45309          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45310          * rows measured to get a columns size. Default is 0 (all rows).
45311          */
45312         maxRowsToMeasure : 0,
45313
45314         /**
45315          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45316          */
45317         trackMouseOver : true,
45318
45319         /**
45320          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45321          */
45322         enableDragDrop : false,
45323
45324         /**
45325          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45326          */
45327         enableColumnMove : true,
45328
45329         /**
45330          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45331          */
45332         enableColumnHide : true,
45333
45334         /**
45335          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45336          */
45337         enableRowHeightSync : false,
45338
45339         /**
45340          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45341          */
45342         stripeRows : true,
45343
45344         /**
45345          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45346          */
45347         autoHeight : false,
45348
45349     /**
45350      * @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.
45351      */
45352     autoExpandColumn : false,
45353
45354     /**
45355     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45356     * Default is 50.
45357     */
45358     autoExpandMin : 50,
45359
45360     /**
45361     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45362     */
45363     autoExpandMax : 1000,
45364
45365     /**
45366          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45367          */
45368         view : null,
45369
45370         /**
45371      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45372          */
45373         loadMask : false,
45374
45375     // private
45376     rendered : false,
45377
45378     /**
45379     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45380     * of a fixed width. Default is false.
45381     */
45382     /**
45383     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45384     */
45385     /**
45386      * Called once after all setup has been completed and the grid is ready to be rendered.
45387      * @return {Roo.grid.Grid} this
45388      */
45389     render : function(){
45390         var c = this.container;
45391         // try to detect autoHeight/width mode
45392         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45393             this.autoHeight = true;
45394         }
45395         var view = this.getView();
45396         view.init(this);
45397
45398         c.on("click", this.onClick, this);
45399         c.on("dblclick", this.onDblClick, this);
45400         c.on("contextmenu", this.onContextMenu, this);
45401         c.on("keydown", this.onKeyDown, this);
45402
45403         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45404
45405         this.getSelectionModel().init(this);
45406
45407         view.render();
45408
45409         if(this.loadMask){
45410             this.loadMask = new Roo.LoadMask(this.container,
45411                     Roo.apply({store:this.dataSource}, this.loadMask));
45412         }
45413         
45414         
45415         if (this.toolbar && this.toolbar.xtype) {
45416             this.toolbar.container = this.getView().getHeaderPanel(true);
45417             this.toolbar = new Ext.Toolbar(this.toolbar);
45418         }
45419         if (this.footer && this.footer.xtype) {
45420             this.footer.dataSource = this.getDataSource();
45421             this.footer.container = this.getView().getFooterPanel(true);
45422             this.footer = Roo.factory(this.footer, Roo);
45423         }
45424         this.rendered = true;
45425         this.fireEvent('render', this);
45426         return this;
45427     },
45428
45429         /**
45430          * Reconfigures the grid to use a different Store and Column Model.
45431          * The View will be bound to the new objects and refreshed.
45432          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45433          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45434          */
45435     reconfigure : function(dataSource, colModel){
45436         if(this.loadMask){
45437             this.loadMask.destroy();
45438             this.loadMask = new Roo.LoadMask(this.container,
45439                     Roo.apply({store:dataSource}, this.loadMask));
45440         }
45441         this.view.bind(dataSource, colModel);
45442         this.dataSource = dataSource;
45443         this.colModel = colModel;
45444         this.view.refresh(true);
45445     },
45446
45447     // private
45448     onKeyDown : function(e){
45449         this.fireEvent("keydown", e);
45450     },
45451
45452     /**
45453      * Destroy this grid.
45454      * @param {Boolean} removeEl True to remove the element
45455      */
45456     destroy : function(removeEl, keepListeners){
45457         if(this.loadMask){
45458             this.loadMask.destroy();
45459         }
45460         var c = this.container;
45461         c.removeAllListeners();
45462         this.view.destroy();
45463         this.colModel.purgeListeners();
45464         if(!keepListeners){
45465             this.purgeListeners();
45466         }
45467         c.update("");
45468         if(removeEl === true){
45469             c.remove();
45470         }
45471     },
45472
45473     // private
45474     processEvent : function(name, e){
45475         this.fireEvent(name, e);
45476         var t = e.getTarget();
45477         var v = this.view;
45478         var header = v.findHeaderIndex(t);
45479         if(header !== false){
45480             this.fireEvent("header" + name, this, header, e);
45481         }else{
45482             var row = v.findRowIndex(t);
45483             var cell = v.findCellIndex(t);
45484             if(row !== false){
45485                 this.fireEvent("row" + name, this, row, e);
45486                 if(cell !== false){
45487                     this.fireEvent("cell" + name, this, row, cell, e);
45488                 }
45489             }
45490         }
45491     },
45492
45493     // private
45494     onClick : function(e){
45495         this.processEvent("click", e);
45496     },
45497
45498     // private
45499     onContextMenu : function(e, t){
45500         this.processEvent("contextmenu", e);
45501     },
45502
45503     // private
45504     onDblClick : function(e){
45505         this.processEvent("dblclick", e);
45506     },
45507
45508     // private
45509     walkCells : function(row, col, step, fn, scope){
45510         var cm = this.colModel, clen = cm.getColumnCount();
45511         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45512         if(step < 0){
45513             if(col < 0){
45514                 row--;
45515                 first = false;
45516             }
45517             while(row >= 0){
45518                 if(!first){
45519                     col = clen-1;
45520                 }
45521                 first = false;
45522                 while(col >= 0){
45523                     if(fn.call(scope || this, row, col, cm) === true){
45524                         return [row, col];
45525                     }
45526                     col--;
45527                 }
45528                 row--;
45529             }
45530         } else {
45531             if(col >= clen){
45532                 row++;
45533                 first = false;
45534             }
45535             while(row < rlen){
45536                 if(!first){
45537                     col = 0;
45538                 }
45539                 first = false;
45540                 while(col < clen){
45541                     if(fn.call(scope || this, row, col, cm) === true){
45542                         return [row, col];
45543                     }
45544                     col++;
45545                 }
45546                 row++;
45547             }
45548         }
45549         return null;
45550     },
45551
45552     // private
45553     getSelections : function(){
45554         return this.selModel.getSelections();
45555     },
45556
45557     /**
45558      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45559      * but if manual update is required this method will initiate it.
45560      */
45561     autoSize : function(){
45562         if(this.rendered){
45563             this.view.layout();
45564             if(this.view.adjustForScroll){
45565                 this.view.adjustForScroll();
45566             }
45567         }
45568     },
45569
45570     /**
45571      * Returns the grid's underlying element.
45572      * @return {Element} The element
45573      */
45574     getGridEl : function(){
45575         return this.container;
45576     },
45577
45578     // private for compatibility, overridden by editor grid
45579     stopEditing : function(){},
45580
45581     /**
45582      * Returns the grid's SelectionModel.
45583      * @return {SelectionModel}
45584      */
45585     getSelectionModel : function(){
45586         if(!this.selModel){
45587             this.selModel = new Roo.grid.RowSelectionModel();
45588         }
45589         return this.selModel;
45590     },
45591
45592     /**
45593      * Returns the grid's DataSource.
45594      * @return {DataSource}
45595      */
45596     getDataSource : function(){
45597         return this.dataSource;
45598     },
45599
45600     /**
45601      * Returns the grid's ColumnModel.
45602      * @return {ColumnModel}
45603      */
45604     getColumnModel : function(){
45605         return this.colModel;
45606     },
45607
45608     /**
45609      * Returns the grid's GridView object.
45610      * @return {GridView}
45611      */
45612     getView : function(){
45613         if(!this.view){
45614             this.view = new Roo.grid.GridView(this.viewConfig);
45615         }
45616         return this.view;
45617     },
45618     /**
45619      * Called to get grid's drag proxy text, by default returns this.ddText.
45620      * @return {String}
45621      */
45622     getDragDropText : function(){
45623         var count = this.selModel.getCount();
45624         return String.format(this.ddText, count, count == 1 ? '' : 's');
45625     }
45626 });
45627 /**
45628  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45629  * %0 is replaced with the number of selected rows.
45630  * @type String
45631  */
45632 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45633  * Based on:
45634  * Ext JS Library 1.1.1
45635  * Copyright(c) 2006-2007, Ext JS, LLC.
45636  *
45637  * Originally Released Under LGPL - original licence link has changed is not relivant.
45638  *
45639  * Fork - LGPL
45640  * <script type="text/javascript">
45641  */
45642  
45643 Roo.grid.AbstractGridView = function(){
45644         this.grid = null;
45645         
45646         this.events = {
45647             "beforerowremoved" : true,
45648             "beforerowsinserted" : true,
45649             "beforerefresh" : true,
45650             "rowremoved" : true,
45651             "rowsinserted" : true,
45652             "rowupdated" : true,
45653             "refresh" : true
45654         };
45655     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45656 };
45657
45658 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45659     rowClass : "x-grid-row",
45660     cellClass : "x-grid-cell",
45661     tdClass : "x-grid-td",
45662     hdClass : "x-grid-hd",
45663     splitClass : "x-grid-hd-split",
45664     
45665         init: function(grid){
45666         this.grid = grid;
45667                 var cid = this.grid.getGridEl().id;
45668         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45669         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45670         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45671         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45672         },
45673         
45674         getColumnRenderers : function(){
45675         var renderers = [];
45676         var cm = this.grid.colModel;
45677         var colCount = cm.getColumnCount();
45678         for(var i = 0; i < colCount; i++){
45679             renderers[i] = cm.getRenderer(i);
45680         }
45681         return renderers;
45682     },
45683     
45684     getColumnIds : function(){
45685         var ids = [];
45686         var cm = this.grid.colModel;
45687         var colCount = cm.getColumnCount();
45688         for(var i = 0; i < colCount; i++){
45689             ids[i] = cm.getColumnId(i);
45690         }
45691         return ids;
45692     },
45693     
45694     getDataIndexes : function(){
45695         if(!this.indexMap){
45696             this.indexMap = this.buildIndexMap();
45697         }
45698         return this.indexMap.colToData;
45699     },
45700     
45701     getColumnIndexByDataIndex : function(dataIndex){
45702         if(!this.indexMap){
45703             this.indexMap = this.buildIndexMap();
45704         }
45705         return this.indexMap.dataToCol[dataIndex];
45706     },
45707     
45708     /**
45709      * Set a css style for a column dynamically. 
45710      * @param {Number} colIndex The index of the column
45711      * @param {String} name The css property name
45712      * @param {String} value The css value
45713      */
45714     setCSSStyle : function(colIndex, name, value){
45715         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45716         Roo.util.CSS.updateRule(selector, name, value);
45717     },
45718     
45719     generateRules : function(cm){
45720         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45721         Roo.util.CSS.removeStyleSheet(rulesId);
45722         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45723             var cid = cm.getColumnId(i);
45724             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45725                          this.tdSelector, cid, " {\n}\n",
45726                          this.hdSelector, cid, " {\n}\n",
45727                          this.splitSelector, cid, " {\n}\n");
45728         }
45729         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45730     }
45731 });/*
45732  * Based on:
45733  * Ext JS Library 1.1.1
45734  * Copyright(c) 2006-2007, Ext JS, LLC.
45735  *
45736  * Originally Released Under LGPL - original licence link has changed is not relivant.
45737  *
45738  * Fork - LGPL
45739  * <script type="text/javascript">
45740  */
45741
45742 // private
45743 // This is a support class used internally by the Grid components
45744 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45745     this.grid = grid;
45746     this.view = grid.getView();
45747     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45748     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45749     if(hd2){
45750         this.setHandleElId(Roo.id(hd));
45751         this.setOuterHandleElId(Roo.id(hd2));
45752     }
45753     this.scroll = false;
45754 };
45755 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45756     maxDragWidth: 120,
45757     getDragData : function(e){
45758         var t = Roo.lib.Event.getTarget(e);
45759         var h = this.view.findHeaderCell(t);
45760         if(h){
45761             return {ddel: h.firstChild, header:h};
45762         }
45763         return false;
45764     },
45765
45766     onInitDrag : function(e){
45767         this.view.headersDisabled = true;
45768         var clone = this.dragData.ddel.cloneNode(true);
45769         clone.id = Roo.id();
45770         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45771         this.proxy.update(clone);
45772         return true;
45773     },
45774
45775     afterValidDrop : function(){
45776         var v = this.view;
45777         setTimeout(function(){
45778             v.headersDisabled = false;
45779         }, 50);
45780     },
45781
45782     afterInvalidDrop : function(){
45783         var v = this.view;
45784         setTimeout(function(){
45785             v.headersDisabled = false;
45786         }, 50);
45787     }
45788 });
45789 /*
45790  * Based on:
45791  * Ext JS Library 1.1.1
45792  * Copyright(c) 2006-2007, Ext JS, LLC.
45793  *
45794  * Originally Released Under LGPL - original licence link has changed is not relivant.
45795  *
45796  * Fork - LGPL
45797  * <script type="text/javascript">
45798  */
45799 // private
45800 // This is a support class used internally by the Grid components
45801 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45802     this.grid = grid;
45803     this.view = grid.getView();
45804     // split the proxies so they don't interfere with mouse events
45805     this.proxyTop = Roo.DomHelper.append(document.body, {
45806         cls:"col-move-top", html:"&#160;"
45807     }, true);
45808     this.proxyBottom = Roo.DomHelper.append(document.body, {
45809         cls:"col-move-bottom", html:"&#160;"
45810     }, true);
45811     this.proxyTop.hide = this.proxyBottom.hide = function(){
45812         this.setLeftTop(-100,-100);
45813         this.setStyle("visibility", "hidden");
45814     };
45815     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45816     // temporarily disabled
45817     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
45818     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
45819 };
45820 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
45821     proxyOffsets : [-4, -9],
45822     fly: Roo.Element.fly,
45823
45824     getTargetFromEvent : function(e){
45825         var t = Roo.lib.Event.getTarget(e);
45826         var cindex = this.view.findCellIndex(t);
45827         if(cindex !== false){
45828             return this.view.getHeaderCell(cindex);
45829         }
45830     },
45831
45832     nextVisible : function(h){
45833         var v = this.view, cm = this.grid.colModel;
45834         h = h.nextSibling;
45835         while(h){
45836             if(!cm.isHidden(v.getCellIndex(h))){
45837                 return h;
45838             }
45839             h = h.nextSibling;
45840         }
45841         return null;
45842     },
45843
45844     prevVisible : function(h){
45845         var v = this.view, cm = this.grid.colModel;
45846         h = h.prevSibling;
45847         while(h){
45848             if(!cm.isHidden(v.getCellIndex(h))){
45849                 return h;
45850             }
45851             h = h.prevSibling;
45852         }
45853         return null;
45854     },
45855
45856     positionIndicator : function(h, n, e){
45857         var x = Roo.lib.Event.getPageX(e);
45858         var r = Roo.lib.Dom.getRegion(n.firstChild);
45859         var px, pt, py = r.top + this.proxyOffsets[1];
45860         if((r.right - x) <= (r.right-r.left)/2){
45861             px = r.right+this.view.borderWidth;
45862             pt = "after";
45863         }else{
45864             px = r.left;
45865             pt = "before";
45866         }
45867         var oldIndex = this.view.getCellIndex(h);
45868         var newIndex = this.view.getCellIndex(n);
45869
45870         if(this.grid.colModel.isFixed(newIndex)){
45871             return false;
45872         }
45873
45874         var locked = this.grid.colModel.isLocked(newIndex);
45875
45876         if(pt == "after"){
45877             newIndex++;
45878         }
45879         if(oldIndex < newIndex){
45880             newIndex--;
45881         }
45882         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
45883             return false;
45884         }
45885         px +=  this.proxyOffsets[0];
45886         this.proxyTop.setLeftTop(px, py);
45887         this.proxyTop.show();
45888         if(!this.bottomOffset){
45889             this.bottomOffset = this.view.mainHd.getHeight();
45890         }
45891         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
45892         this.proxyBottom.show();
45893         return pt;
45894     },
45895
45896     onNodeEnter : function(n, dd, e, data){
45897         if(data.header != n){
45898             this.positionIndicator(data.header, n, e);
45899         }
45900     },
45901
45902     onNodeOver : function(n, dd, e, data){
45903         var result = false;
45904         if(data.header != n){
45905             result = this.positionIndicator(data.header, n, e);
45906         }
45907         if(!result){
45908             this.proxyTop.hide();
45909             this.proxyBottom.hide();
45910         }
45911         return result ? this.dropAllowed : this.dropNotAllowed;
45912     },
45913
45914     onNodeOut : function(n, dd, e, data){
45915         this.proxyTop.hide();
45916         this.proxyBottom.hide();
45917     },
45918
45919     onNodeDrop : function(n, dd, e, data){
45920         var h = data.header;
45921         if(h != n){
45922             var cm = this.grid.colModel;
45923             var x = Roo.lib.Event.getPageX(e);
45924             var r = Roo.lib.Dom.getRegion(n.firstChild);
45925             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
45926             var oldIndex = this.view.getCellIndex(h);
45927             var newIndex = this.view.getCellIndex(n);
45928             var locked = cm.isLocked(newIndex);
45929             if(pt == "after"){
45930                 newIndex++;
45931             }
45932             if(oldIndex < newIndex){
45933                 newIndex--;
45934             }
45935             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
45936                 return false;
45937             }
45938             cm.setLocked(oldIndex, locked, true);
45939             cm.moveColumn(oldIndex, newIndex);
45940             this.grid.fireEvent("columnmove", oldIndex, newIndex);
45941             return true;
45942         }
45943         return false;
45944     }
45945 });
45946 /*
45947  * Based on:
45948  * Ext JS Library 1.1.1
45949  * Copyright(c) 2006-2007, Ext JS, LLC.
45950  *
45951  * Originally Released Under LGPL - original licence link has changed is not relivant.
45952  *
45953  * Fork - LGPL
45954  * <script type="text/javascript">
45955  */
45956   
45957 /**
45958  * @class Roo.grid.GridView
45959  * @extends Roo.util.Observable
45960  *
45961  * @constructor
45962  * @param {Object} config
45963  */
45964 Roo.grid.GridView = function(config){
45965     Roo.grid.GridView.superclass.constructor.call(this);
45966     this.el = null;
45967
45968     Roo.apply(this, config);
45969 };
45970
45971 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
45972
45973     /**
45974      * Override this function to apply custom css classes to rows during rendering
45975      * @param {Record} record The record
45976      * @param {Number} index
45977      * @method getRowClass
45978      */
45979     rowClass : "x-grid-row",
45980
45981     cellClass : "x-grid-col",
45982
45983     tdClass : "x-grid-td",
45984
45985     hdClass : "x-grid-hd",
45986
45987     splitClass : "x-grid-split",
45988
45989     sortClasses : ["sort-asc", "sort-desc"],
45990
45991     enableMoveAnim : false,
45992
45993     hlColor: "C3DAF9",
45994
45995     dh : Roo.DomHelper,
45996
45997     fly : Roo.Element.fly,
45998
45999     css : Roo.util.CSS,
46000
46001     borderWidth: 1,
46002
46003     splitOffset: 3,
46004
46005     scrollIncrement : 22,
46006
46007     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46008
46009     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46010
46011     bind : function(ds, cm){
46012         if(this.ds){
46013             this.ds.un("load", this.onLoad, this);
46014             this.ds.un("datachanged", this.onDataChange, this);
46015             this.ds.un("add", this.onAdd, this);
46016             this.ds.un("remove", this.onRemove, this);
46017             this.ds.un("update", this.onUpdate, this);
46018             this.ds.un("clear", this.onClear, this);
46019         }
46020         if(ds){
46021             ds.on("load", this.onLoad, this);
46022             ds.on("datachanged", this.onDataChange, this);
46023             ds.on("add", this.onAdd, this);
46024             ds.on("remove", this.onRemove, this);
46025             ds.on("update", this.onUpdate, this);
46026             ds.on("clear", this.onClear, this);
46027         }
46028         this.ds = ds;
46029
46030         if(this.cm){
46031             this.cm.un("widthchange", this.onColWidthChange, this);
46032             this.cm.un("headerchange", this.onHeaderChange, this);
46033             this.cm.un("hiddenchange", this.onHiddenChange, this);
46034             this.cm.un("columnmoved", this.onColumnMove, this);
46035             this.cm.un("columnlockchange", this.onColumnLock, this);
46036         }
46037         if(cm){
46038             this.generateRules(cm);
46039             cm.on("widthchange", this.onColWidthChange, this);
46040             cm.on("headerchange", this.onHeaderChange, this);
46041             cm.on("hiddenchange", this.onHiddenChange, this);
46042             cm.on("columnmoved", this.onColumnMove, this);
46043             cm.on("columnlockchange", this.onColumnLock, this);
46044         }
46045         this.cm = cm;
46046     },
46047
46048     init: function(grid){
46049                 Roo.grid.GridView.superclass.init.call(this, grid);
46050
46051                 this.bind(grid.dataSource, grid.colModel);
46052
46053             grid.on("headerclick", this.handleHeaderClick, this);
46054
46055         if(grid.trackMouseOver){
46056             grid.on("mouseover", this.onRowOver, this);
46057                 grid.on("mouseout", this.onRowOut, this);
46058             }
46059             grid.cancelTextSelection = function(){};
46060                 this.gridId = grid.id;
46061
46062                 var tpls = this.templates || {};
46063
46064                 if(!tpls.master){
46065                     tpls.master = new Roo.Template(
46066                        '<div class="x-grid" hidefocus="true">',
46067                           '<div class="x-grid-topbar"></div>',
46068                           '<div class="x-grid-scroller"><div></div></div>',
46069                           '<div class="x-grid-locked">',
46070                               '<div class="x-grid-header">{lockedHeader}</div>',
46071                               '<div class="x-grid-body">{lockedBody}</div>',
46072                           "</div>",
46073                           '<div class="x-grid-viewport">',
46074                               '<div class="x-grid-header">{header}</div>',
46075                               '<div class="x-grid-body">{body}</div>',
46076                           "</div>",
46077                           '<div class="x-grid-bottombar"></div>',
46078                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46079                           '<div class="x-grid-resize-proxy">&#160;</div>',
46080                        "</div>"
46081                     );
46082                     tpls.master.disableformats = true;
46083                 }
46084
46085                 if(!tpls.header){
46086                     tpls.header = new Roo.Template(
46087                        '<table border="0" cellspacing="0" cellpadding="0">',
46088                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46089                        "</table>{splits}"
46090                     );
46091                     tpls.header.disableformats = true;
46092                 }
46093                 tpls.header.compile();
46094
46095                 if(!tpls.hcell){
46096                     tpls.hcell = new Roo.Template(
46097                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46098                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46099                         "</div></td>"
46100                      );
46101                      tpls.hcell.disableFormats = true;
46102                 }
46103                 tpls.hcell.compile();
46104
46105                 if(!tpls.hsplit){
46106                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46107                     tpls.hsplit.disableFormats = true;
46108                 }
46109                 tpls.hsplit.compile();
46110
46111                 if(!tpls.body){
46112                     tpls.body = new Roo.Template(
46113                        '<table border="0" cellspacing="0" cellpadding="0">',
46114                        "<tbody>{rows}</tbody>",
46115                        "</table>"
46116                     );
46117                     tpls.body.disableFormats = true;
46118                 }
46119                 tpls.body.compile();
46120
46121                 if(!tpls.row){
46122                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46123                     tpls.row.disableFormats = true;
46124                 }
46125                 tpls.row.compile();
46126
46127                 if(!tpls.cell){
46128                     tpls.cell = new Roo.Template(
46129                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46130                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46131                         "</td>"
46132                     );
46133             tpls.cell.disableFormats = true;
46134         }
46135                 tpls.cell.compile();
46136
46137                 this.templates = tpls;
46138         },
46139
46140         // remap these for backwards compat
46141     onColWidthChange : function(){
46142         this.updateColumns.apply(this, arguments);
46143     },
46144     onHeaderChange : function(){
46145         this.updateHeaders.apply(this, arguments);
46146     }, 
46147     onHiddenChange : function(){
46148         this.handleHiddenChange.apply(this, arguments);
46149     },
46150     onColumnMove : function(){
46151         this.handleColumnMove.apply(this, arguments);
46152     },
46153     onColumnLock : function(){
46154         this.handleLockChange.apply(this, arguments);
46155     },
46156
46157     onDataChange : function(){
46158         this.refresh();
46159         this.updateHeaderSortState();
46160     },
46161
46162         onClear : function(){
46163         this.refresh();
46164     },
46165
46166         onUpdate : function(ds, record){
46167         this.refreshRow(record);
46168     },
46169
46170     refreshRow : function(record){
46171         var ds = this.ds, index;
46172         if(typeof record == 'number'){
46173             index = record;
46174             record = ds.getAt(index);
46175         }else{
46176             index = ds.indexOf(record);
46177         }
46178         this.insertRows(ds, index, index, true);
46179         this.onRemove(ds, record, index+1, true);
46180         this.syncRowHeights(index, index);
46181         this.layout();
46182         this.fireEvent("rowupdated", this, index, record);
46183     },
46184
46185     onAdd : function(ds, records, index){
46186         this.insertRows(ds, index, index + (records.length-1));
46187     },
46188
46189     onRemove : function(ds, record, index, isUpdate){
46190         if(isUpdate !== true){
46191             this.fireEvent("beforerowremoved", this, index, record);
46192         }
46193         var bt = this.getBodyTable(), lt = this.getLockedTable();
46194         if(bt.rows[index]){
46195             bt.firstChild.removeChild(bt.rows[index]);
46196         }
46197         if(lt.rows[index]){
46198             lt.firstChild.removeChild(lt.rows[index]);
46199         }
46200         if(isUpdate !== true){
46201             this.stripeRows(index);
46202             this.syncRowHeights(index, index);
46203             this.layout();
46204             this.fireEvent("rowremoved", this, index, record);
46205         }
46206     },
46207
46208     onLoad : function(){
46209         this.scrollToTop();
46210     },
46211
46212     /**
46213      * Scrolls the grid to the top
46214      */
46215     scrollToTop : function(){
46216         if(this.scroller){
46217             this.scroller.dom.scrollTop = 0;
46218             this.syncScroll();
46219         }
46220     },
46221
46222     /**
46223      * Gets a panel in the header of the grid that can be used for toolbars etc.
46224      * After modifying the contents of this panel a call to grid.autoSize() may be
46225      * required to register any changes in size.
46226      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46227      * @return Roo.Element
46228      */
46229     getHeaderPanel : function(doShow){
46230         if(doShow){
46231             this.headerPanel.show();
46232         }
46233         return this.headerPanel;
46234         },
46235
46236         /**
46237      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46238      * After modifying the contents of this panel a call to grid.autoSize() may be
46239      * required to register any changes in size.
46240      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46241      * @return Roo.Element
46242      */
46243     getFooterPanel : function(doShow){
46244         if(doShow){
46245             this.footerPanel.show();
46246         }
46247         return this.footerPanel;
46248         },
46249
46250         initElements : function(){
46251             var E = Roo.Element;
46252             var el = this.grid.getGridEl().dom.firstChild;
46253             var cs = el.childNodes;
46254
46255             this.el = new E(el);
46256             this.headerPanel = new E(el.firstChild);
46257             this.headerPanel.enableDisplayMode("block");
46258
46259         this.scroller = new E(cs[1]);
46260             this.scrollSizer = new E(this.scroller.dom.firstChild);
46261
46262             this.lockedWrap = new E(cs[2]);
46263             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46264             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46265
46266             this.mainWrap = new E(cs[3]);
46267             this.mainHd = new E(this.mainWrap.dom.firstChild);
46268             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46269
46270             this.footerPanel = new E(cs[4]);
46271             this.footerPanel.enableDisplayMode("block");
46272
46273         this.focusEl = new E(cs[5]);
46274         this.focusEl.swallowEvent("click", true);
46275         this.resizeProxy = new E(cs[6]);
46276
46277             this.headerSelector = String.format(
46278                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46279                this.lockedHd.id, this.mainHd.id
46280             );
46281
46282             this.splitterSelector = String.format(
46283                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46284                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46285             );
46286     },
46287     idToCssName : function(s)
46288     {
46289         return s.replace(/[^a-z0-9]+/ig, '-');
46290     },
46291
46292         getHeaderCell : function(index){
46293             return Roo.DomQuery.select(this.headerSelector)[index];
46294         },
46295
46296         getHeaderCellMeasure : function(index){
46297             return this.getHeaderCell(index).firstChild;
46298         },
46299
46300         getHeaderCellText : function(index){
46301             return this.getHeaderCell(index).firstChild.firstChild;
46302         },
46303
46304         getLockedTable : function(){
46305             return this.lockedBody.dom.firstChild;
46306         },
46307
46308         getBodyTable : function(){
46309             return this.mainBody.dom.firstChild;
46310         },
46311
46312         getLockedRow : function(index){
46313             return this.getLockedTable().rows[index];
46314         },
46315
46316         getRow : function(index){
46317             return this.getBodyTable().rows[index];
46318         },
46319
46320         getRowComposite : function(index){
46321             if(!this.rowEl){
46322                 this.rowEl = new Roo.CompositeElementLite();
46323             }
46324         var els = [], lrow, mrow;
46325         if(lrow = this.getLockedRow(index)){
46326             els.push(lrow);
46327         }
46328         if(mrow = this.getRow(index)){
46329             els.push(mrow);
46330         }
46331         this.rowEl.elements = els;
46332             return this.rowEl;
46333         },
46334
46335         getCell : function(rowIndex, colIndex){
46336             var locked = this.cm.getLockedCount();
46337             var source;
46338             if(colIndex < locked){
46339                 source = this.lockedBody.dom.firstChild;
46340             }else{
46341                 source = this.mainBody.dom.firstChild;
46342                 colIndex -= locked;
46343             }
46344         return source.rows[rowIndex].childNodes[colIndex];
46345         },
46346
46347         getCellText : function(rowIndex, colIndex){
46348             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46349         },
46350
46351         getCellBox : function(cell){
46352             var b = this.fly(cell).getBox();
46353         if(Roo.isOpera){ // opera fails to report the Y
46354             b.y = cell.offsetTop + this.mainBody.getY();
46355         }
46356         return b;
46357     },
46358
46359     getCellIndex : function(cell){
46360         var id = String(cell.className).match(this.cellRE);
46361         if(id){
46362             return parseInt(id[1], 10);
46363         }
46364         return 0;
46365     },
46366
46367     findHeaderIndex : function(n){
46368         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46369         return r ? this.getCellIndex(r) : false;
46370     },
46371
46372     findHeaderCell : function(n){
46373         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46374         return r ? r : false;
46375     },
46376
46377     findRowIndex : function(n){
46378         if(!n){
46379             return false;
46380         }
46381         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46382         return r ? r.rowIndex : false;
46383     },
46384
46385     findCellIndex : function(node){
46386         var stop = this.el.dom;
46387         while(node && node != stop){
46388             if(this.findRE.test(node.className)){
46389                 return this.getCellIndex(node);
46390             }
46391             node = node.parentNode;
46392         }
46393         return false;
46394     },
46395
46396     getColumnId : function(index){
46397             return this.cm.getColumnId(index);
46398         },
46399
46400         getSplitters : function(){
46401             if(this.splitterSelector){
46402                return Roo.DomQuery.select(this.splitterSelector);
46403             }else{
46404                 return null;
46405             }
46406         },
46407
46408         getSplitter : function(index){
46409             return this.getSplitters()[index];
46410         },
46411
46412     onRowOver : function(e, t){
46413         var row;
46414         if((row = this.findRowIndex(t)) !== false){
46415             this.getRowComposite(row).addClass("x-grid-row-over");
46416         }
46417     },
46418
46419     onRowOut : function(e, t){
46420         var row;
46421         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46422             this.getRowComposite(row).removeClass("x-grid-row-over");
46423         }
46424     },
46425
46426     renderHeaders : function(){
46427             var cm = this.cm;
46428         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46429         var cb = [], lb = [], sb = [], lsb = [], p = {};
46430         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46431             p.cellId = "x-grid-hd-0-" + i;
46432             p.splitId = "x-grid-csplit-0-" + i;
46433             p.id = cm.getColumnId(i);
46434             p.title = cm.getColumnTooltip(i) || "";
46435             p.value = cm.getColumnHeader(i) || "";
46436             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46437             if(!cm.isLocked(i)){
46438                 cb[cb.length] = ct.apply(p);
46439                 sb[sb.length] = st.apply(p);
46440             }else{
46441                 lb[lb.length] = ct.apply(p);
46442                 lsb[lsb.length] = st.apply(p);
46443             }
46444         }
46445         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46446                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46447         },
46448
46449         updateHeaders : function(){
46450         var html = this.renderHeaders();
46451         this.lockedHd.update(html[0]);
46452         this.mainHd.update(html[1]);
46453     },
46454
46455     /**
46456      * Focuses the specified row.
46457      * @param {Number} row The row index
46458      */
46459     focusRow : function(row){
46460         var x = this.scroller.dom.scrollLeft;
46461         this.focusCell(row, 0, false);
46462         this.scroller.dom.scrollLeft = x;
46463     },
46464
46465     /**
46466      * Focuses the specified cell.
46467      * @param {Number} row The row index
46468      * @param {Number} col The column index
46469      * @param {Boolean} hscroll false to disable horizontal scrolling
46470      */
46471     focusCell : function(row, col, hscroll){
46472         var el = this.ensureVisible(row, col, hscroll);
46473         this.focusEl.alignTo(el, "tl-tl");
46474         if(Roo.isGecko){
46475             this.focusEl.focus();
46476         }else{
46477             this.focusEl.focus.defer(1, this.focusEl);
46478         }
46479     },
46480
46481     /**
46482      * Scrolls the specified cell into view
46483      * @param {Number} row The row index
46484      * @param {Number} col The column index
46485      * @param {Boolean} hscroll false to disable horizontal scrolling
46486      */
46487     ensureVisible : function(row, col, hscroll){
46488         if(typeof row != "number"){
46489             row = row.rowIndex;
46490         }
46491         if(row < 0 && row >= this.ds.getCount()){
46492             return;
46493         }
46494         col = (col !== undefined ? col : 0);
46495         var cm = this.grid.colModel;
46496         while(cm.isHidden(col)){
46497             col++;
46498         }
46499
46500         var el = this.getCell(row, col);
46501         if(!el){
46502             return;
46503         }
46504         var c = this.scroller.dom;
46505
46506         var ctop = parseInt(el.offsetTop, 10);
46507         var cleft = parseInt(el.offsetLeft, 10);
46508         var cbot = ctop + el.offsetHeight;
46509         var cright = cleft + el.offsetWidth;
46510
46511         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46512         var stop = parseInt(c.scrollTop, 10);
46513         var sleft = parseInt(c.scrollLeft, 10);
46514         var sbot = stop + ch;
46515         var sright = sleft + c.clientWidth;
46516
46517         if(ctop < stop){
46518                 c.scrollTop = ctop;
46519         }else if(cbot > sbot){
46520             c.scrollTop = cbot-ch;
46521         }
46522
46523         if(hscroll !== false){
46524             if(cleft < sleft){
46525                 c.scrollLeft = cleft;
46526             }else if(cright > sright){
46527                 c.scrollLeft = cright-c.clientWidth;
46528             }
46529         }
46530         return el;
46531     },
46532
46533     updateColumns : function(){
46534         this.grid.stopEditing();
46535         var cm = this.grid.colModel, colIds = this.getColumnIds();
46536         //var totalWidth = cm.getTotalWidth();
46537         var pos = 0;
46538         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46539             //if(cm.isHidden(i)) continue;
46540             var w = cm.getColumnWidth(i);
46541             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46542             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46543         }
46544         this.updateSplitters();
46545     },
46546
46547     generateRules : function(cm){
46548         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46549         Roo.util.CSS.removeStyleSheet(rulesId);
46550         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46551             var cid = cm.getColumnId(i);
46552             var align = '';
46553             if(cm.config[i].align){
46554                 align = 'text-align:'+cm.config[i].align+';';
46555             }
46556             var hidden = '';
46557             if(cm.isHidden(i)){
46558                 hidden = 'display:none;';
46559             }
46560             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46561             ruleBuf.push(
46562                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46563                     this.hdSelector, cid, " {\n", align, width, "}\n",
46564                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46565                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46566         }
46567         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46568     },
46569
46570     updateSplitters : function(){
46571         var cm = this.cm, s = this.getSplitters();
46572         if(s){ // splitters not created yet
46573             var pos = 0, locked = true;
46574             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46575                 if(cm.isHidden(i)) continue;
46576                 var w = cm.getColumnWidth(i);
46577                 if(!cm.isLocked(i) && locked){
46578                     pos = 0;
46579                     locked = false;
46580                 }
46581                 pos += w;
46582                 s[i].style.left = (pos-this.splitOffset) + "px";
46583             }
46584         }
46585     },
46586
46587     handleHiddenChange : function(colModel, colIndex, hidden){
46588         if(hidden){
46589             this.hideColumn(colIndex);
46590         }else{
46591             this.unhideColumn(colIndex);
46592         }
46593     },
46594
46595     hideColumn : function(colIndex){
46596         var cid = this.getColumnId(colIndex);
46597         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46598         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46599         if(Roo.isSafari){
46600             this.updateHeaders();
46601         }
46602         this.updateSplitters();
46603         this.layout();
46604     },
46605
46606     unhideColumn : function(colIndex){
46607         var cid = this.getColumnId(colIndex);
46608         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46609         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46610
46611         if(Roo.isSafari){
46612             this.updateHeaders();
46613         }
46614         this.updateSplitters();
46615         this.layout();
46616     },
46617
46618     insertRows : function(dm, firstRow, lastRow, isUpdate){
46619         if(firstRow == 0 && lastRow == dm.getCount()-1){
46620             this.refresh();
46621         }else{
46622             if(!isUpdate){
46623                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46624             }
46625             var s = this.getScrollState();
46626             var markup = this.renderRows(firstRow, lastRow);
46627             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46628             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46629             this.restoreScroll(s);
46630             if(!isUpdate){
46631                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46632                 this.syncRowHeights(firstRow, lastRow);
46633                 this.stripeRows(firstRow);
46634                 this.layout();
46635             }
46636         }
46637     },
46638
46639     bufferRows : function(markup, target, index){
46640         var before = null, trows = target.rows, tbody = target.tBodies[0];
46641         if(index < trows.length){
46642             before = trows[index];
46643         }
46644         var b = document.createElement("div");
46645         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46646         var rows = b.firstChild.rows;
46647         for(var i = 0, len = rows.length; i < len; i++){
46648             if(before){
46649                 tbody.insertBefore(rows[0], before);
46650             }else{
46651                 tbody.appendChild(rows[0]);
46652             }
46653         }
46654         b.innerHTML = "";
46655         b = null;
46656     },
46657
46658     deleteRows : function(dm, firstRow, lastRow){
46659         if(dm.getRowCount()<1){
46660             this.fireEvent("beforerefresh", this);
46661             this.mainBody.update("");
46662             this.lockedBody.update("");
46663             this.fireEvent("refresh", this);
46664         }else{
46665             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46666             var bt = this.getBodyTable();
46667             var tbody = bt.firstChild;
46668             var rows = bt.rows;
46669             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46670                 tbody.removeChild(rows[firstRow]);
46671             }
46672             this.stripeRows(firstRow);
46673             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46674         }
46675     },
46676
46677     updateRows : function(dataSource, firstRow, lastRow){
46678         var s = this.getScrollState();
46679         this.refresh();
46680         this.restoreScroll(s);
46681     },
46682
46683     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46684         if(!noRefresh){
46685            this.refresh();
46686         }
46687         this.updateHeaderSortState();
46688     },
46689
46690     getScrollState : function(){
46691         var sb = this.scroller.dom;
46692         return {left: sb.scrollLeft, top: sb.scrollTop};
46693     },
46694
46695     stripeRows : function(startRow){
46696         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46697             return;
46698         }
46699         startRow = startRow || 0;
46700         var rows = this.getBodyTable().rows;
46701         var lrows = this.getLockedTable().rows;
46702         var cls = ' x-grid-row-alt ';
46703         for(var i = startRow, len = rows.length; i < len; i++){
46704             var row = rows[i], lrow = lrows[i];
46705             var isAlt = ((i+1) % 2 == 0);
46706             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46707             if(isAlt == hasAlt){
46708                 continue;
46709             }
46710             if(isAlt){
46711                 row.className += " x-grid-row-alt";
46712             }else{
46713                 row.className = row.className.replace("x-grid-row-alt", "");
46714             }
46715             if(lrow){
46716                 lrow.className = row.className;
46717             }
46718         }
46719     },
46720
46721     restoreScroll : function(state){
46722         var sb = this.scroller.dom;
46723         sb.scrollLeft = state.left;
46724         sb.scrollTop = state.top;
46725         this.syncScroll();
46726     },
46727
46728     syncScroll : function(){
46729         var sb = this.scroller.dom;
46730         var sh = this.mainHd.dom;
46731         var bs = this.mainBody.dom;
46732         var lv = this.lockedBody.dom;
46733         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46734         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46735     },
46736
46737     handleScroll : function(e){
46738         this.syncScroll();
46739         var sb = this.scroller.dom;
46740         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46741         e.stopEvent();
46742     },
46743
46744     handleWheel : function(e){
46745         var d = e.getWheelDelta();
46746         this.scroller.dom.scrollTop -= d*22;
46747         // set this here to prevent jumpy scrolling on large tables
46748         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46749         e.stopEvent();
46750     },
46751
46752     renderRows : function(startRow, endRow){
46753         // pull in all the crap needed to render rows
46754         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46755         var colCount = cm.getColumnCount();
46756
46757         if(ds.getCount() < 1){
46758             return ["", ""];
46759         }
46760
46761         // build a map for all the columns
46762         var cs = [];
46763         for(var i = 0; i < colCount; i++){
46764             var name = cm.getDataIndex(i);
46765             cs[i] = {
46766                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46767                 renderer : cm.getRenderer(i),
46768                 id : cm.getColumnId(i),
46769                 locked : cm.isLocked(i)
46770             };
46771         }
46772
46773         startRow = startRow || 0;
46774         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46775
46776         // records to render
46777         var rs = ds.getRange(startRow, endRow);
46778
46779         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46780     },
46781
46782     // As much as I hate to duplicate code, this was branched because FireFox really hates
46783     // [].join("") on strings. The performance difference was substantial enough to
46784     // branch this function
46785     doRender : Roo.isGecko ?
46786             function(cs, rs, ds, startRow, colCount, stripe){
46787                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46788                 // buffers
46789                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46790                 for(var j = 0, len = rs.length; j < len; j++){
46791                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46792                     for(var i = 0; i < colCount; i++){
46793                         c = cs[i];
46794                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46795                         p.id = c.id;
46796                         p.css = p.attr = "";
46797                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46798                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46799                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46800                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46801                         }
46802                         var markup = ct.apply(p);
46803                         if(!c.locked){
46804                             cb+= markup;
46805                         }else{
46806                             lcb+= markup;
46807                         }
46808                     }
46809                     var alt = [];
46810                     if(stripe && ((rowIndex+1) % 2 == 0)){
46811                         alt[0] = "x-grid-row-alt";
46812                     }
46813                     if(r.dirty){
46814                         alt[1] = " x-grid-dirty-row";
46815                     }
46816                     rp.cells = lcb;
46817                     if(this.getRowClass){
46818                         alt[2] = this.getRowClass(r, rowIndex);
46819                     }
46820                     rp.alt = alt.join(" ");
46821                     lbuf+= rt.apply(rp);
46822                     rp.cells = cb;
46823                     buf+=  rt.apply(rp);
46824                 }
46825                 return [lbuf, buf];
46826             } :
46827             function(cs, rs, ds, startRow, colCount, stripe){
46828                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46829                 // buffers
46830                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46831                 for(var j = 0, len = rs.length; j < len; j++){
46832                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
46833                     for(var i = 0; i < colCount; i++){
46834                         c = cs[i];
46835                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46836                         p.id = c.id;
46837                         p.css = p.attr = "";
46838                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46839                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46840                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46841                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46842                         }
46843                         var markup = ct.apply(p);
46844                         if(!c.locked){
46845                             cb[cb.length] = markup;
46846                         }else{
46847                             lcb[lcb.length] = markup;
46848                         }
46849                     }
46850                     var alt = [];
46851                     if(stripe && ((rowIndex+1) % 2 == 0)){
46852                         alt[0] = "x-grid-row-alt";
46853                     }
46854                     if(r.dirty){
46855                         alt[1] = " x-grid-dirty-row";
46856                     }
46857                     rp.cells = lcb;
46858                     if(this.getRowClass){
46859                         alt[2] = this.getRowClass(r, rowIndex);
46860                     }
46861                     rp.alt = alt.join(" ");
46862                     rp.cells = lcb.join("");
46863                     lbuf[lbuf.length] = rt.apply(rp);
46864                     rp.cells = cb.join("");
46865                     buf[buf.length] =  rt.apply(rp);
46866                 }
46867                 return [lbuf.join(""), buf.join("")];
46868             },
46869
46870     renderBody : function(){
46871         var markup = this.renderRows();
46872         var bt = this.templates.body;
46873         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
46874     },
46875
46876     /**
46877      * Refreshes the grid
46878      * @param {Boolean} headersToo
46879      */
46880     refresh : function(headersToo){
46881         this.fireEvent("beforerefresh", this);
46882         this.grid.stopEditing();
46883         var result = this.renderBody();
46884         this.lockedBody.update(result[0]);
46885         this.mainBody.update(result[1]);
46886         if(headersToo === true){
46887             this.updateHeaders();
46888             this.updateColumns();
46889             this.updateSplitters();
46890             this.updateHeaderSortState();
46891         }
46892         this.syncRowHeights();
46893         this.layout();
46894         this.fireEvent("refresh", this);
46895     },
46896
46897     handleColumnMove : function(cm, oldIndex, newIndex){
46898         this.indexMap = null;
46899         var s = this.getScrollState();
46900         this.refresh(true);
46901         this.restoreScroll(s);
46902         this.afterMove(newIndex);
46903     },
46904
46905     afterMove : function(colIndex){
46906         if(this.enableMoveAnim && Roo.enableFx){
46907             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
46908         }
46909     },
46910
46911     updateCell : function(dm, rowIndex, dataIndex){
46912         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
46913         if(typeof colIndex == "undefined"){ // not present in grid
46914             return;
46915         }
46916         var cm = this.grid.colModel;
46917         var cell = this.getCell(rowIndex, colIndex);
46918         var cellText = this.getCellText(rowIndex, colIndex);
46919
46920         var p = {
46921             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
46922             id : cm.getColumnId(colIndex),
46923             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
46924         };
46925         var renderer = cm.getRenderer(colIndex);
46926         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
46927         if(typeof val == "undefined" || val === "") val = "&#160;";
46928         cellText.innerHTML = val;
46929         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
46930         this.syncRowHeights(rowIndex, rowIndex);
46931     },
46932
46933     calcColumnWidth : function(colIndex, maxRowsToMeasure){
46934         var maxWidth = 0;
46935         if(this.grid.autoSizeHeaders){
46936             var h = this.getHeaderCellMeasure(colIndex);
46937             maxWidth = Math.max(maxWidth, h.scrollWidth);
46938         }
46939         var tb, index;
46940         if(this.cm.isLocked(colIndex)){
46941             tb = this.getLockedTable();
46942             index = colIndex;
46943         }else{
46944             tb = this.getBodyTable();
46945             index = colIndex - this.cm.getLockedCount();
46946         }
46947         if(tb && tb.rows){
46948             var rows = tb.rows;
46949             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
46950             for(var i = 0; i < stopIndex; i++){
46951                 var cell = rows[i].childNodes[index].firstChild;
46952                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
46953             }
46954         }
46955         return maxWidth + /*margin for error in IE*/ 5;
46956     },
46957     /**
46958      * Autofit a column to its content.
46959      * @param {Number} colIndex
46960      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
46961      */
46962      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
46963          if(this.cm.isHidden(colIndex)){
46964              return; // can't calc a hidden column
46965          }
46966         if(forceMinSize){
46967             var cid = this.cm.getColumnId(colIndex);
46968             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
46969            if(this.grid.autoSizeHeaders){
46970                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
46971            }
46972         }
46973         var newWidth = this.calcColumnWidth(colIndex);
46974         this.cm.setColumnWidth(colIndex,
46975             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
46976         if(!suppressEvent){
46977             this.grid.fireEvent("columnresize", colIndex, newWidth);
46978         }
46979     },
46980
46981     /**
46982      * Autofits all columns to their content and then expands to fit any extra space in the grid
46983      */
46984      autoSizeColumns : function(){
46985         var cm = this.grid.colModel;
46986         var colCount = cm.getColumnCount();
46987         for(var i = 0; i < colCount; i++){
46988             this.autoSizeColumn(i, true, true);
46989         }
46990         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
46991             this.fitColumns();
46992         }else{
46993             this.updateColumns();
46994             this.layout();
46995         }
46996     },
46997
46998     /**
46999      * Autofits all columns to the grid's width proportionate with their current size
47000      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47001      */
47002     fitColumns : function(reserveScrollSpace){
47003         var cm = this.grid.colModel;
47004         var colCount = cm.getColumnCount();
47005         var cols = [];
47006         var width = 0;
47007         var i, w;
47008         for (i = 0; i < colCount; i++){
47009             if(!cm.isHidden(i) && !cm.isFixed(i)){
47010                 w = cm.getColumnWidth(i);
47011                 cols.push(i);
47012                 cols.push(w);
47013                 width += w;
47014             }
47015         }
47016         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47017         if(reserveScrollSpace){
47018             avail -= 17;
47019         }
47020         var frac = (avail - cm.getTotalWidth())/width;
47021         while (cols.length){
47022             w = cols.pop();
47023             i = cols.pop();
47024             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47025         }
47026         this.updateColumns();
47027         this.layout();
47028     },
47029
47030     onRowSelect : function(rowIndex){
47031         var row = this.getRowComposite(rowIndex);
47032         row.addClass("x-grid-row-selected");
47033     },
47034
47035     onRowDeselect : function(rowIndex){
47036         var row = this.getRowComposite(rowIndex);
47037         row.removeClass("x-grid-row-selected");
47038     },
47039
47040     onCellSelect : function(row, col){
47041         var cell = this.getCell(row, col);
47042         if(cell){
47043             Roo.fly(cell).addClass("x-grid-cell-selected");
47044         }
47045     },
47046
47047     onCellDeselect : function(row, col){
47048         var cell = this.getCell(row, col);
47049         if(cell){
47050             Roo.fly(cell).removeClass("x-grid-cell-selected");
47051         }
47052     },
47053
47054     updateHeaderSortState : function(){
47055         var state = this.ds.getSortState();
47056         if(!state){
47057             return;
47058         }
47059         this.sortState = state;
47060         var sortColumn = this.cm.findColumnIndex(state.field);
47061         if(sortColumn != -1){
47062             var sortDir = state.direction;
47063             var sc = this.sortClasses;
47064             var hds = this.el.select(this.headerSelector).removeClass(sc);
47065             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47066         }
47067     },
47068
47069     handleHeaderClick : function(g, index){
47070         if(this.headersDisabled){
47071             return;
47072         }
47073         var dm = g.dataSource, cm = g.colModel;
47074             if(!cm.isSortable(index)){
47075             return;
47076         }
47077             g.stopEditing();
47078         dm.sort(cm.getDataIndex(index));
47079     },
47080
47081
47082     destroy : function(){
47083         if(this.colMenu){
47084             this.colMenu.removeAll();
47085             Roo.menu.MenuMgr.unregister(this.colMenu);
47086             this.colMenu.getEl().remove();
47087             delete this.colMenu;
47088         }
47089         if(this.hmenu){
47090             this.hmenu.removeAll();
47091             Roo.menu.MenuMgr.unregister(this.hmenu);
47092             this.hmenu.getEl().remove();
47093             delete this.hmenu;
47094         }
47095         if(this.grid.enableColumnMove){
47096             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47097             if(dds){
47098                 for(var dd in dds){
47099                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47100                         var elid = dds[dd].dragElId;
47101                         dds[dd].unreg();
47102                         Roo.get(elid).remove();
47103                     } else if(dds[dd].config.isTarget){
47104                         dds[dd].proxyTop.remove();
47105                         dds[dd].proxyBottom.remove();
47106                         dds[dd].unreg();
47107                     }
47108                     if(Roo.dd.DDM.locationCache[dd]){
47109                         delete Roo.dd.DDM.locationCache[dd];
47110                     }
47111                 }
47112                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47113             }
47114         }
47115         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47116         this.bind(null, null);
47117         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47118     },
47119
47120     handleLockChange : function(){
47121         this.refresh(true);
47122     },
47123
47124     onDenyColumnLock : function(){
47125
47126     },
47127
47128     onDenyColumnHide : function(){
47129
47130     },
47131
47132     handleHdMenuClick : function(item){
47133         var index = this.hdCtxIndex;
47134         var cm = this.cm, ds = this.ds;
47135         switch(item.id){
47136             case "asc":
47137                 ds.sort(cm.getDataIndex(index), "ASC");
47138                 break;
47139             case "desc":
47140                 ds.sort(cm.getDataIndex(index), "DESC");
47141                 break;
47142             case "lock":
47143                 var lc = cm.getLockedCount();
47144                 if(cm.getColumnCount(true) <= lc+1){
47145                     this.onDenyColumnLock();
47146                     return;
47147                 }
47148                 if(lc != index){
47149                     cm.setLocked(index, true, true);
47150                     cm.moveColumn(index, lc);
47151                     this.grid.fireEvent("columnmove", index, lc);
47152                 }else{
47153                     cm.setLocked(index, true);
47154                 }
47155             break;
47156             case "unlock":
47157                 var lc = cm.getLockedCount();
47158                 if((lc-1) != index){
47159                     cm.setLocked(index, false, true);
47160                     cm.moveColumn(index, lc-1);
47161                     this.grid.fireEvent("columnmove", index, lc-1);
47162                 }else{
47163                     cm.setLocked(index, false);
47164                 }
47165             break;
47166             default:
47167                 index = cm.getIndexById(item.id.substr(4));
47168                 if(index != -1){
47169                     if(item.checked && cm.getColumnCount(true) <= 1){
47170                         this.onDenyColumnHide();
47171                         return false;
47172                     }
47173                     cm.setHidden(index, item.checked);
47174                 }
47175         }
47176         return true;
47177     },
47178
47179     beforeColMenuShow : function(){
47180         var cm = this.cm,  colCount = cm.getColumnCount();
47181         this.colMenu.removeAll();
47182         for(var i = 0; i < colCount; i++){
47183             this.colMenu.add(new Roo.menu.CheckItem({
47184                 id: "col-"+cm.getColumnId(i),
47185                 text: cm.getColumnHeader(i),
47186                 checked: !cm.isHidden(i),
47187                 hideOnClick:false
47188             }));
47189         }
47190     },
47191
47192     handleHdCtx : function(g, index, e){
47193         e.stopEvent();
47194         var hd = this.getHeaderCell(index);
47195         this.hdCtxIndex = index;
47196         var ms = this.hmenu.items, cm = this.cm;
47197         ms.get("asc").setDisabled(!cm.isSortable(index));
47198         ms.get("desc").setDisabled(!cm.isSortable(index));
47199         if(this.grid.enableColLock !== false){
47200             ms.get("lock").setDisabled(cm.isLocked(index));
47201             ms.get("unlock").setDisabled(!cm.isLocked(index));
47202         }
47203         this.hmenu.show(hd, "tl-bl");
47204     },
47205
47206     handleHdOver : function(e){
47207         var hd = this.findHeaderCell(e.getTarget());
47208         if(hd && !this.headersDisabled){
47209             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47210                this.fly(hd).addClass("x-grid-hd-over");
47211             }
47212         }
47213     },
47214
47215     handleHdOut : function(e){
47216         var hd = this.findHeaderCell(e.getTarget());
47217         if(hd){
47218             this.fly(hd).removeClass("x-grid-hd-over");
47219         }
47220     },
47221
47222     handleSplitDblClick : function(e, t){
47223         var i = this.getCellIndex(t);
47224         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47225             this.autoSizeColumn(i, true);
47226             this.layout();
47227         }
47228     },
47229
47230     render : function(){
47231
47232         var cm = this.cm;
47233         var colCount = cm.getColumnCount();
47234
47235         if(this.grid.monitorWindowResize === true){
47236             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47237         }
47238         var header = this.renderHeaders();
47239         var body = this.templates.body.apply({rows:""});
47240         var html = this.templates.master.apply({
47241             lockedBody: body,
47242             body: body,
47243             lockedHeader: header[0],
47244             header: header[1]
47245         });
47246
47247         //this.updateColumns();
47248
47249         this.grid.getGridEl().dom.innerHTML = html;
47250
47251         this.initElements();
47252
47253         this.scroller.on("scroll", this.handleScroll, this);
47254         this.lockedBody.on("mousewheel", this.handleWheel, this);
47255         this.mainBody.on("mousewheel", this.handleWheel, this);
47256
47257         this.mainHd.on("mouseover", this.handleHdOver, this);
47258         this.mainHd.on("mouseout", this.handleHdOut, this);
47259         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47260                 {delegate: "."+this.splitClass});
47261
47262         this.lockedHd.on("mouseover", this.handleHdOver, this);
47263         this.lockedHd.on("mouseout", this.handleHdOut, this);
47264         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47265                 {delegate: "."+this.splitClass});
47266
47267         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47268             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47269         }
47270
47271         this.updateSplitters();
47272
47273         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47274             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47275             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47276         }
47277
47278         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47279             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47280             this.hmenu.add(
47281                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47282                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47283             );
47284             if(this.grid.enableColLock !== false){
47285                 this.hmenu.add('-',
47286                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47287                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47288                 );
47289             }
47290             if(this.grid.enableColumnHide !== false){
47291
47292                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47293                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47294                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47295
47296                 this.hmenu.add('-',
47297                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47298                 );
47299             }
47300             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47301
47302             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47303         }
47304
47305         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47306             this.dd = new Roo.grid.GridDragZone(this.grid, {
47307                 ddGroup : this.grid.ddGroup || 'GridDD'
47308             });
47309         }
47310
47311         /*
47312         for(var i = 0; i < colCount; i++){
47313             if(cm.isHidden(i)){
47314                 this.hideColumn(i);
47315             }
47316             if(cm.config[i].align){
47317                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47318                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47319             }
47320         }*/
47321         
47322         this.updateHeaderSortState();
47323
47324         this.beforeInitialResize();
47325         this.layout(true);
47326
47327         // two part rendering gives faster view to the user
47328         this.renderPhase2.defer(1, this);
47329     },
47330
47331     renderPhase2 : function(){
47332         // render the rows now
47333         this.refresh();
47334         if(this.grid.autoSizeColumns){
47335             this.autoSizeColumns();
47336         }
47337     },
47338
47339     beforeInitialResize : function(){
47340
47341     },
47342
47343     onColumnSplitterMoved : function(i, w){
47344         this.userResized = true;
47345         var cm = this.grid.colModel;
47346         cm.setColumnWidth(i, w, true);
47347         var cid = cm.getColumnId(i);
47348         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47349         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47350         this.updateSplitters();
47351         this.layout();
47352         this.grid.fireEvent("columnresize", i, w);
47353     },
47354
47355     syncRowHeights : function(startIndex, endIndex){
47356         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47357             startIndex = startIndex || 0;
47358             var mrows = this.getBodyTable().rows;
47359             var lrows = this.getLockedTable().rows;
47360             var len = mrows.length-1;
47361             endIndex = Math.min(endIndex || len, len);
47362             for(var i = startIndex; i <= endIndex; i++){
47363                 var m = mrows[i], l = lrows[i];
47364                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47365                 m.style.height = l.style.height = h + "px";
47366             }
47367         }
47368     },
47369
47370     layout : function(initialRender, is2ndPass){
47371         var g = this.grid;
47372         var auto = g.autoHeight;
47373         var scrollOffset = 16;
47374         var c = g.getGridEl(), cm = this.cm,
47375                 expandCol = g.autoExpandColumn,
47376                 gv = this;
47377         //c.beginMeasure();
47378
47379         if(!c.dom.offsetWidth){ // display:none?
47380             if(initialRender){
47381                 this.lockedWrap.show();
47382                 this.mainWrap.show();
47383             }
47384             return;
47385         }
47386
47387         var hasLock = this.cm.isLocked(0);
47388
47389         var tbh = this.headerPanel.getHeight();
47390         var bbh = this.footerPanel.getHeight();
47391
47392         if(auto){
47393             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47394             var newHeight = ch + c.getBorderWidth("tb");
47395             if(g.maxHeight){
47396                 newHeight = Math.min(g.maxHeight, newHeight);
47397             }
47398             c.setHeight(newHeight);
47399         }
47400
47401         if(g.autoWidth){
47402             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47403         }
47404
47405         var s = this.scroller;
47406
47407         var csize = c.getSize(true);
47408
47409         this.el.setSize(csize.width, csize.height);
47410
47411         this.headerPanel.setWidth(csize.width);
47412         this.footerPanel.setWidth(csize.width);
47413
47414         var hdHeight = this.mainHd.getHeight();
47415         var vw = csize.width;
47416         var vh = csize.height - (tbh + bbh);
47417
47418         s.setSize(vw, vh);
47419
47420         var bt = this.getBodyTable();
47421         var ltWidth = hasLock ?
47422                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47423
47424         var scrollHeight = bt.offsetHeight;
47425         var scrollWidth = ltWidth + bt.offsetWidth;
47426         var vscroll = false, hscroll = false;
47427
47428         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47429
47430         var lw = this.lockedWrap, mw = this.mainWrap;
47431         var lb = this.lockedBody, mb = this.mainBody;
47432
47433         setTimeout(function(){
47434             var t = s.dom.offsetTop;
47435             var w = s.dom.clientWidth,
47436                 h = s.dom.clientHeight;
47437
47438             lw.setTop(t);
47439             lw.setSize(ltWidth, h);
47440
47441             mw.setLeftTop(ltWidth, t);
47442             mw.setSize(w-ltWidth, h);
47443
47444             lb.setHeight(h-hdHeight);
47445             mb.setHeight(h-hdHeight);
47446
47447             if(is2ndPass !== true && !gv.userResized && expandCol){
47448                 // high speed resize without full column calculation
47449                 
47450                 var ci = cm.getIndexById(expandCol);
47451                 if (ci < 0) {
47452                     ci = cm.findColumnIndex(expandCol);
47453                 }
47454                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47455                 var expandId = cm.getColumnId(ci);
47456                 var  tw = cm.getTotalWidth(false);
47457                 var currentWidth = cm.getColumnWidth(ci);
47458                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47459                 if(currentWidth != cw){
47460                     cm.setColumnWidth(ci, cw, true);
47461                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47462                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47463                     gv.updateSplitters();
47464                     gv.layout(false, true);
47465                 }
47466             }
47467
47468             if(initialRender){
47469                 lw.show();
47470                 mw.show();
47471             }
47472             //c.endMeasure();
47473         }, 10);
47474     },
47475
47476     onWindowResize : function(){
47477         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47478             return;
47479         }
47480         this.layout();
47481     },
47482
47483     appendFooter : function(parentEl){
47484         return null;
47485     },
47486
47487     sortAscText : "Sort Ascending",
47488     sortDescText : "Sort Descending",
47489     lockText : "Lock Column",
47490     unlockText : "Unlock Column",
47491     columnsText : "Columns"
47492 });
47493
47494
47495 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47496     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47497     this.proxy.el.addClass('x-grid3-col-dd');
47498 };
47499
47500 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47501     handleMouseDown : function(e){
47502
47503     },
47504
47505     callHandleMouseDown : function(e){
47506         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47507     }
47508 });
47509 /*
47510  * Based on:
47511  * Ext JS Library 1.1.1
47512  * Copyright(c) 2006-2007, Ext JS, LLC.
47513  *
47514  * Originally Released Under LGPL - original licence link has changed is not relivant.
47515  *
47516  * Fork - LGPL
47517  * <script type="text/javascript">
47518  */
47519  
47520 // private
47521 // This is a support class used internally by the Grid components
47522 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47523     this.grid = grid;
47524     this.view = grid.getView();
47525     this.proxy = this.view.resizeProxy;
47526     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47527         "gridSplitters" + this.grid.getGridEl().id, {
47528         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47529     });
47530     this.setHandleElId(Roo.id(hd));
47531     this.setOuterHandleElId(Roo.id(hd2));
47532     this.scroll = false;
47533 };
47534 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47535     fly: Roo.Element.fly,
47536
47537     b4StartDrag : function(x, y){
47538         this.view.headersDisabled = true;
47539         this.proxy.setHeight(this.view.mainWrap.getHeight());
47540         var w = this.cm.getColumnWidth(this.cellIndex);
47541         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47542         this.resetConstraints();
47543         this.setXConstraint(minw, 1000);
47544         this.setYConstraint(0, 0);
47545         this.minX = x - minw;
47546         this.maxX = x + 1000;
47547         this.startPos = x;
47548         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47549     },
47550
47551
47552     handleMouseDown : function(e){
47553         ev = Roo.EventObject.setEvent(e);
47554         var t = this.fly(ev.getTarget());
47555         if(t.hasClass("x-grid-split")){
47556             this.cellIndex = this.view.getCellIndex(t.dom);
47557             this.split = t.dom;
47558             this.cm = this.grid.colModel;
47559             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47560                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47561             }
47562         }
47563     },
47564
47565     endDrag : function(e){
47566         this.view.headersDisabled = false;
47567         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47568         var diff = endX - this.startPos;
47569         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47570     },
47571
47572     autoOffset : function(){
47573         this.setDelta(0,0);
47574     }
47575 });/*
47576  * Based on:
47577  * Ext JS Library 1.1.1
47578  * Copyright(c) 2006-2007, Ext JS, LLC.
47579  *
47580  * Originally Released Under LGPL - original licence link has changed is not relivant.
47581  *
47582  * Fork - LGPL
47583  * <script type="text/javascript">
47584  */
47585  
47586 // private
47587 // This is a support class used internally by the Grid components
47588 Roo.grid.GridDragZone = function(grid, config){
47589     this.view = grid.getView();
47590     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47591     if(this.view.lockedBody){
47592         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47593         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47594     }
47595     this.scroll = false;
47596     this.grid = grid;
47597     this.ddel = document.createElement('div');
47598     this.ddel.className = 'x-grid-dd-wrap';
47599 };
47600
47601 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47602     ddGroup : "GridDD",
47603
47604     getDragData : function(e){
47605         var t = Roo.lib.Event.getTarget(e);
47606         var rowIndex = this.view.findRowIndex(t);
47607         if(rowIndex !== false){
47608             var sm = this.grid.selModel;
47609             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47610               //  sm.mouseDown(e, t);
47611             //}
47612             if (e.hasModifier()){
47613                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47614             }
47615             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47616         }
47617         return false;
47618     },
47619
47620     onInitDrag : function(e){
47621         var data = this.dragData;
47622         this.ddel.innerHTML = this.grid.getDragDropText();
47623         this.proxy.update(this.ddel);
47624         // fire start drag?
47625     },
47626
47627     afterRepair : function(){
47628         this.dragging = false;
47629     },
47630
47631     getRepairXY : function(e, data){
47632         return false;
47633     },
47634
47635     onEndDrag : function(data, e){
47636         // fire end drag?
47637     },
47638
47639     onValidDrop : function(dd, e, id){
47640         // fire drag drop?
47641         this.hideProxy();
47642     },
47643
47644     beforeInvalidDrop : function(e, id){
47645
47646     }
47647 });/*
47648  * Based on:
47649  * Ext JS Library 1.1.1
47650  * Copyright(c) 2006-2007, Ext JS, LLC.
47651  *
47652  * Originally Released Under LGPL - original licence link has changed is not relivant.
47653  *
47654  * Fork - LGPL
47655  * <script type="text/javascript">
47656  */
47657  
47658
47659 /**
47660  * @class Roo.grid.ColumnModel
47661  * @extends Roo.util.Observable
47662  * This is the default implementation of a ColumnModel used by the Grid. It defines
47663  * the columns in the grid.
47664  * <br>Usage:<br>
47665  <pre><code>
47666  var colModel = new Roo.grid.ColumnModel([
47667         {header: "Ticker", width: 60, sortable: true, locked: true},
47668         {header: "Company Name", width: 150, sortable: true},
47669         {header: "Market Cap.", width: 100, sortable: true},
47670         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47671         {header: "Employees", width: 100, sortable: true, resizable: false}
47672  ]);
47673  </code></pre>
47674  * <p>
47675  
47676  * The config options listed for this class are options which may appear in each
47677  * individual column definition.
47678  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47679  * @constructor
47680  * @param {Object} config An Array of column config objects. See this class's
47681  * config objects for details.
47682 */
47683 Roo.grid.ColumnModel = function(config){
47684         /**
47685      * The config passed into the constructor
47686      */
47687     this.config = config;
47688     this.lookup = {};
47689
47690     // if no id, create one
47691     // if the column does not have a dataIndex mapping,
47692     // map it to the order it is in the config
47693     for(var i = 0, len = config.length; i < len; i++){
47694         var c = config[i];
47695         if(typeof c.dataIndex == "undefined"){
47696             c.dataIndex = i;
47697         }
47698         if(typeof c.renderer == "string"){
47699             c.renderer = Roo.util.Format[c.renderer];
47700         }
47701         if(typeof c.id == "undefined"){
47702             c.id = Roo.id();
47703         }
47704         if(c.editor && c.editor.xtype){
47705             c.editor  = Roo.factory(c.editor, Roo.grid);
47706         }
47707         if(c.editor && c.editor.isFormField){
47708             c.editor = new Roo.grid.GridEditor(c.editor);
47709         }
47710         this.lookup[c.id] = c;
47711     }
47712
47713     /**
47714      * The width of columns which have no width specified (defaults to 100)
47715      * @type Number
47716      */
47717     this.defaultWidth = 100;
47718
47719     /**
47720      * Default sortable of columns which have no sortable specified (defaults to false)
47721      * @type Boolean
47722      */
47723     this.defaultSortable = false;
47724
47725     this.addEvents({
47726         /**
47727              * @event widthchange
47728              * Fires when the width of a column changes.
47729              * @param {ColumnModel} this
47730              * @param {Number} columnIndex The column index
47731              * @param {Number} newWidth The new width
47732              */
47733             "widthchange": true,
47734         /**
47735              * @event headerchange
47736              * Fires when the text of a header changes.
47737              * @param {ColumnModel} this
47738              * @param {Number} columnIndex The column index
47739              * @param {Number} newText The new header text
47740              */
47741             "headerchange": true,
47742         /**
47743              * @event hiddenchange
47744              * Fires when a column is hidden or "unhidden".
47745              * @param {ColumnModel} this
47746              * @param {Number} columnIndex The column index
47747              * @param {Boolean} hidden true if hidden, false otherwise
47748              */
47749             "hiddenchange": true,
47750             /**
47751          * @event columnmoved
47752          * Fires when a column is moved.
47753          * @param {ColumnModel} this
47754          * @param {Number} oldIndex
47755          * @param {Number} newIndex
47756          */
47757         "columnmoved" : true,
47758         /**
47759          * @event columlockchange
47760          * Fires when a column's locked state is changed
47761          * @param {ColumnModel} this
47762          * @param {Number} colIndex
47763          * @param {Boolean} locked true if locked
47764          */
47765         "columnlockchange" : true
47766     });
47767     Roo.grid.ColumnModel.superclass.constructor.call(this);
47768 };
47769 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47770     /**
47771      * @cfg {String} header The header text to display in the Grid view.
47772      */
47773     /**
47774      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47775      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47776      * specified, the column's index is used as an index into the Record's data Array.
47777      */
47778     /**
47779      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47780      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47781      */
47782     /**
47783      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47784      * Defaults to the value of the {@link #defaultSortable} property.
47785      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47786      */
47787     /**
47788      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47789      */
47790     /**
47791      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47792      */
47793     /**
47794      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47795      */
47796     /**
47797      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47798      */
47799     /**
47800      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47801      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47802      * default renderer uses the raw data value.
47803      */
47804        /**
47805      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
47806      */
47807     /**
47808      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
47809      */
47810
47811     /**
47812      * Returns the id of the column at the specified index.
47813      * @param {Number} index The column index
47814      * @return {String} the id
47815      */
47816     getColumnId : function(index){
47817         return this.config[index].id;
47818     },
47819
47820     /**
47821      * Returns the column for a specified id.
47822      * @param {String} id The column id
47823      * @return {Object} the column
47824      */
47825     getColumnById : function(id){
47826         return this.lookup[id];
47827     },
47828
47829     /**
47830      * Returns the index for a specified column id.
47831      * @param {String} id The column id
47832      * @return {Number} the index, or -1 if not found
47833      */
47834     getIndexById : function(id){
47835         for(var i = 0, len = this.config.length; i < len; i++){
47836             if(this.config[i].id == id){
47837                 return i;
47838             }
47839         }
47840         return -1;
47841     },
47842     /**
47843      * Returns the index for a specified column dataIndex.
47844      * @param {String} dataIndex The column dataIndex
47845      * @return {Number} the index, or -1 if not found
47846      */
47847     
47848     findColumnIndex : function(dataIndex){
47849         for(var i = 0, len = this.config.length; i < len; i++){
47850             if(this.config[i].dataIndex == dataIndex){
47851                 return i;
47852             }
47853         }
47854         return -1;
47855     },
47856     
47857     
47858     moveColumn : function(oldIndex, newIndex){
47859         var c = this.config[oldIndex];
47860         this.config.splice(oldIndex, 1);
47861         this.config.splice(newIndex, 0, c);
47862         this.dataMap = null;
47863         this.fireEvent("columnmoved", this, oldIndex, newIndex);
47864     },
47865
47866     isLocked : function(colIndex){
47867         return this.config[colIndex].locked === true;
47868     },
47869
47870     setLocked : function(colIndex, value, suppressEvent){
47871         if(this.isLocked(colIndex) == value){
47872             return;
47873         }
47874         this.config[colIndex].locked = value;
47875         if(!suppressEvent){
47876             this.fireEvent("columnlockchange", this, colIndex, value);
47877         }
47878     },
47879
47880     getTotalLockedWidth : function(){
47881         var totalWidth = 0;
47882         for(var i = 0; i < this.config.length; i++){
47883             if(this.isLocked(i) && !this.isHidden(i)){
47884                 this.totalWidth += this.getColumnWidth(i);
47885             }
47886         }
47887         return totalWidth;
47888     },
47889
47890     getLockedCount : function(){
47891         for(var i = 0, len = this.config.length; i < len; i++){
47892             if(!this.isLocked(i)){
47893                 return i;
47894             }
47895         }
47896     },
47897
47898     /**
47899      * Returns the number of columns.
47900      * @return {Number}
47901      */
47902     getColumnCount : function(visibleOnly){
47903         if(visibleOnly === true){
47904             var c = 0;
47905             for(var i = 0, len = this.config.length; i < len; i++){
47906                 if(!this.isHidden(i)){
47907                     c++;
47908                 }
47909             }
47910             return c;
47911         }
47912         return this.config.length;
47913     },
47914
47915     /**
47916      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
47917      * @param {Function} fn
47918      * @param {Object} scope (optional)
47919      * @return {Array} result
47920      */
47921     getColumnsBy : function(fn, scope){
47922         var r = [];
47923         for(var i = 0, len = this.config.length; i < len; i++){
47924             var c = this.config[i];
47925             if(fn.call(scope||this, c, i) === true){
47926                 r[r.length] = c;
47927             }
47928         }
47929         return r;
47930     },
47931
47932     /**
47933      * Returns true if the specified column is sortable.
47934      * @param {Number} col The column index
47935      * @return {Boolean}
47936      */
47937     isSortable : function(col){
47938         if(typeof this.config[col].sortable == "undefined"){
47939             return this.defaultSortable;
47940         }
47941         return this.config[col].sortable;
47942     },
47943
47944     /**
47945      * Returns the rendering (formatting) function defined for the column.
47946      * @param {Number} col The column index.
47947      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
47948      */
47949     getRenderer : function(col){
47950         if(!this.config[col].renderer){
47951             return Roo.grid.ColumnModel.defaultRenderer;
47952         }
47953         return this.config[col].renderer;
47954     },
47955
47956     /**
47957      * Sets the rendering (formatting) function for a column.
47958      * @param {Number} col The column index
47959      * @param {Function} fn The function to use to process the cell's raw data
47960      * to return HTML markup for the grid view. The render function is called with
47961      * the following parameters:<ul>
47962      * <li>Data value.</li>
47963      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
47964      * <li>css A CSS style string to apply to the table cell.</li>
47965      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
47966      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
47967      * <li>Row index</li>
47968      * <li>Column index</li>
47969      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
47970      */
47971     setRenderer : function(col, fn){
47972         this.config[col].renderer = fn;
47973     },
47974
47975     /**
47976      * Returns the width for the specified column.
47977      * @param {Number} col The column index
47978      * @return {Number}
47979      */
47980     getColumnWidth : function(col){
47981         return this.config[col].width || this.defaultWidth;
47982     },
47983
47984     /**
47985      * Sets the width for a column.
47986      * @param {Number} col The column index
47987      * @param {Number} width The new width
47988      */
47989     setColumnWidth : function(col, width, suppressEvent){
47990         this.config[col].width = width;
47991         this.totalWidth = null;
47992         if(!suppressEvent){
47993              this.fireEvent("widthchange", this, col, width);
47994         }
47995     },
47996
47997     /**
47998      * Returns the total width of all columns.
47999      * @param {Boolean} includeHidden True to include hidden column widths
48000      * @return {Number}
48001      */
48002     getTotalWidth : function(includeHidden){
48003         if(!this.totalWidth){
48004             this.totalWidth = 0;
48005             for(var i = 0, len = this.config.length; i < len; i++){
48006                 if(includeHidden || !this.isHidden(i)){
48007                     this.totalWidth += this.getColumnWidth(i);
48008                 }
48009             }
48010         }
48011         return this.totalWidth;
48012     },
48013
48014     /**
48015      * Returns the header for the specified column.
48016      * @param {Number} col The column index
48017      * @return {String}
48018      */
48019     getColumnHeader : function(col){
48020         return this.config[col].header;
48021     },
48022
48023     /**
48024      * Sets the header for a column.
48025      * @param {Number} col The column index
48026      * @param {String} header The new header
48027      */
48028     setColumnHeader : function(col, header){
48029         this.config[col].header = header;
48030         this.fireEvent("headerchange", this, col, header);
48031     },
48032
48033     /**
48034      * Returns the tooltip for the specified column.
48035      * @param {Number} col The column index
48036      * @return {String}
48037      */
48038     getColumnTooltip : function(col){
48039             return this.config[col].tooltip;
48040     },
48041     /**
48042      * Sets the tooltip for a column.
48043      * @param {Number} col The column index
48044      * @param {String} tooltip The new tooltip
48045      */
48046     setColumnTooltip : function(col, tooltip){
48047             this.config[col].tooltip = tooltip;
48048     },
48049
48050     /**
48051      * Returns the dataIndex for the specified column.
48052      * @param {Number} col The column index
48053      * @return {Number}
48054      */
48055     getDataIndex : function(col){
48056         return this.config[col].dataIndex;
48057     },
48058
48059     /**
48060      * Sets the dataIndex for a column.
48061      * @param {Number} col The column index
48062      * @param {Number} dataIndex The new dataIndex
48063      */
48064     setDataIndex : function(col, dataIndex){
48065         this.config[col].dataIndex = dataIndex;
48066     },
48067
48068     
48069     
48070     /**
48071      * Returns true if the cell is editable.
48072      * @param {Number} colIndex The column index
48073      * @param {Number} rowIndex The row index
48074      * @return {Boolean}
48075      */
48076     isCellEditable : function(colIndex, rowIndex){
48077         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48078     },
48079
48080     /**
48081      * Returns the editor defined for the cell/column.
48082      * return false or null to disable editing.
48083      * @param {Number} colIndex The column index
48084      * @param {Number} rowIndex The row index
48085      * @return {Object}
48086      */
48087     getCellEditor : function(colIndex, rowIndex){
48088         return this.config[colIndex].editor;
48089     },
48090
48091     /**
48092      * Sets if a column is editable.
48093      * @param {Number} col The column index
48094      * @param {Boolean} editable True if the column is editable
48095      */
48096     setEditable : function(col, editable){
48097         this.config[col].editable = editable;
48098     },
48099
48100
48101     /**
48102      * Returns true if the column is hidden.
48103      * @param {Number} colIndex The column index
48104      * @return {Boolean}
48105      */
48106     isHidden : function(colIndex){
48107         return this.config[colIndex].hidden;
48108     },
48109
48110
48111     /**
48112      * Returns true if the column width cannot be changed
48113      */
48114     isFixed : function(colIndex){
48115         return this.config[colIndex].fixed;
48116     },
48117
48118     /**
48119      * Returns true if the column can be resized
48120      * @return {Boolean}
48121      */
48122     isResizable : function(colIndex){
48123         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48124     },
48125     /**
48126      * Sets if a column is hidden.
48127      * @param {Number} colIndex The column index
48128      * @param {Boolean} hidden True if the column is hidden
48129      */
48130     setHidden : function(colIndex, hidden){
48131         this.config[colIndex].hidden = hidden;
48132         this.totalWidth = null;
48133         this.fireEvent("hiddenchange", this, colIndex, hidden);
48134     },
48135
48136     /**
48137      * Sets the editor for a column.
48138      * @param {Number} col The column index
48139      * @param {Object} editor The editor object
48140      */
48141     setEditor : function(col, editor){
48142         this.config[col].editor = editor;
48143     }
48144 });
48145
48146 Roo.grid.ColumnModel.defaultRenderer = function(value){
48147         if(typeof value == "string" && value.length < 1){
48148             return "&#160;";
48149         }
48150         return value;
48151 };
48152
48153 // Alias for backwards compatibility
48154 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48155 /*
48156  * Based on:
48157  * Ext JS Library 1.1.1
48158  * Copyright(c) 2006-2007, Ext JS, LLC.
48159  *
48160  * Originally Released Under LGPL - original licence link has changed is not relivant.
48161  *
48162  * Fork - LGPL
48163  * <script type="text/javascript">
48164  */
48165
48166 /**
48167  * @class Roo.grid.AbstractSelectionModel
48168  * @extends Roo.util.Observable
48169  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48170  * implemented by descendant classes.  This class should not be directly instantiated.
48171  * @constructor
48172  */
48173 Roo.grid.AbstractSelectionModel = function(){
48174     this.locked = false;
48175     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48176 };
48177
48178 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48179     /** @ignore Called by the grid automatically. Do not call directly. */
48180     init : function(grid){
48181         this.grid = grid;
48182         this.initEvents();
48183     },
48184
48185     /**
48186      * Locks the selections.
48187      */
48188     lock : function(){
48189         this.locked = true;
48190     },
48191
48192     /**
48193      * Unlocks the selections.
48194      */
48195     unlock : function(){
48196         this.locked = false;
48197     },
48198
48199     /**
48200      * Returns true if the selections are locked.
48201      * @return {Boolean}
48202      */
48203     isLocked : function(){
48204         return this.locked;
48205     }
48206 });/*
48207  * Based on:
48208  * Ext JS Library 1.1.1
48209  * Copyright(c) 2006-2007, Ext JS, LLC.
48210  *
48211  * Originally Released Under LGPL - original licence link has changed is not relivant.
48212  *
48213  * Fork - LGPL
48214  * <script type="text/javascript">
48215  */
48216 /**
48217  * @extends Roo.grid.AbstractSelectionModel
48218  * @class Roo.grid.RowSelectionModel
48219  * The default SelectionModel used by {@link Roo.grid.Grid}.
48220  * It supports multiple selections and keyboard selection/navigation. 
48221  * @constructor
48222  * @param {Object} config
48223  */
48224 Roo.grid.RowSelectionModel = function(config){
48225     Roo.apply(this, config);
48226     this.selections = new Roo.util.MixedCollection(false, function(o){
48227         return o.id;
48228     });
48229
48230     this.last = false;
48231     this.lastActive = false;
48232
48233     this.addEvents({
48234         /**
48235              * @event selectionchange
48236              * Fires when the selection changes
48237              * @param {SelectionModel} this
48238              */
48239             "selectionchange" : true,
48240         /**
48241              * @event afterselectionchange
48242              * Fires after the selection changes (eg. by key press or clicking)
48243              * @param {SelectionModel} this
48244              */
48245             "afterselectionchange" : true,
48246         /**
48247              * @event beforerowselect
48248              * Fires when a row is selected being selected, return false to cancel.
48249              * @param {SelectionModel} this
48250              * @param {Number} rowIndex The selected index
48251              * @param {Boolean} keepExisting False if other selections will be cleared
48252              */
48253             "beforerowselect" : true,
48254         /**
48255              * @event rowselect
48256              * Fires when a row is selected.
48257              * @param {SelectionModel} this
48258              * @param {Number} rowIndex The selected index
48259              * @param {Roo.data.Record} r The record
48260              */
48261             "rowselect" : true,
48262         /**
48263              * @event rowdeselect
48264              * Fires when a row is deselected.
48265              * @param {SelectionModel} this
48266              * @param {Number} rowIndex The selected index
48267              */
48268         "rowdeselect" : true
48269     });
48270     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48271     this.locked = false;
48272 };
48273
48274 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48275     /**
48276      * @cfg {Boolean} singleSelect
48277      * True to allow selection of only one row at a time (defaults to false)
48278      */
48279     singleSelect : false,
48280
48281     // private
48282     initEvents : function(){
48283
48284         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48285             this.grid.on("mousedown", this.handleMouseDown, this);
48286         }else{ // allow click to work like normal
48287             this.grid.on("rowclick", this.handleDragableRowClick, this);
48288         }
48289
48290         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48291             "up" : function(e){
48292                 if(!e.shiftKey){
48293                     this.selectPrevious(e.shiftKey);
48294                 }else if(this.last !== false && this.lastActive !== false){
48295                     var last = this.last;
48296                     this.selectRange(this.last,  this.lastActive-1);
48297                     this.grid.getView().focusRow(this.lastActive);
48298                     if(last !== false){
48299                         this.last = last;
48300                     }
48301                 }else{
48302                     this.selectFirstRow();
48303                 }
48304                 this.fireEvent("afterselectionchange", this);
48305             },
48306             "down" : function(e){
48307                 if(!e.shiftKey){
48308                     this.selectNext(e.shiftKey);
48309                 }else if(this.last !== false && this.lastActive !== false){
48310                     var last = this.last;
48311                     this.selectRange(this.last,  this.lastActive+1);
48312                     this.grid.getView().focusRow(this.lastActive);
48313                     if(last !== false){
48314                         this.last = last;
48315                     }
48316                 }else{
48317                     this.selectFirstRow();
48318                 }
48319                 this.fireEvent("afterselectionchange", this);
48320             },
48321             scope: this
48322         });
48323
48324         var view = this.grid.view;
48325         view.on("refresh", this.onRefresh, this);
48326         view.on("rowupdated", this.onRowUpdated, this);
48327         view.on("rowremoved", this.onRemove, this);
48328     },
48329
48330     // private
48331     onRefresh : function(){
48332         var ds = this.grid.dataSource, i, v = this.grid.view;
48333         var s = this.selections;
48334         s.each(function(r){
48335             if((i = ds.indexOfId(r.id)) != -1){
48336                 v.onRowSelect(i);
48337             }else{
48338                 s.remove(r);
48339             }
48340         });
48341     },
48342
48343     // private
48344     onRemove : function(v, index, r){
48345         this.selections.remove(r);
48346     },
48347
48348     // private
48349     onRowUpdated : function(v, index, r){
48350         if(this.isSelected(r)){
48351             v.onRowSelect(index);
48352         }
48353     },
48354
48355     /**
48356      * Select records.
48357      * @param {Array} records The records to select
48358      * @param {Boolean} keepExisting (optional) True to keep existing selections
48359      */
48360     selectRecords : function(records, keepExisting){
48361         if(!keepExisting){
48362             this.clearSelections();
48363         }
48364         var ds = this.grid.dataSource;
48365         for(var i = 0, len = records.length; i < len; i++){
48366             this.selectRow(ds.indexOf(records[i]), true);
48367         }
48368     },
48369
48370     /**
48371      * Gets the number of selected rows.
48372      * @return {Number}
48373      */
48374     getCount : function(){
48375         return this.selections.length;
48376     },
48377
48378     /**
48379      * Selects the first row in the grid.
48380      */
48381     selectFirstRow : function(){
48382         this.selectRow(0);
48383     },
48384
48385     /**
48386      * Select the last row.
48387      * @param {Boolean} keepExisting (optional) True to keep existing selections
48388      */
48389     selectLastRow : function(keepExisting){
48390         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48391     },
48392
48393     /**
48394      * Selects the row immediately following the last selected row.
48395      * @param {Boolean} keepExisting (optional) True to keep existing selections
48396      */
48397     selectNext : function(keepExisting){
48398         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48399             this.selectRow(this.last+1, keepExisting);
48400             this.grid.getView().focusRow(this.last);
48401         }
48402     },
48403
48404     /**
48405      * Selects the row that precedes the last selected row.
48406      * @param {Boolean} keepExisting (optional) True to keep existing selections
48407      */
48408     selectPrevious : function(keepExisting){
48409         if(this.last){
48410             this.selectRow(this.last-1, keepExisting);
48411             this.grid.getView().focusRow(this.last);
48412         }
48413     },
48414
48415     /**
48416      * Returns the selected records
48417      * @return {Array} Array of selected records
48418      */
48419     getSelections : function(){
48420         return [].concat(this.selections.items);
48421     },
48422
48423     /**
48424      * Returns the first selected record.
48425      * @return {Record}
48426      */
48427     getSelected : function(){
48428         return this.selections.itemAt(0);
48429     },
48430
48431
48432     /**
48433      * Clears all selections.
48434      */
48435     clearSelections : function(fast){
48436         if(this.locked) return;
48437         if(fast !== true){
48438             var ds = this.grid.dataSource;
48439             var s = this.selections;
48440             s.each(function(r){
48441                 this.deselectRow(ds.indexOfId(r.id));
48442             }, this);
48443             s.clear();
48444         }else{
48445             this.selections.clear();
48446         }
48447         this.last = false;
48448     },
48449
48450
48451     /**
48452      * Selects all rows.
48453      */
48454     selectAll : function(){
48455         if(this.locked) return;
48456         this.selections.clear();
48457         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48458             this.selectRow(i, true);
48459         }
48460     },
48461
48462     /**
48463      * Returns True if there is a selection.
48464      * @return {Boolean}
48465      */
48466     hasSelection : function(){
48467         return this.selections.length > 0;
48468     },
48469
48470     /**
48471      * Returns True if the specified row is selected.
48472      * @param {Number/Record} record The record or index of the record to check
48473      * @return {Boolean}
48474      */
48475     isSelected : function(index){
48476         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48477         return (r && this.selections.key(r.id) ? true : false);
48478     },
48479
48480     /**
48481      * Returns True if the specified record id is selected.
48482      * @param {String} id The id of record to check
48483      * @return {Boolean}
48484      */
48485     isIdSelected : function(id){
48486         return (this.selections.key(id) ? true : false);
48487     },
48488
48489     // private
48490     handleMouseDown : function(e, t){
48491         var view = this.grid.getView(), rowIndex;
48492         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48493             return;
48494         };
48495         if(e.shiftKey && this.last !== false){
48496             var last = this.last;
48497             this.selectRange(last, rowIndex, e.ctrlKey);
48498             this.last = last; // reset the last
48499             view.focusRow(rowIndex);
48500         }else{
48501             var isSelected = this.isSelected(rowIndex);
48502             if(e.button !== 0 && isSelected){
48503                 view.focusRow(rowIndex);
48504             }else if(e.ctrlKey && isSelected){
48505                 this.deselectRow(rowIndex);
48506             }else if(!isSelected){
48507                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48508                 view.focusRow(rowIndex);
48509             }
48510         }
48511         this.fireEvent("afterselectionchange", this);
48512     },
48513     // private
48514     handleDragableRowClick :  function(grid, rowIndex, e) 
48515     {
48516         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48517             this.selectRow(rowIndex, false);
48518             grid.view.focusRow(rowIndex);
48519              this.fireEvent("afterselectionchange", this);
48520         }
48521     },
48522     
48523     /**
48524      * Selects multiple rows.
48525      * @param {Array} rows Array of the indexes of the row to select
48526      * @param {Boolean} keepExisting (optional) True to keep existing selections
48527      */
48528     selectRows : function(rows, keepExisting){
48529         if(!keepExisting){
48530             this.clearSelections();
48531         }
48532         for(var i = 0, len = rows.length; i < len; i++){
48533             this.selectRow(rows[i], true);
48534         }
48535     },
48536
48537     /**
48538      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48539      * @param {Number} startRow The index of the first row in the range
48540      * @param {Number} endRow The index of the last row in the range
48541      * @param {Boolean} keepExisting (optional) True to retain existing selections
48542      */
48543     selectRange : function(startRow, endRow, keepExisting){
48544         if(this.locked) return;
48545         if(!keepExisting){
48546             this.clearSelections();
48547         }
48548         if(startRow <= endRow){
48549             for(var i = startRow; i <= endRow; i++){
48550                 this.selectRow(i, true);
48551             }
48552         }else{
48553             for(var i = startRow; i >= endRow; i--){
48554                 this.selectRow(i, true);
48555             }
48556         }
48557     },
48558
48559     /**
48560      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48561      * @param {Number} startRow The index of the first row in the range
48562      * @param {Number} endRow The index of the last row in the range
48563      */
48564     deselectRange : function(startRow, endRow, preventViewNotify){
48565         if(this.locked) return;
48566         for(var i = startRow; i <= endRow; i++){
48567             this.deselectRow(i, preventViewNotify);
48568         }
48569     },
48570
48571     /**
48572      * Selects a row.
48573      * @param {Number} row The index of the row to select
48574      * @param {Boolean} keepExisting (optional) True to keep existing selections
48575      */
48576     selectRow : function(index, keepExisting, preventViewNotify){
48577         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48578         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48579             if(!keepExisting || this.singleSelect){
48580                 this.clearSelections();
48581             }
48582             var r = this.grid.dataSource.getAt(index);
48583             this.selections.add(r);
48584             this.last = this.lastActive = index;
48585             if(!preventViewNotify){
48586                 this.grid.getView().onRowSelect(index);
48587             }
48588             this.fireEvent("rowselect", this, index, r);
48589             this.fireEvent("selectionchange", this);
48590         }
48591     },
48592
48593     /**
48594      * Deselects a row.
48595      * @param {Number} row The index of the row to deselect
48596      */
48597     deselectRow : function(index, preventViewNotify){
48598         if(this.locked) return;
48599         if(this.last == index){
48600             this.last = false;
48601         }
48602         if(this.lastActive == index){
48603             this.lastActive = false;
48604         }
48605         var r = this.grid.dataSource.getAt(index);
48606         this.selections.remove(r);
48607         if(!preventViewNotify){
48608             this.grid.getView().onRowDeselect(index);
48609         }
48610         this.fireEvent("rowdeselect", this, index);
48611         this.fireEvent("selectionchange", this);
48612     },
48613
48614     // private
48615     restoreLast : function(){
48616         if(this._last){
48617             this.last = this._last;
48618         }
48619     },
48620
48621     // private
48622     acceptsNav : function(row, col, cm){
48623         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48624     },
48625
48626     // private
48627     onEditorKey : function(field, e){
48628         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48629         if(k == e.TAB){
48630             e.stopEvent();
48631             ed.completeEdit();
48632             if(e.shiftKey){
48633                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48634             }else{
48635                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48636             }
48637         }else if(k == e.ENTER && !e.ctrlKey){
48638             e.stopEvent();
48639             ed.completeEdit();
48640             if(e.shiftKey){
48641                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48642             }else{
48643                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48644             }
48645         }else if(k == e.ESC){
48646             ed.cancelEdit();
48647         }
48648         if(newCell){
48649             g.startEditing(newCell[0], newCell[1]);
48650         }
48651     }
48652 });/*
48653  * Based on:
48654  * Ext JS Library 1.1.1
48655  * Copyright(c) 2006-2007, Ext JS, LLC.
48656  *
48657  * Originally Released Under LGPL - original licence link has changed is not relivant.
48658  *
48659  * Fork - LGPL
48660  * <script type="text/javascript">
48661  */
48662 /**
48663  * @class Roo.grid.CellSelectionModel
48664  * @extends Roo.grid.AbstractSelectionModel
48665  * This class provides the basic implementation for cell selection in a grid.
48666  * @constructor
48667  * @param {Object} config The object containing the configuration of this model.
48668  */
48669 Roo.grid.CellSelectionModel = function(config){
48670     Roo.apply(this, config);
48671
48672     this.selection = null;
48673
48674     this.addEvents({
48675         /**
48676              * @event beforerowselect
48677              * Fires before a cell is selected.
48678              * @param {SelectionModel} this
48679              * @param {Number} rowIndex The selected row index
48680              * @param {Number} colIndex The selected cell index
48681              */
48682             "beforecellselect" : true,
48683         /**
48684              * @event cellselect
48685              * Fires when a cell is selected.
48686              * @param {SelectionModel} this
48687              * @param {Number} rowIndex The selected row index
48688              * @param {Number} colIndex The selected cell index
48689              */
48690             "cellselect" : true,
48691         /**
48692              * @event selectionchange
48693              * Fires when the active selection changes.
48694              * @param {SelectionModel} this
48695              * @param {Object} selection null for no selection or an object (o) with two properties
48696                 <ul>
48697                 <li>o.record: the record object for the row the selection is in</li>
48698                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48699                 </ul>
48700              */
48701             "selectionchange" : true
48702     });
48703     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48704 };
48705
48706 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48707
48708     /** @ignore */
48709     initEvents : function(){
48710         this.grid.on("mousedown", this.handleMouseDown, this);
48711         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48712         var view = this.grid.view;
48713         view.on("refresh", this.onViewChange, this);
48714         view.on("rowupdated", this.onRowUpdated, this);
48715         view.on("beforerowremoved", this.clearSelections, this);
48716         view.on("beforerowsinserted", this.clearSelections, this);
48717         if(this.grid.isEditor){
48718             this.grid.on("beforeedit", this.beforeEdit,  this);
48719         }
48720     },
48721
48722         //private
48723     beforeEdit : function(e){
48724         this.select(e.row, e.column, false, true, e.record);
48725     },
48726
48727         //private
48728     onRowUpdated : function(v, index, r){
48729         if(this.selection && this.selection.record == r){
48730             v.onCellSelect(index, this.selection.cell[1]);
48731         }
48732     },
48733
48734         //private
48735     onViewChange : function(){
48736         this.clearSelections(true);
48737     },
48738
48739         /**
48740          * Returns the currently selected cell,.
48741          * @return {Array} The selected cell (row, column) or null if none selected.
48742          */
48743     getSelectedCell : function(){
48744         return this.selection ? this.selection.cell : null;
48745     },
48746
48747     /**
48748      * Clears all selections.
48749      * @param {Boolean} true to prevent the gridview from being notified about the change.
48750      */
48751     clearSelections : function(preventNotify){
48752         var s = this.selection;
48753         if(s){
48754             if(preventNotify !== true){
48755                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48756             }
48757             this.selection = null;
48758             this.fireEvent("selectionchange", this, null);
48759         }
48760     },
48761
48762     /**
48763      * Returns true if there is a selection.
48764      * @return {Boolean}
48765      */
48766     hasSelection : function(){
48767         return this.selection ? true : false;
48768     },
48769
48770     /** @ignore */
48771     handleMouseDown : function(e, t){
48772         var v = this.grid.getView();
48773         if(this.isLocked()){
48774             return;
48775         };
48776         var row = v.findRowIndex(t);
48777         var cell = v.findCellIndex(t);
48778         if(row !== false && cell !== false){
48779             this.select(row, cell);
48780         }
48781     },
48782
48783     /**
48784      * Selects a cell.
48785      * @param {Number} rowIndex
48786      * @param {Number} collIndex
48787      */
48788     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48789         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48790             this.clearSelections();
48791             r = r || this.grid.dataSource.getAt(rowIndex);
48792             this.selection = {
48793                 record : r,
48794                 cell : [rowIndex, colIndex]
48795             };
48796             if(!preventViewNotify){
48797                 var v = this.grid.getView();
48798                 v.onCellSelect(rowIndex, colIndex);
48799                 if(preventFocus !== true){
48800                     v.focusCell(rowIndex, colIndex);
48801                 }
48802             }
48803             this.fireEvent("cellselect", this, rowIndex, colIndex);
48804             this.fireEvent("selectionchange", this, this.selection);
48805         }
48806     },
48807
48808         //private
48809     isSelectable : function(rowIndex, colIndex, cm){
48810         return !cm.isHidden(colIndex);
48811     },
48812
48813     /** @ignore */
48814     handleKeyDown : function(e){
48815         if(!e.isNavKeyPress()){
48816             return;
48817         }
48818         var g = this.grid, s = this.selection;
48819         if(!s){
48820             e.stopEvent();
48821             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
48822             if(cell){
48823                 this.select(cell[0], cell[1]);
48824             }
48825             return;
48826         }
48827         var sm = this;
48828         var walk = function(row, col, step){
48829             return g.walkCells(row, col, step, sm.isSelectable,  sm);
48830         };
48831         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
48832         var newCell;
48833
48834         switch(k){
48835              case e.TAB:
48836                  if(e.shiftKey){
48837                      newCell = walk(r, c-1, -1);
48838                  }else{
48839                      newCell = walk(r, c+1, 1);
48840                  }
48841              break;
48842              case e.DOWN:
48843                  newCell = walk(r+1, c, 1);
48844              break;
48845              case e.UP:
48846                  newCell = walk(r-1, c, -1);
48847              break;
48848              case e.RIGHT:
48849                  newCell = walk(r, c+1, 1);
48850              break;
48851              case e.LEFT:
48852                  newCell = walk(r, c-1, -1);
48853              break;
48854              case e.ENTER:
48855                  if(g.isEditor && !g.editing){
48856                     g.startEditing(r, c);
48857                     e.stopEvent();
48858                     return;
48859                 }
48860              break;
48861         };
48862         if(newCell){
48863             this.select(newCell[0], newCell[1]);
48864             e.stopEvent();
48865         }
48866     },
48867
48868     acceptsNav : function(row, col, cm){
48869         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48870     },
48871
48872     onEditorKey : function(field, e){
48873         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48874         if(k == e.TAB){
48875             if(e.shiftKey){
48876                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48877             }else{
48878                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48879             }
48880             e.stopEvent();
48881         }else if(k == e.ENTER && !e.ctrlKey){
48882             ed.completeEdit();
48883             e.stopEvent();
48884         }else if(k == e.ESC){
48885             ed.cancelEdit();
48886         }
48887         if(newCell){
48888             g.startEditing(newCell[0], newCell[1]);
48889         }
48890     }
48891 });