Roo/Template.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 if (Roo.debug) console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     },
847     /**
848      * Map (JS 1.6 compatibility)
849      * @param {Function} function  to call
850      */
851     map : function(fun )
852     {
853         var len = this.length >>> 0;
854         if (typeof fun != "function")
855             throw new TypeError();
856
857         var res = new Array(len);
858         var thisp = arguments[1];
859         for (var i = 0; i < len; i++)
860         {
861             if (i in this)
862                 res[i] = fun.call(thisp, this[i], i, this);
863         }
864
865         return res;
866     }
867     
868 });
869
870
871  /*
872  * Based on:
873  * Ext JS Library 1.1.1
874  * Copyright(c) 2006-2007, Ext JS, LLC.
875  *
876  * Originally Released Under LGPL - original licence link has changed is not relivant.
877  *
878  * Fork - LGPL
879  * <script type="text/javascript">
880  */
881
882 /**
883  * @class Date
884  *
885  * The date parsing and format syntax is a subset of
886  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
887  * supported will provide results equivalent to their PHP versions.
888  *
889  * Following is the list of all currently supported formats:
890  *<pre>
891 Sample date:
892 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
893
894 Format  Output      Description
895 ------  ----------  --------------------------------------------------------------
896   d      10         Day of the month, 2 digits with leading zeros
897   D      Wed        A textual representation of a day, three letters
898   j      10         Day of the month without leading zeros
899   l      Wednesday  A full textual representation of the day of the week
900   S      th         English ordinal day of month suffix, 2 chars (use with j)
901   w      3          Numeric representation of the day of the week
902   z      9          The julian date, or day of the year (0-365)
903   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
904   F      January    A full textual representation of the month
905   m      01         Numeric representation of a month, with leading zeros
906   M      Jan        Month name abbreviation, three letters
907   n      1          Numeric representation of a month, without leading zeros
908   t      31         Number of days in the given month
909   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
910   Y      2007       A full numeric representation of a year, 4 digits
911   y      07         A two digit representation of a year
912   a      pm         Lowercase Ante meridiem and Post meridiem
913   A      PM         Uppercase Ante meridiem and Post meridiem
914   g      3          12-hour format of an hour without leading zeros
915   G      15         24-hour format of an hour without leading zeros
916   h      03         12-hour format of an hour with leading zeros
917   H      15         24-hour format of an hour with leading zeros
918   i      05         Minutes with leading zeros
919   s      01         Seconds, with leading zeros
920   O      -0600      Difference to Greenwich time (GMT) in hours
921   T      CST        Timezone setting of the machine running the code
922   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
923 </pre>
924  *
925  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
926  * <pre><code>
927 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
928 document.write(dt.format('Y-m-d'));                         //2007-01-10
929 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
930 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
931  </code></pre>
932  *
933  * Here are some standard date/time patterns that you might find helpful.  They
934  * are not part of the source of Date.js, but to use them you can simply copy this
935  * block of code into any script that is included after Date.js and they will also become
936  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
937  * <pre><code>
938 Date.patterns = {
939     ISO8601Long:"Y-m-d H:i:s",
940     ISO8601Short:"Y-m-d",
941     ShortDate: "n/j/Y",
942     LongDate: "l, F d, Y",
943     FullDateTime: "l, F d, Y g:i:s A",
944     MonthDay: "F d",
945     ShortTime: "g:i A",
946     LongTime: "g:i:s A",
947     SortableDateTime: "Y-m-d\\TH:i:s",
948     UniversalSortableDateTime: "Y-m-d H:i:sO",
949     YearMonth: "F, Y"
950 };
951 </code></pre>
952  *
953  * Example usage:
954  * <pre><code>
955 var dt = new Date();
956 document.write(dt.format(Date.patterns.ShortDate));
957  </code></pre>
958  */
959
960 /*
961  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
962  * They generate precompiled functions from date formats instead of parsing and
963  * processing the pattern every time you format a date.  These functions are available
964  * on every Date object (any javascript function).
965  *
966  * The original article and download are here:
967  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
968  *
969  */
970  
971  
972  // was in core
973 /**
974  Returns the number of milliseconds between this date and date
975  @param {Date} date (optional) Defaults to now
976  @return {Number} The diff in milliseconds
977  @member Date getElapsed
978  */
979 Date.prototype.getElapsed = function(date) {
980         return Math.abs((date || new Date()).getTime()-this.getTime());
981 };
982 // was in date file..
983
984
985 // private
986 Date.parseFunctions = {count:0};
987 // private
988 Date.parseRegexes = [];
989 // private
990 Date.formatFunctions = {count:0};
991
992 // private
993 Date.prototype.dateFormat = function(format) {
994     if (Date.formatFunctions[format] == null) {
995         Date.createNewFormat(format);
996     }
997     var func = Date.formatFunctions[format];
998     return this[func]();
999 };
1000
1001
1002 /**
1003  * Formats a date given the supplied format string
1004  * @param {String} format The format string
1005  * @return {String} The formatted date
1006  * @method
1007  */
1008 Date.prototype.format = Date.prototype.dateFormat;
1009
1010 // private
1011 Date.createNewFormat = function(format) {
1012     var funcName = "format" + Date.formatFunctions.count++;
1013     Date.formatFunctions[format] = funcName;
1014     var code = "Date.prototype." + funcName + " = function(){return ";
1015     var special = false;
1016     var ch = '';
1017     for (var i = 0; i < format.length; ++i) {
1018         ch = format.charAt(i);
1019         if (!special && ch == "\\") {
1020             special = true;
1021         }
1022         else if (special) {
1023             special = false;
1024             code += "'" + String.escape(ch) + "' + ";
1025         }
1026         else {
1027             code += Date.getFormatCode(ch);
1028         }
1029     }
1030     /** eval:var:zzzzzzzzzzzzz */
1031     eval(code.substring(0, code.length - 3) + ";}");
1032 };
1033
1034 // private
1035 Date.getFormatCode = function(character) {
1036     switch (character) {
1037     case "d":
1038         return "String.leftPad(this.getDate(), 2, '0') + ";
1039     case "D":
1040         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1041     case "j":
1042         return "this.getDate() + ";
1043     case "l":
1044         return "Date.dayNames[this.getDay()] + ";
1045     case "S":
1046         return "this.getSuffix() + ";
1047     case "w":
1048         return "this.getDay() + ";
1049     case "z":
1050         return "this.getDayOfYear() + ";
1051     case "W":
1052         return "this.getWeekOfYear() + ";
1053     case "F":
1054         return "Date.monthNames[this.getMonth()] + ";
1055     case "m":
1056         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1057     case "M":
1058         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1059     case "n":
1060         return "(this.getMonth() + 1) + ";
1061     case "t":
1062         return "this.getDaysInMonth() + ";
1063     case "L":
1064         return "(this.isLeapYear() ? 1 : 0) + ";
1065     case "Y":
1066         return "this.getFullYear() + ";
1067     case "y":
1068         return "('' + this.getFullYear()).substring(2, 4) + ";
1069     case "a":
1070         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1071     case "A":
1072         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1073     case "g":
1074         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1075     case "G":
1076         return "this.getHours() + ";
1077     case "h":
1078         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1079     case "H":
1080         return "String.leftPad(this.getHours(), 2, '0') + ";
1081     case "i":
1082         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1083     case "s":
1084         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1085     case "O":
1086         return "this.getGMTOffset() + ";
1087     case "T":
1088         return "this.getTimezone() + ";
1089     case "Z":
1090         return "(this.getTimezoneOffset() * -60) + ";
1091     default:
1092         return "'" + String.escape(character) + "' + ";
1093     }
1094 };
1095
1096 /**
1097  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1098  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1099  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1100  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1101  * string or the parse operation will fail.
1102  * Example Usage:
1103 <pre><code>
1104 //dt = Fri May 25 2007 (current date)
1105 var dt = new Date();
1106
1107 //dt = Thu May 25 2006 (today's month/day in 2006)
1108 dt = Date.parseDate("2006", "Y");
1109
1110 //dt = Sun Jan 15 2006 (all date parts specified)
1111 dt = Date.parseDate("2006-1-15", "Y-m-d");
1112
1113 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1114 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1115 </code></pre>
1116  * @param {String} input The unparsed date as a string
1117  * @param {String} format The format the date is in
1118  * @return {Date} The parsed date
1119  * @static
1120  */
1121 Date.parseDate = function(input, format) {
1122     if (Date.parseFunctions[format] == null) {
1123         Date.createParser(format);
1124     }
1125     var func = Date.parseFunctions[format];
1126     return Date[func](input);
1127 };
1128 /**
1129  * @private
1130  */
1131 Date.createParser = function(format) {
1132     var funcName = "parse" + Date.parseFunctions.count++;
1133     var regexNum = Date.parseRegexes.length;
1134     var currentGroup = 1;
1135     Date.parseFunctions[format] = funcName;
1136
1137     var code = "Date." + funcName + " = function(input){\n"
1138         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1139         + "var d = new Date();\n"
1140         + "y = d.getFullYear();\n"
1141         + "m = d.getMonth();\n"
1142         + "d = d.getDate();\n"
1143         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1144         + "if (results && results.length > 0) {";
1145     var regex = "";
1146
1147     var special = false;
1148     var ch = '';
1149     for (var i = 0; i < format.length; ++i) {
1150         ch = format.charAt(i);
1151         if (!special && ch == "\\") {
1152             special = true;
1153         }
1154         else if (special) {
1155             special = false;
1156             regex += String.escape(ch);
1157         }
1158         else {
1159             var obj = Date.formatCodeToRegex(ch, currentGroup);
1160             currentGroup += obj.g;
1161             regex += obj.s;
1162             if (obj.g && obj.c) {
1163                 code += obj.c;
1164             }
1165         }
1166     }
1167
1168     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1169         + "{v = new Date(y, m, d, h, i, s);}\n"
1170         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1171         + "{v = new Date(y, m, d, h, i);}\n"
1172         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1173         + "{v = new Date(y, m, d, h);}\n"
1174         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1175         + "{v = new Date(y, m, d);}\n"
1176         + "else if (y >= 0 && m >= 0)\n"
1177         + "{v = new Date(y, m);}\n"
1178         + "else if (y >= 0)\n"
1179         + "{v = new Date(y);}\n"
1180         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1181         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1182         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1183         + ";}";
1184
1185     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1186     /** eval:var:zzzzzzzzzzzzz */
1187     eval(code);
1188 };
1189
1190 // private
1191 Date.formatCodeToRegex = function(character, currentGroup) {
1192     switch (character) {
1193     case "D":
1194         return {g:0,
1195         c:null,
1196         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1197     case "j":
1198         return {g:1,
1199             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1200             s:"(\\d{1,2})"}; // day of month without leading zeroes
1201     case "d":
1202         return {g:1,
1203             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1204             s:"(\\d{2})"}; // day of month with leading zeroes
1205     case "l":
1206         return {g:0,
1207             c:null,
1208             s:"(?:" + Date.dayNames.join("|") + ")"};
1209     case "S":
1210         return {g:0,
1211             c:null,
1212             s:"(?:st|nd|rd|th)"};
1213     case "w":
1214         return {g:0,
1215             c:null,
1216             s:"\\d"};
1217     case "z":
1218         return {g:0,
1219             c:null,
1220             s:"(?:\\d{1,3})"};
1221     case "W":
1222         return {g:0,
1223             c:null,
1224             s:"(?:\\d{2})"};
1225     case "F":
1226         return {g:1,
1227             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1228             s:"(" + Date.monthNames.join("|") + ")"};
1229     case "M":
1230         return {g:1,
1231             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1232             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1233     case "n":
1234         return {g:1,
1235             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1236             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1237     case "m":
1238         return {g:1,
1239             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1240             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1241     case "t":
1242         return {g:0,
1243             c:null,
1244             s:"\\d{1,2}"};
1245     case "L":
1246         return {g:0,
1247             c:null,
1248             s:"(?:1|0)"};
1249     case "Y":
1250         return {g:1,
1251             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1252             s:"(\\d{4})"};
1253     case "y":
1254         return {g:1,
1255             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1256                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1257             s:"(\\d{1,2})"};
1258     case "a":
1259         return {g:1,
1260             c:"if (results[" + currentGroup + "] == 'am') {\n"
1261                 + "if (h == 12) { h = 0; }\n"
1262                 + "} else { if (h < 12) { h += 12; }}",
1263             s:"(am|pm)"};
1264     case "A":
1265         return {g:1,
1266             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1267                 + "if (h == 12) { h = 0; }\n"
1268                 + "} else { if (h < 12) { h += 12; }}",
1269             s:"(AM|PM)"};
1270     case "g":
1271     case "G":
1272         return {g:1,
1273             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1274             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1275     case "h":
1276     case "H":
1277         return {g:1,
1278             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1279             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1280     case "i":
1281         return {g:1,
1282             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1283             s:"(\\d{2})"};
1284     case "s":
1285         return {g:1,
1286             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{2})"};
1288     case "O":
1289         return {g:1,
1290             c:[
1291                 "o = results[", currentGroup, "];\n",
1292                 "var sn = o.substring(0,1);\n", // get + / - sign
1293                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1294                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1295                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1296                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1297             ].join(""),
1298             s:"([+\-]\\d{4})"};
1299     case "T":
1300         return {g:0,
1301             c:null,
1302             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1303     case "Z":
1304         return {g:1,
1305             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1306                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1307             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1308     default:
1309         return {g:0,
1310             c:null,
1311             s:String.escape(character)};
1312     }
1313 };
1314
1315 /**
1316  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1317  * @return {String} The abbreviated timezone name (e.g. 'CST')
1318  */
1319 Date.prototype.getTimezone = function() {
1320     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1321 };
1322
1323 /**
1324  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1325  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1326  */
1327 Date.prototype.getGMTOffset = function() {
1328     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1329         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1330         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1331 };
1332
1333 /**
1334  * Get the numeric day number of the year, adjusted for leap year.
1335  * @return {Number} 0 through 364 (365 in leap years)
1336  */
1337 Date.prototype.getDayOfYear = function() {
1338     var num = 0;
1339     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1340     for (var i = 0; i < this.getMonth(); ++i) {
1341         num += Date.daysInMonth[i];
1342     }
1343     return num + this.getDate() - 1;
1344 };
1345
1346 /**
1347  * Get the string representation of the numeric week number of the year
1348  * (equivalent to the format specifier 'W').
1349  * @return {String} '00' through '52'
1350  */
1351 Date.prototype.getWeekOfYear = function() {
1352     // Skip to Thursday of this week
1353     var now = this.getDayOfYear() + (4 - this.getDay());
1354     // Find the first Thursday of the year
1355     var jan1 = new Date(this.getFullYear(), 0, 1);
1356     var then = (7 - jan1.getDay() + 4);
1357     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1358 };
1359
1360 /**
1361  * Whether or not the current date is in a leap year.
1362  * @return {Boolean} True if the current date is in a leap year, else false
1363  */
1364 Date.prototype.isLeapYear = function() {
1365     var year = this.getFullYear();
1366     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1367 };
1368
1369 /**
1370  * Get the first day of the current month, adjusted for leap year.  The returned value
1371  * is the numeric day index within the week (0-6) which can be used in conjunction with
1372  * the {@link #monthNames} array to retrieve the textual day name.
1373  * Example:
1374  *<pre><code>
1375 var dt = new Date('1/10/2007');
1376 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1377 </code></pre>
1378  * @return {Number} The day number (0-6)
1379  */
1380 Date.prototype.getFirstDayOfMonth = function() {
1381     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1382     return (day < 0) ? (day + 7) : day;
1383 };
1384
1385 /**
1386  * Get the last day of the current month, adjusted for leap year.  The returned value
1387  * is the numeric day index within the week (0-6) which can be used in conjunction with
1388  * the {@link #monthNames} array to retrieve the textual day name.
1389  * Example:
1390  *<pre><code>
1391 var dt = new Date('1/10/2007');
1392 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1393 </code></pre>
1394  * @return {Number} The day number (0-6)
1395  */
1396 Date.prototype.getLastDayOfMonth = function() {
1397     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1398     return (day < 0) ? (day + 7) : day;
1399 };
1400
1401
1402 /**
1403  * Get the first date of this date's month
1404  * @return {Date}
1405  */
1406 Date.prototype.getFirstDateOfMonth = function() {
1407     return new Date(this.getFullYear(), this.getMonth(), 1);
1408 };
1409
1410 /**
1411  * Get the last date of this date's month
1412  * @return {Date}
1413  */
1414 Date.prototype.getLastDateOfMonth = function() {
1415     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1416 };
1417 /**
1418  * Get the number of days in the current month, adjusted for leap year.
1419  * @return {Number} The number of days in the month
1420  */
1421 Date.prototype.getDaysInMonth = function() {
1422     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1423     return Date.daysInMonth[this.getMonth()];
1424 };
1425
1426 /**
1427  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1428  * @return {String} 'st, 'nd', 'rd' or 'th'
1429  */
1430 Date.prototype.getSuffix = function() {
1431     switch (this.getDate()) {
1432         case 1:
1433         case 21:
1434         case 31:
1435             return "st";
1436         case 2:
1437         case 22:
1438             return "nd";
1439         case 3:
1440         case 23:
1441             return "rd";
1442         default:
1443             return "th";
1444     }
1445 };
1446
1447 // private
1448 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1449
1450 /**
1451  * An array of textual month names.
1452  * Override these values for international dates, for example...
1453  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1454  * @type Array
1455  * @static
1456  */
1457 Date.monthNames =
1458    ["January",
1459     "February",
1460     "March",
1461     "April",
1462     "May",
1463     "June",
1464     "July",
1465     "August",
1466     "September",
1467     "October",
1468     "November",
1469     "December"];
1470
1471 /**
1472  * An array of textual day names.
1473  * Override these values for international dates, for example...
1474  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1475  * @type Array
1476  * @static
1477  */
1478 Date.dayNames =
1479    ["Sunday",
1480     "Monday",
1481     "Tuesday",
1482     "Wednesday",
1483     "Thursday",
1484     "Friday",
1485     "Saturday"];
1486
1487 // private
1488 Date.y2kYear = 50;
1489 // private
1490 Date.monthNumbers = {
1491     Jan:0,
1492     Feb:1,
1493     Mar:2,
1494     Apr:3,
1495     May:4,
1496     Jun:5,
1497     Jul:6,
1498     Aug:7,
1499     Sep:8,
1500     Oct:9,
1501     Nov:10,
1502     Dec:11};
1503
1504 /**
1505  * Creates and returns a new Date instance with the exact same date value as the called instance.
1506  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1507  * variable will also be changed.  When the intention is to create a new variable that will not
1508  * modify the original instance, you should create a clone.
1509  *
1510  * Example of correctly cloning a date:
1511  * <pre><code>
1512 //wrong way:
1513 var orig = new Date('10/1/2006');
1514 var copy = orig;
1515 copy.setDate(5);
1516 document.write(orig);  //returns 'Thu Oct 05 2006'!
1517
1518 //correct way:
1519 var orig = new Date('10/1/2006');
1520 var copy = orig.clone();
1521 copy.setDate(5);
1522 document.write(orig);  //returns 'Thu Oct 01 2006'
1523 </code></pre>
1524  * @return {Date} The new Date instance
1525  */
1526 Date.prototype.clone = function() {
1527         return new Date(this.getTime());
1528 };
1529
1530 /**
1531  * Clears any time information from this date
1532  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1533  @return {Date} this or the clone
1534  */
1535 Date.prototype.clearTime = function(clone){
1536     if(clone){
1537         return this.clone().clearTime();
1538     }
1539     this.setHours(0);
1540     this.setMinutes(0);
1541     this.setSeconds(0);
1542     this.setMilliseconds(0);
1543     return this;
1544 };
1545
1546 // private
1547 // safari setMonth is broken
1548 if(Roo.isSafari){
1549     Date.brokenSetMonth = Date.prototype.setMonth;
1550         Date.prototype.setMonth = function(num){
1551                 if(num <= -1){
1552                         var n = Math.ceil(-num);
1553                         var back_year = Math.ceil(n/12);
1554                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1555                         this.setFullYear(this.getFullYear() - back_year);
1556                         return Date.brokenSetMonth.call(this, month);
1557                 } else {
1558                         return Date.brokenSetMonth.apply(this, arguments);
1559                 }
1560         };
1561 }
1562
1563 /** Date interval constant 
1564 * @static 
1565 * @type String */
1566 Date.MILLI = "ms";
1567 /** Date interval constant 
1568 * @static 
1569 * @type String */
1570 Date.SECOND = "s";
1571 /** Date interval constant 
1572 * @static 
1573 * @type String */
1574 Date.MINUTE = "mi";
1575 /** Date interval constant 
1576 * @static 
1577 * @type String */
1578 Date.HOUR = "h";
1579 /** Date interval constant 
1580 * @static 
1581 * @type String */
1582 Date.DAY = "d";
1583 /** Date interval constant 
1584 * @static 
1585 * @type String */
1586 Date.MONTH = "mo";
1587 /** Date interval constant 
1588 * @static 
1589 * @type String */
1590 Date.YEAR = "y";
1591
1592 /**
1593  * Provides a convenient method of performing basic date arithmetic.  This method
1594  * does not modify the Date instance being called - it creates and returns
1595  * a new Date instance containing the resulting date value.
1596  *
1597  * Examples:
1598  * <pre><code>
1599 //Basic usage:
1600 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1601 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1602
1603 //Negative values will subtract correctly:
1604 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1605 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1606
1607 //You can even chain several calls together in one line!
1608 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1609 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1610  </code></pre>
1611  *
1612  * @param {String} interval   A valid date interval enum value
1613  * @param {Number} value      The amount to add to the current date
1614  * @return {Date} The new Date instance
1615  */
1616 Date.prototype.add = function(interval, value){
1617   var d = this.clone();
1618   if (!interval || value === 0) return d;
1619   switch(interval.toLowerCase()){
1620     case Date.MILLI:
1621       d.setMilliseconds(this.getMilliseconds() + value);
1622       break;
1623     case Date.SECOND:
1624       d.setSeconds(this.getSeconds() + value);
1625       break;
1626     case Date.MINUTE:
1627       d.setMinutes(this.getMinutes() + value);
1628       break;
1629     case Date.HOUR:
1630       d.setHours(this.getHours() + value);
1631       break;
1632     case Date.DAY:
1633       d.setDate(this.getDate() + value);
1634       break;
1635     case Date.MONTH:
1636       var day = this.getDate();
1637       if(day > 28){
1638           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1639       }
1640       d.setDate(day);
1641       d.setMonth(this.getMonth() + value);
1642       break;
1643     case Date.YEAR:
1644       d.setFullYear(this.getFullYear() + value);
1645       break;
1646   }
1647   return d;
1648 };/*
1649  * Based on:
1650  * Ext JS Library 1.1.1
1651  * Copyright(c) 2006-2007, Ext JS, LLC.
1652  *
1653  * Originally Released Under LGPL - original licence link has changed is not relivant.
1654  *
1655  * Fork - LGPL
1656  * <script type="text/javascript">
1657  */
1658
1659 Roo.lib.Dom = {
1660     getViewWidth : function(full) {
1661         return full ? this.getDocumentWidth() : this.getViewportWidth();
1662     },
1663
1664     getViewHeight : function(full) {
1665         return full ? this.getDocumentHeight() : this.getViewportHeight();
1666     },
1667
1668     getDocumentHeight: function() {
1669         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1670         return Math.max(scrollHeight, this.getViewportHeight());
1671     },
1672
1673     getDocumentWidth: function() {
1674         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1675         return Math.max(scrollWidth, this.getViewportWidth());
1676     },
1677
1678     getViewportHeight: function() {
1679         var height = self.innerHeight;
1680         var mode = document.compatMode;
1681
1682         if ((mode || Roo.isIE) && !Roo.isOpera) {
1683             height = (mode == "CSS1Compat") ?
1684                      document.documentElement.clientHeight :
1685                      document.body.clientHeight;
1686         }
1687
1688         return height;
1689     },
1690
1691     getViewportWidth: function() {
1692         var width = self.innerWidth;
1693         var mode = document.compatMode;
1694
1695         if (mode || Roo.isIE) {
1696             width = (mode == "CSS1Compat") ?
1697                     document.documentElement.clientWidth :
1698                     document.body.clientWidth;
1699         }
1700         return width;
1701     },
1702
1703     isAncestor : function(p, c) {
1704         p = Roo.getDom(p);
1705         c = Roo.getDom(c);
1706         if (!p || !c) {
1707             return false;
1708         }
1709
1710         if (p.contains && !Roo.isSafari) {
1711             return p.contains(c);
1712         } else if (p.compareDocumentPosition) {
1713             return !!(p.compareDocumentPosition(c) & 16);
1714         } else {
1715             var parent = c.parentNode;
1716             while (parent) {
1717                 if (parent == p) {
1718                     return true;
1719                 }
1720                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1721                     return false;
1722                 }
1723                 parent = parent.parentNode;
1724             }
1725             return false;
1726         }
1727     },
1728
1729     getRegion : function(el) {
1730         return Roo.lib.Region.getRegion(el);
1731     },
1732
1733     getY : function(el) {
1734         return this.getXY(el)[1];
1735     },
1736
1737     getX : function(el) {
1738         return this.getXY(el)[0];
1739     },
1740
1741     getXY : function(el) {
1742         var p, pe, b, scroll, bd = document.body;
1743         el = Roo.getDom(el);
1744         var fly = Roo.lib.AnimBase.fly;
1745         if (el.getBoundingClientRect) {
1746             b = el.getBoundingClientRect();
1747             scroll = fly(document).getScroll();
1748             return [b.left + scroll.left, b.top + scroll.top];
1749         }
1750         var x = 0, y = 0;
1751
1752         p = el;
1753
1754         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1755
1756         while (p) {
1757
1758             x += p.offsetLeft;
1759             y += p.offsetTop;
1760
1761             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1762                 hasAbsolute = true;
1763             }
1764
1765             if (Roo.isGecko) {
1766                 pe = fly(p);
1767
1768                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1769                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1770
1771
1772                 x += bl;
1773                 y += bt;
1774
1775
1776                 if (p != el && pe.getStyle('overflow') != 'visible') {
1777                     x += bl;
1778                     y += bt;
1779                 }
1780             }
1781             p = p.offsetParent;
1782         }
1783
1784         if (Roo.isSafari && hasAbsolute) {
1785             x -= bd.offsetLeft;
1786             y -= bd.offsetTop;
1787         }
1788
1789         if (Roo.isGecko && !hasAbsolute) {
1790             var dbd = fly(bd);
1791             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1792             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1793         }
1794
1795         p = el.parentNode;
1796         while (p && p != bd) {
1797             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1798                 x -= p.scrollLeft;
1799                 y -= p.scrollTop;
1800             }
1801             p = p.parentNode;
1802         }
1803         return [x, y];
1804     },
1805  
1806   
1807
1808
1809     setXY : function(el, xy) {
1810         el = Roo.fly(el, '_setXY');
1811         el.position();
1812         var pts = el.translatePoints(xy);
1813         if (xy[0] !== false) {
1814             el.dom.style.left = pts.left + "px";
1815         }
1816         if (xy[1] !== false) {
1817             el.dom.style.top = pts.top + "px";
1818         }
1819     },
1820
1821     setX : function(el, x) {
1822         this.setXY(el, [x, false]);
1823     },
1824
1825     setY : function(el, y) {
1826         this.setXY(el, [false, y]);
1827     }
1828 };
1829 /*
1830  * Portions of this file are based on pieces of Yahoo User Interface Library
1831  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1832  * YUI licensed under the BSD License:
1833  * http://developer.yahoo.net/yui/license.txt
1834  * <script type="text/javascript">
1835  *
1836  */
1837
1838 Roo.lib.Event = function() {
1839     var loadComplete = false;
1840     var listeners = [];
1841     var unloadListeners = [];
1842     var retryCount = 0;
1843     var onAvailStack = [];
1844     var counter = 0;
1845     var lastError = null;
1846
1847     return {
1848         POLL_RETRYS: 200,
1849         POLL_INTERVAL: 20,
1850         EL: 0,
1851         TYPE: 1,
1852         FN: 2,
1853         WFN: 3,
1854         OBJ: 3,
1855         ADJ_SCOPE: 4,
1856         _interval: null,
1857
1858         startInterval: function() {
1859             if (!this._interval) {
1860                 var self = this;
1861                 var callback = function() {
1862                     self._tryPreloadAttach();
1863                 };
1864                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1865
1866             }
1867         },
1868
1869         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1870             onAvailStack.push({ id:         p_id,
1871                 fn:         p_fn,
1872                 obj:        p_obj,
1873                 override:   p_override,
1874                 checkReady: false    });
1875
1876             retryCount = this.POLL_RETRYS;
1877             this.startInterval();
1878         },
1879
1880
1881         addListener: function(el, eventName, fn) {
1882             el = Roo.getDom(el);
1883             if (!el || !fn) {
1884                 return false;
1885             }
1886
1887             if ("unload" == eventName) {
1888                 unloadListeners[unloadListeners.length] =
1889                 [el, eventName, fn];
1890                 return true;
1891             }
1892
1893             var wrappedFn = function(e) {
1894                 return fn(Roo.lib.Event.getEvent(e));
1895             };
1896
1897             var li = [el, eventName, fn, wrappedFn];
1898
1899             var index = listeners.length;
1900             listeners[index] = li;
1901
1902             this.doAdd(el, eventName, wrappedFn, false);
1903             return true;
1904
1905         },
1906
1907
1908         removeListener: function(el, eventName, fn) {
1909             var i, len;
1910
1911             el = Roo.getDom(el);
1912
1913             if(!fn) {
1914                 return this.purgeElement(el, false, eventName);
1915             }
1916
1917
1918             if ("unload" == eventName) {
1919
1920                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1921                     var li = unloadListeners[i];
1922                     if (li &&
1923                         li[0] == el &&
1924                         li[1] == eventName &&
1925                         li[2] == fn) {
1926                         unloadListeners.splice(i, 1);
1927                         return true;
1928                     }
1929                 }
1930
1931                 return false;
1932             }
1933
1934             var cacheItem = null;
1935
1936
1937             var index = arguments[3];
1938
1939             if ("undefined" == typeof index) {
1940                 index = this._getCacheIndex(el, eventName, fn);
1941             }
1942
1943             if (index >= 0) {
1944                 cacheItem = listeners[index];
1945             }
1946
1947             if (!el || !cacheItem) {
1948                 return false;
1949             }
1950
1951             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1952
1953             delete listeners[index][this.WFN];
1954             delete listeners[index][this.FN];
1955             listeners.splice(index, 1);
1956
1957             return true;
1958
1959         },
1960
1961
1962         getTarget: function(ev, resolveTextNode) {
1963             ev = ev.browserEvent || ev;
1964             var t = ev.target || ev.srcElement;
1965             return this.resolveTextNode(t);
1966         },
1967
1968
1969         resolveTextNode: function(node) {
1970             if (Roo.isSafari && node && 3 == node.nodeType) {
1971                 return node.parentNode;
1972             } else {
1973                 return node;
1974             }
1975         },
1976
1977
1978         getPageX: function(ev) {
1979             ev = ev.browserEvent || ev;
1980             var x = ev.pageX;
1981             if (!x && 0 !== x) {
1982                 x = ev.clientX || 0;
1983
1984                 if (Roo.isIE) {
1985                     x += this.getScroll()[1];
1986                 }
1987             }
1988
1989             return x;
1990         },
1991
1992
1993         getPageY: function(ev) {
1994             ev = ev.browserEvent || ev;
1995             var y = ev.pageY;
1996             if (!y && 0 !== y) {
1997                 y = ev.clientY || 0;
1998
1999                 if (Roo.isIE) {
2000                     y += this.getScroll()[0];
2001                 }
2002             }
2003
2004
2005             return y;
2006         },
2007
2008
2009         getXY: function(ev) {
2010             ev = ev.browserEvent || ev;
2011             return [this.getPageX(ev), this.getPageY(ev)];
2012         },
2013
2014
2015         getRelatedTarget: function(ev) {
2016             ev = ev.browserEvent || ev;
2017             var t = ev.relatedTarget;
2018             if (!t) {
2019                 if (ev.type == "mouseout") {
2020                     t = ev.toElement;
2021                 } else if (ev.type == "mouseover") {
2022                     t = ev.fromElement;
2023                 }
2024             }
2025
2026             return this.resolveTextNode(t);
2027         },
2028
2029
2030         getTime: function(ev) {
2031             ev = ev.browserEvent || ev;
2032             if (!ev.time) {
2033                 var t = new Date().getTime();
2034                 try {
2035                     ev.time = t;
2036                 } catch(ex) {
2037                     this.lastError = ex;
2038                     return t;
2039                 }
2040             }
2041
2042             return ev.time;
2043         },
2044
2045
2046         stopEvent: function(ev) {
2047             this.stopPropagation(ev);
2048             this.preventDefault(ev);
2049         },
2050
2051
2052         stopPropagation: function(ev) {
2053             ev = ev.browserEvent || ev;
2054             if (ev.stopPropagation) {
2055                 ev.stopPropagation();
2056             } else {
2057                 ev.cancelBubble = true;
2058             }
2059         },
2060
2061
2062         preventDefault: function(ev) {
2063             ev = ev.browserEvent || ev;
2064             if(ev.preventDefault) {
2065                 ev.preventDefault();
2066             } else {
2067                 ev.returnValue = false;
2068             }
2069         },
2070
2071
2072         getEvent: function(e) {
2073             var ev = e || window.event;
2074             if (!ev) {
2075                 var c = this.getEvent.caller;
2076                 while (c) {
2077                     ev = c.arguments[0];
2078                     if (ev && Event == ev.constructor) {
2079                         break;
2080                     }
2081                     c = c.caller;
2082                 }
2083             }
2084             return ev;
2085         },
2086
2087
2088         getCharCode: function(ev) {
2089             ev = ev.browserEvent || ev;
2090             return ev.charCode || ev.keyCode || 0;
2091         },
2092
2093
2094         _getCacheIndex: function(el, eventName, fn) {
2095             for (var i = 0,len = listeners.length; i < len; ++i) {
2096                 var li = listeners[i];
2097                 if (li &&
2098                     li[this.FN] == fn &&
2099                     li[this.EL] == el &&
2100                     li[this.TYPE] == eventName) {
2101                     return i;
2102                 }
2103             }
2104
2105             return -1;
2106         },
2107
2108
2109         elCache: {},
2110
2111
2112         getEl: function(id) {
2113             return document.getElementById(id);
2114         },
2115
2116
2117         clearCache: function() {
2118         },
2119
2120
2121         _load: function(e) {
2122             loadComplete = true;
2123             var EU = Roo.lib.Event;
2124
2125
2126             if (Roo.isIE) {
2127                 EU.doRemove(window, "load", EU._load);
2128             }
2129         },
2130
2131
2132         _tryPreloadAttach: function() {
2133
2134             if (this.locked) {
2135                 return false;
2136             }
2137
2138             this.locked = true;
2139
2140
2141             var tryAgain = !loadComplete;
2142             if (!tryAgain) {
2143                 tryAgain = (retryCount > 0);
2144             }
2145
2146
2147             var notAvail = [];
2148             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2149                 var item = onAvailStack[i];
2150                 if (item) {
2151                     var el = this.getEl(item.id);
2152
2153                     if (el) {
2154                         if (!item.checkReady ||
2155                             loadComplete ||
2156                             el.nextSibling ||
2157                             (document && document.body)) {
2158
2159                             var scope = el;
2160                             if (item.override) {
2161                                 if (item.override === true) {
2162                                     scope = item.obj;
2163                                 } else {
2164                                     scope = item.override;
2165                                 }
2166                             }
2167                             item.fn.call(scope, item.obj);
2168                             onAvailStack[i] = null;
2169                         }
2170                     } else {
2171                         notAvail.push(item);
2172                     }
2173                 }
2174             }
2175
2176             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2177
2178             if (tryAgain) {
2179
2180                 this.startInterval();
2181             } else {
2182                 clearInterval(this._interval);
2183                 this._interval = null;
2184             }
2185
2186             this.locked = false;
2187
2188             return true;
2189
2190         },
2191
2192
2193         purgeElement: function(el, recurse, eventName) {
2194             var elListeners = this.getListeners(el, eventName);
2195             if (elListeners) {
2196                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2197                     var l = elListeners[i];
2198                     this.removeListener(el, l.type, l.fn);
2199                 }
2200             }
2201
2202             if (recurse && el && el.childNodes) {
2203                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2204                     this.purgeElement(el.childNodes[i], recurse, eventName);
2205                 }
2206             }
2207         },
2208
2209
2210         getListeners: function(el, eventName) {
2211             var results = [], searchLists;
2212             if (!eventName) {
2213                 searchLists = [listeners, unloadListeners];
2214             } else if (eventName == "unload") {
2215                 searchLists = [unloadListeners];
2216             } else {
2217                 searchLists = [listeners];
2218             }
2219
2220             for (var j = 0; j < searchLists.length; ++j) {
2221                 var searchList = searchLists[j];
2222                 if (searchList && searchList.length > 0) {
2223                     for (var i = 0,len = searchList.length; i < len; ++i) {
2224                         var l = searchList[i];
2225                         if (l && l[this.EL] === el &&
2226                             (!eventName || eventName === l[this.TYPE])) {
2227                             results.push({
2228                                 type:   l[this.TYPE],
2229                                 fn:     l[this.FN],
2230                                 obj:    l[this.OBJ],
2231                                 adjust: l[this.ADJ_SCOPE],
2232                                 index:  i
2233                             });
2234                         }
2235                     }
2236                 }
2237             }
2238
2239             return (results.length) ? results : null;
2240         },
2241
2242
2243         _unload: function(e) {
2244
2245             var EU = Roo.lib.Event, i, j, l, len, index;
2246
2247             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2248                 l = unloadListeners[i];
2249                 if (l) {
2250                     var scope = window;
2251                     if (l[EU.ADJ_SCOPE]) {
2252                         if (l[EU.ADJ_SCOPE] === true) {
2253                             scope = l[EU.OBJ];
2254                         } else {
2255                             scope = l[EU.ADJ_SCOPE];
2256                         }
2257                     }
2258                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2259                     unloadListeners[i] = null;
2260                     l = null;
2261                     scope = null;
2262                 }
2263             }
2264
2265             unloadListeners = null;
2266
2267             if (listeners && listeners.length > 0) {
2268                 j = listeners.length;
2269                 while (j) {
2270                     index = j - 1;
2271                     l = listeners[index];
2272                     if (l) {
2273                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2274                                 l[EU.FN], index);
2275                     }
2276                     j = j - 1;
2277                 }
2278                 l = null;
2279
2280                 EU.clearCache();
2281             }
2282
2283             EU.doRemove(window, "unload", EU._unload);
2284
2285         },
2286
2287
2288         getScroll: function() {
2289             var dd = document.documentElement, db = document.body;
2290             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2291                 return [dd.scrollTop, dd.scrollLeft];
2292             } else if (db) {
2293                 return [db.scrollTop, db.scrollLeft];
2294             } else {
2295                 return [0, 0];
2296             }
2297         },
2298
2299
2300         doAdd: function () {
2301             if (window.addEventListener) {
2302                 return function(el, eventName, fn, capture) {
2303                     el.addEventListener(eventName, fn, (capture));
2304                 };
2305             } else if (window.attachEvent) {
2306                 return function(el, eventName, fn, capture) {
2307                     el.attachEvent("on" + eventName, fn);
2308                 };
2309             } else {
2310                 return function() {
2311                 };
2312             }
2313         }(),
2314
2315
2316         doRemove: function() {
2317             if (window.removeEventListener) {
2318                 return function (el, eventName, fn, capture) {
2319                     el.removeEventListener(eventName, fn, (capture));
2320                 };
2321             } else if (window.detachEvent) {
2322                 return function (el, eventName, fn) {
2323                     el.detachEvent("on" + eventName, fn);
2324                 };
2325             } else {
2326                 return function() {
2327                 };
2328             }
2329         }()
2330     };
2331     
2332 }();
2333 (function() {     
2334    
2335     var E = Roo.lib.Event;
2336     E.on = E.addListener;
2337     E.un = E.removeListener;
2338
2339     if (document && document.body) {
2340         E._load();
2341     } else {
2342         E.doAdd(window, "load", E._load);
2343     }
2344     E.doAdd(window, "unload", E._unload);
2345     E._tryPreloadAttach();
2346 })();
2347
2348 /*
2349  * Portions of this file are based on pieces of Yahoo User Interface Library
2350  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2351  * YUI licensed under the BSD License:
2352  * http://developer.yahoo.net/yui/license.txt
2353  * <script type="text/javascript">
2354  *
2355  */
2356
2357 (function() {
2358     
2359     Roo.lib.Ajax = {
2360         request : function(method, uri, cb, data, options) {
2361             if(options){
2362                 var hs = options.headers;
2363                 if(hs){
2364                     for(var h in hs){
2365                         if(hs.hasOwnProperty(h)){
2366                             this.initHeader(h, hs[h], false);
2367                         }
2368                     }
2369                 }
2370                 if(options.xmlData){
2371                     this.initHeader('Content-Type', 'text/xml', false);
2372                     method = 'POST';
2373                     data = options.xmlData;
2374                 }
2375             }
2376
2377             return this.asyncRequest(method, uri, cb, data);
2378         },
2379
2380         serializeForm : function(form) {
2381             if(typeof form == 'string') {
2382                 form = (document.getElementById(form) || document.forms[form]);
2383             }
2384
2385             var el, name, val, disabled, data = '', hasSubmit = false;
2386             for (var i = 0; i < form.elements.length; i++) {
2387                 el = form.elements[i];
2388                 disabled = form.elements[i].disabled;
2389                 name = form.elements[i].name;
2390                 val = form.elements[i].value;
2391
2392                 if (!disabled && name){
2393                     switch (el.type)
2394                             {
2395                         case 'select-one':
2396                         case 'select-multiple':
2397                             for (var j = 0; j < el.options.length; j++) {
2398                                 if (el.options[j].selected) {
2399                                     if (Roo.isIE) {
2400                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2401                                     }
2402                                     else {
2403                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2404                                     }
2405                                 }
2406                             }
2407                             break;
2408                         case 'radio':
2409                         case 'checkbox':
2410                             if (el.checked) {
2411                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2412                             }
2413                             break;
2414                         case 'file':
2415
2416                         case undefined:
2417
2418                         case 'reset':
2419
2420                         case 'button':
2421
2422                             break;
2423                         case 'submit':
2424                             if(hasSubmit == false) {
2425                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2426                                 hasSubmit = true;
2427                             }
2428                             break;
2429                         default:
2430                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             break;
2432                     }
2433                 }
2434             }
2435             data = data.substr(0, data.length - 1);
2436             return data;
2437         },
2438
2439         headers:{},
2440
2441         hasHeaders:false,
2442
2443         useDefaultHeader:true,
2444
2445         defaultPostHeader:'application/x-www-form-urlencoded',
2446
2447         useDefaultXhrHeader:true,
2448
2449         defaultXhrHeader:'XMLHttpRequest',
2450
2451         hasDefaultHeaders:true,
2452
2453         defaultHeaders:{},
2454
2455         poll:{},
2456
2457         timeout:{},
2458
2459         pollInterval:50,
2460
2461         transactionId:0,
2462
2463         setProgId:function(id)
2464         {
2465             this.activeX.unshift(id);
2466         },
2467
2468         setDefaultPostHeader:function(b)
2469         {
2470             this.useDefaultHeader = b;
2471         },
2472
2473         setDefaultXhrHeader:function(b)
2474         {
2475             this.useDefaultXhrHeader = b;
2476         },
2477
2478         setPollingInterval:function(i)
2479         {
2480             if (typeof i == 'number' && isFinite(i)) {
2481                 this.pollInterval = i;
2482             }
2483         },
2484
2485         createXhrObject:function(transactionId)
2486         {
2487             var obj,http;
2488             try
2489             {
2490
2491                 http = new XMLHttpRequest();
2492
2493                 obj = { conn:http, tId:transactionId };
2494             }
2495             catch(e)
2496             {
2497                 for (var i = 0; i < this.activeX.length; ++i) {
2498                     try
2499                     {
2500
2501                         http = new ActiveXObject(this.activeX[i]);
2502
2503                         obj = { conn:http, tId:transactionId };
2504                         break;
2505                     }
2506                     catch(e) {
2507                     }
2508                 }
2509             }
2510             finally
2511             {
2512                 return obj;
2513             }
2514         },
2515
2516         getConnectionObject:function()
2517         {
2518             var o;
2519             var tId = this.transactionId;
2520
2521             try
2522             {
2523                 o = this.createXhrObject(tId);
2524                 if (o) {
2525                     this.transactionId++;
2526                 }
2527             }
2528             catch(e) {
2529             }
2530             finally
2531             {
2532                 return o;
2533             }
2534         },
2535
2536         asyncRequest:function(method, uri, callback, postData)
2537         {
2538             var o = this.getConnectionObject();
2539
2540             if (!o) {
2541                 return null;
2542             }
2543             else {
2544                 o.conn.open(method, uri, true);
2545
2546                 if (this.useDefaultXhrHeader) {
2547                     if (!this.defaultHeaders['X-Requested-With']) {
2548                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2549                     }
2550                 }
2551
2552                 if(postData && this.useDefaultHeader){
2553                     this.initHeader('Content-Type', this.defaultPostHeader);
2554                 }
2555
2556                  if (this.hasDefaultHeaders || this.hasHeaders) {
2557                     this.setHeader(o);
2558                 }
2559
2560                 this.handleReadyState(o, callback);
2561                 o.conn.send(postData || null);
2562
2563                 return o;
2564             }
2565         },
2566
2567         handleReadyState:function(o, callback)
2568         {
2569             var oConn = this;
2570
2571             if (callback && callback.timeout) {
2572                 this.timeout[o.tId] = window.setTimeout(function() {
2573                     oConn.abort(o, callback, true);
2574                 }, callback.timeout);
2575             }
2576
2577             this.poll[o.tId] = window.setInterval(
2578                     function() {
2579                         if (o.conn && o.conn.readyState == 4) {
2580                             window.clearInterval(oConn.poll[o.tId]);
2581                             delete oConn.poll[o.tId];
2582
2583                             if(callback && callback.timeout) {
2584                                 window.clearTimeout(oConn.timeout[o.tId]);
2585                                 delete oConn.timeout[o.tId];
2586                             }
2587
2588                             oConn.handleTransactionResponse(o, callback);
2589                         }
2590                     }
2591                     , this.pollInterval);
2592         },
2593
2594         handleTransactionResponse:function(o, callback, isAbort)
2595         {
2596
2597             if (!callback) {
2598                 this.releaseObject(o);
2599                 return;
2600             }
2601
2602             var httpStatus, responseObject;
2603
2604             try
2605             {
2606                 if (o.conn.status !== undefined && o.conn.status != 0) {
2607                     httpStatus = o.conn.status;
2608                 }
2609                 else {
2610                     httpStatus = 13030;
2611                 }
2612             }
2613             catch(e) {
2614
2615
2616                 httpStatus = 13030;
2617             }
2618
2619             if (httpStatus >= 200 && httpStatus < 300) {
2620                 responseObject = this.createResponseObject(o, callback.argument);
2621                 if (callback.success) {
2622                     if (!callback.scope) {
2623                         callback.success(responseObject);
2624                     }
2625                     else {
2626
2627
2628                         callback.success.apply(callback.scope, [responseObject]);
2629                     }
2630                 }
2631             }
2632             else {
2633                 switch (httpStatus) {
2634
2635                     case 12002:
2636                     case 12029:
2637                     case 12030:
2638                     case 12031:
2639                     case 12152:
2640                     case 13030:
2641                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2642                         if (callback.failure) {
2643                             if (!callback.scope) {
2644                                 callback.failure(responseObject);
2645                             }
2646                             else {
2647                                 callback.failure.apply(callback.scope, [responseObject]);
2648                             }
2649                         }
2650                         break;
2651                     default:
2652                         responseObject = this.createResponseObject(o, callback.argument);
2653                         if (callback.failure) {
2654                             if (!callback.scope) {
2655                                 callback.failure(responseObject);
2656                             }
2657                             else {
2658                                 callback.failure.apply(callback.scope, [responseObject]);
2659                             }
2660                         }
2661                 }
2662             }
2663
2664             this.releaseObject(o);
2665             responseObject = null;
2666         },
2667
2668         createResponseObject:function(o, callbackArg)
2669         {
2670             var obj = {};
2671             var headerObj = {};
2672
2673             try
2674             {
2675                 var headerStr = o.conn.getAllResponseHeaders();
2676                 var header = headerStr.split('\n');
2677                 for (var i = 0; i < header.length; i++) {
2678                     var delimitPos = header[i].indexOf(':');
2679                     if (delimitPos != -1) {
2680                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2681                     }
2682                 }
2683             }
2684             catch(e) {
2685             }
2686
2687             obj.tId = o.tId;
2688             obj.status = o.conn.status;
2689             obj.statusText = o.conn.statusText;
2690             obj.getResponseHeader = headerObj;
2691             obj.getAllResponseHeaders = headerStr;
2692             obj.responseText = o.conn.responseText;
2693             obj.responseXML = o.conn.responseXML;
2694
2695             if (typeof callbackArg !== undefined) {
2696                 obj.argument = callbackArg;
2697             }
2698
2699             return obj;
2700         },
2701
2702         createExceptionObject:function(tId, callbackArg, isAbort)
2703         {
2704             var COMM_CODE = 0;
2705             var COMM_ERROR = 'communication failure';
2706             var ABORT_CODE = -1;
2707             var ABORT_ERROR = 'transaction aborted';
2708
2709             var obj = {};
2710
2711             obj.tId = tId;
2712             if (isAbort) {
2713                 obj.status = ABORT_CODE;
2714                 obj.statusText = ABORT_ERROR;
2715             }
2716             else {
2717                 obj.status = COMM_CODE;
2718                 obj.statusText = COMM_ERROR;
2719             }
2720
2721             if (callbackArg) {
2722                 obj.argument = callbackArg;
2723             }
2724
2725             return obj;
2726         },
2727
2728         initHeader:function(label, value, isDefault)
2729         {
2730             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2731
2732             if (headerObj[label] === undefined) {
2733                 headerObj[label] = value;
2734             }
2735             else {
2736
2737
2738                 headerObj[label] = value + "," + headerObj[label];
2739             }
2740
2741             if (isDefault) {
2742                 this.hasDefaultHeaders = true;
2743             }
2744             else {
2745                 this.hasHeaders = true;
2746             }
2747         },
2748
2749
2750         setHeader:function(o)
2751         {
2752             if (this.hasDefaultHeaders) {
2753                 for (var prop in this.defaultHeaders) {
2754                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2755                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2756                     }
2757                 }
2758             }
2759
2760             if (this.hasHeaders) {
2761                 for (var prop in this.headers) {
2762                     if (this.headers.hasOwnProperty(prop)) {
2763                         o.conn.setRequestHeader(prop, this.headers[prop]);
2764                     }
2765                 }
2766                 this.headers = {};
2767                 this.hasHeaders = false;
2768             }
2769         },
2770
2771         resetDefaultHeaders:function() {
2772             delete this.defaultHeaders;
2773             this.defaultHeaders = {};
2774             this.hasDefaultHeaders = false;
2775         },
2776
2777         abort:function(o, callback, isTimeout)
2778         {
2779             if(this.isCallInProgress(o)) {
2780                 o.conn.abort();
2781                 window.clearInterval(this.poll[o.tId]);
2782                 delete this.poll[o.tId];
2783                 if (isTimeout) {
2784                     delete this.timeout[o.tId];
2785                 }
2786
2787                 this.handleTransactionResponse(o, callback, true);
2788
2789                 return true;
2790             }
2791             else {
2792                 return false;
2793             }
2794         },
2795
2796
2797         isCallInProgress:function(o)
2798         {
2799             if (o && o.conn) {
2800                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2801             }
2802             else {
2803
2804                 return false;
2805             }
2806         },
2807
2808
2809         releaseObject:function(o)
2810         {
2811
2812             o.conn = null;
2813
2814             o = null;
2815         },
2816
2817         activeX:[
2818         'MSXML2.XMLHTTP.3.0',
2819         'MSXML2.XMLHTTP',
2820         'Microsoft.XMLHTTP'
2821         ]
2822
2823
2824     };
2825 })();/*
2826  * Portions of this file are based on pieces of Yahoo User Interface Library
2827  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2828  * YUI licensed under the BSD License:
2829  * http://developer.yahoo.net/yui/license.txt
2830  * <script type="text/javascript">
2831  *
2832  */
2833
2834 Roo.lib.Region = function(t, r, b, l) {
2835     this.top = t;
2836     this[1] = t;
2837     this.right = r;
2838     this.bottom = b;
2839     this.left = l;
2840     this[0] = l;
2841 };
2842
2843
2844 Roo.lib.Region.prototype = {
2845     contains : function(region) {
2846         return ( region.left >= this.left &&
2847                  region.right <= this.right &&
2848                  region.top >= this.top &&
2849                  region.bottom <= this.bottom    );
2850
2851     },
2852
2853     getArea : function() {
2854         return ( (this.bottom - this.top) * (this.right - this.left) );
2855     },
2856
2857     intersect : function(region) {
2858         var t = Math.max(this.top, region.top);
2859         var r = Math.min(this.right, region.right);
2860         var b = Math.min(this.bottom, region.bottom);
2861         var l = Math.max(this.left, region.left);
2862
2863         if (b >= t && r >= l) {
2864             return new Roo.lib.Region(t, r, b, l);
2865         } else {
2866             return null;
2867         }
2868     },
2869     union : function(region) {
2870         var t = Math.min(this.top, region.top);
2871         var r = Math.max(this.right, region.right);
2872         var b = Math.max(this.bottom, region.bottom);
2873         var l = Math.min(this.left, region.left);
2874
2875         return new Roo.lib.Region(t, r, b, l);
2876     },
2877
2878     adjust : function(t, l, b, r) {
2879         this.top += t;
2880         this.left += l;
2881         this.right += r;
2882         this.bottom += b;
2883         return this;
2884     }
2885 };
2886
2887 Roo.lib.Region.getRegion = function(el) {
2888     var p = Roo.lib.Dom.getXY(el);
2889
2890     var t = p[1];
2891     var r = p[0] + el.offsetWidth;
2892     var b = p[1] + el.offsetHeight;
2893     var l = p[0];
2894
2895     return new Roo.lib.Region(t, r, b, l);
2896 };
2897 /*
2898  * Portions of this file are based on pieces of Yahoo User Interface Library
2899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2900  * YUI licensed under the BSD License:
2901  * http://developer.yahoo.net/yui/license.txt
2902  * <script type="text/javascript">
2903  *
2904  */
2905 //@@dep Roo.lib.Region
2906
2907
2908 Roo.lib.Point = function(x, y) {
2909     if (x instanceof Array) {
2910         y = x[1];
2911         x = x[0];
2912     }
2913     this.x = this.right = this.left = this[0] = x;
2914     this.y = this.top = this.bottom = this[1] = y;
2915 };
2916
2917 Roo.lib.Point.prototype = new Roo.lib.Region();
2918 /*
2919  * Portions of this file are based on pieces of Yahoo User Interface Library
2920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2921  * YUI licensed under the BSD License:
2922  * http://developer.yahoo.net/yui/license.txt
2923  * <script type="text/javascript">
2924  *
2925  */
2926  
2927 (function() {   
2928
2929     Roo.lib.Anim = {
2930         scroll : function(el, args, duration, easing, cb, scope) {
2931             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2932         },
2933
2934         motion : function(el, args, duration, easing, cb, scope) {
2935             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2936         },
2937
2938         color : function(el, args, duration, easing, cb, scope) {
2939             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2940         },
2941
2942         run : function(el, args, duration, easing, cb, scope, type) {
2943             type = type || Roo.lib.AnimBase;
2944             if (typeof easing == "string") {
2945                 easing = Roo.lib.Easing[easing];
2946             }
2947             var anim = new type(el, args, duration, easing);
2948             anim.animateX(function() {
2949                 Roo.callback(cb, scope);
2950             });
2951             return anim;
2952         }
2953     };
2954 })();/*
2955  * Portions of this file are based on pieces of Yahoo User Interface Library
2956  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2957  * YUI licensed under the BSD License:
2958  * http://developer.yahoo.net/yui/license.txt
2959  * <script type="text/javascript">
2960  *
2961  */
2962
2963 (function() {    
2964     var libFlyweight;
2965     
2966     function fly(el) {
2967         if (!libFlyweight) {
2968             libFlyweight = new Roo.Element.Flyweight();
2969         }
2970         libFlyweight.dom = el;
2971         return libFlyweight;
2972     }
2973
2974     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2975     
2976    
2977     
2978     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2979         if (el) {
2980             this.init(el, attributes, duration, method);
2981         }
2982     };
2983
2984     Roo.lib.AnimBase.fly = fly;
2985     
2986     
2987     
2988     Roo.lib.AnimBase.prototype = {
2989
2990         toString: function() {
2991             var el = this.getEl();
2992             var id = el.id || el.tagName;
2993             return ("Anim " + id);
2994         },
2995
2996         patterns: {
2997             noNegatives:        /width|height|opacity|padding/i,
2998             offsetAttribute:  /^((width|height)|(top|left))$/,
2999             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3000             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3001         },
3002
3003
3004         doMethod: function(attr, start, end) {
3005             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3006         },
3007
3008
3009         setAttribute: function(attr, val, unit) {
3010             if (this.patterns.noNegatives.test(attr)) {
3011                 val = (val > 0) ? val : 0;
3012             }
3013
3014             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3015         },
3016
3017
3018         getAttribute: function(attr) {
3019             var el = this.getEl();
3020             var val = fly(el).getStyle(attr);
3021
3022             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3023                 return parseFloat(val);
3024             }
3025
3026             var a = this.patterns.offsetAttribute.exec(attr) || [];
3027             var pos = !!( a[3] );
3028             var box = !!( a[2] );
3029
3030
3031             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3032                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3033             } else {
3034                 val = 0;
3035             }
3036
3037             return val;
3038         },
3039
3040
3041         getDefaultUnit: function(attr) {
3042             if (this.patterns.defaultUnit.test(attr)) {
3043                 return 'px';
3044             }
3045
3046             return '';
3047         },
3048
3049         animateX : function(callback, scope) {
3050             var f = function() {
3051                 this.onComplete.removeListener(f);
3052                 if (typeof callback == "function") {
3053                     callback.call(scope || this, this);
3054                 }
3055             };
3056             this.onComplete.addListener(f, this);
3057             this.animate();
3058         },
3059
3060
3061         setRuntimeAttribute: function(attr) {
3062             var start;
3063             var end;
3064             var attributes = this.attributes;
3065
3066             this.runtimeAttributes[attr] = {};
3067
3068             var isset = function(prop) {
3069                 return (typeof prop !== 'undefined');
3070             };
3071
3072             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3073                 return false;
3074             }
3075
3076             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3077
3078
3079             if (isset(attributes[attr]['to'])) {
3080                 end = attributes[attr]['to'];
3081             } else if (isset(attributes[attr]['by'])) {
3082                 if (start.constructor == Array) {
3083                     end = [];
3084                     for (var i = 0, len = start.length; i < len; ++i) {
3085                         end[i] = start[i] + attributes[attr]['by'][i];
3086                     }
3087                 } else {
3088                     end = start + attributes[attr]['by'];
3089                 }
3090             }
3091
3092             this.runtimeAttributes[attr].start = start;
3093             this.runtimeAttributes[attr].end = end;
3094
3095
3096             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3097         },
3098
3099
3100         init: function(el, attributes, duration, method) {
3101
3102             var isAnimated = false;
3103
3104
3105             var startTime = null;
3106
3107
3108             var actualFrames = 0;
3109
3110
3111             el = Roo.getDom(el);
3112
3113
3114             this.attributes = attributes || {};
3115
3116
3117             this.duration = duration || 1;
3118
3119
3120             this.method = method || Roo.lib.Easing.easeNone;
3121
3122
3123             this.useSeconds = true;
3124
3125
3126             this.currentFrame = 0;
3127
3128
3129             this.totalFrames = Roo.lib.AnimMgr.fps;
3130
3131
3132             this.getEl = function() {
3133                 return el;
3134             };
3135
3136
3137             this.isAnimated = function() {
3138                 return isAnimated;
3139             };
3140
3141
3142             this.getStartTime = function() {
3143                 return startTime;
3144             };
3145
3146             this.runtimeAttributes = {};
3147
3148
3149             this.animate = function() {
3150                 if (this.isAnimated()) {
3151                     return false;
3152                 }
3153
3154                 this.currentFrame = 0;
3155
3156                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3157
3158                 Roo.lib.AnimMgr.registerElement(this);
3159             };
3160
3161
3162             this.stop = function(finish) {
3163                 if (finish) {
3164                     this.currentFrame = this.totalFrames;
3165                     this._onTween.fire();
3166                 }
3167                 Roo.lib.AnimMgr.stop(this);
3168             };
3169
3170             var onStart = function() {
3171                 this.onStart.fire();
3172
3173                 this.runtimeAttributes = {};
3174                 for (var attr in this.attributes) {
3175                     this.setRuntimeAttribute(attr);
3176                 }
3177
3178                 isAnimated = true;
3179                 actualFrames = 0;
3180                 startTime = new Date();
3181             };
3182
3183
3184             var onTween = function() {
3185                 var data = {
3186                     duration: new Date() - this.getStartTime(),
3187                     currentFrame: this.currentFrame
3188                 };
3189
3190                 data.toString = function() {
3191                     return (
3192                             'duration: ' + data.duration +
3193                             ', currentFrame: ' + data.currentFrame
3194                             );
3195                 };
3196
3197                 this.onTween.fire(data);
3198
3199                 var runtimeAttributes = this.runtimeAttributes;
3200
3201                 for (var attr in runtimeAttributes) {
3202                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3203                 }
3204
3205                 actualFrames += 1;
3206             };
3207
3208             var onComplete = function() {
3209                 var actual_duration = (new Date() - startTime) / 1000 ;
3210
3211                 var data = {
3212                     duration: actual_duration,
3213                     frames: actualFrames,
3214                     fps: actualFrames / actual_duration
3215                 };
3216
3217                 data.toString = function() {
3218                     return (
3219                             'duration: ' + data.duration +
3220                             ', frames: ' + data.frames +
3221                             ', fps: ' + data.fps
3222                             );
3223                 };
3224
3225                 isAnimated = false;
3226                 actualFrames = 0;
3227                 this.onComplete.fire(data);
3228             };
3229
3230
3231             this._onStart = new Roo.util.Event(this);
3232             this.onStart = new Roo.util.Event(this);
3233             this.onTween = new Roo.util.Event(this);
3234             this._onTween = new Roo.util.Event(this);
3235             this.onComplete = new Roo.util.Event(this);
3236             this._onComplete = new Roo.util.Event(this);
3237             this._onStart.addListener(onStart);
3238             this._onTween.addListener(onTween);
3239             this._onComplete.addListener(onComplete);
3240         }
3241     };
3242 })();
3243 /*
3244  * Portions of this file are based on pieces of Yahoo User Interface Library
3245  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3246  * YUI licensed under the BSD License:
3247  * http://developer.yahoo.net/yui/license.txt
3248  * <script type="text/javascript">
3249  *
3250  */
3251
3252 Roo.lib.AnimMgr = new function() {
3253
3254         var thread = null;
3255
3256
3257         var queue = [];
3258
3259
3260         var tweenCount = 0;
3261
3262
3263         this.fps = 1000;
3264
3265
3266         this.delay = 1;
3267
3268
3269         this.registerElement = function(tween) {
3270             queue[queue.length] = tween;
3271             tweenCount += 1;
3272             tween._onStart.fire();
3273             this.start();
3274         };
3275
3276
3277         this.unRegister = function(tween, index) {
3278             tween._onComplete.fire();
3279             index = index || getIndex(tween);
3280             if (index != -1) {
3281                 queue.splice(index, 1);
3282             }
3283
3284             tweenCount -= 1;
3285             if (tweenCount <= 0) {
3286                 this.stop();
3287             }
3288         };
3289
3290
3291         this.start = function() {
3292             if (thread === null) {
3293                 thread = setInterval(this.run, this.delay);
3294             }
3295         };
3296
3297
3298         this.stop = function(tween) {
3299             if (!tween) {
3300                 clearInterval(thread);
3301
3302                 for (var i = 0, len = queue.length; i < len; ++i) {
3303                     if (queue[0].isAnimated()) {
3304                         this.unRegister(queue[0], 0);
3305                     }
3306                 }
3307
3308                 queue = [];
3309                 thread = null;
3310                 tweenCount = 0;
3311             }
3312             else {
3313                 this.unRegister(tween);
3314             }
3315         };
3316
3317
3318         this.run = function() {
3319             for (var i = 0, len = queue.length; i < len; ++i) {
3320                 var tween = queue[i];
3321                 if (!tween || !tween.isAnimated()) {
3322                     continue;
3323                 }
3324
3325                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3326                 {
3327                     tween.currentFrame += 1;
3328
3329                     if (tween.useSeconds) {
3330                         correctFrame(tween);
3331                     }
3332                     tween._onTween.fire();
3333                 }
3334                 else {
3335                     Roo.lib.AnimMgr.stop(tween, i);
3336                 }
3337             }
3338         };
3339
3340         var getIndex = function(anim) {
3341             for (var i = 0, len = queue.length; i < len; ++i) {
3342                 if (queue[i] == anim) {
3343                     return i;
3344                 }
3345             }
3346             return -1;
3347         };
3348
3349
3350         var correctFrame = function(tween) {
3351             var frames = tween.totalFrames;
3352             var frame = tween.currentFrame;
3353             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3354             var elapsed = (new Date() - tween.getStartTime());
3355             var tweak = 0;
3356
3357             if (elapsed < tween.duration * 1000) {
3358                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3359             } else {
3360                 tweak = frames - (frame + 1);
3361             }
3362             if (tweak > 0 && isFinite(tweak)) {
3363                 if (tween.currentFrame + tweak >= frames) {
3364                     tweak = frames - (frame + 1);
3365                 }
3366
3367                 tween.currentFrame += tweak;
3368             }
3369         };
3370     };/*
3371  * Portions of this file are based on pieces of Yahoo User Interface Library
3372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3373  * YUI licensed under the BSD License:
3374  * http://developer.yahoo.net/yui/license.txt
3375  * <script type="text/javascript">
3376  *
3377  */
3378 Roo.lib.Bezier = new function() {
3379
3380         this.getPosition = function(points, t) {
3381             var n = points.length;
3382             var tmp = [];
3383
3384             for (var i = 0; i < n; ++i) {
3385                 tmp[i] = [points[i][0], points[i][1]];
3386             }
3387
3388             for (var j = 1; j < n; ++j) {
3389                 for (i = 0; i < n - j; ++i) {
3390                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3391                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3392                 }
3393             }
3394
3395             return [ tmp[0][0], tmp[0][1] ];
3396
3397         };
3398     };/*
3399  * Portions of this file are based on pieces of Yahoo User Interface Library
3400  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3401  * YUI licensed under the BSD License:
3402  * http://developer.yahoo.net/yui/license.txt
3403  * <script type="text/javascript">
3404  *
3405  */
3406 (function() {
3407
3408     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3409         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3410     };
3411
3412     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3413
3414     var fly = Roo.lib.AnimBase.fly;
3415     var Y = Roo.lib;
3416     var superclass = Y.ColorAnim.superclass;
3417     var proto = Y.ColorAnim.prototype;
3418
3419     proto.toString = function() {
3420         var el = this.getEl();
3421         var id = el.id || el.tagName;
3422         return ("ColorAnim " + id);
3423     };
3424
3425     proto.patterns.color = /color$/i;
3426     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3427     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3428     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3429     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3430
3431
3432     proto.parseColor = function(s) {
3433         if (s.length == 3) {
3434             return s;
3435         }
3436
3437         var c = this.patterns.hex.exec(s);
3438         if (c && c.length == 4) {
3439             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3440         }
3441
3442         c = this.patterns.rgb.exec(s);
3443         if (c && c.length == 4) {
3444             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3445         }
3446
3447         c = this.patterns.hex3.exec(s);
3448         if (c && c.length == 4) {
3449             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3450         }
3451
3452         return null;
3453     };
3454     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3455     proto.getAttribute = function(attr) {
3456         var el = this.getEl();
3457         if (this.patterns.color.test(attr)) {
3458             var val = fly(el).getStyle(attr);
3459
3460             if (this.patterns.transparent.test(val)) {
3461                 var parent = el.parentNode;
3462                 val = fly(parent).getStyle(attr);
3463
3464                 while (parent && this.patterns.transparent.test(val)) {
3465                     parent = parent.parentNode;
3466                     val = fly(parent).getStyle(attr);
3467                     if (parent.tagName.toUpperCase() == 'HTML') {
3468                         val = '#fff';
3469                     }
3470                 }
3471             }
3472         } else {
3473             val = superclass.getAttribute.call(this, attr);
3474         }
3475
3476         return val;
3477     };
3478     proto.getAttribute = function(attr) {
3479         var el = this.getEl();
3480         if (this.patterns.color.test(attr)) {
3481             var val = fly(el).getStyle(attr);
3482
3483             if (this.patterns.transparent.test(val)) {
3484                 var parent = el.parentNode;
3485                 val = fly(parent).getStyle(attr);
3486
3487                 while (parent && this.patterns.transparent.test(val)) {
3488                     parent = parent.parentNode;
3489                     val = fly(parent).getStyle(attr);
3490                     if (parent.tagName.toUpperCase() == 'HTML') {
3491                         val = '#fff';
3492                     }
3493                 }
3494             }
3495         } else {
3496             val = superclass.getAttribute.call(this, attr);
3497         }
3498
3499         return val;
3500     };
3501
3502     proto.doMethod = function(attr, start, end) {
3503         var val;
3504
3505         if (this.patterns.color.test(attr)) {
3506             val = [];
3507             for (var i = 0, len = start.length; i < len; ++i) {
3508                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3509             }
3510
3511             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3512         }
3513         else {
3514             val = superclass.doMethod.call(this, attr, start, end);
3515         }
3516
3517         return val;
3518     };
3519
3520     proto.setRuntimeAttribute = function(attr) {
3521         superclass.setRuntimeAttribute.call(this, attr);
3522
3523         if (this.patterns.color.test(attr)) {
3524             var attributes = this.attributes;
3525             var start = this.parseColor(this.runtimeAttributes[attr].start);
3526             var end = this.parseColor(this.runtimeAttributes[attr].end);
3527
3528             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3529                 end = this.parseColor(attributes[attr].by);
3530
3531                 for (var i = 0, len = start.length; i < len; ++i) {
3532                     end[i] = start[i] + end[i];
3533                 }
3534             }
3535
3536             this.runtimeAttributes[attr].start = start;
3537             this.runtimeAttributes[attr].end = end;
3538         }
3539     };
3540 })();
3541
3542 /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Easing = {
3551
3552
3553     easeNone: function (t, b, c, d) {
3554         return c * t / d + b;
3555     },
3556
3557
3558     easeIn: function (t, b, c, d) {
3559         return c * (t /= d) * t + b;
3560     },
3561
3562
3563     easeOut: function (t, b, c, d) {
3564         return -c * (t /= d) * (t - 2) + b;
3565     },
3566
3567
3568     easeBoth: function (t, b, c, d) {
3569         if ((t /= d / 2) < 1) {
3570             return c / 2 * t * t + b;
3571         }
3572
3573         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3574     },
3575
3576
3577     easeInStrong: function (t, b, c, d) {
3578         return c * (t /= d) * t * t * t + b;
3579     },
3580
3581
3582     easeOutStrong: function (t, b, c, d) {
3583         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3584     },
3585
3586
3587     easeBothStrong: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t * t * t + b;
3590         }
3591
3592         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3593     },
3594
3595
3596
3597     elasticIn: function (t, b, c, d, a, p) {
3598         if (t == 0) {
3599             return b;
3600         }
3601         if ((t /= d) == 1) {
3602             return b + c;
3603         }
3604         if (!p) {
3605             p = d * .3;
3606         }
3607
3608         if (!a || a < Math.abs(c)) {
3609             a = c;
3610             var s = p / 4;
3611         }
3612         else {
3613             var s = p / (2 * Math.PI) * Math.asin(c / a);
3614         }
3615
3616         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3617     },
3618
3619
3620     elasticOut: function (t, b, c, d, a, p) {
3621         if (t == 0) {
3622             return b;
3623         }
3624         if ((t /= d) == 1) {
3625             return b + c;
3626         }
3627         if (!p) {
3628             p = d * .3;
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3640     },
3641
3642
3643     elasticBoth: function (t, b, c, d, a, p) {
3644         if (t == 0) {
3645             return b;
3646         }
3647
3648         if ((t /= d / 2) == 2) {
3649             return b + c;
3650         }
3651
3652         if (!p) {
3653             p = d * (.3 * 1.5);
3654         }
3655
3656         if (!a || a < Math.abs(c)) {
3657             a = c;
3658             var s = p / 4;
3659         }
3660         else {
3661             var s = p / (2 * Math.PI) * Math.asin(c / a);
3662         }
3663
3664         if (t < 1) {
3665             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3666                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3667         }
3668         return a * Math.pow(2, -10 * (t -= 1)) *
3669                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3670     },
3671
3672
3673
3674     backIn: function (t, b, c, d, s) {
3675         if (typeof s == 'undefined') {
3676             s = 1.70158;
3677         }
3678         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3679     },
3680
3681
3682     backOut: function (t, b, c, d, s) {
3683         if (typeof s == 'undefined') {
3684             s = 1.70158;
3685         }
3686         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3687     },
3688
3689
3690     backBoth: function (t, b, c, d, s) {
3691         if (typeof s == 'undefined') {
3692             s = 1.70158;
3693         }
3694
3695         if ((t /= d / 2 ) < 1) {
3696             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3697         }
3698         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3699     },
3700
3701
3702     bounceIn: function (t, b, c, d) {
3703         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3704     },
3705
3706
3707     bounceOut: function (t, b, c, d) {
3708         if ((t /= d) < (1 / 2.75)) {
3709             return c * (7.5625 * t * t) + b;
3710         } else if (t < (2 / 2.75)) {
3711             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3712         } else if (t < (2.5 / 2.75)) {
3713             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3714         }
3715         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3716     },
3717
3718
3719     bounceBoth: function (t, b, c, d) {
3720         if (t < d / 2) {
3721             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3722         }
3723         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3724     }
3725 };/*
3726  * Portions of this file are based on pieces of Yahoo User Interface Library
3727  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3728  * YUI licensed under the BSD License:
3729  * http://developer.yahoo.net/yui/license.txt
3730  * <script type="text/javascript">
3731  *
3732  */
3733     (function() {
3734         Roo.lib.Motion = function(el, attributes, duration, method) {
3735             if (el) {
3736                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3737             }
3738         };
3739
3740         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3741
3742
3743         var Y = Roo.lib;
3744         var superclass = Y.Motion.superclass;
3745         var proto = Y.Motion.prototype;
3746
3747         proto.toString = function() {
3748             var el = this.getEl();
3749             var id = el.id || el.tagName;
3750             return ("Motion " + id);
3751         };
3752
3753         proto.patterns.points = /^points$/i;
3754
3755         proto.setAttribute = function(attr, val, unit) {
3756             if (this.patterns.points.test(attr)) {
3757                 unit = unit || 'px';
3758                 superclass.setAttribute.call(this, 'left', val[0], unit);
3759                 superclass.setAttribute.call(this, 'top', val[1], unit);
3760             } else {
3761                 superclass.setAttribute.call(this, attr, val, unit);
3762             }
3763         };
3764
3765         proto.getAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var val = [
3768                         superclass.getAttribute.call(this, 'left'),
3769                         superclass.getAttribute.call(this, 'top')
3770                         ];
3771             } else {
3772                 val = superclass.getAttribute.call(this, attr);
3773             }
3774
3775             return val;
3776         };
3777
3778         proto.doMethod = function(attr, start, end) {
3779             var val = null;
3780
3781             if (this.patterns.points.test(attr)) {
3782                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3783                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3784             } else {
3785                 val = superclass.doMethod.call(this, attr, start, end);
3786             }
3787             return val;
3788         };
3789
3790         proto.setRuntimeAttribute = function(attr) {
3791             if (this.patterns.points.test(attr)) {
3792                 var el = this.getEl();
3793                 var attributes = this.attributes;
3794                 var start;
3795                 var control = attributes['points']['control'] || [];
3796                 var end;
3797                 var i, len;
3798
3799                 if (control.length > 0 && !(control[0] instanceof Array)) {
3800                     control = [control];
3801                 } else {
3802                     var tmp = [];
3803                     for (i = 0,len = control.length; i < len; ++i) {
3804                         tmp[i] = control[i];
3805                     }
3806                     control = tmp;
3807                 }
3808
3809                 Roo.fly(el).position();
3810
3811                 if (isset(attributes['points']['from'])) {
3812                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3813                 }
3814                 else {
3815                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3816                 }
3817
3818                 start = this.getAttribute('points');
3819
3820
3821                 if (isset(attributes['points']['to'])) {
3822                     end = translateValues.call(this, attributes['points']['to'], start);
3823
3824                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3825                     for (i = 0,len = control.length; i < len; ++i) {
3826                         control[i] = translateValues.call(this, control[i], start);
3827                     }
3828
3829
3830                 } else if (isset(attributes['points']['by'])) {
3831                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3832
3833                     for (i = 0,len = control.length; i < len; ++i) {
3834                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3835                     }
3836                 }
3837
3838                 this.runtimeAttributes[attr] = [start];
3839
3840                 if (control.length > 0) {
3841                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3842                 }
3843
3844                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3845             }
3846             else {
3847                 superclass.setRuntimeAttribute.call(this, attr);
3848             }
3849         };
3850
3851         var translateValues = function(val, start) {
3852             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3853             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3854
3855             return val;
3856         };
3857
3858         var isset = function(prop) {
3859             return (typeof prop !== 'undefined');
3860         };
3861     })();
3862 /*
3863  * Portions of this file are based on pieces of Yahoo User Interface Library
3864  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3865  * YUI licensed under the BSD License:
3866  * http://developer.yahoo.net/yui/license.txt
3867  * <script type="text/javascript">
3868  *
3869  */
3870     (function() {
3871         Roo.lib.Scroll = function(el, attributes, duration, method) {
3872             if (el) {
3873                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3874             }
3875         };
3876
3877         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3878
3879
3880         var Y = Roo.lib;
3881         var superclass = Y.Scroll.superclass;
3882         var proto = Y.Scroll.prototype;
3883
3884         proto.toString = function() {
3885             var el = this.getEl();
3886             var id = el.id || el.tagName;
3887             return ("Scroll " + id);
3888         };
3889
3890         proto.doMethod = function(attr, start, end) {
3891             var val = null;
3892
3893             if (attr == 'scroll') {
3894                 val = [
3895                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3896                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3897                         ];
3898
3899             } else {
3900                 val = superclass.doMethod.call(this, attr, start, end);
3901             }
3902             return val;
3903         };
3904
3905         proto.getAttribute = function(attr) {
3906             var val = null;
3907             var el = this.getEl();
3908
3909             if (attr == 'scroll') {
3910                 val = [ el.scrollLeft, el.scrollTop ];
3911             } else {
3912                 val = superclass.getAttribute.call(this, attr);
3913             }
3914
3915             return val;
3916         };
3917
3918         proto.setAttribute = function(attr, val, unit) {
3919             var el = this.getEl();
3920
3921             if (attr == 'scroll') {
3922                 el.scrollLeft = val[0];
3923                 el.scrollTop = val[1];
3924             } else {
3925                 superclass.setAttribute.call(this, attr, val, unit);
3926             }
3927         };
3928     })();
3929 /*
3930  * Based on:
3931  * Ext JS Library 1.1.1
3932  * Copyright(c) 2006-2007, Ext JS, LLC.
3933  *
3934  * Originally Released Under LGPL - original licence link has changed is not relivant.
3935  *
3936  * Fork - LGPL
3937  * <script type="text/javascript">
3938  */
3939  
3940
3941 /**
3942  * @class Roo.DomHelper
3943  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3944  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3945  * @singleton
3946  */
3947 Roo.DomHelper = function(){
3948     var tempTableEl = null;
3949     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3950     var tableRe = /^table|tbody|tr|td$/i;
3951     var xmlns = {};
3952     // build as innerHTML where available
3953     /** @ignore */
3954     var createHtml = function(o){
3955         if(typeof o == 'string'){
3956             return o;
3957         }
3958         var b = "";
3959         if(!o.tag){
3960             o.tag = "div";
3961         }
3962         b += "<" + o.tag;
3963         for(var attr in o){
3964             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3965             if(attr == "style"){
3966                 var s = o["style"];
3967                 if(typeof s == "function"){
3968                     s = s.call();
3969                 }
3970                 if(typeof s == "string"){
3971                     b += ' style="' + s + '"';
3972                 }else if(typeof s == "object"){
3973                     b += ' style="';
3974                     for(var key in s){
3975                         if(typeof s[key] != "function"){
3976                             b += key + ":" + s[key] + ";";
3977                         }
3978                     }
3979                     b += '"';
3980                 }
3981             }else{
3982                 if(attr == "cls"){
3983                     b += ' class="' + o["cls"] + '"';
3984                 }else if(attr == "htmlFor"){
3985                     b += ' for="' + o["htmlFor"] + '"';
3986                 }else{
3987                     b += " " + attr + '="' + o[attr] + '"';
3988                 }
3989             }
3990         }
3991         if(emptyTags.test(o.tag)){
3992             b += "/>";
3993         }else{
3994             b += ">";
3995             var cn = o.children || o.cn;
3996             if(cn){
3997                 //http://bugs.kde.org/show_bug.cgi?id=71506
3998                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3999                     for(var i = 0, len = cn.length; i < len; i++) {
4000                         b += createHtml(cn[i], b);
4001                     }
4002                 }else{
4003                     b += createHtml(cn, b);
4004                 }
4005             }
4006             if(o.html){
4007                 b += o.html;
4008             }
4009             b += "</" + o.tag + ">";
4010         }
4011         return b;
4012     };
4013
4014     // build as dom
4015     /** @ignore */
4016     var createDom = function(o, parentNode){
4017          
4018         // defininition craeted..
4019         var ns = false;
4020         if (o.ns && o.ns != 'html') {
4021                
4022             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4023                 xmlns[o.ns] = o.xmlns;
4024                 ns = o.xmlns;
4025             }
4026             if (typeof(xmlns[o.ns]) == 'undefined') {
4027                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4028             }
4029             ns = xmlns[o.ns];
4030         }
4031         
4032         
4033         if (typeof(o) == 'string') {
4034             return parentNode.appendChild(document.createTextNode(o));
4035         }
4036         o.tag = o.tag || div;
4037         if (o.ns && Roo.isIE) {
4038             ns = false;
4039             o.tag = o.ns + ':' + o.tag;
4040             
4041         }
4042         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4043         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4044         for(var attr in o){
4045             
4046             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4047                     attr == "style" || typeof o[attr] == "function") continue;
4048                     
4049             if(attr=="cls" && Roo.isIE){
4050                 el.className = o["cls"];
4051             }else{
4052                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4053                 else el[attr] = o[attr];
4054             }
4055         }
4056         Roo.DomHelper.applyStyles(el, o.style);
4057         var cn = o.children || o.cn;
4058         if(cn){
4059             //http://bugs.kde.org/show_bug.cgi?id=71506
4060              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4061                 for(var i = 0, len = cn.length; i < len; i++) {
4062                     createDom(cn[i], el);
4063                 }
4064             }else{
4065                 createDom(cn, el);
4066             }
4067         }
4068         if(o.html){
4069             el.innerHTML = o.html;
4070         }
4071         if(parentNode){
4072            parentNode.appendChild(el);
4073         }
4074         return el;
4075     };
4076
4077     var ieTable = function(depth, s, h, e){
4078         tempTableEl.innerHTML = [s, h, e].join('');
4079         var i = -1, el = tempTableEl;
4080         while(++i < depth){
4081             el = el.firstChild;
4082         }
4083         return el;
4084     };
4085
4086     // kill repeat to save bytes
4087     var ts = '<table>',
4088         te = '</table>',
4089         tbs = ts+'<tbody>',
4090         tbe = '</tbody>'+te,
4091         trs = tbs + '<tr>',
4092         tre = '</tr>'+tbe;
4093
4094     /**
4095      * @ignore
4096      * Nasty code for IE's broken table implementation
4097      */
4098     var insertIntoTable = function(tag, where, el, html){
4099         if(!tempTableEl){
4100             tempTableEl = document.createElement('div');
4101         }
4102         var node;
4103         var before = null;
4104         if(tag == 'td'){
4105             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4106                 return;
4107             }
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111             } else{
4112                 before = el.nextSibling;
4113                 el = el.parentNode;
4114             }
4115             node = ieTable(4, trs, html, tre);
4116         }
4117         else if(tag == 'tr'){
4118             if(where == 'beforebegin'){
4119                 before = el;
4120                 el = el.parentNode;
4121                 node = ieTable(3, tbs, html, tbe);
4122             } else if(where == 'afterend'){
4123                 before = el.nextSibling;
4124                 el = el.parentNode;
4125                 node = ieTable(3, tbs, html, tbe);
4126             } else{ // INTO a TR
4127                 if(where == 'afterbegin'){
4128                     before = el.firstChild;
4129                 }
4130                 node = ieTable(4, trs, html, tre);
4131             }
4132         } else if(tag == 'tbody'){
4133             if(where == 'beforebegin'){
4134                 before = el;
4135                 el = el.parentNode;
4136                 node = ieTable(2, ts, html, te);
4137             } else if(where == 'afterend'){
4138                 before = el.nextSibling;
4139                 el = el.parentNode;
4140                 node = ieTable(2, ts, html, te);
4141             } else{
4142                 if(where == 'afterbegin'){
4143                     before = el.firstChild;
4144                 }
4145                 node = ieTable(3, tbs, html, tbe);
4146             }
4147         } else{ // TABLE
4148             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4149                 return;
4150             }
4151             if(where == 'afterbegin'){
4152                 before = el.firstChild;
4153             }
4154             node = ieTable(2, ts, html, te);
4155         }
4156         el.insertBefore(node, before);
4157         return node;
4158     };
4159
4160     return {
4161     /** True to force the use of DOM instead of html fragments @type Boolean */
4162     useDom : false,
4163
4164     /**
4165      * Returns the markup for the passed Element(s) config
4166      * @param {Object} o The Dom object spec (and children)
4167      * @return {String}
4168      */
4169     markup : function(o){
4170         return createHtml(o);
4171     },
4172
4173     /**
4174      * Applies a style specification to an element
4175      * @param {String/HTMLElement} el The element to apply styles to
4176      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4177      * a function which returns such a specification.
4178      */
4179     applyStyles : function(el, styles){
4180         if(styles){
4181            el = Roo.fly(el);
4182            if(typeof styles == "string"){
4183                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4184                var matches;
4185                while ((matches = re.exec(styles)) != null){
4186                    el.setStyle(matches[1], matches[2]);
4187                }
4188            }else if (typeof styles == "object"){
4189                for (var style in styles){
4190                   el.setStyle(style, styles[style]);
4191                }
4192            }else if (typeof styles == "function"){
4193                 Roo.DomHelper.applyStyles(el, styles.call());
4194            }
4195         }
4196     },
4197
4198     /**
4199      * Inserts an HTML fragment into the Dom
4200      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4201      * @param {HTMLElement} el The context element
4202      * @param {String} html The HTML fragmenet
4203      * @return {HTMLElement} The new node
4204      */
4205     insertHtml : function(where, el, html){
4206         where = where.toLowerCase();
4207         if(el.insertAdjacentHTML){
4208             if(tableRe.test(el.tagName)){
4209                 var rs;
4210                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4211                     return rs;
4212                 }
4213             }
4214             switch(where){
4215                 case "beforebegin":
4216                     el.insertAdjacentHTML('BeforeBegin', html);
4217                     return el.previousSibling;
4218                 case "afterbegin":
4219                     el.insertAdjacentHTML('AfterBegin', html);
4220                     return el.firstChild;
4221                 case "beforeend":
4222                     el.insertAdjacentHTML('BeforeEnd', html);
4223                     return el.lastChild;
4224                 case "afterend":
4225                     el.insertAdjacentHTML('AfterEnd', html);
4226                     return el.nextSibling;
4227             }
4228             throw 'Illegal insertion point -> "' + where + '"';
4229         }
4230         var range = el.ownerDocument.createRange();
4231         var frag;
4232         switch(where){
4233              case "beforebegin":
4234                 range.setStartBefore(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el);
4237                 return el.previousSibling;
4238              case "afterbegin":
4239                 if(el.firstChild){
4240                     range.setStartBefore(el.firstChild);
4241                     frag = range.createContextualFragment(html);
4242                     el.insertBefore(frag, el.firstChild);
4243                     return el.firstChild;
4244                 }else{
4245                     el.innerHTML = html;
4246                     return el.firstChild;
4247                 }
4248             case "beforeend":
4249                 if(el.lastChild){
4250                     range.setStartAfter(el.lastChild);
4251                     frag = range.createContextualFragment(html);
4252                     el.appendChild(frag);
4253                     return el.lastChild;
4254                 }else{
4255                     el.innerHTML = html;
4256                     return el.lastChild;
4257                 }
4258             case "afterend":
4259                 range.setStartAfter(el);
4260                 frag = range.createContextualFragment(html);
4261                 el.parentNode.insertBefore(frag, el.nextSibling);
4262                 return el.nextSibling;
4263             }
4264             throw 'Illegal insertion point -> "' + where + '"';
4265     },
4266
4267     /**
4268      * Creates new Dom element(s) and inserts them before el
4269      * @param {String/HTMLElement/Element} el The context element
4270      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4272      * @return {HTMLElement/Roo.Element} The new node
4273      */
4274     insertBefore : function(el, o, returnElement){
4275         return this.doInsert(el, o, returnElement, "beforeBegin");
4276     },
4277
4278     /**
4279      * Creates new Dom element(s) and inserts them after el
4280      * @param {String/HTMLElement/Element} el The context element
4281      * @param {Object} o The Dom object spec (and children)
4282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4283      * @return {HTMLElement/Roo.Element} The new node
4284      */
4285     insertAfter : function(el, o, returnElement){
4286         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and inserts them as the first child of el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     insertFirst : function(el, o, returnElement){
4297         return this.doInsert(el, o, returnElement, "afterBegin");
4298     },
4299
4300     // private
4301     doInsert : function(el, o, returnElement, pos, sibling){
4302         el = Roo.getDom(el);
4303         var newNode;
4304         if(this.useDom || o.ns){
4305             newNode = createDom(o, null);
4306             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4307         }else{
4308             var html = createHtml(o);
4309             newNode = this.insertHtml(pos, el, html);
4310         }
4311         return returnElement ? Roo.get(newNode, true) : newNode;
4312     },
4313
4314     /**
4315      * Creates new Dom element(s) and appends them to el
4316      * @param {String/HTMLElement/Element} el The context element
4317      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4318      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4319      * @return {HTMLElement/Roo.Element} The new node
4320      */
4321     append : function(el, o, returnElement){
4322         el = Roo.getDom(el);
4323         var newNode;
4324         if(this.useDom || o.ns){
4325             newNode = createDom(o, null);
4326             el.appendChild(newNode);
4327         }else{
4328             var html = createHtml(o);
4329             newNode = this.insertHtml("beforeEnd", el, html);
4330         }
4331         return returnElement ? Roo.get(newNode, true) : newNode;
4332     },
4333
4334     /**
4335      * Creates new Dom element(s) and overwrites the contents of el with them
4336      * @param {String/HTMLElement/Element} el The context element
4337      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4338      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4339      * @return {HTMLElement/Roo.Element} The new node
4340      */
4341     overwrite : function(el, o, returnElement){
4342         el = Roo.getDom(el);
4343         if (o.ns) {
4344           
4345             while (el.childNodes.length) {
4346                 el.removeChild(el.firstChild);
4347             }
4348             createDom(o, el);
4349         } else {
4350             el.innerHTML = createHtml(o);   
4351         }
4352         
4353         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4354     },
4355
4356     /**
4357      * Creates a new Roo.DomHelper.Template from the Dom object spec
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {Roo.DomHelper.Template} The new template
4360      */
4361     createTemplate : function(o){
4362         var html = createHtml(o);
4363         return new Roo.Template(html);
4364     }
4365     };
4366 }();
4367 /*
4368  * Based on:
4369  * Ext JS Library 1.1.1
4370  * Copyright(c) 2006-2007, Ext JS, LLC.
4371  *
4372  * Originally Released Under LGPL - original licence link has changed is not relivant.
4373  *
4374  * Fork - LGPL
4375  * <script type="text/javascript">
4376  */
4377  
4378 /**
4379 * @class Roo.Template
4380 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4381 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4382 * Usage:
4383 <pre><code>
4384 var t = new Roo.Template(
4385     '&lt;div name="{id}"&gt;',
4386         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4387     '&lt;/div&gt;'
4388 );
4389 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4390 </code></pre>
4391 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4392 * @constructor
4393 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4394 */
4395 Roo.Template = function(html){
4396     if(html instanceof Array){
4397         html = html.join("");
4398     }else if(arguments.length > 1){
4399         html = Array.prototype.join.call(arguments, "");
4400     }
4401     /**@private*/
4402     this.html = html;
4403     
4404 };
4405 Roo.Template.prototype = {
4406     /**
4407      * Returns an HTML fragment of this template with the specified values applied.
4408      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4409      * @return {String} The HTML fragment
4410      */
4411     applyTemplate : function(values){
4412         if(this.compiled){
4413             return this.compiled(values);
4414         }
4415         var useF = this.disableFormats !== true;
4416         var fm = Roo.util.Format, tpl = this;
4417         var fn = function(m, name, format, args){
4418             if(format && useF){
4419                 if(format.substr(0, 5) == "this."){
4420                     return tpl.call(format.substr(5), values[name], values);
4421                 }else{
4422                     if(args){
4423                         // quoted values are required for strings in compiled templates, 
4424                         // but for non compiled we need to strip them
4425                         // quoted reversed for jsmin
4426                         var re = /^\s*['"](.*)["']\s*$/;
4427                         args = args.split(',');
4428                         for(var i = 0, len = args.length; i < len; i++){
4429                             args[i] = args[i].replace(re, "$1");
4430                         }
4431                         args = [values[name]].concat(args);
4432                     }else{
4433                         args = [values[name]];
4434                     }
4435                     return fm[format].apply(fm, args);
4436                 }
4437             }else{
4438                 return values[name] !== undefined ? values[name] : "";
4439             }
4440         };
4441         return this.html.replace(this.re, fn);
4442     },
4443     
4444     /**
4445      * Sets the HTML used as the template and optionally compiles it.
4446      * @param {String} html
4447      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4448      * @return {Roo.Template} this
4449      */
4450     set : function(html, compile){
4451         this.html = html;
4452         this.compiled = null;
4453         if(compile){
4454             this.compile();
4455         }
4456         return this;
4457     },
4458     
4459     /**
4460      * True to disable format functions (defaults to false)
4461      * @type Boolean
4462      */
4463     disableFormats : false,
4464     
4465     /**
4466     * The regular expression used to match template variables 
4467     * @type RegExp
4468     * @property 
4469     */
4470     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4471     
4472     /**
4473      * Compiles the template into an internal function, eliminating the RegEx overhead.
4474      * @return {Roo.Template} this
4475      */
4476     compile : function(){
4477         var fm = Roo.util.Format;
4478         var useF = this.disableFormats !== true;
4479         var sep = Roo.isGecko ? "+" : ",";
4480         var fn = function(m, name, format, args){
4481             if(format && useF){
4482                 args = args ? ',' + args : "";
4483                 if(format.substr(0, 5) != "this."){
4484                     format = "fm." + format + '(';
4485                 }else{
4486                     format = 'this.call("'+ format.substr(5) + '", ';
4487                     args = ", values";
4488                 }
4489             }else{
4490                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4491             }
4492             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4493         };
4494         var body;
4495         // branched to use + in gecko and [].join() in others
4496         if(Roo.isGecko){
4497             body = "this.compiled = function(values){ return '" +
4498                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4499                     "';};";
4500         }else{
4501             body = ["this.compiled = function(values){ return ['"];
4502             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4503             body.push("'].join('');};");
4504             body = body.join('');
4505         }
4506         /**
4507          * eval:var:values
4508          * eval:var:fm
4509          */
4510         eval(body);
4511         return this;
4512     },
4513     
4514     // private function used to call members
4515     call : function(fnName, value, allValues){
4516         return this[fnName](value, allValues);
4517     },
4518     
4519     /**
4520      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4521      * @param {String/HTMLElement/Roo.Element} el The context element
4522      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4523      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4524      * @return {HTMLElement/Roo.Element} The new node or Element
4525      */
4526     insertFirst: function(el, values, returnElement){
4527         return this.doInsert('afterBegin', el, values, returnElement);
4528     },
4529
4530     /**
4531      * Applies the supplied values to the template and inserts the new node(s) before el.
4532      * @param {String/HTMLElement/Roo.Element} el The context element
4533      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4534      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4535      * @return {HTMLElement/Roo.Element} The new node or Element
4536      */
4537     insertBefore: function(el, values, returnElement){
4538         return this.doInsert('beforeBegin', el, values, returnElement);
4539     },
4540
4541     /**
4542      * Applies the supplied values to the template and inserts the new node(s) after el.
4543      * @param {String/HTMLElement/Roo.Element} el The context element
4544      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4545      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4546      * @return {HTMLElement/Roo.Element} The new node or Element
4547      */
4548     insertAfter : function(el, values, returnElement){
4549         return this.doInsert('afterEnd', el, values, returnElement);
4550     },
4551     
4552     /**
4553      * Applies the supplied values to the template and appends the new node(s) to el.
4554      * @param {String/HTMLElement/Roo.Element} el The context element
4555      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4556      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4557      * @return {HTMLElement/Roo.Element} The new node or Element
4558      */
4559     append : function(el, values, returnElement){
4560         return this.doInsert('beforeEnd', el, values, returnElement);
4561     },
4562
4563     doInsert : function(where, el, values, returnEl){
4564         el = Roo.getDom(el);
4565         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4566         return returnEl ? Roo.get(newNode, true) : newNode;
4567     },
4568
4569     /**
4570      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4571      * @param {String/HTMLElement/Roo.Element} el The context element
4572      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4573      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4574      * @return {HTMLElement/Roo.Element} The new node or Element
4575      */
4576     overwrite : function(el, values, returnElement){
4577         el = Roo.getDom(el);
4578         el.innerHTML = this.applyTemplate(values);
4579         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4580     }
4581 };
4582 /**
4583  * Alias for {@link #applyTemplate}
4584  * @method
4585  */
4586 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4587
4588 // backwards compat
4589 Roo.DomHelper.Template = Roo.Template;
4590
4591 /**
4592  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4593  * @param {String/HTMLElement} el A DOM element or its id
4594  * @returns {Roo.Template} The created template
4595  * @static
4596  */
4597 Roo.Template.from = function(el){
4598     el = Roo.getDom(el);
4599     return new Roo.Template(el.value || el.innerHTML);
4600 };/*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611
4612 /*
4613  * This is code is also distributed under MIT license for use
4614  * with jQuery and prototype JavaScript libraries.
4615  */
4616 /**
4617  * @class Roo.DomQuery
4618 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4619 <p>
4620 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4621
4622 <p>
4623 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4624 </p>
4625 <h4>Element Selectors:</h4>
4626 <ul class="list">
4627     <li> <b>*</b> any element</li>
4628     <li> <b>E</b> an element with the tag E</li>
4629     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4630     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4631     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4632     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4633 </ul>
4634 <h4>Attribute Selectors:</h4>
4635 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4636 <ul class="list">
4637     <li> <b>E[foo]</b> has an attribute "foo"</li>
4638     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4639     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4640     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4641     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4642     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4643     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4644 </ul>
4645 <h4>Pseudo Classes:</h4>
4646 <ul class="list">
4647     <li> <b>E:first-child</b> E is the first child of its parent</li>
4648     <li> <b>E:last-child</b> E is the last child of its parent</li>
4649     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4650     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4651     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4652     <li> <b>E:only-child</b> E is the only child of its parent</li>
4653     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4654     <li> <b>E:first</b> the first E in the resultset</li>
4655     <li> <b>E:last</b> the last E in the resultset</li>
4656     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4657     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4658     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4659     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4660     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4661     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4662     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4663     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4664     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4665 </ul>
4666 <h4>CSS Value Selectors:</h4>
4667 <ul class="list">
4668     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4669     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4670     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4671     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4672     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4673     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4674 </ul>
4675  * @singleton
4676  */
4677 Roo.DomQuery = function(){
4678     var cache = {}, simpleCache = {}, valueCache = {};
4679     var nonSpace = /\S/;
4680     var trimRe = /^\s+|\s+$/g;
4681     var tplRe = /\{(\d+)\}/g;
4682     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4683     var tagTokenRe = /^(#)?([\w-\*]+)/;
4684     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4685
4686     function child(p, index){
4687         var i = 0;
4688         var n = p.firstChild;
4689         while(n){
4690             if(n.nodeType == 1){
4691                if(++i == index){
4692                    return n;
4693                }
4694             }
4695             n = n.nextSibling;
4696         }
4697         return null;
4698     };
4699
4700     function next(n){
4701         while((n = n.nextSibling) && n.nodeType != 1);
4702         return n;
4703     };
4704
4705     function prev(n){
4706         while((n = n.previousSibling) && n.nodeType != 1);
4707         return n;
4708     };
4709
4710     function children(d){
4711         var n = d.firstChild, ni = -1;
4712             while(n){
4713                 var nx = n.nextSibling;
4714                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4715                     d.removeChild(n);
4716                 }else{
4717                     n.nodeIndex = ++ni;
4718                 }
4719                 n = nx;
4720             }
4721             return this;
4722         };
4723
4724     function byClassName(c, a, v){
4725         if(!v){
4726             return c;
4727         }
4728         var r = [], ri = -1, cn;
4729         for(var i = 0, ci; ci = c[i]; i++){
4730             if((' '+ci.className+' ').indexOf(v) != -1){
4731                 r[++ri] = ci;
4732             }
4733         }
4734         return r;
4735     };
4736
4737     function attrValue(n, attr){
4738         if(!n.tagName && typeof n.length != "undefined"){
4739             n = n[0];
4740         }
4741         if(!n){
4742             return null;
4743         }
4744         if(attr == "for"){
4745             return n.htmlFor;
4746         }
4747         if(attr == "class" || attr == "className"){
4748             return n.className;
4749         }
4750         return n.getAttribute(attr) || n[attr];
4751
4752     };
4753
4754     function getNodes(ns, mode, tagName){
4755         var result = [], ri = -1, cs;
4756         if(!ns){
4757             return result;
4758         }
4759         tagName = tagName || "*";
4760         if(typeof ns.getElementsByTagName != "undefined"){
4761             ns = [ns];
4762         }
4763         if(!mode){
4764             for(var i = 0, ni; ni = ns[i]; i++){
4765                 cs = ni.getElementsByTagName(tagName);
4766                 for(var j = 0, ci; ci = cs[j]; j++){
4767                     result[++ri] = ci;
4768                 }
4769             }
4770         }else if(mode == "/" || mode == ">"){
4771             var utag = tagName.toUpperCase();
4772             for(var i = 0, ni, cn; ni = ns[i]; i++){
4773                 cn = ni.children || ni.childNodes;
4774                 for(var j = 0, cj; cj = cn[j]; j++){
4775                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4776                         result[++ri] = cj;
4777                     }
4778                 }
4779             }
4780         }else if(mode == "+"){
4781             var utag = tagName.toUpperCase();
4782             for(var i = 0, n; n = ns[i]; i++){
4783                 while((n = n.nextSibling) && n.nodeType != 1);
4784                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4785                     result[++ri] = n;
4786                 }
4787             }
4788         }else if(mode == "~"){
4789             for(var i = 0, n; n = ns[i]; i++){
4790                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4791                 if(n){
4792                     result[++ri] = n;
4793                 }
4794             }
4795         }
4796         return result;
4797     };
4798
4799     function concat(a, b){
4800         if(b.slice){
4801             return a.concat(b);
4802         }
4803         for(var i = 0, l = b.length; i < l; i++){
4804             a[a.length] = b[i];
4805         }
4806         return a;
4807     }
4808
4809     function byTag(cs, tagName){
4810         if(cs.tagName || cs == document){
4811             cs = [cs];
4812         }
4813         if(!tagName){
4814             return cs;
4815         }
4816         var r = [], ri = -1;
4817         tagName = tagName.toLowerCase();
4818         for(var i = 0, ci; ci = cs[i]; i++){
4819             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4820                 r[++ri] = ci;
4821             }
4822         }
4823         return r;
4824     };
4825
4826     function byId(cs, attr, id){
4827         if(cs.tagName || cs == document){
4828             cs = [cs];
4829         }
4830         if(!id){
4831             return cs;
4832         }
4833         var r = [], ri = -1;
4834         for(var i = 0,ci; ci = cs[i]; i++){
4835             if(ci && ci.id == id){
4836                 r[++ri] = ci;
4837                 return r;
4838             }
4839         }
4840         return r;
4841     };
4842
4843     function byAttribute(cs, attr, value, op, custom){
4844         var r = [], ri = -1, st = custom=="{";
4845         var f = Roo.DomQuery.operators[op];
4846         for(var i = 0, ci; ci = cs[i]; i++){
4847             var a;
4848             if(st){
4849                 a = Roo.DomQuery.getStyle(ci, attr);
4850             }
4851             else if(attr == "class" || attr == "className"){
4852                 a = ci.className;
4853             }else if(attr == "for"){
4854                 a = ci.htmlFor;
4855             }else if(attr == "href"){
4856                 a = ci.getAttribute("href", 2);
4857             }else{
4858                 a = ci.getAttribute(attr);
4859             }
4860             if((f && f(a, value)) || (!f && a)){
4861                 r[++ri] = ci;
4862             }
4863         }
4864         return r;
4865     };
4866
4867     function byPseudo(cs, name, value){
4868         return Roo.DomQuery.pseudos[name](cs, value);
4869     };
4870
4871     // This is for IE MSXML which does not support expandos.
4872     // IE runs the same speed using setAttribute, however FF slows way down
4873     // and Safari completely fails so they need to continue to use expandos.
4874     var isIE = window.ActiveXObject ? true : false;
4875
4876     // this eval is stop the compressor from
4877     // renaming the variable to something shorter
4878     
4879     /** eval:var:batch */
4880     var batch = 30803; 
4881
4882     var key = 30803;
4883
4884     function nodupIEXml(cs){
4885         var d = ++key;
4886         cs[0].setAttribute("_nodup", d);
4887         var r = [cs[0]];
4888         for(var i = 1, len = cs.length; i < len; i++){
4889             var c = cs[i];
4890             if(!c.getAttribute("_nodup") != d){
4891                 c.setAttribute("_nodup", d);
4892                 r[r.length] = c;
4893             }
4894         }
4895         for(var i = 0, len = cs.length; i < len; i++){
4896             cs[i].removeAttribute("_nodup");
4897         }
4898         return r;
4899     }
4900
4901     function nodup(cs){
4902         if(!cs){
4903             return [];
4904         }
4905         var len = cs.length, c, i, r = cs, cj, ri = -1;
4906         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4907             return cs;
4908         }
4909         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4910             return nodupIEXml(cs);
4911         }
4912         var d = ++key;
4913         cs[0]._nodup = d;
4914         for(i = 1; c = cs[i]; i++){
4915             if(c._nodup != d){
4916                 c._nodup = d;
4917             }else{
4918                 r = [];
4919                 for(var j = 0; j < i; j++){
4920                     r[++ri] = cs[j];
4921                 }
4922                 for(j = i+1; cj = cs[j]; j++){
4923                     if(cj._nodup != d){
4924                         cj._nodup = d;
4925                         r[++ri] = cj;
4926                     }
4927                 }
4928                 return r;
4929             }
4930         }
4931         return r;
4932     }
4933
4934     function quickDiffIEXml(c1, c2){
4935         var d = ++key;
4936         for(var i = 0, len = c1.length; i < len; i++){
4937             c1[i].setAttribute("_qdiff", d);
4938         }
4939         var r = [];
4940         for(var i = 0, len = c2.length; i < len; i++){
4941             if(c2[i].getAttribute("_qdiff") != d){
4942                 r[r.length] = c2[i];
4943             }
4944         }
4945         for(var i = 0, len = c1.length; i < len; i++){
4946            c1[i].removeAttribute("_qdiff");
4947         }
4948         return r;
4949     }
4950
4951     function quickDiff(c1, c2){
4952         var len1 = c1.length;
4953         if(!len1){
4954             return c2;
4955         }
4956         if(isIE && c1[0].selectSingleNode){
4957             return quickDiffIEXml(c1, c2);
4958         }
4959         var d = ++key;
4960         for(var i = 0; i < len1; i++){
4961             c1[i]._qdiff = d;
4962         }
4963         var r = [];
4964         for(var i = 0, len = c2.length; i < len; i++){
4965             if(c2[i]._qdiff != d){
4966                 r[r.length] = c2[i];
4967             }
4968         }
4969         return r;
4970     }
4971
4972     function quickId(ns, mode, root, id){
4973         if(ns == root){
4974            var d = root.ownerDocument || root;
4975            return d.getElementById(id);
4976         }
4977         ns = getNodes(ns, mode, "*");
4978         return byId(ns, null, id);
4979     }
4980
4981     return {
4982         getStyle : function(el, name){
4983             return Roo.fly(el).getStyle(name);
4984         },
4985         /**
4986          * Compiles a selector/xpath query into a reusable function. The returned function
4987          * takes one parameter "root" (optional), which is the context node from where the query should start.
4988          * @param {String} selector The selector/xpath query
4989          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4990          * @return {Function}
4991          */
4992         compile : function(path, type){
4993             type = type || "select";
4994             
4995             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4996             var q = path, mode, lq;
4997             var tk = Roo.DomQuery.matchers;
4998             var tklen = tk.length;
4999             var mm;
5000
5001             // accept leading mode switch
5002             var lmode = q.match(modeRe);
5003             if(lmode && lmode[1]){
5004                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5005                 q = q.replace(lmode[1], "");
5006             }
5007             // strip leading slashes
5008             while(path.substr(0, 1)=="/"){
5009                 path = path.substr(1);
5010             }
5011
5012             while(q && lq != q){
5013                 lq = q;
5014                 var tm = q.match(tagTokenRe);
5015                 if(type == "select"){
5016                     if(tm){
5017                         if(tm[1] == "#"){
5018                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5019                         }else{
5020                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5021                         }
5022                         q = q.replace(tm[0], "");
5023                     }else if(q.substr(0, 1) != '@'){
5024                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5025                     }
5026                 }else{
5027                     if(tm){
5028                         if(tm[1] == "#"){
5029                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5030                         }else{
5031                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5032                         }
5033                         q = q.replace(tm[0], "");
5034                     }
5035                 }
5036                 while(!(mm = q.match(modeRe))){
5037                     var matched = false;
5038                     for(var j = 0; j < tklen; j++){
5039                         var t = tk[j];
5040                         var m = q.match(t.re);
5041                         if(m){
5042                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5043                                                     return m[i];
5044                                                 });
5045                             q = q.replace(m[0], "");
5046                             matched = true;
5047                             break;
5048                         }
5049                     }
5050                     // prevent infinite loop on bad selector
5051                     if(!matched){
5052                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5053                     }
5054                 }
5055                 if(mm[1]){
5056                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5057                     q = q.replace(mm[1], "");
5058                 }
5059             }
5060             fn[fn.length] = "return nodup(n);\n}";
5061             
5062              /** 
5063               * list of variables that need from compression as they are used by eval.
5064              *  eval:var:batch 
5065              *  eval:var:nodup
5066              *  eval:var:byTag
5067              *  eval:var:ById
5068              *  eval:var:getNodes
5069              *  eval:var:quickId
5070              *  eval:var:mode
5071              *  eval:var:root
5072              *  eval:var:n
5073              *  eval:var:byClassName
5074              *  eval:var:byPseudo
5075              *  eval:var:byAttribute
5076              *  eval:var:attrValue
5077              * 
5078              **/ 
5079             eval(fn.join(""));
5080             return f;
5081         },
5082
5083         /**
5084          * Selects a group of elements.
5085          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5086          * @param {Node} root (optional) The start of the query (defaults to document).
5087          * @return {Array}
5088          */
5089         select : function(path, root, type){
5090             if(!root || root == document){
5091                 root = document;
5092             }
5093             if(typeof root == "string"){
5094                 root = document.getElementById(root);
5095             }
5096             var paths = path.split(",");
5097             var results = [];
5098             for(var i = 0, len = paths.length; i < len; i++){
5099                 var p = paths[i].replace(trimRe, "");
5100                 if(!cache[p]){
5101                     cache[p] = Roo.DomQuery.compile(p);
5102                     if(!cache[p]){
5103                         throw p + " is not a valid selector";
5104                     }
5105                 }
5106                 var result = cache[p](root);
5107                 if(result && result != document){
5108                     results = results.concat(result);
5109                 }
5110             }
5111             if(paths.length > 1){
5112                 return nodup(results);
5113             }
5114             return results;
5115         },
5116
5117         /**
5118          * Selects a single element.
5119          * @param {String} selector The selector/xpath query
5120          * @param {Node} root (optional) The start of the query (defaults to document).
5121          * @return {Element}
5122          */
5123         selectNode : function(path, root){
5124             return Roo.DomQuery.select(path, root)[0];
5125         },
5126
5127         /**
5128          * Selects the value of a node, optionally replacing null with the defaultValue.
5129          * @param {String} selector The selector/xpath query
5130          * @param {Node} root (optional) The start of the query (defaults to document).
5131          * @param {String} defaultValue
5132          */
5133         selectValue : function(path, root, defaultValue){
5134             path = path.replace(trimRe, "");
5135             if(!valueCache[path]){
5136                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5137             }
5138             var n = valueCache[path](root);
5139             n = n[0] ? n[0] : n;
5140             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5141             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5142         },
5143
5144         /**
5145          * Selects the value of a node, parsing integers and floats.
5146          * @param {String} selector The selector/xpath query
5147          * @param {Node} root (optional) The start of the query (defaults to document).
5148          * @param {Number} defaultValue
5149          * @return {Number}
5150          */
5151         selectNumber : function(path, root, defaultValue){
5152             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5153             return parseFloat(v);
5154         },
5155
5156         /**
5157          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5158          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5159          * @param {String} selector The simple selector to test
5160          * @return {Boolean}
5161          */
5162         is : function(el, ss){
5163             if(typeof el == "string"){
5164                 el = document.getElementById(el);
5165             }
5166             var isArray = (el instanceof Array);
5167             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5168             return isArray ? (result.length == el.length) : (result.length > 0);
5169         },
5170
5171         /**
5172          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5173          * @param {Array} el An array of elements to filter
5174          * @param {String} selector The simple selector to test
5175          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5176          * the selector instead of the ones that match
5177          * @return {Array}
5178          */
5179         filter : function(els, ss, nonMatches){
5180             ss = ss.replace(trimRe, "");
5181             if(!simpleCache[ss]){
5182                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5183             }
5184             var result = simpleCache[ss](els);
5185             return nonMatches ? quickDiff(result, els) : result;
5186         },
5187
5188         /**
5189          * Collection of matching regular expressions and code snippets.
5190          */
5191         matchers : [{
5192                 re: /^\.([\w-]+)/,
5193                 select: 'n = byClassName(n, null, " {1} ");'
5194             }, {
5195                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5196                 select: 'n = byPseudo(n, "{1}", "{2}");'
5197             },{
5198                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5199                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5200             }, {
5201                 re: /^#([\w-]+)/,
5202                 select: 'n = byId(n, null, "{1}");'
5203             },{
5204                 re: /^@([\w-]+)/,
5205                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5206             }
5207         ],
5208
5209         /**
5210          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5211          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5212          */
5213         operators : {
5214             "=" : function(a, v){
5215                 return a == v;
5216             },
5217             "!=" : function(a, v){
5218                 return a != v;
5219             },
5220             "^=" : function(a, v){
5221                 return a && a.substr(0, v.length) == v;
5222             },
5223             "$=" : function(a, v){
5224                 return a && a.substr(a.length-v.length) == v;
5225             },
5226             "*=" : function(a, v){
5227                 return a && a.indexOf(v) !== -1;
5228             },
5229             "%=" : function(a, v){
5230                 return (a % v) == 0;
5231             },
5232             "|=" : function(a, v){
5233                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5234             },
5235             "~=" : function(a, v){
5236                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5237             }
5238         },
5239
5240         /**
5241          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5242          * and the argument (if any) supplied in the selector.
5243          */
5244         pseudos : {
5245             "first-child" : function(c){
5246                 var r = [], ri = -1, n;
5247                 for(var i = 0, ci; ci = n = c[i]; i++){
5248                     while((n = n.previousSibling) && n.nodeType != 1);
5249                     if(!n){
5250                         r[++ri] = ci;
5251                     }
5252                 }
5253                 return r;
5254             },
5255
5256             "last-child" : function(c){
5257                 var r = [], ri = -1, n;
5258                 for(var i = 0, ci; ci = n = c[i]; i++){
5259                     while((n = n.nextSibling) && n.nodeType != 1);
5260                     if(!n){
5261                         r[++ri] = ci;
5262                     }
5263                 }
5264                 return r;
5265             },
5266
5267             "nth-child" : function(c, a) {
5268                 var r = [], ri = -1;
5269                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5270                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5271                 for(var i = 0, n; n = c[i]; i++){
5272                     var pn = n.parentNode;
5273                     if (batch != pn._batch) {
5274                         var j = 0;
5275                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5276                             if(cn.nodeType == 1){
5277                                cn.nodeIndex = ++j;
5278                             }
5279                         }
5280                         pn._batch = batch;
5281                     }
5282                     if (f == 1) {
5283                         if (l == 0 || n.nodeIndex == l){
5284                             r[++ri] = n;
5285                         }
5286                     } else if ((n.nodeIndex + l) % f == 0){
5287                         r[++ri] = n;
5288                     }
5289                 }
5290
5291                 return r;
5292             },
5293
5294             "only-child" : function(c){
5295                 var r = [], ri = -1;;
5296                 for(var i = 0, ci; ci = c[i]; i++){
5297                     if(!prev(ci) && !next(ci)){
5298                         r[++ri] = ci;
5299                     }
5300                 }
5301                 return r;
5302             },
5303
5304             "empty" : function(c){
5305                 var r = [], ri = -1;
5306                 for(var i = 0, ci; ci = c[i]; i++){
5307                     var cns = ci.childNodes, j = 0, cn, empty = true;
5308                     while(cn = cns[j]){
5309                         ++j;
5310                         if(cn.nodeType == 1 || cn.nodeType == 3){
5311                             empty = false;
5312                             break;
5313                         }
5314                     }
5315                     if(empty){
5316                         r[++ri] = ci;
5317                     }
5318                 }
5319                 return r;
5320             },
5321
5322             "contains" : function(c, v){
5323                 var r = [], ri = -1;
5324                 for(var i = 0, ci; ci = c[i]; i++){
5325                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5326                         r[++ri] = ci;
5327                     }
5328                 }
5329                 return r;
5330             },
5331
5332             "nodeValue" : function(c, v){
5333                 var r = [], ri = -1;
5334                 for(var i = 0, ci; ci = c[i]; i++){
5335                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5336                         r[++ri] = ci;
5337                     }
5338                 }
5339                 return r;
5340             },
5341
5342             "checked" : function(c){
5343                 var r = [], ri = -1;
5344                 for(var i = 0, ci; ci = c[i]; i++){
5345                     if(ci.checked == true){
5346                         r[++ri] = ci;
5347                     }
5348                 }
5349                 return r;
5350             },
5351
5352             "not" : function(c, ss){
5353                 return Roo.DomQuery.filter(c, ss, true);
5354             },
5355
5356             "odd" : function(c){
5357                 return this["nth-child"](c, "odd");
5358             },
5359
5360             "even" : function(c){
5361                 return this["nth-child"](c, "even");
5362             },
5363
5364             "nth" : function(c, a){
5365                 return c[a-1] || [];
5366             },
5367
5368             "first" : function(c){
5369                 return c[0] || [];
5370             },
5371
5372             "last" : function(c){
5373                 return c[c.length-1] || [];
5374             },
5375
5376             "has" : function(c, ss){
5377                 var s = Roo.DomQuery.select;
5378                 var r = [], ri = -1;
5379                 for(var i = 0, ci; ci = c[i]; i++){
5380                     if(s(ss, ci).length > 0){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             },
5386
5387             "next" : function(c, ss){
5388                 var is = Roo.DomQuery.is;
5389                 var r = [], ri = -1;
5390                 for(var i = 0, ci; ci = c[i]; i++){
5391                     var n = next(ci);
5392                     if(n && is(n, ss)){
5393                         r[++ri] = ci;
5394                     }
5395                 }
5396                 return r;
5397             },
5398
5399             "prev" : function(c, ss){
5400                 var is = Roo.DomQuery.is;
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     var n = prev(ci);
5404                     if(n && is(n, ss)){
5405                         r[++ri] = ci;
5406                     }
5407                 }
5408                 return r;
5409             }
5410         }
5411     };
5412 }();
5413
5414 /**
5415  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5416  * @param {String} path The selector/xpath query
5417  * @param {Node} root (optional) The start of the query (defaults to document).
5418  * @return {Array}
5419  * @member Roo
5420  * @method query
5421  */
5422 Roo.query = Roo.DomQuery.select;
5423 /*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433
5434 /**
5435  * @class Roo.util.Observable
5436  * Base class that provides a common interface for publishing events. Subclasses are expected to
5437  * to have a property "events" with all the events defined.<br>
5438  * For example:
5439  * <pre><code>
5440  Employee = function(name){
5441     this.name = name;
5442     this.addEvents({
5443         "fired" : true,
5444         "quit" : true
5445     });
5446  }
5447  Roo.extend(Employee, Roo.util.Observable);
5448 </code></pre>
5449  * @param {Object} config properties to use (incuding events / listeners)
5450  */
5451
5452 Roo.util.Observable = function(cfg){
5453     
5454     cfg = cfg|| {};
5455     this.addEvents(cfg.events || {});
5456     if (cfg.events) {
5457         delete cfg.events; // make sure
5458     }
5459      
5460     Roo.apply(this, cfg);
5461     
5462     if(this.listeners){
5463         this.on(this.listeners);
5464         delete this.listeners;
5465     }
5466 };
5467 Roo.util.Observable.prototype = {
5468     /** 
5469  * @cfg {Object} listeners  list of events and functions to call for this object, 
5470  * For example :
5471  * <pre><code>
5472     listeners :  { 
5473        'click' : function(e) {
5474            ..... 
5475         } ,
5476         .... 
5477     } 
5478   </code></pre>
5479  */
5480     
5481     
5482     /**
5483      * Fires the specified event with the passed parameters (minus the event name).
5484      * @param {String} eventName
5485      * @param {Object...} args Variable number of parameters are passed to handlers
5486      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5487      */
5488     fireEvent : function(){
5489         var ce = this.events[arguments[0].toLowerCase()];
5490         if(typeof ce == "object"){
5491             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5492         }else{
5493             return true;
5494         }
5495     },
5496
5497     // private
5498     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5499
5500     /**
5501      * Appends an event handler to this component
5502      * @param {String}   eventName The type of event to listen for
5503      * @param {Function} handler The method the event invokes
5504      * @param {Object}   scope (optional) The scope in which to execute the handler
5505      * function. The handler function's "this" context.
5506      * @param {Object}   options (optional) An object containing handler configuration
5507      * properties. This may contain any of the following properties:<ul>
5508      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5509      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5510      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5511      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5512      * by the specified number of milliseconds. If the event fires again within that time, the original
5513      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5514      * </ul><br>
5515      * <p>
5516      * <b>Combining Options</b><br>
5517      * Using the options argument, it is possible to combine different types of listeners:<br>
5518      * <br>
5519      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5520                 <pre><code>
5521                 el.on('click', this.onClick, this, {
5522                         single: true,
5523                 delay: 100,
5524                 forumId: 4
5525                 });
5526                 </code></pre>
5527      * <p>
5528      * <b>Attaching multiple handlers in 1 call</b><br>
5529      * The method also allows for a single argument to be passed which is a config object containing properties
5530      * which specify multiple handlers.
5531      * <pre><code>
5532                 el.on({
5533                         'click': {
5534                         fn: this.onClick,
5535                         scope: this,
5536                         delay: 100
5537                 }, 
5538                 'mouseover': {
5539                         fn: this.onMouseOver,
5540                         scope: this
5541                 },
5542                 'mouseout': {
5543                         fn: this.onMouseOut,
5544                         scope: this
5545                 }
5546                 });
5547                 </code></pre>
5548      * <p>
5549      * Or a shorthand syntax which passes the same scope object to all handlers:
5550         <pre><code>
5551                 el.on({
5552                         'click': this.onClick,
5553                 'mouseover': this.onMouseOver,
5554                 'mouseout': this.onMouseOut,
5555                 scope: this
5556                 });
5557                 </code></pre>
5558      */
5559     addListener : function(eventName, fn, scope, o){
5560         if(typeof eventName == "object"){
5561             o = eventName;
5562             for(var e in o){
5563                 if(this.filterOptRe.test(e)){
5564                     continue;
5565                 }
5566                 if(typeof o[e] == "function"){
5567                     // shared options
5568                     this.addListener(e, o[e], o.scope,  o);
5569                 }else{
5570                     // individual options
5571                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5572                 }
5573             }
5574             return;
5575         }
5576         o = (!o || typeof o == "boolean") ? {} : o;
5577         eventName = eventName.toLowerCase();
5578         var ce = this.events[eventName] || true;
5579         if(typeof ce == "boolean"){
5580             ce = new Roo.util.Event(this, eventName);
5581             this.events[eventName] = ce;
5582         }
5583         ce.addListener(fn, scope, o);
5584     },
5585
5586     /**
5587      * Removes a listener
5588      * @param {String}   eventName     The type of event to listen for
5589      * @param {Function} handler        The handler to remove
5590      * @param {Object}   scope  (optional) The scope (this object) for the handler
5591      */
5592     removeListener : function(eventName, fn, scope){
5593         var ce = this.events[eventName.toLowerCase()];
5594         if(typeof ce == "object"){
5595             ce.removeListener(fn, scope);
5596         }
5597     },
5598
5599     /**
5600      * Removes all listeners for this object
5601      */
5602     purgeListeners : function(){
5603         for(var evt in this.events){
5604             if(typeof this.events[evt] == "object"){
5605                  this.events[evt].clearListeners();
5606             }
5607         }
5608     },
5609
5610     relayEvents : function(o, events){
5611         var createHandler = function(ename){
5612             return function(){
5613                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5614             };
5615         };
5616         for(var i = 0, len = events.length; i < len; i++){
5617             var ename = events[i];
5618             if(!this.events[ename]){ this.events[ename] = true; };
5619             o.on(ename, createHandler(ename), this);
5620         }
5621     },
5622
5623     /**
5624      * Used to define events on this Observable
5625      * @param {Object} object The object with the events defined
5626      */
5627     addEvents : function(o){
5628         if(!this.events){
5629             this.events = {};
5630         }
5631         Roo.applyIf(this.events, o);
5632     },
5633
5634     /**
5635      * Checks to see if this object has any listeners for a specified event
5636      * @param {String} eventName The name of the event to check for
5637      * @return {Boolean} True if the event is being listened for, else false
5638      */
5639     hasListener : function(eventName){
5640         var e = this.events[eventName];
5641         return typeof e == "object" && e.listeners.length > 0;
5642     }
5643 };
5644 /**
5645  * Appends an event handler to this element (shorthand for addListener)
5646  * @param {String}   eventName     The type of event to listen for
5647  * @param {Function} handler        The method the event invokes
5648  * @param {Object}   scope (optional) The scope in which to execute the handler
5649  * function. The handler function's "this" context.
5650  * @param {Object}   options  (optional)
5651  * @method
5652  */
5653 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5654 /**
5655  * Removes a listener (shorthand for removeListener)
5656  * @param {String}   eventName     The type of event to listen for
5657  * @param {Function} handler        The handler to remove
5658  * @param {Object}   scope  (optional) The scope (this object) for the handler
5659  * @method
5660  */
5661 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5662
5663 /**
5664  * Starts capture on the specified Observable. All events will be passed
5665  * to the supplied function with the event name + standard signature of the event
5666  * <b>before</b> the event is fired. If the supplied function returns false,
5667  * the event will not fire.
5668  * @param {Observable} o The Observable to capture
5669  * @param {Function} fn The function to call
5670  * @param {Object} scope (optional) The scope (this object) for the fn
5671  * @static
5672  */
5673 Roo.util.Observable.capture = function(o, fn, scope){
5674     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5675 };
5676
5677 /**
5678  * Removes <b>all</b> added captures from the Observable.
5679  * @param {Observable} o The Observable to release
5680  * @static
5681  */
5682 Roo.util.Observable.releaseCapture = function(o){
5683     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5684 };
5685
5686 (function(){
5687
5688     var createBuffered = function(h, o, scope){
5689         var task = new Roo.util.DelayedTask();
5690         return function(){
5691             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5692         };
5693     };
5694
5695     var createSingle = function(h, e, fn, scope){
5696         return function(){
5697             e.removeListener(fn, scope);
5698             return h.apply(scope, arguments);
5699         };
5700     };
5701
5702     var createDelayed = function(h, o, scope){
5703         return function(){
5704             var args = Array.prototype.slice.call(arguments, 0);
5705             setTimeout(function(){
5706                 h.apply(scope, args);
5707             }, o.delay || 10);
5708         };
5709     };
5710
5711     Roo.util.Event = function(obj, name){
5712         this.name = name;
5713         this.obj = obj;
5714         this.listeners = [];
5715     };
5716
5717     Roo.util.Event.prototype = {
5718         addListener : function(fn, scope, options){
5719             var o = options || {};
5720             scope = scope || this.obj;
5721             if(!this.isListening(fn, scope)){
5722                 var l = {fn: fn, scope: scope, options: o};
5723                 var h = fn;
5724                 if(o.delay){
5725                     h = createDelayed(h, o, scope);
5726                 }
5727                 if(o.single){
5728                     h = createSingle(h, this, fn, scope);
5729                 }
5730                 if(o.buffer){
5731                     h = createBuffered(h, o, scope);
5732                 }
5733                 l.fireFn = h;
5734                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5735                     this.listeners.push(l);
5736                 }else{
5737                     this.listeners = this.listeners.slice(0);
5738                     this.listeners.push(l);
5739                 }
5740             }
5741         },
5742
5743         findListener : function(fn, scope){
5744             scope = scope || this.obj;
5745             var ls = this.listeners;
5746             for(var i = 0, len = ls.length; i < len; i++){
5747                 var l = ls[i];
5748                 if(l.fn == fn && l.scope == scope){
5749                     return i;
5750                 }
5751             }
5752             return -1;
5753         },
5754
5755         isListening : function(fn, scope){
5756             return this.findListener(fn, scope) != -1;
5757         },
5758
5759         removeListener : function(fn, scope){
5760             var index;
5761             if((index = this.findListener(fn, scope)) != -1){
5762                 if(!this.firing){
5763                     this.listeners.splice(index, 1);
5764                 }else{
5765                     this.listeners = this.listeners.slice(0);
5766                     this.listeners.splice(index, 1);
5767                 }
5768                 return true;
5769             }
5770             return false;
5771         },
5772
5773         clearListeners : function(){
5774             this.listeners = [];
5775         },
5776
5777         fire : function(){
5778             var ls = this.listeners, scope, len = ls.length;
5779             if(len > 0){
5780                 this.firing = true;
5781                 var args = Array.prototype.slice.call(arguments, 0);
5782                 for(var i = 0; i < len; i++){
5783                     var l = ls[i];
5784                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5785                         this.firing = false;
5786                         return false;
5787                     }
5788                 }
5789                 this.firing = false;
5790             }
5791             return true;
5792         }
5793     };
5794 })();/*
5795  * Based on:
5796  * Ext JS Library 1.1.1
5797  * Copyright(c) 2006-2007, Ext JS, LLC.
5798  *
5799  * Originally Released Under LGPL - original licence link has changed is not relivant.
5800  *
5801  * Fork - LGPL
5802  * <script type="text/javascript">
5803  */
5804
5805 /**
5806  * @class Roo.EventManager
5807  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5808  * several useful events directly.
5809  * See {@link Roo.EventObject} for more details on normalized event objects.
5810  * @singleton
5811  */
5812 Roo.EventManager = function(){
5813     var docReadyEvent, docReadyProcId, docReadyState = false;
5814     var resizeEvent, resizeTask, textEvent, textSize;
5815     var E = Roo.lib.Event;
5816     var D = Roo.lib.Dom;
5817
5818
5819     var fireDocReady = function(){
5820         if(!docReadyState){
5821             docReadyState = true;
5822             Roo.isReady = true;
5823             if(docReadyProcId){
5824                 clearInterval(docReadyProcId);
5825             }
5826             if(Roo.isGecko || Roo.isOpera) {
5827                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5828             }
5829             if(Roo.isIE){
5830                 var defer = document.getElementById("ie-deferred-loader");
5831                 if(defer){
5832                     defer.onreadystatechange = null;
5833                     defer.parentNode.removeChild(defer);
5834                 }
5835             }
5836             if(docReadyEvent){
5837                 docReadyEvent.fire();
5838                 docReadyEvent.clearListeners();
5839             }
5840         }
5841     };
5842     
5843     var initDocReady = function(){
5844         docReadyEvent = new Roo.util.Event();
5845         if(Roo.isGecko || Roo.isOpera) {
5846             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5847         }else if(Roo.isIE){
5848             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5849             var defer = document.getElementById("ie-deferred-loader");
5850             defer.onreadystatechange = function(){
5851                 if(this.readyState == "complete"){
5852                     fireDocReady();
5853                 }
5854             };
5855         }else if(Roo.isSafari){ 
5856             docReadyProcId = setInterval(function(){
5857                 var rs = document.readyState;
5858                 if(rs == "complete") {
5859                     fireDocReady();     
5860                  }
5861             }, 10);
5862         }
5863         // no matter what, make sure it fires on load
5864         E.on(window, "load", fireDocReady);
5865     };
5866
5867     var createBuffered = function(h, o){
5868         var task = new Roo.util.DelayedTask(h);
5869         return function(e){
5870             // create new event object impl so new events don't wipe out properties
5871             e = new Roo.EventObjectImpl(e);
5872             task.delay(o.buffer, h, null, [e]);
5873         };
5874     };
5875
5876     var createSingle = function(h, el, ename, fn){
5877         return function(e){
5878             Roo.EventManager.removeListener(el, ename, fn);
5879             h(e);
5880         };
5881     };
5882
5883     var createDelayed = function(h, o){
5884         return function(e){
5885             // create new event object impl so new events don't wipe out properties
5886             e = new Roo.EventObjectImpl(e);
5887             setTimeout(function(){
5888                 h(e);
5889             }, o.delay || 10);
5890         };
5891     };
5892
5893     var listen = function(element, ename, opt, fn, scope){
5894         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5895         fn = fn || o.fn; scope = scope || o.scope;
5896         var el = Roo.getDom(element);
5897         if(!el){
5898             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5899         }
5900         var h = function(e){
5901             e = Roo.EventObject.setEvent(e);
5902             var t;
5903             if(o.delegate){
5904                 t = e.getTarget(o.delegate, el);
5905                 if(!t){
5906                     return;
5907                 }
5908             }else{
5909                 t = e.target;
5910             }
5911             if(o.stopEvent === true){
5912                 e.stopEvent();
5913             }
5914             if(o.preventDefault === true){
5915                e.preventDefault();
5916             }
5917             if(o.stopPropagation === true){
5918                 e.stopPropagation();
5919             }
5920
5921             if(o.normalized === false){
5922                 e = e.browserEvent;
5923             }
5924
5925             fn.call(scope || el, e, t, o);
5926         };
5927         if(o.delay){
5928             h = createDelayed(h, o);
5929         }
5930         if(o.single){
5931             h = createSingle(h, el, ename, fn);
5932         }
5933         if(o.buffer){
5934             h = createBuffered(h, o);
5935         }
5936         fn._handlers = fn._handlers || [];
5937         fn._handlers.push([Roo.id(el), ename, h]);
5938
5939         E.on(el, ename, h);
5940         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5941             el.addEventListener("DOMMouseScroll", h, false);
5942             E.on(window, 'unload', function(){
5943                 el.removeEventListener("DOMMouseScroll", h, false);
5944             });
5945         }
5946         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5947             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5948         }
5949         return h;
5950     };
5951
5952     var stopListening = function(el, ename, fn){
5953         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5954         if(hds){
5955             for(var i = 0, len = hds.length; i < len; i++){
5956                 var h = hds[i];
5957                 if(h[0] == id && h[1] == ename){
5958                     hd = h[2];
5959                     hds.splice(i, 1);
5960                     break;
5961                 }
5962             }
5963         }
5964         E.un(el, ename, hd);
5965         el = Roo.getDom(el);
5966         if(ename == "mousewheel" && el.addEventListener){
5967             el.removeEventListener("DOMMouseScroll", hd, false);
5968         }
5969         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5970             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5971         }
5972     };
5973
5974     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5975     
5976     var pub = {
5977         
5978         
5979         /** 
5980          * Fix for doc tools
5981          * @scope Roo.EventManager
5982          */
5983         
5984         
5985         /** 
5986          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5987          * object with a Roo.EventObject
5988          * @param {Function} fn        The method the event invokes
5989          * @param {Object}   scope    An object that becomes the scope of the handler
5990          * @param {boolean}  override If true, the obj passed in becomes
5991          *                             the execution scope of the listener
5992          * @return {Function} The wrapped function
5993          * @deprecated
5994          */
5995         wrap : function(fn, scope, override){
5996             return function(e){
5997                 Roo.EventObject.setEvent(e);
5998                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5999             };
6000         },
6001         
6002         /**
6003      * Appends an event handler to an element (shorthand for addListener)
6004      * @param {String/HTMLElement}   element        The html element or id to assign the
6005      * @param {String}   eventName The type of event to listen for
6006      * @param {Function} handler The method the event invokes
6007      * @param {Object}   scope (optional) The scope in which to execute the handler
6008      * function. The handler function's "this" context.
6009      * @param {Object}   options (optional) An object containing handler configuration
6010      * properties. This may contain any of the following properties:<ul>
6011      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6012      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6013      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6014      * <li>preventDefault {Boolean} True to prevent the default action</li>
6015      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6016      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6017      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6018      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6019      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6020      * by the specified number of milliseconds. If the event fires again within that time, the original
6021      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6022      * </ul><br>
6023      * <p>
6024      * <b>Combining Options</b><br>
6025      * Using the options argument, it is possible to combine different types of listeners:<br>
6026      * <br>
6027      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6028      * Code:<pre><code>
6029 el.on('click', this.onClick, this, {
6030     single: true,
6031     delay: 100,
6032     stopEvent : true,
6033     forumId: 4
6034 });</code></pre>
6035      * <p>
6036      * <b>Attaching multiple handlers in 1 call</b><br>
6037       * The method also allows for a single argument to be passed which is a config object containing properties
6038      * which specify multiple handlers.
6039      * <p>
6040      * Code:<pre><code>
6041 el.on({
6042     'click' : {
6043         fn: this.onClick
6044         scope: this,
6045         delay: 100
6046     },
6047     'mouseover' : {
6048         fn: this.onMouseOver
6049         scope: this
6050     },
6051     'mouseout' : {
6052         fn: this.onMouseOut
6053         scope: this
6054     }
6055 });</code></pre>
6056      * <p>
6057      * Or a shorthand syntax:<br>
6058      * Code:<pre><code>
6059 el.on({
6060     'click' : this.onClick,
6061     'mouseover' : this.onMouseOver,
6062     'mouseout' : this.onMouseOut
6063     scope: this
6064 });</code></pre>
6065      */
6066         addListener : function(element, eventName, fn, scope, options){
6067             if(typeof eventName == "object"){
6068                 var o = eventName;
6069                 for(var e in o){
6070                     if(propRe.test(e)){
6071                         continue;
6072                     }
6073                     if(typeof o[e] == "function"){
6074                         // shared options
6075                         listen(element, e, o, o[e], o.scope);
6076                     }else{
6077                         // individual options
6078                         listen(element, e, o[e]);
6079                     }
6080                 }
6081                 return;
6082             }
6083             return listen(element, eventName, options, fn, scope);
6084         },
6085         
6086         /**
6087          * Removes an event handler
6088          *
6089          * @param {String/HTMLElement}   element        The id or html element to remove the 
6090          *                             event from
6091          * @param {String}   eventName     The type of event
6092          * @param {Function} fn
6093          * @return {Boolean} True if a listener was actually removed
6094          */
6095         removeListener : function(element, eventName, fn){
6096             return stopListening(element, eventName, fn);
6097         },
6098         
6099         /**
6100          * Fires when the document is ready (before onload and before images are loaded). Can be 
6101          * accessed shorthanded Roo.onReady().
6102          * @param {Function} fn        The method the event invokes
6103          * @param {Object}   scope    An  object that becomes the scope of the handler
6104          * @param {boolean}  options
6105          */
6106         onDocumentReady : function(fn, scope, options){
6107             if(docReadyState){ // if it already fired
6108                 docReadyEvent.addListener(fn, scope, options);
6109                 docReadyEvent.fire();
6110                 docReadyEvent.clearListeners();
6111                 return;
6112             }
6113             if(!docReadyEvent){
6114                 initDocReady();
6115             }
6116             docReadyEvent.addListener(fn, scope, options);
6117         },
6118         
6119         /**
6120          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6121          * @param {Function} fn        The method the event invokes
6122          * @param {Object}   scope    An object that becomes the scope of the handler
6123          * @param {boolean}  options
6124          */
6125         onWindowResize : function(fn, scope, options){
6126             if(!resizeEvent){
6127                 resizeEvent = new Roo.util.Event();
6128                 resizeTask = new Roo.util.DelayedTask(function(){
6129                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6130                 });
6131                 E.on(window, "resize", function(){
6132                     if(Roo.isIE){
6133                         resizeTask.delay(50);
6134                     }else{
6135                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6136                     }
6137                 });
6138             }
6139             resizeEvent.addListener(fn, scope, options);
6140         },
6141
6142         /**
6143          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onTextResize : function(fn, scope, options){
6149             if(!textEvent){
6150                 textEvent = new Roo.util.Event();
6151                 var textEl = new Roo.Element(document.createElement('div'));
6152                 textEl.dom.className = 'x-text-resize';
6153                 textEl.dom.innerHTML = 'X';
6154                 textEl.appendTo(document.body);
6155                 textSize = textEl.dom.offsetHeight;
6156                 setInterval(function(){
6157                     if(textEl.dom.offsetHeight != textSize){
6158                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6159                     }
6160                 }, this.textResizeInterval);
6161             }
6162             textEvent.addListener(fn, scope, options);
6163         },
6164
6165         /**
6166          * Removes the passed window resize listener.
6167          * @param {Function} fn        The method the event invokes
6168          * @param {Object}   scope    The scope of handler
6169          */
6170         removeResizeListener : function(fn, scope){
6171             if(resizeEvent){
6172                 resizeEvent.removeListener(fn, scope);
6173             }
6174         },
6175
6176         // private
6177         fireResize : function(){
6178             if(resizeEvent){
6179                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6180             }   
6181         },
6182         /**
6183          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6184          */
6185         ieDeferSrc : false,
6186         /**
6187          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6188          */
6189         textResizeInterval : 50
6190     };
6191     
6192     /**
6193      * Fix for doc tools
6194      * @scopeAlias pub=Roo.EventManager
6195      */
6196     
6197      /**
6198      * Appends an event handler to an element (shorthand for addListener)
6199      * @param {String/HTMLElement}   element        The html element or id to assign the
6200      * @param {String}   eventName The type of event to listen for
6201      * @param {Function} handler The method the event invokes
6202      * @param {Object}   scope (optional) The scope in which to execute the handler
6203      * function. The handler function's "this" context.
6204      * @param {Object}   options (optional) An object containing handler configuration
6205      * properties. This may contain any of the following properties:<ul>
6206      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6207      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6208      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6209      * <li>preventDefault {Boolean} True to prevent the default action</li>
6210      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6211      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6212      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6213      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6214      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6215      * by the specified number of milliseconds. If the event fires again within that time, the original
6216      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6217      * </ul><br>
6218      * <p>
6219      * <b>Combining Options</b><br>
6220      * Using the options argument, it is possible to combine different types of listeners:<br>
6221      * <br>
6222      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6223      * Code:<pre><code>
6224 el.on('click', this.onClick, this, {
6225     single: true,
6226     delay: 100,
6227     stopEvent : true,
6228     forumId: 4
6229 });</code></pre>
6230      * <p>
6231      * <b>Attaching multiple handlers in 1 call</b><br>
6232       * The method also allows for a single argument to be passed which is a config object containing properties
6233      * which specify multiple handlers.
6234      * <p>
6235      * Code:<pre><code>
6236 el.on({
6237     'click' : {
6238         fn: this.onClick
6239         scope: this,
6240         delay: 100
6241     },
6242     'mouseover' : {
6243         fn: this.onMouseOver
6244         scope: this
6245     },
6246     'mouseout' : {
6247         fn: this.onMouseOut
6248         scope: this
6249     }
6250 });</code></pre>
6251      * <p>
6252      * Or a shorthand syntax:<br>
6253      * Code:<pre><code>
6254 el.on({
6255     'click' : this.onClick,
6256     'mouseover' : this.onMouseOver,
6257     'mouseout' : this.onMouseOut
6258     scope: this
6259 });</code></pre>
6260      */
6261     pub.on = pub.addListener;
6262     pub.un = pub.removeListener;
6263
6264     pub.stoppedMouseDownEvent = new Roo.util.Event();
6265     return pub;
6266 }();
6267 /**
6268   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6269   * @param {Function} fn        The method the event invokes
6270   * @param {Object}   scope    An  object that becomes the scope of the handler
6271   * @param {boolean}  override If true, the obj passed in becomes
6272   *                             the execution scope of the listener
6273   * @member Roo
6274   * @method onReady
6275  */
6276 Roo.onReady = Roo.EventManager.onDocumentReady;
6277
6278 Roo.onReady(function(){
6279     var bd = Roo.get(document.body);
6280     if(!bd){ return; }
6281
6282     var cls = [
6283             Roo.isIE ? "roo-ie"
6284             : Roo.isGecko ? "roo-gecko"
6285             : Roo.isOpera ? "roo-opera"
6286             : Roo.isSafari ? "roo-safari" : ""];
6287
6288     if(Roo.isMac){
6289         cls.push("roo-mac");
6290     }
6291     if(Roo.isLinux){
6292         cls.push("roo-linux");
6293     }
6294     if(Roo.isBorderBox){
6295         cls.push('roo-border-box');
6296     }
6297     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6298         var p = bd.dom.parentNode;
6299         if(p){
6300             p.className += ' roo-strict';
6301         }
6302     }
6303     bd.addClass(cls.join(' '));
6304 });
6305
6306 /**
6307  * @class Roo.EventObject
6308  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6309  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6310  * Example:
6311  * <pre><code>
6312  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6313     e.preventDefault();
6314     var target = e.getTarget();
6315     ...
6316  }
6317  var myDiv = Roo.get("myDiv");
6318  myDiv.on("click", handleClick);
6319  //or
6320  Roo.EventManager.on("myDiv", 'click', handleClick);
6321  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6322  </code></pre>
6323  * @singleton
6324  */
6325 Roo.EventObject = function(){
6326     
6327     var E = Roo.lib.Event;
6328     
6329     // safari keypress events for special keys return bad keycodes
6330     var safariKeys = {
6331         63234 : 37, // left
6332         63235 : 39, // right
6333         63232 : 38, // up
6334         63233 : 40, // down
6335         63276 : 33, // page up
6336         63277 : 34, // page down
6337         63272 : 46, // delete
6338         63273 : 36, // home
6339         63275 : 35  // end
6340     };
6341
6342     // normalize button clicks
6343     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6344                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6345
6346     Roo.EventObjectImpl = function(e){
6347         if(e){
6348             this.setEvent(e.browserEvent || e);
6349         }
6350     };
6351     Roo.EventObjectImpl.prototype = {
6352         /**
6353          * Used to fix doc tools.
6354          * @scope Roo.EventObject.prototype
6355          */
6356             
6357
6358         
6359         
6360         /** The normal browser event */
6361         browserEvent : null,
6362         /** The button pressed in a mouse event */
6363         button : -1,
6364         /** True if the shift key was down during the event */
6365         shiftKey : false,
6366         /** True if the control key was down during the event */
6367         ctrlKey : false,
6368         /** True if the alt key was down during the event */
6369         altKey : false,
6370
6371         /** Key constant 
6372         * @type Number */
6373         BACKSPACE : 8,
6374         /** Key constant 
6375         * @type Number */
6376         TAB : 9,
6377         /** Key constant 
6378         * @type Number */
6379         RETURN : 13,
6380         /** Key constant 
6381         * @type Number */
6382         ENTER : 13,
6383         /** Key constant 
6384         * @type Number */
6385         SHIFT : 16,
6386         /** Key constant 
6387         * @type Number */
6388         CONTROL : 17,
6389         /** Key constant 
6390         * @type Number */
6391         ESC : 27,
6392         /** Key constant 
6393         * @type Number */
6394         SPACE : 32,
6395         /** Key constant 
6396         * @type Number */
6397         PAGEUP : 33,
6398         /** Key constant 
6399         * @type Number */
6400         PAGEDOWN : 34,
6401         /** Key constant 
6402         * @type Number */
6403         END : 35,
6404         /** Key constant 
6405         * @type Number */
6406         HOME : 36,
6407         /** Key constant 
6408         * @type Number */
6409         LEFT : 37,
6410         /** Key constant 
6411         * @type Number */
6412         UP : 38,
6413         /** Key constant 
6414         * @type Number */
6415         RIGHT : 39,
6416         /** Key constant 
6417         * @type Number */
6418         DOWN : 40,
6419         /** Key constant 
6420         * @type Number */
6421         DELETE : 46,
6422         /** Key constant 
6423         * @type Number */
6424         F5 : 116,
6425
6426            /** @private */
6427         setEvent : function(e){
6428             if(e == this || (e && e.browserEvent)){ // already wrapped
6429                 return e;
6430             }
6431             this.browserEvent = e;
6432             if(e){
6433                 // normalize buttons
6434                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6435                 if(e.type == 'click' && this.button == -1){
6436                     this.button = 0;
6437                 }
6438                 this.type = e.type;
6439                 this.shiftKey = e.shiftKey;
6440                 // mac metaKey behaves like ctrlKey
6441                 this.ctrlKey = e.ctrlKey || e.metaKey;
6442                 this.altKey = e.altKey;
6443                 // in getKey these will be normalized for the mac
6444                 this.keyCode = e.keyCode;
6445                 // keyup warnings on firefox.
6446                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6447                 // cache the target for the delayed and or buffered events
6448                 this.target = E.getTarget(e);
6449                 // same for XY
6450                 this.xy = E.getXY(e);
6451             }else{
6452                 this.button = -1;
6453                 this.shiftKey = false;
6454                 this.ctrlKey = false;
6455                 this.altKey = false;
6456                 this.keyCode = 0;
6457                 this.charCode =0;
6458                 this.target = null;
6459                 this.xy = [0, 0];
6460             }
6461             return this;
6462         },
6463
6464         /**
6465          * Stop the event (preventDefault and stopPropagation)
6466          */
6467         stopEvent : function(){
6468             if(this.browserEvent){
6469                 if(this.browserEvent.type == 'mousedown'){
6470                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6471                 }
6472                 E.stopEvent(this.browserEvent);
6473             }
6474         },
6475
6476         /**
6477          * Prevents the browsers default handling of the event.
6478          */
6479         preventDefault : function(){
6480             if(this.browserEvent){
6481                 E.preventDefault(this.browserEvent);
6482             }
6483         },
6484
6485         /** @private */
6486         isNavKeyPress : function(){
6487             var k = this.keyCode;
6488             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6489             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6490         },
6491
6492         isSpecialKey : function(){
6493             var k = this.keyCode;
6494             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6495             (k == 16) || (k == 17) ||
6496             (k >= 18 && k <= 20) ||
6497             (k >= 33 && k <= 35) ||
6498             (k >= 36 && k <= 39) ||
6499             (k >= 44 && k <= 45);
6500         },
6501         /**
6502          * Cancels bubbling of the event.
6503          */
6504         stopPropagation : function(){
6505             if(this.browserEvent){
6506                 if(this.type == 'mousedown'){
6507                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6508                 }
6509                 E.stopPropagation(this.browserEvent);
6510             }
6511         },
6512
6513         /**
6514          * Gets the key code for the event.
6515          * @return {Number}
6516          */
6517         getCharCode : function(){
6518             return this.charCode || this.keyCode;
6519         },
6520
6521         /**
6522          * Returns a normalized keyCode for the event.
6523          * @return {Number} The key code
6524          */
6525         getKey : function(){
6526             var k = this.keyCode || this.charCode;
6527             return Roo.isSafari ? (safariKeys[k] || k) : k;
6528         },
6529
6530         /**
6531          * Gets the x coordinate of the event.
6532          * @return {Number}
6533          */
6534         getPageX : function(){
6535             return this.xy[0];
6536         },
6537
6538         /**
6539          * Gets the y coordinate of the event.
6540          * @return {Number}
6541          */
6542         getPageY : function(){
6543             return this.xy[1];
6544         },
6545
6546         /**
6547          * Gets the time of the event.
6548          * @return {Number}
6549          */
6550         getTime : function(){
6551             if(this.browserEvent){
6552                 return E.getTime(this.browserEvent);
6553             }
6554             return null;
6555         },
6556
6557         /**
6558          * Gets the page coordinates of the event.
6559          * @return {Array} The xy values like [x, y]
6560          */
6561         getXY : function(){
6562             return this.xy;
6563         },
6564
6565         /**
6566          * Gets the target for the event.
6567          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6568          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6569                 search as a number or element (defaults to 10 || document.body)
6570          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6571          * @return {HTMLelement}
6572          */
6573         getTarget : function(selector, maxDepth, returnEl){
6574             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6575         },
6576         /**
6577          * Gets the related target.
6578          * @return {HTMLElement}
6579          */
6580         getRelatedTarget : function(){
6581             if(this.browserEvent){
6582                 return E.getRelatedTarget(this.browserEvent);
6583             }
6584             return null;
6585         },
6586
6587         /**
6588          * Normalizes mouse wheel delta across browsers
6589          * @return {Number} The delta
6590          */
6591         getWheelDelta : function(){
6592             var e = this.browserEvent;
6593             var delta = 0;
6594             if(e.wheelDelta){ /* IE/Opera. */
6595                 delta = e.wheelDelta/120;
6596             }else if(e.detail){ /* Mozilla case. */
6597                 delta = -e.detail/3;
6598             }
6599             return delta;
6600         },
6601
6602         /**
6603          * Returns true if the control, meta, shift or alt key was pressed during this event.
6604          * @return {Boolean}
6605          */
6606         hasModifier : function(){
6607             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6608         },
6609
6610         /**
6611          * Returns true if the target of this event equals el or is a child of el
6612          * @param {String/HTMLElement/Element} el
6613          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6614          * @return {Boolean}
6615          */
6616         within : function(el, related){
6617             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6618             return t && Roo.fly(el).contains(t);
6619         },
6620
6621         getPoint : function(){
6622             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6623         }
6624     };
6625
6626     return new Roo.EventObjectImpl();
6627 }();
6628             
6629     /*
6630  * Based on:
6631  * Ext JS Library 1.1.1
6632  * Copyright(c) 2006-2007, Ext JS, LLC.
6633  *
6634  * Originally Released Under LGPL - original licence link has changed is not relivant.
6635  *
6636  * Fork - LGPL
6637  * <script type="text/javascript">
6638  */
6639
6640  
6641 // was in Composite Element!??!?!
6642  
6643 (function(){
6644     var D = Roo.lib.Dom;
6645     var E = Roo.lib.Event;
6646     var A = Roo.lib.Anim;
6647
6648     // local style camelizing for speed
6649     var propCache = {};
6650     var camelRe = /(-[a-z])/gi;
6651     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6652     var view = document.defaultView;
6653
6654 /**
6655  * @class Roo.Element
6656  * Represents an Element in the DOM.<br><br>
6657  * Usage:<br>
6658 <pre><code>
6659 var el = Roo.get("my-div");
6660
6661 // or with getEl
6662 var el = getEl("my-div");
6663
6664 // or with a DOM element
6665 var el = Roo.get(myDivElement);
6666 </code></pre>
6667  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6668  * each call instead of constructing a new one.<br><br>
6669  * <b>Animations</b><br />
6670  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6671  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6672 <pre>
6673 Option    Default   Description
6674 --------- --------  ---------------------------------------------
6675 duration  .35       The duration of the animation in seconds
6676 easing    easeOut   The YUI easing method
6677 callback  none      A function to execute when the anim completes
6678 scope     this      The scope (this) of the callback function
6679 </pre>
6680 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6681 * manipulate the animation. Here's an example:
6682 <pre><code>
6683 var el = Roo.get("my-div");
6684
6685 // no animation
6686 el.setWidth(100);
6687
6688 // default animation
6689 el.setWidth(100, true);
6690
6691 // animation with some options set
6692 el.setWidth(100, {
6693     duration: 1,
6694     callback: this.foo,
6695     scope: this
6696 });
6697
6698 // using the "anim" property to get the Anim object
6699 var opt = {
6700     duration: 1,
6701     callback: this.foo,
6702     scope: this
6703 };
6704 el.setWidth(100, opt);
6705 ...
6706 if(opt.anim.isAnimated()){
6707     opt.anim.stop();
6708 }
6709 </code></pre>
6710 * <b> Composite (Collections of) Elements</b><br />
6711  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6712  * @constructor Create a new Element directly.
6713  * @param {String/HTMLElement} element
6714  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6715  */
6716     Roo.Element = function(element, forceNew){
6717         var dom = typeof element == "string" ?
6718                 document.getElementById(element) : element;
6719         if(!dom){ // invalid id/element
6720             return null;
6721         }
6722         var id = dom.id;
6723         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6724             return Roo.Element.cache[id];
6725         }
6726
6727         /**
6728          * The DOM element
6729          * @type HTMLElement
6730          */
6731         this.dom = dom;
6732
6733         /**
6734          * The DOM element ID
6735          * @type String
6736          */
6737         this.id = id || Roo.id(dom);
6738     };
6739
6740     var El = Roo.Element;
6741
6742     El.prototype = {
6743         /**
6744          * The element's default display mode  (defaults to "")
6745          * @type String
6746          */
6747         originalDisplay : "",
6748
6749         visibilityMode : 1,
6750         /**
6751          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6752          * @type String
6753          */
6754         defaultUnit : "px",
6755         /**
6756          * Sets the element's visibility mode. When setVisible() is called it
6757          * will use this to determine whether to set the visibility or the display property.
6758          * @param visMode Element.VISIBILITY or Element.DISPLAY
6759          * @return {Roo.Element} this
6760          */
6761         setVisibilityMode : function(visMode){
6762             this.visibilityMode = visMode;
6763             return this;
6764         },
6765         /**
6766          * Convenience method for setVisibilityMode(Element.DISPLAY)
6767          * @param {String} display (optional) What to set display to when visible
6768          * @return {Roo.Element} this
6769          */
6770         enableDisplayMode : function(display){
6771             this.setVisibilityMode(El.DISPLAY);
6772             if(typeof display != "undefined") this.originalDisplay = display;
6773             return this;
6774         },
6775
6776         /**
6777          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6778          * @param {String} selector The simple selector to test
6779          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6780                 search as a number or element (defaults to 10 || document.body)
6781          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6782          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6783          */
6784         findParent : function(simpleSelector, maxDepth, returnEl){
6785             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6786             maxDepth = maxDepth || 50;
6787             if(typeof maxDepth != "number"){
6788                 stopEl = Roo.getDom(maxDepth);
6789                 maxDepth = 10;
6790             }
6791             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6792                 if(dq.is(p, simpleSelector)){
6793                     return returnEl ? Roo.get(p) : p;
6794                 }
6795                 depth++;
6796                 p = p.parentNode;
6797             }
6798             return null;
6799         },
6800
6801
6802         /**
6803          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6804          * @param {String} selector The simple selector to test
6805          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6806                 search as a number or element (defaults to 10 || document.body)
6807          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6808          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6809          */
6810         findParentNode : function(simpleSelector, maxDepth, returnEl){
6811             var p = Roo.fly(this.dom.parentNode, '_internal');
6812             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6813         },
6814
6815         /**
6816          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6817          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6818          * @param {String} selector The simple selector to test
6819          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6820                 search as a number or element (defaults to 10 || document.body)
6821          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6822          */
6823         up : function(simpleSelector, maxDepth){
6824             return this.findParentNode(simpleSelector, maxDepth, true);
6825         },
6826
6827
6828
6829         /**
6830          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6831          * @param {String} selector The simple selector to test
6832          * @return {Boolean} True if this element matches the selector, else false
6833          */
6834         is : function(simpleSelector){
6835             return Roo.DomQuery.is(this.dom, simpleSelector);
6836         },
6837
6838         /**
6839          * Perform animation on this element.
6840          * @param {Object} args The YUI animation control args
6841          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6842          * @param {Function} onComplete (optional) Function to call when animation completes
6843          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6844          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6845          * @return {Roo.Element} this
6846          */
6847         animate : function(args, duration, onComplete, easing, animType){
6848             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6849             return this;
6850         },
6851
6852         /*
6853          * @private Internal animation call
6854          */
6855         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6856             animType = animType || 'run';
6857             opt = opt || {};
6858             var anim = Roo.lib.Anim[animType](
6859                 this.dom, args,
6860                 (opt.duration || defaultDur) || .35,
6861                 (opt.easing || defaultEase) || 'easeOut',
6862                 function(){
6863                     Roo.callback(cb, this);
6864                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6865                 },
6866                 this
6867             );
6868             opt.anim = anim;
6869             return anim;
6870         },
6871
6872         // private legacy anim prep
6873         preanim : function(a, i){
6874             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6875         },
6876
6877         /**
6878          * Removes worthless text nodes
6879          * @param {Boolean} forceReclean (optional) By default the element
6880          * keeps track if it has been cleaned already so
6881          * you can call this over and over. However, if you update the element and
6882          * need to force a reclean, you can pass true.
6883          */
6884         clean : function(forceReclean){
6885             if(this.isCleaned && forceReclean !== true){
6886                 return this;
6887             }
6888             var ns = /\S/;
6889             var d = this.dom, n = d.firstChild, ni = -1;
6890             while(n){
6891                 var nx = n.nextSibling;
6892                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6893                     d.removeChild(n);
6894                 }else{
6895                     n.nodeIndex = ++ni;
6896                 }
6897                 n = nx;
6898             }
6899             this.isCleaned = true;
6900             return this;
6901         },
6902
6903         // private
6904         calcOffsetsTo : function(el){
6905             el = Roo.get(el);
6906             var d = el.dom;
6907             var restorePos = false;
6908             if(el.getStyle('position') == 'static'){
6909                 el.position('relative');
6910                 restorePos = true;
6911             }
6912             var x = 0, y =0;
6913             var op = this.dom;
6914             while(op && op != d && op.tagName != 'HTML'){
6915                 x+= op.offsetLeft;
6916                 y+= op.offsetTop;
6917                 op = op.offsetParent;
6918             }
6919             if(restorePos){
6920                 el.position('static');
6921             }
6922             return [x, y];
6923         },
6924
6925         /**
6926          * Scrolls this element into view within the passed container.
6927          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6928          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6929          * @return {Roo.Element} this
6930          */
6931         scrollIntoView : function(container, hscroll){
6932             var c = Roo.getDom(container) || document.body;
6933             var el = this.dom;
6934
6935             var o = this.calcOffsetsTo(c),
6936                 l = o[0],
6937                 t = o[1],
6938                 b = t+el.offsetHeight,
6939                 r = l+el.offsetWidth;
6940
6941             var ch = c.clientHeight;
6942             var ct = parseInt(c.scrollTop, 10);
6943             var cl = parseInt(c.scrollLeft, 10);
6944             var cb = ct + ch;
6945             var cr = cl + c.clientWidth;
6946
6947             if(t < ct){
6948                 c.scrollTop = t;
6949             }else if(b > cb){
6950                 c.scrollTop = b-ch;
6951             }
6952
6953             if(hscroll !== false){
6954                 if(l < cl){
6955                     c.scrollLeft = l;
6956                 }else if(r > cr){
6957                     c.scrollLeft = r-c.clientWidth;
6958                 }
6959             }
6960             return this;
6961         },
6962
6963         // private
6964         scrollChildIntoView : function(child, hscroll){
6965             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6966         },
6967
6968         /**
6969          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6970          * the new height may not be available immediately.
6971          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6972          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6973          * @param {Function} onComplete (optional) Function to call when animation completes
6974          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6975          * @return {Roo.Element} this
6976          */
6977         autoHeight : function(animate, duration, onComplete, easing){
6978             var oldHeight = this.getHeight();
6979             this.clip();
6980             this.setHeight(1); // force clipping
6981             setTimeout(function(){
6982                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6983                 if(!animate){
6984                     this.setHeight(height);
6985                     this.unclip();
6986                     if(typeof onComplete == "function"){
6987                         onComplete();
6988                     }
6989                 }else{
6990                     this.setHeight(oldHeight); // restore original height
6991                     this.setHeight(height, animate, duration, function(){
6992                         this.unclip();
6993                         if(typeof onComplete == "function") onComplete();
6994                     }.createDelegate(this), easing);
6995                 }
6996             }.createDelegate(this), 0);
6997             return this;
6998         },
6999
7000         /**
7001          * Returns true if this element is an ancestor of the passed element
7002          * @param {HTMLElement/String} el The element to check
7003          * @return {Boolean} True if this element is an ancestor of el, else false
7004          */
7005         contains : function(el){
7006             if(!el){return false;}
7007             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7008         },
7009
7010         /**
7011          * Checks whether the element is currently visible using both visibility and display properties.
7012          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7013          * @return {Boolean} True if the element is currently visible, else false
7014          */
7015         isVisible : function(deep) {
7016             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7017             if(deep !== true || !vis){
7018                 return vis;
7019             }
7020             var p = this.dom.parentNode;
7021             while(p && p.tagName.toLowerCase() != "body"){
7022                 if(!Roo.fly(p, '_isVisible').isVisible()){
7023                     return false;
7024                 }
7025                 p = p.parentNode;
7026             }
7027             return true;
7028         },
7029
7030         /**
7031          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7032          * @param {String} selector The CSS selector
7033          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7034          * @return {CompositeElement/CompositeElementLite} The composite element
7035          */
7036         select : function(selector, unique){
7037             return El.select(selector, unique, this.dom);
7038         },
7039
7040         /**
7041          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7042          * @param {String} selector The CSS selector
7043          * @return {Array} An array of the matched nodes
7044          */
7045         query : function(selector, unique){
7046             return Roo.DomQuery.select(selector, this.dom);
7047         },
7048
7049         /**
7050          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7051          * @param {String} selector The CSS selector
7052          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7053          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7054          */
7055         child : function(selector, returnDom){
7056             var n = Roo.DomQuery.selectNode(selector, this.dom);
7057             return returnDom ? n : Roo.get(n);
7058         },
7059
7060         /**
7061          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7062          * @param {String} selector The CSS selector
7063          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7064          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7065          */
7066         down : function(selector, returnDom){
7067             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7068             return returnDom ? n : Roo.get(n);
7069         },
7070
7071         /**
7072          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7073          * @param {String} group The group the DD object is member of
7074          * @param {Object} config The DD config object
7075          * @param {Object} overrides An object containing methods to override/implement on the DD object
7076          * @return {Roo.dd.DD} The DD object
7077          */
7078         initDD : function(group, config, overrides){
7079             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7080             return Roo.apply(dd, overrides);
7081         },
7082
7083         /**
7084          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7085          * @param {String} group The group the DDProxy object is member of
7086          * @param {Object} config The DDProxy config object
7087          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7088          * @return {Roo.dd.DDProxy} The DDProxy object
7089          */
7090         initDDProxy : function(group, config, overrides){
7091             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7092             return Roo.apply(dd, overrides);
7093         },
7094
7095         /**
7096          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7097          * @param {String} group The group the DDTarget object is member of
7098          * @param {Object} config The DDTarget config object
7099          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7100          * @return {Roo.dd.DDTarget} The DDTarget object
7101          */
7102         initDDTarget : function(group, config, overrides){
7103             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7104             return Roo.apply(dd, overrides);
7105         },
7106
7107         /**
7108          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7109          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7110          * @param {Boolean} visible Whether the element is visible
7111          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7112          * @return {Roo.Element} this
7113          */
7114          setVisible : function(visible, animate){
7115             if(!animate || !A){
7116                 if(this.visibilityMode == El.DISPLAY){
7117                     this.setDisplayed(visible);
7118                 }else{
7119                     this.fixDisplay();
7120                     this.dom.style.visibility = visible ? "visible" : "hidden";
7121                 }
7122             }else{
7123                 // closure for composites
7124                 var dom = this.dom;
7125                 var visMode = this.visibilityMode;
7126                 if(visible){
7127                     this.setOpacity(.01);
7128                     this.setVisible(true);
7129                 }
7130                 this.anim({opacity: { to: (visible?1:0) }},
7131                       this.preanim(arguments, 1),
7132                       null, .35, 'easeIn', function(){
7133                          if(!visible){
7134                              if(visMode == El.DISPLAY){
7135                                  dom.style.display = "none";
7136                              }else{
7137                                  dom.style.visibility = "hidden";
7138                              }
7139                              Roo.get(dom).setOpacity(1);
7140                          }
7141                      });
7142             }
7143             return this;
7144         },
7145
7146         /**
7147          * Returns true if display is not "none"
7148          * @return {Boolean}
7149          */
7150         isDisplayed : function() {
7151             return this.getStyle("display") != "none";
7152         },
7153
7154         /**
7155          * Toggles the element's visibility or display, depending on visibility mode.
7156          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7157          * @return {Roo.Element} this
7158          */
7159         toggle : function(animate){
7160             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7161             return this;
7162         },
7163
7164         /**
7165          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7166          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7167          * @return {Roo.Element} this
7168          */
7169         setDisplayed : function(value) {
7170             if(typeof value == "boolean"){
7171                value = value ? this.originalDisplay : "none";
7172             }
7173             this.setStyle("display", value);
7174             return this;
7175         },
7176
7177         /**
7178          * Tries to focus the element. Any exceptions are caught and ignored.
7179          * @return {Roo.Element} this
7180          */
7181         focus : function() {
7182             try{
7183                 this.dom.focus();
7184             }catch(e){}
7185             return this;
7186         },
7187
7188         /**
7189          * Tries to blur the element. Any exceptions are caught and ignored.
7190          * @return {Roo.Element} this
7191          */
7192         blur : function() {
7193             try{
7194                 this.dom.blur();
7195             }catch(e){}
7196             return this;
7197         },
7198
7199         /**
7200          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7201          * @param {String/Array} className The CSS class to add, or an array of classes
7202          * @return {Roo.Element} this
7203          */
7204         addClass : function(className){
7205             if(className instanceof Array){
7206                 for(var i = 0, len = className.length; i < len; i++) {
7207                     this.addClass(className[i]);
7208                 }
7209             }else{
7210                 if(className && !this.hasClass(className)){
7211                     this.dom.className = this.dom.className + " " + className;
7212                 }
7213             }
7214             return this;
7215         },
7216
7217         /**
7218          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7219          * @param {String/Array} className The CSS class to add, or an array of classes
7220          * @return {Roo.Element} this
7221          */
7222         radioClass : function(className){
7223             var siblings = this.dom.parentNode.childNodes;
7224             for(var i = 0; i < siblings.length; i++) {
7225                 var s = siblings[i];
7226                 if(s.nodeType == 1){
7227                     Roo.get(s).removeClass(className);
7228                 }
7229             }
7230             this.addClass(className);
7231             return this;
7232         },
7233
7234         /**
7235          * Removes one or more CSS classes from the element.
7236          * @param {String/Array} className The CSS class to remove, or an array of classes
7237          * @return {Roo.Element} this
7238          */
7239         removeClass : function(className){
7240             if(!className || !this.dom.className){
7241                 return this;
7242             }
7243             if(className instanceof Array){
7244                 for(var i = 0, len = className.length; i < len; i++) {
7245                     this.removeClass(className[i]);
7246                 }
7247             }else{
7248                 if(this.hasClass(className)){
7249                     var re = this.classReCache[className];
7250                     if (!re) {
7251                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7252                        this.classReCache[className] = re;
7253                     }
7254                     this.dom.className =
7255                         this.dom.className.replace(re, " ");
7256                 }
7257             }
7258             return this;
7259         },
7260
7261         // private
7262         classReCache: {},
7263
7264         /**
7265          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7266          * @param {String} className The CSS class to toggle
7267          * @return {Roo.Element} this
7268          */
7269         toggleClass : function(className){
7270             if(this.hasClass(className)){
7271                 this.removeClass(className);
7272             }else{
7273                 this.addClass(className);
7274             }
7275             return this;
7276         },
7277
7278         /**
7279          * Checks if the specified CSS class exists on this element's DOM node.
7280          * @param {String} className The CSS class to check for
7281          * @return {Boolean} True if the class exists, else false
7282          */
7283         hasClass : function(className){
7284             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7285         },
7286
7287         /**
7288          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7289          * @param {String} oldClassName The CSS class to replace
7290          * @param {String} newClassName The replacement CSS class
7291          * @return {Roo.Element} this
7292          */
7293         replaceClass : function(oldClassName, newClassName){
7294             this.removeClass(oldClassName);
7295             this.addClass(newClassName);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns an object with properties matching the styles requested.
7301          * For example, el.getStyles('color', 'font-size', 'width') might return
7302          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7303          * @param {String} style1 A style name
7304          * @param {String} style2 A style name
7305          * @param {String} etc.
7306          * @return {Object} The style object
7307          */
7308         getStyles : function(){
7309             var a = arguments, len = a.length, r = {};
7310             for(var i = 0; i < len; i++){
7311                 r[a[i]] = this.getStyle(a[i]);
7312             }
7313             return r;
7314         },
7315
7316         /**
7317          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7318          * @param {String} property The style property whose value is returned.
7319          * @return {String} The current value of the style property for this element.
7320          */
7321         getStyle : function(){
7322             return view && view.getComputedStyle ?
7323                 function(prop){
7324                     var el = this.dom, v, cs, camel;
7325                     if(prop == 'float'){
7326                         prop = "cssFloat";
7327                     }
7328                     if(el.style && (v = el.style[prop])){
7329                         return v;
7330                     }
7331                     if(cs = view.getComputedStyle(el, "")){
7332                         if(!(camel = propCache[prop])){
7333                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7334                         }
7335                         return cs[camel];
7336                     }
7337                     return null;
7338                 } :
7339                 function(prop){
7340                     var el = this.dom, v, cs, camel;
7341                     if(prop == 'opacity'){
7342                         if(typeof el.style.filter == 'string'){
7343                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7344                             if(m){
7345                                 var fv = parseFloat(m[1]);
7346                                 if(!isNaN(fv)){
7347                                     return fv ? fv / 100 : 0;
7348                                 }
7349                             }
7350                         }
7351                         return 1;
7352                     }else if(prop == 'float'){
7353                         prop = "styleFloat";
7354                     }
7355                     if(!(camel = propCache[prop])){
7356                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7357                     }
7358                     if(v = el.style[camel]){
7359                         return v;
7360                     }
7361                     if(cs = el.currentStyle){
7362                         return cs[camel];
7363                     }
7364                     return null;
7365                 };
7366         }(),
7367
7368         /**
7369          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7370          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7371          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7372          * @return {Roo.Element} this
7373          */
7374         setStyle : function(prop, value){
7375             if(typeof prop == "string"){
7376                 
7377                 if (prop == 'float') {
7378                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7379                     return this;
7380                 }
7381                 
7382                 var camel;
7383                 if(!(camel = propCache[prop])){
7384                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7385                 }
7386                 
7387                 if(camel == 'opacity') {
7388                     this.setOpacity(value);
7389                 }else{
7390                     this.dom.style[camel] = value;
7391                 }
7392             }else{
7393                 for(var style in prop){
7394                     if(typeof prop[style] != "function"){
7395                        this.setStyle(style, prop[style]);
7396                     }
7397                 }
7398             }
7399             return this;
7400         },
7401
7402         /**
7403          * More flexible version of {@link #setStyle} for setting style properties.
7404          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7405          * a function which returns such a specification.
7406          * @return {Roo.Element} this
7407          */
7408         applyStyles : function(style){
7409             Roo.DomHelper.applyStyles(this.dom, style);
7410             return this;
7411         },
7412
7413         /**
7414           * 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).
7415           * @return {Number} The X position of the element
7416           */
7417         getX : function(){
7418             return D.getX(this.dom);
7419         },
7420
7421         /**
7422           * 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).
7423           * @return {Number} The Y position of the element
7424           */
7425         getY : function(){
7426             return D.getY(this.dom);
7427         },
7428
7429         /**
7430           * 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).
7431           * @return {Array} The XY position of the element
7432           */
7433         getXY : function(){
7434             return D.getXY(this.dom);
7435         },
7436
7437         /**
7438          * 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).
7439          * @param {Number} The X position of the element
7440          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7441          * @return {Roo.Element} this
7442          */
7443         setX : function(x, animate){
7444             if(!animate || !A){
7445                 D.setX(this.dom, x);
7446             }else{
7447                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7448             }
7449             return this;
7450         },
7451
7452         /**
7453          * 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).
7454          * @param {Number} The Y position of the element
7455          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7456          * @return {Roo.Element} this
7457          */
7458         setY : function(y, animate){
7459             if(!animate || !A){
7460                 D.setY(this.dom, y);
7461             }else{
7462                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7463             }
7464             return this;
7465         },
7466
7467         /**
7468          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7469          * @param {String} left The left CSS property value
7470          * @return {Roo.Element} this
7471          */
7472         setLeft : function(left){
7473             this.setStyle("left", this.addUnits(left));
7474             return this;
7475         },
7476
7477         /**
7478          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7479          * @param {String} top The top CSS property value
7480          * @return {Roo.Element} this
7481          */
7482         setTop : function(top){
7483             this.setStyle("top", this.addUnits(top));
7484             return this;
7485         },
7486
7487         /**
7488          * Sets the element's CSS right style.
7489          * @param {String} right The right CSS property value
7490          * @return {Roo.Element} this
7491          */
7492         setRight : function(right){
7493             this.setStyle("right", this.addUnits(right));
7494             return this;
7495         },
7496
7497         /**
7498          * Sets the element's CSS bottom style.
7499          * @param {String} bottom The bottom CSS property value
7500          * @return {Roo.Element} this
7501          */
7502         setBottom : function(bottom){
7503             this.setStyle("bottom", this.addUnits(bottom));
7504             return this;
7505         },
7506
7507         /**
7508          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7509          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7510          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7511          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7512          * @return {Roo.Element} this
7513          */
7514         setXY : function(pos, animate){
7515             if(!animate || !A){
7516                 D.setXY(this.dom, pos);
7517             }else{
7518                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7519             }
7520             return this;
7521         },
7522
7523         /**
7524          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7525          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7526          * @param {Number} x X value for new position (coordinates are page-based)
7527          * @param {Number} y Y value for new position (coordinates are page-based)
7528          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7529          * @return {Roo.Element} this
7530          */
7531         setLocation : function(x, y, animate){
7532             this.setXY([x, y], this.preanim(arguments, 2));
7533             return this;
7534         },
7535
7536         /**
7537          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7538          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7539          * @param {Number} x X value for new position (coordinates are page-based)
7540          * @param {Number} y Y value for new position (coordinates are page-based)
7541          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7542          * @return {Roo.Element} this
7543          */
7544         moveTo : function(x, y, animate){
7545             this.setXY([x, y], this.preanim(arguments, 2));
7546             return this;
7547         },
7548
7549         /**
7550          * Returns the region of the given element.
7551          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7552          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7553          */
7554         getRegion : function(){
7555             return D.getRegion(this.dom);
7556         },
7557
7558         /**
7559          * Returns the offset height of the element
7560          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7561          * @return {Number} The element's height
7562          */
7563         getHeight : function(contentHeight){
7564             var h = this.dom.offsetHeight || 0;
7565             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7566         },
7567
7568         /**
7569          * Returns the offset width of the element
7570          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7571          * @return {Number} The element's width
7572          */
7573         getWidth : function(contentWidth){
7574             var w = this.dom.offsetWidth || 0;
7575             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7576         },
7577
7578         /**
7579          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7580          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7581          * if a height has not been set using CSS.
7582          * @return {Number}
7583          */
7584         getComputedHeight : function(){
7585             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7586             if(!h){
7587                 h = parseInt(this.getStyle('height'), 10) || 0;
7588                 if(!this.isBorderBox()){
7589                     h += this.getFrameWidth('tb');
7590                 }
7591             }
7592             return h;
7593         },
7594
7595         /**
7596          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7597          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7598          * if a width has not been set using CSS.
7599          * @return {Number}
7600          */
7601         getComputedWidth : function(){
7602             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7603             if(!w){
7604                 w = parseInt(this.getStyle('width'), 10) || 0;
7605                 if(!this.isBorderBox()){
7606                     w += this.getFrameWidth('lr');
7607                 }
7608             }
7609             return w;
7610         },
7611
7612         /**
7613          * Returns the size of the element.
7614          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7615          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7616          */
7617         getSize : function(contentSize){
7618             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7619         },
7620
7621         /**
7622          * Returns the width and height of the viewport.
7623          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7624          */
7625         getViewSize : function(){
7626             var d = this.dom, doc = document, aw = 0, ah = 0;
7627             if(d == doc || d == doc.body){
7628                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7629             }else{
7630                 return {
7631                     width : d.clientWidth,
7632                     height: d.clientHeight
7633                 };
7634             }
7635         },
7636
7637         /**
7638          * Returns the value of the "value" attribute
7639          * @param {Boolean} asNumber true to parse the value as a number
7640          * @return {String/Number}
7641          */
7642         getValue : function(asNumber){
7643             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7644         },
7645
7646         // private
7647         adjustWidth : function(width){
7648             if(typeof width == "number"){
7649                 if(this.autoBoxAdjust && !this.isBorderBox()){
7650                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7651                 }
7652                 if(width < 0){
7653                     width = 0;
7654                 }
7655             }
7656             return width;
7657         },
7658
7659         // private
7660         adjustHeight : function(height){
7661             if(typeof height == "number"){
7662                if(this.autoBoxAdjust && !this.isBorderBox()){
7663                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7664                }
7665                if(height < 0){
7666                    height = 0;
7667                }
7668             }
7669             return height;
7670         },
7671
7672         /**
7673          * Set the width of the element
7674          * @param {Number} width The new width
7675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7676          * @return {Roo.Element} this
7677          */
7678         setWidth : function(width, animate){
7679             width = this.adjustWidth(width);
7680             if(!animate || !A){
7681                 this.dom.style.width = this.addUnits(width);
7682             }else{
7683                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7684             }
7685             return this;
7686         },
7687
7688         /**
7689          * Set the height of the element
7690          * @param {Number} height The new height
7691          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7692          * @return {Roo.Element} this
7693          */
7694          setHeight : function(height, animate){
7695             height = this.adjustHeight(height);
7696             if(!animate || !A){
7697                 this.dom.style.height = this.addUnits(height);
7698             }else{
7699                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7700             }
7701             return this;
7702         },
7703
7704         /**
7705          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7706          * @param {Number} width The new width
7707          * @param {Number} height The new height
7708          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711          setSize : function(width, height, animate){
7712             if(typeof width == "object"){ // in case of object from getSize()
7713                 height = width.height; width = width.width;
7714             }
7715             width = this.adjustWidth(width); height = this.adjustHeight(height);
7716             if(!animate || !A){
7717                 this.dom.style.width = this.addUnits(width);
7718                 this.dom.style.height = this.addUnits(height);
7719             }else{
7720                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7721             }
7722             return this;
7723         },
7724
7725         /**
7726          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7727          * @param {Number} x X value for new position (coordinates are page-based)
7728          * @param {Number} y Y value for new position (coordinates are page-based)
7729          * @param {Number} width The new width
7730          * @param {Number} height The new height
7731          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7732          * @return {Roo.Element} this
7733          */
7734         setBounds : function(x, y, width, height, animate){
7735             if(!animate || !A){
7736                 this.setSize(width, height);
7737                 this.setLocation(x, y);
7738             }else{
7739                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7740                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7741                               this.preanim(arguments, 4), 'motion');
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * 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.
7748          * @param {Roo.lib.Region} region The region to fill
7749          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752         setRegion : function(region, animate){
7753             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7754             return this;
7755         },
7756
7757         /**
7758          * Appends an event handler
7759          *
7760          * @param {String}   eventName     The type of event to append
7761          * @param {Function} fn        The method the event invokes
7762          * @param {Object} scope       (optional) The scope (this object) of the fn
7763          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7764          */
7765         addListener : function(eventName, fn, scope, options){
7766             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7767         },
7768
7769         /**
7770          * Removes an event handler from this element
7771          * @param {String} eventName the type of event to remove
7772          * @param {Function} fn the method the event invokes
7773          * @return {Roo.Element} this
7774          */
7775         removeListener : function(eventName, fn){
7776             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7777             return this;
7778         },
7779
7780         /**
7781          * Removes all previous added listeners from this element
7782          * @return {Roo.Element} this
7783          */
7784         removeAllListeners : function(){
7785             E.purgeElement(this.dom);
7786             return this;
7787         },
7788
7789         relayEvent : function(eventName, observable){
7790             this.on(eventName, function(e){
7791                 observable.fireEvent(eventName, e);
7792             });
7793         },
7794
7795         /**
7796          * Set the opacity of the element
7797          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7798          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7799          * @return {Roo.Element} this
7800          */
7801          setOpacity : function(opacity, animate){
7802             if(!animate || !A){
7803                 var s = this.dom.style;
7804                 if(Roo.isIE){
7805                     s.zoom = 1;
7806                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7807                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7808                 }else{
7809                     s.opacity = opacity;
7810                 }
7811             }else{
7812                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7813             }
7814             return this;
7815         },
7816
7817         /**
7818          * Gets the left X coordinate
7819          * @param {Boolean} local True to get the local css position instead of page coordinate
7820          * @return {Number}
7821          */
7822         getLeft : function(local){
7823             if(!local){
7824                 return this.getX();
7825             }else{
7826                 return parseInt(this.getStyle("left"), 10) || 0;
7827             }
7828         },
7829
7830         /**
7831          * Gets the right X coordinate of the element (element X position + element width)
7832          * @param {Boolean} local True to get the local css position instead of page coordinate
7833          * @return {Number}
7834          */
7835         getRight : function(local){
7836             if(!local){
7837                 return this.getX() + this.getWidth();
7838             }else{
7839                 return (this.getLeft(true) + this.getWidth()) || 0;
7840             }
7841         },
7842
7843         /**
7844          * Gets the top Y coordinate
7845          * @param {Boolean} local True to get the local css position instead of page coordinate
7846          * @return {Number}
7847          */
7848         getTop : function(local) {
7849             if(!local){
7850                 return this.getY();
7851             }else{
7852                 return parseInt(this.getStyle("top"), 10) || 0;
7853             }
7854         },
7855
7856         /**
7857          * Gets the bottom Y coordinate of the element (element Y position + element height)
7858          * @param {Boolean} local True to get the local css position instead of page coordinate
7859          * @return {Number}
7860          */
7861         getBottom : function(local){
7862             if(!local){
7863                 return this.getY() + this.getHeight();
7864             }else{
7865                 return (this.getTop(true) + this.getHeight()) || 0;
7866             }
7867         },
7868
7869         /**
7870         * Initializes positioning on this element. If a desired position is not passed, it will make the
7871         * the element positioned relative IF it is not already positioned.
7872         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7873         * @param {Number} zIndex (optional) The zIndex to apply
7874         * @param {Number} x (optional) Set the page X position
7875         * @param {Number} y (optional) Set the page Y position
7876         */
7877         position : function(pos, zIndex, x, y){
7878             if(!pos){
7879                if(this.getStyle('position') == 'static'){
7880                    this.setStyle('position', 'relative');
7881                }
7882             }else{
7883                 this.setStyle("position", pos);
7884             }
7885             if(zIndex){
7886                 this.setStyle("z-index", zIndex);
7887             }
7888             if(x !== undefined && y !== undefined){
7889                 this.setXY([x, y]);
7890             }else if(x !== undefined){
7891                 this.setX(x);
7892             }else if(y !== undefined){
7893                 this.setY(y);
7894             }
7895         },
7896
7897         /**
7898         * Clear positioning back to the default when the document was loaded
7899         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7900         * @return {Roo.Element} this
7901          */
7902         clearPositioning : function(value){
7903             value = value ||'';
7904             this.setStyle({
7905                 "left": value,
7906                 "right": value,
7907                 "top": value,
7908                 "bottom": value,
7909                 "z-index": "",
7910                 "position" : "static"
7911             });
7912             return this;
7913         },
7914
7915         /**
7916         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7917         * snapshot before performing an update and then restoring the element.
7918         * @return {Object}
7919         */
7920         getPositioning : function(){
7921             var l = this.getStyle("left");
7922             var t = this.getStyle("top");
7923             return {
7924                 "position" : this.getStyle("position"),
7925                 "left" : l,
7926                 "right" : l ? "" : this.getStyle("right"),
7927                 "top" : t,
7928                 "bottom" : t ? "" : this.getStyle("bottom"),
7929                 "z-index" : this.getStyle("z-index")
7930             };
7931         },
7932
7933         /**
7934          * Gets the width of the border(s) for the specified side(s)
7935          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7936          * passing lr would get the border (l)eft width + the border (r)ight width.
7937          * @return {Number} The width of the sides passed added together
7938          */
7939         getBorderWidth : function(side){
7940             return this.addStyles(side, El.borders);
7941         },
7942
7943         /**
7944          * Gets the width of the padding(s) for the specified side(s)
7945          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7946          * passing lr would get the padding (l)eft + the padding (r)ight.
7947          * @return {Number} The padding of the sides passed added together
7948          */
7949         getPadding : function(side){
7950             return this.addStyles(side, El.paddings);
7951         },
7952
7953         /**
7954         * Set positioning with an object returned by getPositioning().
7955         * @param {Object} posCfg
7956         * @return {Roo.Element} this
7957          */
7958         setPositioning : function(pc){
7959             this.applyStyles(pc);
7960             if(pc.right == "auto"){
7961                 this.dom.style.right = "";
7962             }
7963             if(pc.bottom == "auto"){
7964                 this.dom.style.bottom = "";
7965             }
7966             return this;
7967         },
7968
7969         // private
7970         fixDisplay : function(){
7971             if(this.getStyle("display") == "none"){
7972                 this.setStyle("visibility", "hidden");
7973                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7974                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7975                     this.setStyle("display", "block");
7976                 }
7977             }
7978         },
7979
7980         /**
7981          * Quick set left and top adding default units
7982          * @param {String} left The left CSS property value
7983          * @param {String} top The top CSS property value
7984          * @return {Roo.Element} this
7985          */
7986          setLeftTop : function(left, top){
7987             this.dom.style.left = this.addUnits(left);
7988             this.dom.style.top = this.addUnits(top);
7989             return this;
7990         },
7991
7992         /**
7993          * Move this element relative to its current position.
7994          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7995          * @param {Number} distance How far to move the element in pixels
7996          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7997          * @return {Roo.Element} this
7998          */
7999          move : function(direction, distance, animate){
8000             var xy = this.getXY();
8001             direction = direction.toLowerCase();
8002             switch(direction){
8003                 case "l":
8004                 case "left":
8005                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8006                     break;
8007                case "r":
8008                case "right":
8009                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8010                     break;
8011                case "t":
8012                case "top":
8013                case "up":
8014                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8015                     break;
8016                case "b":
8017                case "bottom":
8018                case "down":
8019                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8020                     break;
8021             }
8022             return this;
8023         },
8024
8025         /**
8026          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8027          * @return {Roo.Element} this
8028          */
8029         clip : function(){
8030             if(!this.isClipped){
8031                this.isClipped = true;
8032                this.originalClip = {
8033                    "o": this.getStyle("overflow"),
8034                    "x": this.getStyle("overflow-x"),
8035                    "y": this.getStyle("overflow-y")
8036                };
8037                this.setStyle("overflow", "hidden");
8038                this.setStyle("overflow-x", "hidden");
8039                this.setStyle("overflow-y", "hidden");
8040             }
8041             return this;
8042         },
8043
8044         /**
8045          *  Return clipping (overflow) to original clipping before clip() was called
8046          * @return {Roo.Element} this
8047          */
8048         unclip : function(){
8049             if(this.isClipped){
8050                 this.isClipped = false;
8051                 var o = this.originalClip;
8052                 if(o.o){this.setStyle("overflow", o.o);}
8053                 if(o.x){this.setStyle("overflow-x", o.x);}
8054                 if(o.y){this.setStyle("overflow-y", o.y);}
8055             }
8056             return this;
8057         },
8058
8059
8060         /**
8061          * Gets the x,y coordinates specified by the anchor position on the element.
8062          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8063          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8064          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8065          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8066          * @return {Array} [x, y] An array containing the element's x and y coordinates
8067          */
8068         getAnchorXY : function(anchor, local, s){
8069             //Passing a different size is useful for pre-calculating anchors,
8070             //especially for anchored animations that change the el size.
8071
8072             var w, h, vp = false;
8073             if(!s){
8074                 var d = this.dom;
8075                 if(d == document.body || d == document){
8076                     vp = true;
8077                     w = D.getViewWidth(); h = D.getViewHeight();
8078                 }else{
8079                     w = this.getWidth(); h = this.getHeight();
8080                 }
8081             }else{
8082                 w = s.width;  h = s.height;
8083             }
8084             var x = 0, y = 0, r = Math.round;
8085             switch((anchor || "tl").toLowerCase()){
8086                 case "c":
8087                     x = r(w*.5);
8088                     y = r(h*.5);
8089                 break;
8090                 case "t":
8091                     x = r(w*.5);
8092                     y = 0;
8093                 break;
8094                 case "l":
8095                     x = 0;
8096                     y = r(h*.5);
8097                 break;
8098                 case "r":
8099                     x = w;
8100                     y = r(h*.5);
8101                 break;
8102                 case "b":
8103                     x = r(w*.5);
8104                     y = h;
8105                 break;
8106                 case "tl":
8107                     x = 0;
8108                     y = 0;
8109                 break;
8110                 case "bl":
8111                     x = 0;
8112                     y = h;
8113                 break;
8114                 case "br":
8115                     x = w;
8116                     y = h;
8117                 break;
8118                 case "tr":
8119                     x = w;
8120                     y = 0;
8121                 break;
8122             }
8123             if(local === true){
8124                 return [x, y];
8125             }
8126             if(vp){
8127                 var sc = this.getScroll();
8128                 return [x + sc.left, y + sc.top];
8129             }
8130             //Add the element's offset xy
8131             var o = this.getXY();
8132             return [x+o[0], y+o[1]];
8133         },
8134
8135         /**
8136          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8137          * supported position values.
8138          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8139          * @param {String} position The position to align to.
8140          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8141          * @return {Array} [x, y]
8142          */
8143         getAlignToXY : function(el, p, o){
8144             el = Roo.get(el);
8145             var d = this.dom;
8146             if(!el.dom){
8147                 throw "Element.alignTo with an element that doesn't exist";
8148             }
8149             var c = false; //constrain to viewport
8150             var p1 = "", p2 = "";
8151             o = o || [0,0];
8152
8153             if(!p){
8154                 p = "tl-bl";
8155             }else if(p == "?"){
8156                 p = "tl-bl?";
8157             }else if(p.indexOf("-") == -1){
8158                 p = "tl-" + p;
8159             }
8160             p = p.toLowerCase();
8161             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8162             if(!m){
8163                throw "Element.alignTo with an invalid alignment " + p;
8164             }
8165             p1 = m[1]; p2 = m[2]; c = !!m[3];
8166
8167             //Subtract the aligned el's internal xy from the target's offset xy
8168             //plus custom offset to get the aligned el's new offset xy
8169             var a1 = this.getAnchorXY(p1, true);
8170             var a2 = el.getAnchorXY(p2, false);
8171             var x = a2[0] - a1[0] + o[0];
8172             var y = a2[1] - a1[1] + o[1];
8173             if(c){
8174                 //constrain the aligned el to viewport if necessary
8175                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8176                 // 5px of margin for ie
8177                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8178
8179                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8180                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8181                 //otherwise swap the aligned el to the opposite border of the target.
8182                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8183                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8184                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8185                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8186
8187                var doc = document;
8188                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8189                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8190
8191                if((x+w) > dw + scrollX){
8192                     x = swapX ? r.left-w : dw+scrollX-w;
8193                 }
8194                if(x < scrollX){
8195                    x = swapX ? r.right : scrollX;
8196                }
8197                if((y+h) > dh + scrollY){
8198                     y = swapY ? r.top-h : dh+scrollY-h;
8199                 }
8200                if (y < scrollY){
8201                    y = swapY ? r.bottom : scrollY;
8202                }
8203             }
8204             return [x,y];
8205         },
8206
8207         // private
8208         getConstrainToXY : function(){
8209             var os = {top:0, left:0, bottom:0, right: 0};
8210
8211             return function(el, local, offsets, proposedXY){
8212                 el = Roo.get(el);
8213                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8214
8215                 var vw, vh, vx = 0, vy = 0;
8216                 if(el.dom == document.body || el.dom == document){
8217                     vw = Roo.lib.Dom.getViewWidth();
8218                     vh = Roo.lib.Dom.getViewHeight();
8219                 }else{
8220                     vw = el.dom.clientWidth;
8221                     vh = el.dom.clientHeight;
8222                     if(!local){
8223                         var vxy = el.getXY();
8224                         vx = vxy[0];
8225                         vy = vxy[1];
8226                     }
8227                 }
8228
8229                 var s = el.getScroll();
8230
8231                 vx += offsets.left + s.left;
8232                 vy += offsets.top + s.top;
8233
8234                 vw -= offsets.right;
8235                 vh -= offsets.bottom;
8236
8237                 var vr = vx+vw;
8238                 var vb = vy+vh;
8239
8240                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8241                 var x = xy[0], y = xy[1];
8242                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8243
8244                 // only move it if it needs it
8245                 var moved = false;
8246
8247                 // first validate right/bottom
8248                 if((x + w) > vr){
8249                     x = vr - w;
8250                     moved = true;
8251                 }
8252                 if((y + h) > vb){
8253                     y = vb - h;
8254                     moved = true;
8255                 }
8256                 // then make sure top/left isn't negative
8257                 if(x < vx){
8258                     x = vx;
8259                     moved = true;
8260                 }
8261                 if(y < vy){
8262                     y = vy;
8263                     moved = true;
8264                 }
8265                 return moved ? [x, y] : false;
8266             };
8267         }(),
8268
8269         // private
8270         adjustForConstraints : function(xy, parent, offsets){
8271             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8272         },
8273
8274         /**
8275          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8276          * document it aligns it to the viewport.
8277          * The position parameter is optional, and can be specified in any one of the following formats:
8278          * <ul>
8279          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8280          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8281          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8282          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8283          *   <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
8284          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8285          * </ul>
8286          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8287          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8288          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8289          * that specified in order to enforce the viewport constraints.
8290          * Following are all of the supported anchor positions:
8291     <pre>
8292     Value  Description
8293     -----  -----------------------------
8294     tl     The top left corner (default)
8295     t      The center of the top edge
8296     tr     The top right corner
8297     l      The center of the left edge
8298     c      In the center of the element
8299     r      The center of the right edge
8300     bl     The bottom left corner
8301     b      The center of the bottom edge
8302     br     The bottom right corner
8303     </pre>
8304     Example Usage:
8305     <pre><code>
8306     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8307     el.alignTo("other-el");
8308
8309     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8310     el.alignTo("other-el", "tr?");
8311
8312     // align the bottom right corner of el with the center left edge of other-el
8313     el.alignTo("other-el", "br-l?");
8314
8315     // align the center of el with the bottom left corner of other-el and
8316     // adjust the x position by -6 pixels (and the y position by 0)
8317     el.alignTo("other-el", "c-bl", [-6, 0]);
8318     </code></pre>
8319          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8320          * @param {String} position The position to align to.
8321          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8322          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8323          * @return {Roo.Element} this
8324          */
8325         alignTo : function(element, position, offsets, animate){
8326             var xy = this.getAlignToXY(element, position, offsets);
8327             this.setXY(xy, this.preanim(arguments, 3));
8328             return this;
8329         },
8330
8331         /**
8332          * Anchors an element to another element and realigns it when the window is resized.
8333          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8334          * @param {String} position The position to align to.
8335          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8336          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8337          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8338          * is a number, it is used as the buffer delay (defaults to 50ms).
8339          * @param {Function} callback The function to call after the animation finishes
8340          * @return {Roo.Element} this
8341          */
8342         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8343             var action = function(){
8344                 this.alignTo(el, alignment, offsets, animate);
8345                 Roo.callback(callback, this);
8346             };
8347             Roo.EventManager.onWindowResize(action, this);
8348             var tm = typeof monitorScroll;
8349             if(tm != 'undefined'){
8350                 Roo.EventManager.on(window, 'scroll', action, this,
8351                     {buffer: tm == 'number' ? monitorScroll : 50});
8352             }
8353             action.call(this); // align immediately
8354             return this;
8355         },
8356         /**
8357          * Clears any opacity settings from this element. Required in some cases for IE.
8358          * @return {Roo.Element} this
8359          */
8360         clearOpacity : function(){
8361             if (window.ActiveXObject) {
8362                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8363                     this.dom.style.filter = "";
8364                 }
8365             } else {
8366                 this.dom.style.opacity = "";
8367                 this.dom.style["-moz-opacity"] = "";
8368                 this.dom.style["-khtml-opacity"] = "";
8369             }
8370             return this;
8371         },
8372
8373         /**
8374          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8375          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8376          * @return {Roo.Element} this
8377          */
8378         hide : function(animate){
8379             this.setVisible(false, this.preanim(arguments, 0));
8380             return this;
8381         },
8382
8383         /**
8384         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8385         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8386          * @return {Roo.Element} this
8387          */
8388         show : function(animate){
8389             this.setVisible(true, this.preanim(arguments, 0));
8390             return this;
8391         },
8392
8393         /**
8394          * @private Test if size has a unit, otherwise appends the default
8395          */
8396         addUnits : function(size){
8397             return Roo.Element.addUnits(size, this.defaultUnit);
8398         },
8399
8400         /**
8401          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8402          * @return {Roo.Element} this
8403          */
8404         beginMeasure : function(){
8405             var el = this.dom;
8406             if(el.offsetWidth || el.offsetHeight){
8407                 return this; // offsets work already
8408             }
8409             var changed = [];
8410             var p = this.dom, b = document.body; // start with this element
8411             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8412                 var pe = Roo.get(p);
8413                 if(pe.getStyle('display') == 'none'){
8414                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8415                     p.style.visibility = "hidden";
8416                     p.style.display = "block";
8417                 }
8418                 p = p.parentNode;
8419             }
8420             this._measureChanged = changed;
8421             return this;
8422
8423         },
8424
8425         /**
8426          * Restores displays to before beginMeasure was called
8427          * @return {Roo.Element} this
8428          */
8429         endMeasure : function(){
8430             var changed = this._measureChanged;
8431             if(changed){
8432                 for(var i = 0, len = changed.length; i < len; i++) {
8433                     var r = changed[i];
8434                     r.el.style.visibility = r.visibility;
8435                     r.el.style.display = "none";
8436                 }
8437                 this._measureChanged = null;
8438             }
8439             return this;
8440         },
8441
8442         /**
8443         * Update the innerHTML of this element, optionally searching for and processing scripts
8444         * @param {String} html The new HTML
8445         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8446         * @param {Function} callback For async script loading you can be noticed when the update completes
8447         * @return {Roo.Element} this
8448          */
8449         update : function(html, loadScripts, callback){
8450             if(typeof html == "undefined"){
8451                 html = "";
8452             }
8453             if(loadScripts !== true){
8454                 this.dom.innerHTML = html;
8455                 if(typeof callback == "function"){
8456                     callback();
8457                 }
8458                 return this;
8459             }
8460             var id = Roo.id();
8461             var dom = this.dom;
8462
8463             html += '<span id="' + id + '"></span>';
8464
8465             E.onAvailable(id, function(){
8466                 var hd = document.getElementsByTagName("head")[0];
8467                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8468                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8469                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8470
8471                 var match;
8472                 while(match = re.exec(html)){
8473                     var attrs = match[1];
8474                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8475                     if(srcMatch && srcMatch[2]){
8476                        var s = document.createElement("script");
8477                        s.src = srcMatch[2];
8478                        var typeMatch = attrs.match(typeRe);
8479                        if(typeMatch && typeMatch[2]){
8480                            s.type = typeMatch[2];
8481                        }
8482                        hd.appendChild(s);
8483                     }else if(match[2] && match[2].length > 0){
8484                         if(window.execScript) {
8485                            window.execScript(match[2]);
8486                         } else {
8487                             /**
8488                              * eval:var:id
8489                              * eval:var:dom
8490                              * eval:var:html
8491                              * 
8492                              */
8493                            window.eval(match[2]);
8494                         }
8495                     }
8496                 }
8497                 var el = document.getElementById(id);
8498                 if(el){el.parentNode.removeChild(el);}
8499                 if(typeof callback == "function"){
8500                     callback();
8501                 }
8502             });
8503             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8504             return this;
8505         },
8506
8507         /**
8508          * Direct access to the UpdateManager update() method (takes the same parameters).
8509          * @param {String/Function} url The url for this request or a function to call to get the url
8510          * @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}
8511          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8512          * @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.
8513          * @return {Roo.Element} this
8514          */
8515         load : function(){
8516             var um = this.getUpdateManager();
8517             um.update.apply(um, arguments);
8518             return this;
8519         },
8520
8521         /**
8522         * Gets this element's UpdateManager
8523         * @return {Roo.UpdateManager} The UpdateManager
8524         */
8525         getUpdateManager : function(){
8526             if(!this.updateManager){
8527                 this.updateManager = new Roo.UpdateManager(this);
8528             }
8529             return this.updateManager;
8530         },
8531
8532         /**
8533          * Disables text selection for this element (normalized across browsers)
8534          * @return {Roo.Element} this
8535          */
8536         unselectable : function(){
8537             this.dom.unselectable = "on";
8538             this.swallowEvent("selectstart", true);
8539             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8540             this.addClass("x-unselectable");
8541             return this;
8542         },
8543
8544         /**
8545         * Calculates the x, y to center this element on the screen
8546         * @return {Array} The x, y values [x, y]
8547         */
8548         getCenterXY : function(){
8549             return this.getAlignToXY(document, 'c-c');
8550         },
8551
8552         /**
8553         * Centers the Element in either the viewport, or another Element.
8554         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8555         */
8556         center : function(centerIn){
8557             this.alignTo(centerIn || document, 'c-c');
8558             return this;
8559         },
8560
8561         /**
8562          * Tests various css rules/browsers to determine if this element uses a border box
8563          * @return {Boolean}
8564          */
8565         isBorderBox : function(){
8566             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8567         },
8568
8569         /**
8570          * Return a box {x, y, width, height} that can be used to set another elements
8571          * size/location to match this element.
8572          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8573          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8574          * @return {Object} box An object in the format {x, y, width, height}
8575          */
8576         getBox : function(contentBox, local){
8577             var xy;
8578             if(!local){
8579                 xy = this.getXY();
8580             }else{
8581                 var left = parseInt(this.getStyle("left"), 10) || 0;
8582                 var top = parseInt(this.getStyle("top"), 10) || 0;
8583                 xy = [left, top];
8584             }
8585             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8586             if(!contentBox){
8587                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8588             }else{
8589                 var l = this.getBorderWidth("l")+this.getPadding("l");
8590                 var r = this.getBorderWidth("r")+this.getPadding("r");
8591                 var t = this.getBorderWidth("t")+this.getPadding("t");
8592                 var b = this.getBorderWidth("b")+this.getPadding("b");
8593                 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)};
8594             }
8595             bx.right = bx.x + bx.width;
8596             bx.bottom = bx.y + bx.height;
8597             return bx;
8598         },
8599
8600         /**
8601          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8602          for more information about the sides.
8603          * @param {String} sides
8604          * @return {Number}
8605          */
8606         getFrameWidth : function(sides, onlyContentBox){
8607             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8608         },
8609
8610         /**
8611          * 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.
8612          * @param {Object} box The box to fill {x, y, width, height}
8613          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8614          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8615          * @return {Roo.Element} this
8616          */
8617         setBox : function(box, adjust, animate){
8618             var w = box.width, h = box.height;
8619             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8620                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8621                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8622             }
8623             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8624             return this;
8625         },
8626
8627         /**
8628          * Forces the browser to repaint this element
8629          * @return {Roo.Element} this
8630          */
8631          repaint : function(){
8632             var dom = this.dom;
8633             this.addClass("x-repaint");
8634             setTimeout(function(){
8635                 Roo.get(dom).removeClass("x-repaint");
8636             }, 1);
8637             return this;
8638         },
8639
8640         /**
8641          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8642          * then it returns the calculated width of the sides (see getPadding)
8643          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8644          * @return {Object/Number}
8645          */
8646         getMargins : function(side){
8647             if(!side){
8648                 return {
8649                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8650                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8651                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8652                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8653                 };
8654             }else{
8655                 return this.addStyles(side, El.margins);
8656              }
8657         },
8658
8659         // private
8660         addStyles : function(sides, styles){
8661             var val = 0, v, w;
8662             for(var i = 0, len = sides.length; i < len; i++){
8663                 v = this.getStyle(styles[sides.charAt(i)]);
8664                 if(v){
8665                      w = parseInt(v, 10);
8666                      if(w){ val += w; }
8667                 }
8668             }
8669             return val;
8670         },
8671
8672         /**
8673          * Creates a proxy element of this element
8674          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8675          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8676          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8677          * @return {Roo.Element} The new proxy element
8678          */
8679         createProxy : function(config, renderTo, matchBox){
8680             if(renderTo){
8681                 renderTo = Roo.getDom(renderTo);
8682             }else{
8683                 renderTo = document.body;
8684             }
8685             config = typeof config == "object" ?
8686                 config : {tag : "div", cls: config};
8687             var proxy = Roo.DomHelper.append(renderTo, config, true);
8688             if(matchBox){
8689                proxy.setBox(this.getBox());
8690             }
8691             return proxy;
8692         },
8693
8694         /**
8695          * Puts a mask over this element to disable user interaction. Requires core.css.
8696          * This method can only be applied to elements which accept child nodes.
8697          * @param {String} msg (optional) A message to display in the mask
8698          * @param {String} msgCls (optional) A css class to apply to the msg element
8699          * @return {Element} The mask  element
8700          */
8701         mask : function(msg, msgCls){
8702             if(this.getStyle("position") == "static"){
8703                 this.setStyle("position", "relative");
8704             }
8705             if(!this._mask){
8706                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8707             }
8708             this.addClass("x-masked");
8709             this._mask.setDisplayed(true);
8710             if(typeof msg == 'string'){
8711                 if(!this._maskMsg){
8712                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8713                 }
8714                 var mm = this._maskMsg;
8715                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8716                 mm.dom.firstChild.innerHTML = msg;
8717                 mm.setDisplayed(true);
8718                 mm.center(this);
8719             }
8720             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8721                 this._mask.setHeight(this.getHeight());
8722             }
8723             return this._mask;
8724         },
8725
8726         /**
8727          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8728          * it is cached for reuse.
8729          */
8730         unmask : function(removeEl){
8731             if(this._mask){
8732                 if(removeEl === true){
8733                     this._mask.remove();
8734                     delete this._mask;
8735                     if(this._maskMsg){
8736                         this._maskMsg.remove();
8737                         delete this._maskMsg;
8738                     }
8739                 }else{
8740                     this._mask.setDisplayed(false);
8741                     if(this._maskMsg){
8742                         this._maskMsg.setDisplayed(false);
8743                     }
8744                 }
8745             }
8746             this.removeClass("x-masked");
8747         },
8748
8749         /**
8750          * Returns true if this element is masked
8751          * @return {Boolean}
8752          */
8753         isMasked : function(){
8754             return this._mask && this._mask.isVisible();
8755         },
8756
8757         /**
8758          * Creates an iframe shim for this element to keep selects and other windowed objects from
8759          * showing through.
8760          * @return {Roo.Element} The new shim element
8761          */
8762         createShim : function(){
8763             var el = document.createElement('iframe');
8764             el.frameBorder = 'no';
8765             el.className = 'roo-shim';
8766             if(Roo.isIE && Roo.isSecure){
8767                 el.src = Roo.SSL_SECURE_URL;
8768             }
8769             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8770             shim.autoBoxAdjust = false;
8771             return shim;
8772         },
8773
8774         /**
8775          * Removes this element from the DOM and deletes it from the cache
8776          */
8777         remove : function(){
8778             if(this.dom.parentNode){
8779                 this.dom.parentNode.removeChild(this.dom);
8780             }
8781             delete El.cache[this.dom.id];
8782         },
8783
8784         /**
8785          * Sets up event handlers to add and remove a css class when the mouse is over this element
8786          * @param {String} className
8787          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8788          * mouseout events for children elements
8789          * @return {Roo.Element} this
8790          */
8791         addClassOnOver : function(className, preventFlicker){
8792             this.on("mouseover", function(){
8793                 Roo.fly(this, '_internal').addClass(className);
8794             }, this.dom);
8795             var removeFn = function(e){
8796                 if(preventFlicker !== true || !e.within(this, true)){
8797                     Roo.fly(this, '_internal').removeClass(className);
8798                 }
8799             };
8800             this.on("mouseout", removeFn, this.dom);
8801             return this;
8802         },
8803
8804         /**
8805          * Sets up event handlers to add and remove a css class when this element has the focus
8806          * @param {String} className
8807          * @return {Roo.Element} this
8808          */
8809         addClassOnFocus : function(className){
8810             this.on("focus", function(){
8811                 Roo.fly(this, '_internal').addClass(className);
8812             }, this.dom);
8813             this.on("blur", function(){
8814                 Roo.fly(this, '_internal').removeClass(className);
8815             }, this.dom);
8816             return this;
8817         },
8818         /**
8819          * 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)
8820          * @param {String} className
8821          * @return {Roo.Element} this
8822          */
8823         addClassOnClick : function(className){
8824             var dom = this.dom;
8825             this.on("mousedown", function(){
8826                 Roo.fly(dom, '_internal').addClass(className);
8827                 var d = Roo.get(document);
8828                 var fn = function(){
8829                     Roo.fly(dom, '_internal').removeClass(className);
8830                     d.removeListener("mouseup", fn);
8831                 };
8832                 d.on("mouseup", fn);
8833             });
8834             return this;
8835         },
8836
8837         /**
8838          * Stops the specified event from bubbling and optionally prevents the default action
8839          * @param {String} eventName
8840          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8841          * @return {Roo.Element} this
8842          */
8843         swallowEvent : function(eventName, preventDefault){
8844             var fn = function(e){
8845                 e.stopPropagation();
8846                 if(preventDefault){
8847                     e.preventDefault();
8848                 }
8849             };
8850             if(eventName instanceof Array){
8851                 for(var i = 0, len = eventName.length; i < len; i++){
8852                      this.on(eventName[i], fn);
8853                 }
8854                 return this;
8855             }
8856             this.on(eventName, fn);
8857             return this;
8858         },
8859
8860         /**
8861          * @private
8862          */
8863       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8864
8865         /**
8866          * Sizes this element to its parent element's dimensions performing
8867          * neccessary box adjustments.
8868          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8869          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8870          * @return {Roo.Element} this
8871          */
8872         fitToParent : function(monitorResize, targetParent) {
8873           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8874           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8875           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8876             return;
8877           }
8878           var p = Roo.get(targetParent || this.dom.parentNode);
8879           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8880           if (monitorResize === true) {
8881             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8882             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8883           }
8884           return this;
8885         },
8886
8887         /**
8888          * Gets the next sibling, skipping text nodes
8889          * @return {HTMLElement} The next sibling or null
8890          */
8891         getNextSibling : function(){
8892             var n = this.dom.nextSibling;
8893             while(n && n.nodeType != 1){
8894                 n = n.nextSibling;
8895             }
8896             return n;
8897         },
8898
8899         /**
8900          * Gets the previous sibling, skipping text nodes
8901          * @return {HTMLElement} The previous sibling or null
8902          */
8903         getPrevSibling : function(){
8904             var n = this.dom.previousSibling;
8905             while(n && n.nodeType != 1){
8906                 n = n.previousSibling;
8907             }
8908             return n;
8909         },
8910
8911
8912         /**
8913          * Appends the passed element(s) to this element
8914          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8915          * @return {Roo.Element} this
8916          */
8917         appendChild: function(el){
8918             el = Roo.get(el);
8919             el.appendTo(this);
8920             return this;
8921         },
8922
8923         /**
8924          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8925          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8926          * automatically generated with the specified attributes.
8927          * @param {HTMLElement} insertBefore (optional) a child element of this element
8928          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8929          * @return {Roo.Element} The new child element
8930          */
8931         createChild: function(config, insertBefore, returnDom){
8932             config = config || {tag:'div'};
8933             if(insertBefore){
8934                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8935             }
8936             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8937         },
8938
8939         /**
8940          * Appends this element to the passed element
8941          * @param {String/HTMLElement/Element} el The new parent element
8942          * @return {Roo.Element} this
8943          */
8944         appendTo: function(el){
8945             el = Roo.getDom(el);
8946             el.appendChild(this.dom);
8947             return this;
8948         },
8949
8950         /**
8951          * Inserts this element before the passed element in the DOM
8952          * @param {String/HTMLElement/Element} el The element to insert before
8953          * @return {Roo.Element} this
8954          */
8955         insertBefore: function(el){
8956             el = Roo.getDom(el);
8957             el.parentNode.insertBefore(this.dom, el);
8958             return this;
8959         },
8960
8961         /**
8962          * Inserts this element after the passed element in the DOM
8963          * @param {String/HTMLElement/Element} el The element to insert after
8964          * @return {Roo.Element} this
8965          */
8966         insertAfter: function(el){
8967             el = Roo.getDom(el);
8968             el.parentNode.insertBefore(this.dom, el.nextSibling);
8969             return this;
8970         },
8971
8972         /**
8973          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8974          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8975          * @return {Roo.Element} The new child
8976          */
8977         insertFirst: function(el, returnDom){
8978             el = el || {};
8979             if(typeof el == 'object' && !el.nodeType){ // dh config
8980                 return this.createChild(el, this.dom.firstChild, returnDom);
8981             }else{
8982                 el = Roo.getDom(el);
8983                 this.dom.insertBefore(el, this.dom.firstChild);
8984                 return !returnDom ? Roo.get(el) : el;
8985             }
8986         },
8987
8988         /**
8989          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8990          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8991          * @param {String} where (optional) 'before' or 'after' defaults to before
8992          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8993          * @return {Roo.Element} the inserted Element
8994          */
8995         insertSibling: function(el, where, returnDom){
8996             where = where ? where.toLowerCase() : 'before';
8997             el = el || {};
8998             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8999
9000             if(typeof el == 'object' && !el.nodeType){ // dh config
9001                 if(where == 'after' && !this.dom.nextSibling){
9002                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9003                 }else{
9004                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9005                 }
9006
9007             }else{
9008                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9009                             where == 'before' ? this.dom : this.dom.nextSibling);
9010                 if(!returnDom){
9011                     rt = Roo.get(rt);
9012                 }
9013             }
9014             return rt;
9015         },
9016
9017         /**
9018          * Creates and wraps this element with another element
9019          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9020          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9021          * @return {HTMLElement/Element} The newly created wrapper element
9022          */
9023         wrap: function(config, returnDom){
9024             if(!config){
9025                 config = {tag: "div"};
9026             }
9027             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9028             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9029             return newEl;
9030         },
9031
9032         /**
9033          * Replaces the passed element with this element
9034          * @param {String/HTMLElement/Element} el The element to replace
9035          * @return {Roo.Element} this
9036          */
9037         replace: function(el){
9038             el = Roo.get(el);
9039             this.insertBefore(el);
9040             el.remove();
9041             return this;
9042         },
9043
9044         /**
9045          * Inserts an html fragment into this element
9046          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9047          * @param {String} html The HTML fragment
9048          * @param {Boolean} returnEl True to return an Roo.Element
9049          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9050          */
9051         insertHtml : function(where, html, returnEl){
9052             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9053             return returnEl ? Roo.get(el) : el;
9054         },
9055
9056         /**
9057          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9058          * @param {Object} o The object with the attributes
9059          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9060          * @return {Roo.Element} this
9061          */
9062         set : function(o, useSet){
9063             var el = this.dom;
9064             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9065             for(var attr in o){
9066                 if(attr == "style" || typeof o[attr] == "function") continue;
9067                 if(attr=="cls"){
9068                     el.className = o["cls"];
9069                 }else{
9070                     if(useSet) el.setAttribute(attr, o[attr]);
9071                     else el[attr] = o[attr];
9072                 }
9073             }
9074             if(o.style){
9075                 Roo.DomHelper.applyStyles(el, o.style);
9076             }
9077             return this;
9078         },
9079
9080         /**
9081          * Convenience method for constructing a KeyMap
9082          * @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:
9083          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9084          * @param {Function} fn The function to call
9085          * @param {Object} scope (optional) The scope of the function
9086          * @return {Roo.KeyMap} The KeyMap created
9087          */
9088         addKeyListener : function(key, fn, scope){
9089             var config;
9090             if(typeof key != "object" || key instanceof Array){
9091                 config = {
9092                     key: key,
9093                     fn: fn,
9094                     scope: scope
9095                 };
9096             }else{
9097                 config = {
9098                     key : key.key,
9099                     shift : key.shift,
9100                     ctrl : key.ctrl,
9101                     alt : key.alt,
9102                     fn: fn,
9103                     scope: scope
9104                 };
9105             }
9106             return new Roo.KeyMap(this, config);
9107         },
9108
9109         /**
9110          * Creates a KeyMap for this element
9111          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9112          * @return {Roo.KeyMap} The KeyMap created
9113          */
9114         addKeyMap : function(config){
9115             return new Roo.KeyMap(this, config);
9116         },
9117
9118         /**
9119          * Returns true if this element is scrollable.
9120          * @return {Boolean}
9121          */
9122          isScrollable : function(){
9123             var dom = this.dom;
9124             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9125         },
9126
9127         /**
9128          * 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().
9129          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9130          * @param {Number} value The new scroll value
9131          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9132          * @return {Element} this
9133          */
9134
9135         scrollTo : function(side, value, animate){
9136             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9137             if(!animate || !A){
9138                 this.dom[prop] = value;
9139             }else{
9140                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9141                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9142             }
9143             return this;
9144         },
9145
9146         /**
9147          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9148          * within this element's scrollable range.
9149          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9150          * @param {Number} distance How far to scroll the element in pixels
9151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9152          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9153          * was scrolled as far as it could go.
9154          */
9155          scroll : function(direction, distance, animate){
9156              if(!this.isScrollable()){
9157                  return;
9158              }
9159              var el = this.dom;
9160              var l = el.scrollLeft, t = el.scrollTop;
9161              var w = el.scrollWidth, h = el.scrollHeight;
9162              var cw = el.clientWidth, ch = el.clientHeight;
9163              direction = direction.toLowerCase();
9164              var scrolled = false;
9165              var a = this.preanim(arguments, 2);
9166              switch(direction){
9167                  case "l":
9168                  case "left":
9169                      if(w - l > cw){
9170                          var v = Math.min(l + distance, w-cw);
9171                          this.scrollTo("left", v, a);
9172                          scrolled = true;
9173                      }
9174                      break;
9175                 case "r":
9176                 case "right":
9177                      if(l > 0){
9178                          var v = Math.max(l - distance, 0);
9179                          this.scrollTo("left", v, a);
9180                          scrolled = true;
9181                      }
9182                      break;
9183                 case "t":
9184                 case "top":
9185                 case "up":
9186                      if(t > 0){
9187                          var v = Math.max(t - distance, 0);
9188                          this.scrollTo("top", v, a);
9189                          scrolled = true;
9190                      }
9191                      break;
9192                 case "b":
9193                 case "bottom":
9194                 case "down":
9195                      if(h - t > ch){
9196                          var v = Math.min(t + distance, h-ch);
9197                          this.scrollTo("top", v, a);
9198                          scrolled = true;
9199                      }
9200                      break;
9201              }
9202              return scrolled;
9203         },
9204
9205         /**
9206          * Translates the passed page coordinates into left/top css values for this element
9207          * @param {Number/Array} x The page x or an array containing [x, y]
9208          * @param {Number} y The page y
9209          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9210          */
9211         translatePoints : function(x, y){
9212             if(typeof x == 'object' || x instanceof Array){
9213                 y = x[1]; x = x[0];
9214             }
9215             var p = this.getStyle('position');
9216             var o = this.getXY();
9217
9218             var l = parseInt(this.getStyle('left'), 10);
9219             var t = parseInt(this.getStyle('top'), 10);
9220
9221             if(isNaN(l)){
9222                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9223             }
9224             if(isNaN(t)){
9225                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9226             }
9227
9228             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9229         },
9230
9231         /**
9232          * Returns the current scroll position of the element.
9233          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9234          */
9235         getScroll : function(){
9236             var d = this.dom, doc = document;
9237             if(d == doc || d == doc.body){
9238                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9239                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9240                 return {left: l, top: t};
9241             }else{
9242                 return {left: d.scrollLeft, top: d.scrollTop};
9243             }
9244         },
9245
9246         /**
9247          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9248          * are convert to standard 6 digit hex color.
9249          * @param {String} attr The css attribute
9250          * @param {String} defaultValue The default value to use when a valid color isn't found
9251          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9252          * YUI color anims.
9253          */
9254         getColor : function(attr, defaultValue, prefix){
9255             var v = this.getStyle(attr);
9256             if(!v || v == "transparent" || v == "inherit") {
9257                 return defaultValue;
9258             }
9259             var color = typeof prefix == "undefined" ? "#" : prefix;
9260             if(v.substr(0, 4) == "rgb("){
9261                 var rvs = v.slice(4, v.length -1).split(",");
9262                 for(var i = 0; i < 3; i++){
9263                     var h = parseInt(rvs[i]).toString(16);
9264                     if(h < 16){
9265                         h = "0" + h;
9266                     }
9267                     color += h;
9268                 }
9269             } else {
9270                 if(v.substr(0, 1) == "#"){
9271                     if(v.length == 4) {
9272                         for(var i = 1; i < 4; i++){
9273                             var c = v.charAt(i);
9274                             color +=  c + c;
9275                         }
9276                     }else if(v.length == 7){
9277                         color += v.substr(1);
9278                     }
9279                 }
9280             }
9281             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9282         },
9283
9284         /**
9285          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9286          * gradient background, rounded corners and a 4-way shadow.
9287          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9288          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9289          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9290          * @return {Roo.Element} this
9291          */
9292         boxWrap : function(cls){
9293             cls = cls || 'x-box';
9294             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9295             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9296             return el;
9297         },
9298
9299         /**
9300          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9301          * @param {String} namespace The namespace in which to look for the attribute
9302          * @param {String} name The attribute name
9303          * @return {String} The attribute value
9304          */
9305         getAttributeNS : Roo.isIE ? function(ns, name){
9306             var d = this.dom;
9307             var type = typeof d[ns+":"+name];
9308             if(type != 'undefined' && type != 'unknown'){
9309                 return d[ns+":"+name];
9310             }
9311             return d[name];
9312         } : function(ns, name){
9313             var d = this.dom;
9314             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9315         }
9316     };
9317
9318     var ep = El.prototype;
9319
9320     /**
9321      * Appends an event handler (Shorthand for addListener)
9322      * @param {String}   eventName     The type of event to append
9323      * @param {Function} fn        The method the event invokes
9324      * @param {Object} scope       (optional) The scope (this object) of the fn
9325      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9326      * @method
9327      */
9328     ep.on = ep.addListener;
9329         // backwards compat
9330     ep.mon = ep.addListener;
9331
9332     /**
9333      * Removes an event handler from this element (shorthand for removeListener)
9334      * @param {String} eventName the type of event to remove
9335      * @param {Function} fn the method the event invokes
9336      * @return {Roo.Element} this
9337      * @method
9338      */
9339     ep.un = ep.removeListener;
9340
9341     /**
9342      * true to automatically adjust width and height settings for box-model issues (default to true)
9343      */
9344     ep.autoBoxAdjust = true;
9345
9346     // private
9347     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9348
9349     // private
9350     El.addUnits = function(v, defaultUnit){
9351         if(v === "" || v == "auto"){
9352             return v;
9353         }
9354         if(v === undefined){
9355             return '';
9356         }
9357         if(typeof v == "number" || !El.unitPattern.test(v)){
9358             return v + (defaultUnit || 'px');
9359         }
9360         return v;
9361     };
9362
9363     // special markup used throughout Roo when box wrapping elements
9364     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>';
9365     /**
9366      * Visibility mode constant - Use visibility to hide element
9367      * @static
9368      * @type Number
9369      */
9370     El.VISIBILITY = 1;
9371     /**
9372      * Visibility mode constant - Use display to hide element
9373      * @static
9374      * @type Number
9375      */
9376     El.DISPLAY = 2;
9377
9378     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9379     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9380     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9381
9382
9383
9384     /**
9385      * @private
9386      */
9387     El.cache = {};
9388
9389     var docEl;
9390
9391     /**
9392      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9393      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9394      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9395      * @return {Element} The Element object
9396      * @static
9397      */
9398     El.get = function(el){
9399         var ex, elm, id;
9400         if(!el){ return null; }
9401         if(typeof el == "string"){ // element id
9402             if(!(elm = document.getElementById(el))){
9403                 return null;
9404             }
9405             if(ex = El.cache[el]){
9406                 ex.dom = elm;
9407             }else{
9408                 ex = El.cache[el] = new El(elm);
9409             }
9410             return ex;
9411         }else if(el.tagName){ // dom element
9412             if(!(id = el.id)){
9413                 id = Roo.id(el);
9414             }
9415             if(ex = El.cache[id]){
9416                 ex.dom = el;
9417             }else{
9418                 ex = El.cache[id] = new El(el);
9419             }
9420             return ex;
9421         }else if(el instanceof El){
9422             if(el != docEl){
9423                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9424                                                               // catch case where it hasn't been appended
9425                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9426             }
9427             return el;
9428         }else if(el.isComposite){
9429             return el;
9430         }else if(el instanceof Array){
9431             return El.select(el);
9432         }else if(el == document){
9433             // create a bogus element object representing the document object
9434             if(!docEl){
9435                 var f = function(){};
9436                 f.prototype = El.prototype;
9437                 docEl = new f();
9438                 docEl.dom = document;
9439             }
9440             return docEl;
9441         }
9442         return null;
9443     };
9444
9445     // private
9446     El.uncache = function(el){
9447         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9448             if(a[i]){
9449                 delete El.cache[a[i].id || a[i]];
9450             }
9451         }
9452     };
9453
9454     // private
9455     // Garbage collection - uncache elements/purge listeners on orphaned elements
9456     // so we don't hold a reference and cause the browser to retain them
9457     El.garbageCollect = function(){
9458         if(!Roo.enableGarbageCollector){
9459             clearInterval(El.collectorThread);
9460             return;
9461         }
9462         for(var eid in El.cache){
9463             var el = El.cache[eid], d = el.dom;
9464             // -------------------------------------------------------
9465             // Determining what is garbage:
9466             // -------------------------------------------------------
9467             // !d
9468             // dom node is null, definitely garbage
9469             // -------------------------------------------------------
9470             // !d.parentNode
9471             // no parentNode == direct orphan, definitely garbage
9472             // -------------------------------------------------------
9473             // !d.offsetParent && !document.getElementById(eid)
9474             // display none elements have no offsetParent so we will
9475             // also try to look it up by it's id. However, check
9476             // offsetParent first so we don't do unneeded lookups.
9477             // This enables collection of elements that are not orphans
9478             // directly, but somewhere up the line they have an orphan
9479             // parent.
9480             // -------------------------------------------------------
9481             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9482                 delete El.cache[eid];
9483                 if(d && Roo.enableListenerCollection){
9484                     E.purgeElement(d);
9485                 }
9486             }
9487         }
9488     }
9489     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9490
9491
9492     // dom is optional
9493     El.Flyweight = function(dom){
9494         this.dom = dom;
9495     };
9496     El.Flyweight.prototype = El.prototype;
9497
9498     El._flyweights = {};
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      * @param {String/HTMLElement} el The dom node or id
9503      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9504      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9505      * @static
9506      * @return {Element} The shared Element object
9507      */
9508     El.fly = function(el, named){
9509         named = named || '_global';
9510         el = Roo.getDom(el);
9511         if(!el){
9512             return null;
9513         }
9514         if(!El._flyweights[named]){
9515             El._flyweights[named] = new El.Flyweight();
9516         }
9517         El._flyweights[named].dom = el;
9518         return El._flyweights[named];
9519     };
9520
9521     /**
9522      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9523      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9524      * Shorthand of {@link Roo.Element#get}
9525      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9526      * @return {Element} The Element object
9527      * @member Roo
9528      * @method get
9529      */
9530     Roo.get = El.get;
9531     /**
9532      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9533      * the dom node can be overwritten by other code.
9534      * Shorthand of {@link Roo.Element#fly}
9535      * @param {String/HTMLElement} el The dom node or id
9536      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9537      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9538      * @static
9539      * @return {Element} The shared Element object
9540      * @member Roo
9541      * @method fly
9542      */
9543     Roo.fly = El.fly;
9544
9545     // speedy lookup for elements never to box adjust
9546     var noBoxAdjust = Roo.isStrict ? {
9547         select:1
9548     } : {
9549         input:1, select:1, textarea:1
9550     };
9551     if(Roo.isIE || Roo.isGecko){
9552         noBoxAdjust['button'] = 1;
9553     }
9554
9555
9556     Roo.EventManager.on(window, 'unload', function(){
9557         delete El.cache;
9558         delete El._flyweights;
9559     });
9560 })();
9561
9562
9563
9564
9565 if(Roo.DomQuery){
9566     Roo.Element.selectorFunction = Roo.DomQuery.select;
9567 }
9568
9569 Roo.Element.select = function(selector, unique, root){
9570     var els;
9571     if(typeof selector == "string"){
9572         els = Roo.Element.selectorFunction(selector, root);
9573     }else if(selector.length !== undefined){
9574         els = selector;
9575     }else{
9576         throw "Invalid selector";
9577     }
9578     if(unique === true){
9579         return new Roo.CompositeElement(els);
9580     }else{
9581         return new Roo.CompositeElementLite(els);
9582     }
9583 };
9584 /**
9585  * Selects elements based on the passed CSS selector to enable working on them as 1.
9586  * @param {String/Array} selector The CSS selector or an array of elements
9587  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9588  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9589  * @return {CompositeElementLite/CompositeElement}
9590  * @member Roo
9591  * @method select
9592  */
9593 Roo.select = Roo.Element.select;
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
9607
9608 /*
9609  * Based on:
9610  * Ext JS Library 1.1.1
9611  * Copyright(c) 2006-2007, Ext JS, LLC.
9612  *
9613  * Originally Released Under LGPL - original licence link has changed is not relivant.
9614  *
9615  * Fork - LGPL
9616  * <script type="text/javascript">
9617  */
9618
9619
9620
9621 //Notifies Element that fx methods are available
9622 Roo.enableFx = true;
9623
9624 /**
9625  * @class Roo.Fx
9626  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9627  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9628  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9629  * Element effects to work.</p><br/>
9630  *
9631  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9632  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9633  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9634  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9635  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9636  * expected results and should be done with care.</p><br/>
9637  *
9638  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9639  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9640 <pre>
9641 Value  Description
9642 -----  -----------------------------
9643 tl     The top left corner
9644 t      The center of the top edge
9645 tr     The top right corner
9646 l      The center of the left edge
9647 r      The center of the right edge
9648 bl     The bottom left corner
9649 b      The center of the bottom edge
9650 br     The bottom right corner
9651 </pre>
9652  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9653  * below are common options that can be passed to any Fx method.</b>
9654  * @cfg {Function} callback A function called when the effect is finished
9655  * @cfg {Object} scope The scope of the effect function
9656  * @cfg {String} easing A valid Easing value for the effect
9657  * @cfg {String} afterCls A css class to apply after the effect
9658  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9659  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9660  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9661  * effects that end with the element being visually hidden, ignored otherwise)
9662  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9663  * a function which returns such a specification that will be applied to the Element after the effect finishes
9664  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9665  * @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
9666  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9667  */
9668 Roo.Fx = {
9669         /**
9670          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9671          * origin for the slide effect.  This function automatically handles wrapping the element with
9672          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9673          * Usage:
9674          *<pre><code>
9675 // default: slide the element in from the top
9676 el.slideIn();
9677
9678 // custom: slide the element in from the right with a 2-second duration
9679 el.slideIn('r', { duration: 2 });
9680
9681 // common config options shown with default values
9682 el.slideIn('t', {
9683     easing: 'easeOut',
9684     duration: .5
9685 });
9686 </code></pre>
9687          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9688          * @param {Object} options (optional) Object literal with any of the Fx config options
9689          * @return {Roo.Element} The Element
9690          */
9691     slideIn : function(anchor, o){
9692         var el = this.getFxEl();
9693         o = o || {};
9694
9695         el.queueFx(o, function(){
9696
9697             anchor = anchor || "t";
9698
9699             // fix display to visibility
9700             this.fixDisplay();
9701
9702             // restore values after effect
9703             var r = this.getFxRestore();
9704             var b = this.getBox();
9705             // fixed size for slide
9706             this.setSize(b);
9707
9708             // wrap if needed
9709             var wrap = this.fxWrap(r.pos, o, "hidden");
9710
9711             var st = this.dom.style;
9712             st.visibility = "visible";
9713             st.position = "absolute";
9714
9715             // clear out temp styles after slide and unwrap
9716             var after = function(){
9717                 el.fxUnwrap(wrap, r.pos, o);
9718                 st.width = r.width;
9719                 st.height = r.height;
9720                 el.afterFx(o);
9721             };
9722             // time to calc the positions
9723             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9724
9725             switch(anchor.toLowerCase()){
9726                 case "t":
9727                     wrap.setSize(b.width, 0);
9728                     st.left = st.bottom = "0";
9729                     a = {height: bh};
9730                 break;
9731                 case "l":
9732                     wrap.setSize(0, b.height);
9733                     st.right = st.top = "0";
9734                     a = {width: bw};
9735                 break;
9736                 case "r":
9737                     wrap.setSize(0, b.height);
9738                     wrap.setX(b.right);
9739                     st.left = st.top = "0";
9740                     a = {width: bw, points: pt};
9741                 break;
9742                 case "b":
9743                     wrap.setSize(b.width, 0);
9744                     wrap.setY(b.bottom);
9745                     st.left = st.top = "0";
9746                     a = {height: bh, points: pt};
9747                 break;
9748                 case "tl":
9749                     wrap.setSize(0, 0);
9750                     st.right = st.bottom = "0";
9751                     a = {width: bw, height: bh};
9752                 break;
9753                 case "bl":
9754                     wrap.setSize(0, 0);
9755                     wrap.setY(b.y+b.height);
9756                     st.right = st.top = "0";
9757                     a = {width: bw, height: bh, points: pt};
9758                 break;
9759                 case "br":
9760                     wrap.setSize(0, 0);
9761                     wrap.setXY([b.right, b.bottom]);
9762                     st.left = st.top = "0";
9763                     a = {width: bw, height: bh, points: pt};
9764                 break;
9765                 case "tr":
9766                     wrap.setSize(0, 0);
9767                     wrap.setX(b.x+b.width);
9768                     st.left = st.bottom = "0";
9769                     a = {width: bw, height: bh, points: pt};
9770                 break;
9771             }
9772             this.dom.style.visibility = "visible";
9773             wrap.show();
9774
9775             arguments.callee.anim = wrap.fxanim(a,
9776                 o,
9777                 'motion',
9778                 .5,
9779                 'easeOut', after);
9780         });
9781         return this;
9782     },
9783     
9784         /**
9785          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9786          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9787          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9788          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9789          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9790          * Usage:
9791          *<pre><code>
9792 // default: slide the element out to the top
9793 el.slideOut();
9794
9795 // custom: slide the element out to the right with a 2-second duration
9796 el.slideOut('r', { duration: 2 });
9797
9798 // common config options shown with default values
9799 el.slideOut('t', {
9800     easing: 'easeOut',
9801     duration: .5,
9802     remove: false,
9803     useDisplay: false
9804 });
9805 </code></pre>
9806          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9807          * @param {Object} options (optional) Object literal with any of the Fx config options
9808          * @return {Roo.Element} The Element
9809          */
9810     slideOut : function(anchor, o){
9811         var el = this.getFxEl();
9812         o = o || {};
9813
9814         el.queueFx(o, function(){
9815
9816             anchor = anchor || "t";
9817
9818             // restore values after effect
9819             var r = this.getFxRestore();
9820             
9821             var b = this.getBox();
9822             // fixed size for slide
9823             this.setSize(b);
9824
9825             // wrap if needed
9826             var wrap = this.fxWrap(r.pos, o, "visible");
9827
9828             var st = this.dom.style;
9829             st.visibility = "visible";
9830             st.position = "absolute";
9831
9832             wrap.setSize(b);
9833
9834             var after = function(){
9835                 if(o.useDisplay){
9836                     el.setDisplayed(false);
9837                 }else{
9838                     el.hide();
9839                 }
9840
9841                 el.fxUnwrap(wrap, r.pos, o);
9842
9843                 st.width = r.width;
9844                 st.height = r.height;
9845
9846                 el.afterFx(o);
9847             };
9848
9849             var a, zero = {to: 0};
9850             switch(anchor.toLowerCase()){
9851                 case "t":
9852                     st.left = st.bottom = "0";
9853                     a = {height: zero};
9854                 break;
9855                 case "l":
9856                     st.right = st.top = "0";
9857                     a = {width: zero};
9858                 break;
9859                 case "r":
9860                     st.left = st.top = "0";
9861                     a = {width: zero, points: {to:[b.right, b.y]}};
9862                 break;
9863                 case "b":
9864                     st.left = st.top = "0";
9865                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9866                 break;
9867                 case "tl":
9868                     st.right = st.bottom = "0";
9869                     a = {width: zero, height: zero};
9870                 break;
9871                 case "bl":
9872                     st.right = st.top = "0";
9873                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9874                 break;
9875                 case "br":
9876                     st.left = st.top = "0";
9877                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9878                 break;
9879                 case "tr":
9880                     st.left = st.bottom = "0";
9881                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9882                 break;
9883             }
9884
9885             arguments.callee.anim = wrap.fxanim(a,
9886                 o,
9887                 'motion',
9888                 .5,
9889                 "easeOut", after);
9890         });
9891         return this;
9892     },
9893
9894         /**
9895          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9896          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9897          * The element must be removed from the DOM using the 'remove' config option if desired.
9898          * Usage:
9899          *<pre><code>
9900 // default
9901 el.puff();
9902
9903 // common config options shown with default values
9904 el.puff({
9905     easing: 'easeOut',
9906     duration: .5,
9907     remove: false,
9908     useDisplay: false
9909 });
9910 </code></pre>
9911          * @param {Object} options (optional) Object literal with any of the Fx config options
9912          * @return {Roo.Element} The Element
9913          */
9914     puff : function(o){
9915         var el = this.getFxEl();
9916         o = o || {};
9917
9918         el.queueFx(o, function(){
9919             this.clearOpacity();
9920             this.show();
9921
9922             // restore values after effect
9923             var r = this.getFxRestore();
9924             var st = this.dom.style;
9925
9926             var after = function(){
9927                 if(o.useDisplay){
9928                     el.setDisplayed(false);
9929                 }else{
9930                     el.hide();
9931                 }
9932
9933                 el.clearOpacity();
9934
9935                 el.setPositioning(r.pos);
9936                 st.width = r.width;
9937                 st.height = r.height;
9938                 st.fontSize = '';
9939                 el.afterFx(o);
9940             };
9941
9942             var width = this.getWidth();
9943             var height = this.getHeight();
9944
9945             arguments.callee.anim = this.fxanim({
9946                     width : {to: this.adjustWidth(width * 2)},
9947                     height : {to: this.adjustHeight(height * 2)},
9948                     points : {by: [-(width * .5), -(height * .5)]},
9949                     opacity : {to: 0},
9950                     fontSize: {to:200, unit: "%"}
9951                 },
9952                 o,
9953                 'motion',
9954                 .5,
9955                 "easeOut", after);
9956         });
9957         return this;
9958     },
9959
9960         /**
9961          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9962          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9963          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9964          * Usage:
9965          *<pre><code>
9966 // default
9967 el.switchOff();
9968
9969 // all config options shown with default values
9970 el.switchOff({
9971     easing: 'easeIn',
9972     duration: .3,
9973     remove: false,
9974     useDisplay: false
9975 });
9976 </code></pre>
9977          * @param {Object} options (optional) Object literal with any of the Fx config options
9978          * @return {Roo.Element} The Element
9979          */
9980     switchOff : function(o){
9981         var el = this.getFxEl();
9982         o = o || {};
9983
9984         el.queueFx(o, function(){
9985             this.clearOpacity();
9986             this.clip();
9987
9988             // restore values after effect
9989             var r = this.getFxRestore();
9990             var st = this.dom.style;
9991
9992             var after = function(){
9993                 if(o.useDisplay){
9994                     el.setDisplayed(false);
9995                 }else{
9996                     el.hide();
9997                 }
9998
9999                 el.clearOpacity();
10000                 el.setPositioning(r.pos);
10001                 st.width = r.width;
10002                 st.height = r.height;
10003
10004                 el.afterFx(o);
10005             };
10006
10007             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10008                 this.clearOpacity();
10009                 (function(){
10010                     this.fxanim({
10011                         height:{to:1},
10012                         points:{by:[0, this.getHeight() * .5]}
10013                     }, o, 'motion', 0.3, 'easeIn', after);
10014                 }).defer(100, this);
10015             });
10016         });
10017         return this;
10018     },
10019
10020     /**
10021      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10022      * changed using the "attr" config option) and then fading back to the original color. If no original
10023      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10024      * Usage:
10025 <pre><code>
10026 // default: highlight background to yellow
10027 el.highlight();
10028
10029 // custom: highlight foreground text to blue for 2 seconds
10030 el.highlight("0000ff", { attr: 'color', duration: 2 });
10031
10032 // common config options shown with default values
10033 el.highlight("ffff9c", {
10034     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10035     endColor: (current color) or "ffffff",
10036     easing: 'easeIn',
10037     duration: 1
10038 });
10039 </code></pre>
10040      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10041      * @param {Object} options (optional) Object literal with any of the Fx config options
10042      * @return {Roo.Element} The Element
10043      */ 
10044     highlight : function(color, o){
10045         var el = this.getFxEl();
10046         o = o || {};
10047
10048         el.queueFx(o, function(){
10049             color = color || "ffff9c";
10050             attr = o.attr || "backgroundColor";
10051
10052             this.clearOpacity();
10053             this.show();
10054
10055             var origColor = this.getColor(attr);
10056             var restoreColor = this.dom.style[attr];
10057             endColor = (o.endColor || origColor) || "ffffff";
10058
10059             var after = function(){
10060                 el.dom.style[attr] = restoreColor;
10061                 el.afterFx(o);
10062             };
10063
10064             var a = {};
10065             a[attr] = {from: color, to: endColor};
10066             arguments.callee.anim = this.fxanim(a,
10067                 o,
10068                 'color',
10069                 1,
10070                 'easeIn', after);
10071         });
10072         return this;
10073     },
10074
10075    /**
10076     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10077     * Usage:
10078 <pre><code>
10079 // default: a single light blue ripple
10080 el.frame();
10081
10082 // custom: 3 red ripples lasting 3 seconds total
10083 el.frame("ff0000", 3, { duration: 3 });
10084
10085 // common config options shown with default values
10086 el.frame("C3DAF9", 1, {
10087     duration: 1 //duration of entire animation (not each individual ripple)
10088     // Note: Easing is not configurable and will be ignored if included
10089 });
10090 </code></pre>
10091     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10092     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10093     * @param {Object} options (optional) Object literal with any of the Fx config options
10094     * @return {Roo.Element} The Element
10095     */
10096     frame : function(color, count, o){
10097         var el = this.getFxEl();
10098         o = o || {};
10099
10100         el.queueFx(o, function(){
10101             color = color || "#C3DAF9";
10102             if(color.length == 6){
10103                 color = "#" + color;
10104             }
10105             count = count || 1;
10106             duration = o.duration || 1;
10107             this.show();
10108
10109             var b = this.getBox();
10110             var animFn = function(){
10111                 var proxy = this.createProxy({
10112
10113                      style:{
10114                         visbility:"hidden",
10115                         position:"absolute",
10116                         "z-index":"35000", // yee haw
10117                         border:"0px solid " + color
10118                      }
10119                   });
10120                 var scale = Roo.isBorderBox ? 2 : 1;
10121                 proxy.animate({
10122                     top:{from:b.y, to:b.y - 20},
10123                     left:{from:b.x, to:b.x - 20},
10124                     borderWidth:{from:0, to:10},
10125                     opacity:{from:1, to:0},
10126                     height:{from:b.height, to:(b.height + (20*scale))},
10127                     width:{from:b.width, to:(b.width + (20*scale))}
10128                 }, duration, function(){
10129                     proxy.remove();
10130                 });
10131                 if(--count > 0){
10132                      animFn.defer((duration/2)*1000, this);
10133                 }else{
10134                     el.afterFx(o);
10135                 }
10136             };
10137             animFn.call(this);
10138         });
10139         return this;
10140     },
10141
10142    /**
10143     * Creates a pause before any subsequent queued effects begin.  If there are
10144     * no effects queued after the pause it will have no effect.
10145     * Usage:
10146 <pre><code>
10147 el.pause(1);
10148 </code></pre>
10149     * @param {Number} seconds The length of time to pause (in seconds)
10150     * @return {Roo.Element} The Element
10151     */
10152     pause : function(seconds){
10153         var el = this.getFxEl();
10154         var o = {};
10155
10156         el.queueFx(o, function(){
10157             setTimeout(function(){
10158                 el.afterFx(o);
10159             }, seconds * 1000);
10160         });
10161         return this;
10162     },
10163
10164    /**
10165     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10166     * using the "endOpacity" config option.
10167     * Usage:
10168 <pre><code>
10169 // default: fade in from opacity 0 to 100%
10170 el.fadeIn();
10171
10172 // custom: fade in from opacity 0 to 75% over 2 seconds
10173 el.fadeIn({ endOpacity: .75, duration: 2});
10174
10175 // common config options shown with default values
10176 el.fadeIn({
10177     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10178     easing: 'easeOut',
10179     duration: .5
10180 });
10181 </code></pre>
10182     * @param {Object} options (optional) Object literal with any of the Fx config options
10183     * @return {Roo.Element} The Element
10184     */
10185     fadeIn : function(o){
10186         var el = this.getFxEl();
10187         o = o || {};
10188         el.queueFx(o, function(){
10189             this.setOpacity(0);
10190             this.fixDisplay();
10191             this.dom.style.visibility = 'visible';
10192             var to = o.endOpacity || 1;
10193             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10194                 o, null, .5, "easeOut", function(){
10195                 if(to == 1){
10196                     this.clearOpacity();
10197                 }
10198                 el.afterFx(o);
10199             });
10200         });
10201         return this;
10202     },
10203
10204    /**
10205     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10206     * using the "endOpacity" config option.
10207     * Usage:
10208 <pre><code>
10209 // default: fade out from the element's current opacity to 0
10210 el.fadeOut();
10211
10212 // custom: fade out from the element's current opacity to 25% over 2 seconds
10213 el.fadeOut({ endOpacity: .25, duration: 2});
10214
10215 // common config options shown with default values
10216 el.fadeOut({
10217     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10218     easing: 'easeOut',
10219     duration: .5
10220     remove: false,
10221     useDisplay: false
10222 });
10223 </code></pre>
10224     * @param {Object} options (optional) Object literal with any of the Fx config options
10225     * @return {Roo.Element} The Element
10226     */
10227     fadeOut : function(o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230         el.queueFx(o, function(){
10231             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10232                 o, null, .5, "easeOut", function(){
10233                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10234                      this.dom.style.display = "none";
10235                 }else{
10236                      this.dom.style.visibility = "hidden";
10237                 }
10238                 this.clearOpacity();
10239                 el.afterFx(o);
10240             });
10241         });
10242         return this;
10243     },
10244
10245    /**
10246     * Animates the transition of an element's dimensions from a starting height/width
10247     * to an ending height/width.
10248     * Usage:
10249 <pre><code>
10250 // change height and width to 100x100 pixels
10251 el.scale(100, 100);
10252
10253 // common config options shown with default values.  The height and width will default to
10254 // the element's existing values if passed as null.
10255 el.scale(
10256     [element's width],
10257     [element's height], {
10258     easing: 'easeOut',
10259     duration: .35
10260 });
10261 </code></pre>
10262     * @param {Number} width  The new width (pass undefined to keep the original width)
10263     * @param {Number} height  The new height (pass undefined to keep the original height)
10264     * @param {Object} options (optional) Object literal with any of the Fx config options
10265     * @return {Roo.Element} The Element
10266     */
10267     scale : function(w, h, o){
10268         this.shift(Roo.apply({}, o, {
10269             width: w,
10270             height: h
10271         }));
10272         return this;
10273     },
10274
10275    /**
10276     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10277     * Any of these properties not specified in the config object will not be changed.  This effect 
10278     * requires that at least one new dimension, position or opacity setting must be passed in on
10279     * the config object in order for the function to have any effect.
10280     * Usage:
10281 <pre><code>
10282 // slide the element horizontally to x position 200 while changing the height and opacity
10283 el.shift({ x: 200, height: 50, opacity: .8 });
10284
10285 // common config options shown with default values.
10286 el.shift({
10287     width: [element's width],
10288     height: [element's height],
10289     x: [element's x position],
10290     y: [element's y position],
10291     opacity: [element's opacity],
10292     easing: 'easeOut',
10293     duration: .35
10294 });
10295 </code></pre>
10296     * @param {Object} options  Object literal with any of the Fx config options
10297     * @return {Roo.Element} The Element
10298     */
10299     shift : function(o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302         el.queueFx(o, function(){
10303             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10304             if(w !== undefined){
10305                 a.width = {to: this.adjustWidth(w)};
10306             }
10307             if(h !== undefined){
10308                 a.height = {to: this.adjustHeight(h)};
10309             }
10310             if(x !== undefined || y !== undefined){
10311                 a.points = {to: [
10312                     x !== undefined ? x : this.getX(),
10313                     y !== undefined ? y : this.getY()
10314                 ]};
10315             }
10316             if(op !== undefined){
10317                 a.opacity = {to: op};
10318             }
10319             if(o.xy !== undefined){
10320                 a.points = {to: o.xy};
10321             }
10322             arguments.callee.anim = this.fxanim(a,
10323                 o, 'motion', .35, "easeOut", function(){
10324                 el.afterFx(o);
10325             });
10326         });
10327         return this;
10328     },
10329
10330         /**
10331          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10332          * ending point of the effect.
10333          * Usage:
10334          *<pre><code>
10335 // default: slide the element downward while fading out
10336 el.ghost();
10337
10338 // custom: slide the element out to the right with a 2-second duration
10339 el.ghost('r', { duration: 2 });
10340
10341 // common config options shown with default values
10342 el.ghost('b', {
10343     easing: 'easeOut',
10344     duration: .5
10345     remove: false,
10346     useDisplay: false
10347 });
10348 </code></pre>
10349          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10350          * @param {Object} options (optional) Object literal with any of the Fx config options
10351          * @return {Roo.Element} The Element
10352          */
10353     ghost : function(anchor, o){
10354         var el = this.getFxEl();
10355         o = o || {};
10356
10357         el.queueFx(o, function(){
10358             anchor = anchor || "b";
10359
10360             // restore values after effect
10361             var r = this.getFxRestore();
10362             var w = this.getWidth(),
10363                 h = this.getHeight();
10364
10365             var st = this.dom.style;
10366
10367             var after = function(){
10368                 if(o.useDisplay){
10369                     el.setDisplayed(false);
10370                 }else{
10371                     el.hide();
10372                 }
10373
10374                 el.clearOpacity();
10375                 el.setPositioning(r.pos);
10376                 st.width = r.width;
10377                 st.height = r.height;
10378
10379                 el.afterFx(o);
10380             };
10381
10382             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10383             switch(anchor.toLowerCase()){
10384                 case "t":
10385                     pt.by = [0, -h];
10386                 break;
10387                 case "l":
10388                     pt.by = [-w, 0];
10389                 break;
10390                 case "r":
10391                     pt.by = [w, 0];
10392                 break;
10393                 case "b":
10394                     pt.by = [0, h];
10395                 break;
10396                 case "tl":
10397                     pt.by = [-w, -h];
10398                 break;
10399                 case "bl":
10400                     pt.by = [-w, h];
10401                 break;
10402                 case "br":
10403                     pt.by = [w, h];
10404                 break;
10405                 case "tr":
10406                     pt.by = [w, -h];
10407                 break;
10408             }
10409
10410             arguments.callee.anim = this.fxanim(a,
10411                 o,
10412                 'motion',
10413                 .5,
10414                 "easeOut", after);
10415         });
10416         return this;
10417     },
10418
10419         /**
10420          * Ensures that all effects queued after syncFx is called on the element are
10421          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10422          * @return {Roo.Element} The Element
10423          */
10424     syncFx : function(){
10425         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10426             block : false,
10427             concurrent : true,
10428             stopFx : false
10429         });
10430         return this;
10431     },
10432
10433         /**
10434          * Ensures that all effects queued after sequenceFx is called on the element are
10435          * run in sequence.  This is the opposite of {@link #syncFx}.
10436          * @return {Roo.Element} The Element
10437          */
10438     sequenceFx : function(){
10439         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10440             block : false,
10441             concurrent : false,
10442             stopFx : false
10443         });
10444         return this;
10445     },
10446
10447         /* @private */
10448     nextFx : function(){
10449         var ef = this.fxQueue[0];
10450         if(ef){
10451             ef.call(this);
10452         }
10453     },
10454
10455         /**
10456          * Returns true if the element has any effects actively running or queued, else returns false.
10457          * @return {Boolean} True if element has active effects, else false
10458          */
10459     hasActiveFx : function(){
10460         return this.fxQueue && this.fxQueue[0];
10461     },
10462
10463         /**
10464          * Stops any running effects and clears the element's internal effects queue if it contains
10465          * any additional effects that haven't started yet.
10466          * @return {Roo.Element} The Element
10467          */
10468     stopFx : function(){
10469         if(this.hasActiveFx()){
10470             var cur = this.fxQueue[0];
10471             if(cur && cur.anim && cur.anim.isAnimated()){
10472                 this.fxQueue = [cur]; // clear out others
10473                 cur.anim.stop(true);
10474             }
10475         }
10476         return this;
10477     },
10478
10479         /* @private */
10480     beforeFx : function(o){
10481         if(this.hasActiveFx() && !o.concurrent){
10482            if(o.stopFx){
10483                this.stopFx();
10484                return true;
10485            }
10486            return false;
10487         }
10488         return true;
10489     },
10490
10491         /**
10492          * Returns true if the element is currently blocking so that no other effect can be queued
10493          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10494          * used to ensure that an effect initiated by a user action runs to completion prior to the
10495          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10496          * @return {Boolean} True if blocking, else false
10497          */
10498     hasFxBlock : function(){
10499         var q = this.fxQueue;
10500         return q && q[0] && q[0].block;
10501     },
10502
10503         /* @private */
10504     queueFx : function(o, fn){
10505         if(!this.fxQueue){
10506             this.fxQueue = [];
10507         }
10508         if(!this.hasFxBlock()){
10509             Roo.applyIf(o, this.fxDefaults);
10510             if(!o.concurrent){
10511                 var run = this.beforeFx(o);
10512                 fn.block = o.block;
10513                 this.fxQueue.push(fn);
10514                 if(run){
10515                     this.nextFx();
10516                 }
10517             }else{
10518                 fn.call(this);
10519             }
10520         }
10521         return this;
10522     },
10523
10524         /* @private */
10525     fxWrap : function(pos, o, vis){
10526         var wrap;
10527         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10528             var wrapXY;
10529             if(o.fixPosition){
10530                 wrapXY = this.getXY();
10531             }
10532             var div = document.createElement("div");
10533             div.style.visibility = vis;
10534             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10535             wrap.setPositioning(pos);
10536             if(wrap.getStyle("position") == "static"){
10537                 wrap.position("relative");
10538             }
10539             this.clearPositioning('auto');
10540             wrap.clip();
10541             wrap.dom.appendChild(this.dom);
10542             if(wrapXY){
10543                 wrap.setXY(wrapXY);
10544             }
10545         }
10546         return wrap;
10547     },
10548
10549         /* @private */
10550     fxUnwrap : function(wrap, pos, o){
10551         this.clearPositioning();
10552         this.setPositioning(pos);
10553         if(!o.wrap){
10554             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10555             wrap.remove();
10556         }
10557     },
10558
10559         /* @private */
10560     getFxRestore : function(){
10561         var st = this.dom.style;
10562         return {pos: this.getPositioning(), width: st.width, height : st.height};
10563     },
10564
10565         /* @private */
10566     afterFx : function(o){
10567         if(o.afterStyle){
10568             this.applyStyles(o.afterStyle);
10569         }
10570         if(o.afterCls){
10571             this.addClass(o.afterCls);
10572         }
10573         if(o.remove === true){
10574             this.remove();
10575         }
10576         Roo.callback(o.callback, o.scope, [this]);
10577         if(!o.concurrent){
10578             this.fxQueue.shift();
10579             this.nextFx();
10580         }
10581     },
10582
10583         /* @private */
10584     getFxEl : function(){ // support for composite element fx
10585         return Roo.get(this.dom);
10586     },
10587
10588         /* @private */
10589     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10590         animType = animType || 'run';
10591         opt = opt || {};
10592         var anim = Roo.lib.Anim[animType](
10593             this.dom, args,
10594             (opt.duration || defaultDur) || .35,
10595             (opt.easing || defaultEase) || 'easeOut',
10596             function(){
10597                 Roo.callback(cb, this);
10598             },
10599             this
10600         );
10601         opt.anim = anim;
10602         return anim;
10603     }
10604 };
10605
10606 // backwords compat
10607 Roo.Fx.resize = Roo.Fx.scale;
10608
10609 //When included, Roo.Fx is automatically applied to Element so that all basic
10610 //effects are available directly via the Element API
10611 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10612  * Based on:
10613  * Ext JS Library 1.1.1
10614  * Copyright(c) 2006-2007, Ext JS, LLC.
10615  *
10616  * Originally Released Under LGPL - original licence link has changed is not relivant.
10617  *
10618  * Fork - LGPL
10619  * <script type="text/javascript">
10620  */
10621
10622
10623 /**
10624  * @class Roo.CompositeElement
10625  * Standard composite class. Creates a Roo.Element for every element in the collection.
10626  * <br><br>
10627  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10628  * actions will be performed on all the elements in this collection.</b>
10629  * <br><br>
10630  * All methods return <i>this</i> and can be chained.
10631  <pre><code>
10632  var els = Roo.select("#some-el div.some-class", true);
10633  // or select directly from an existing element
10634  var el = Roo.get('some-el');
10635  el.select('div.some-class', true);
10636
10637  els.setWidth(100); // all elements become 100 width
10638  els.hide(true); // all elements fade out and hide
10639  // or
10640  els.setWidth(100).hide(true);
10641  </code></pre>
10642  */
10643 Roo.CompositeElement = function(els){
10644     this.elements = [];
10645     this.addElements(els);
10646 };
10647 Roo.CompositeElement.prototype = {
10648     isComposite: true,
10649     addElements : function(els){
10650         if(!els) return this;
10651         if(typeof els == "string"){
10652             els = Roo.Element.selectorFunction(els);
10653         }
10654         var yels = this.elements;
10655         var index = yels.length-1;
10656         for(var i = 0, len = els.length; i < len; i++) {
10657                 yels[++index] = Roo.get(els[i]);
10658         }
10659         return this;
10660     },
10661
10662     /**
10663     * Clears this composite and adds the elements returned by the passed selector.
10664     * @param {String/Array} els A string CSS selector, an array of elements or an element
10665     * @return {CompositeElement} this
10666     */
10667     fill : function(els){
10668         this.elements = [];
10669         this.add(els);
10670         return this;
10671     },
10672
10673     /**
10674     * Filters this composite to only elements that match the passed selector.
10675     * @param {String} selector A string CSS selector
10676     * @return {CompositeElement} this
10677     */
10678     filter : function(selector){
10679         var els = [];
10680         this.each(function(el){
10681             if(el.is(selector)){
10682                 els[els.length] = el.dom;
10683             }
10684         });
10685         this.fill(els);
10686         return this;
10687     },
10688
10689     invoke : function(fn, args){
10690         var els = this.elements;
10691         for(var i = 0, len = els.length; i < len; i++) {
10692                 Roo.Element.prototype[fn].apply(els[i], args);
10693         }
10694         return this;
10695     },
10696     /**
10697     * Adds elements to this composite.
10698     * @param {String/Array} els A string CSS selector, an array of elements or an element
10699     * @return {CompositeElement} this
10700     */
10701     add : function(els){
10702         if(typeof els == "string"){
10703             this.addElements(Roo.Element.selectorFunction(els));
10704         }else if(els.length !== undefined){
10705             this.addElements(els);
10706         }else{
10707             this.addElements([els]);
10708         }
10709         return this;
10710     },
10711     /**
10712     * Calls the passed function passing (el, this, index) for each element in this composite.
10713     * @param {Function} fn The function to call
10714     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10715     * @return {CompositeElement} this
10716     */
10717     each : function(fn, scope){
10718         var els = this.elements;
10719         for(var i = 0, len = els.length; i < len; i++){
10720             if(fn.call(scope || els[i], els[i], this, i) === false) {
10721                 break;
10722             }
10723         }
10724         return this;
10725     },
10726
10727     /**
10728      * Returns the Element object at the specified index
10729      * @param {Number} index
10730      * @return {Roo.Element}
10731      */
10732     item : function(index){
10733         return this.elements[index] || null;
10734     },
10735
10736     /**
10737      * Returns the first Element
10738      * @return {Roo.Element}
10739      */
10740     first : function(){
10741         return this.item(0);
10742     },
10743
10744     /**
10745      * Returns the last Element
10746      * @return {Roo.Element}
10747      */
10748     last : function(){
10749         return this.item(this.elements.length-1);
10750     },
10751
10752     /**
10753      * Returns the number of elements in this composite
10754      * @return Number
10755      */
10756     getCount : function(){
10757         return this.elements.length;
10758     },
10759
10760     /**
10761      * Returns true if this composite contains the passed element
10762      * @return Boolean
10763      */
10764     contains : function(el){
10765         return this.indexOf(el) !== -1;
10766     },
10767
10768     /**
10769      * Returns true if this composite contains the passed element
10770      * @return Boolean
10771      */
10772     indexOf : function(el){
10773         return this.elements.indexOf(Roo.get(el));
10774     },
10775
10776
10777     /**
10778     * Removes the specified element(s).
10779     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10780     * or an array of any of those.
10781     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10782     * @return {CompositeElement} this
10783     */
10784     removeElement : function(el, removeDom){
10785         if(el instanceof Array){
10786             for(var i = 0, len = el.length; i < len; i++){
10787                 this.removeElement(el[i]);
10788             }
10789             return this;
10790         }
10791         var index = typeof el == 'number' ? el : this.indexOf(el);
10792         if(index !== -1){
10793             if(removeDom){
10794                 var d = this.elements[index];
10795                 if(d.dom){
10796                     d.remove();
10797                 }else{
10798                     d.parentNode.removeChild(d);
10799                 }
10800             }
10801             this.elements.splice(index, 1);
10802         }
10803         return this;
10804     },
10805
10806     /**
10807     * Replaces the specified element with the passed element.
10808     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10809     * to replace.
10810     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10811     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10812     * @return {CompositeElement} this
10813     */
10814     replaceElement : function(el, replacement, domReplace){
10815         var index = typeof el == 'number' ? el : this.indexOf(el);
10816         if(index !== -1){
10817             if(domReplace){
10818                 this.elements[index].replaceWith(replacement);
10819             }else{
10820                 this.elements.splice(index, 1, Roo.get(replacement))
10821             }
10822         }
10823         return this;
10824     },
10825
10826     /**
10827      * Removes all elements.
10828      */
10829     clear : function(){
10830         this.elements = [];
10831     }
10832 };
10833 (function(){
10834     Roo.CompositeElement.createCall = function(proto, fnName){
10835         if(!proto[fnName]){
10836             proto[fnName] = function(){
10837                 return this.invoke(fnName, arguments);
10838             };
10839         }
10840     };
10841     for(var fnName in Roo.Element.prototype){
10842         if(typeof Roo.Element.prototype[fnName] == "function"){
10843             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10844         }
10845     };
10846 })();
10847 /*
10848  * Based on:
10849  * Ext JS Library 1.1.1
10850  * Copyright(c) 2006-2007, Ext JS, LLC.
10851  *
10852  * Originally Released Under LGPL - original licence link has changed is not relivant.
10853  *
10854  * Fork - LGPL
10855  * <script type="text/javascript">
10856  */
10857
10858 /**
10859  * @class Roo.CompositeElementLite
10860  * @extends Roo.CompositeElement
10861  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10862  <pre><code>
10863  var els = Roo.select("#some-el div.some-class");
10864  // or select directly from an existing element
10865  var el = Roo.get('some-el');
10866  el.select('div.some-class');
10867
10868  els.setWidth(100); // all elements become 100 width
10869  els.hide(true); // all elements fade out and hide
10870  // or
10871  els.setWidth(100).hide(true);
10872  </code></pre><br><br>
10873  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10874  * actions will be performed on all the elements in this collection.</b>
10875  */
10876 Roo.CompositeElementLite = function(els){
10877     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10878     this.el = new Roo.Element.Flyweight();
10879 };
10880 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10881     addElements : function(els){
10882         if(els){
10883             if(els instanceof Array){
10884                 this.elements = this.elements.concat(els);
10885             }else{
10886                 var yels = this.elements;
10887                 var index = yels.length-1;
10888                 for(var i = 0, len = els.length; i < len; i++) {
10889                     yels[++index] = els[i];
10890                 }
10891             }
10892         }
10893         return this;
10894     },
10895     invoke : function(fn, args){
10896         var els = this.elements;
10897         var el = this.el;
10898         for(var i = 0, len = els.length; i < len; i++) {
10899             el.dom = els[i];
10900                 Roo.Element.prototype[fn].apply(el, args);
10901         }
10902         return this;
10903     },
10904     /**
10905      * Returns a flyweight Element of the dom element object at the specified index
10906      * @param {Number} index
10907      * @return {Roo.Element}
10908      */
10909     item : function(index){
10910         if(!this.elements[index]){
10911             return null;
10912         }
10913         this.el.dom = this.elements[index];
10914         return this.el;
10915     },
10916
10917     // fixes scope with flyweight
10918     addListener : function(eventName, handler, scope, opt){
10919         var els = this.elements;
10920         for(var i = 0, len = els.length; i < len; i++) {
10921             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10922         }
10923         return this;
10924     },
10925
10926     /**
10927     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10928     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10929     * a reference to the dom node, use el.dom.</b>
10930     * @param {Function} fn The function to call
10931     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10932     * @return {CompositeElement} this
10933     */
10934     each : function(fn, scope){
10935         var els = this.elements;
10936         var el = this.el;
10937         for(var i = 0, len = els.length; i < len; i++){
10938             el.dom = els[i];
10939                 if(fn.call(scope || el, el, this, i) === false){
10940                 break;
10941             }
10942         }
10943         return this;
10944     },
10945
10946     indexOf : function(el){
10947         return this.elements.indexOf(Roo.getDom(el));
10948     },
10949
10950     replaceElement : function(el, replacement, domReplace){
10951         var index = typeof el == 'number' ? el : this.indexOf(el);
10952         if(index !== -1){
10953             replacement = Roo.getDom(replacement);
10954             if(domReplace){
10955                 var d = this.elements[index];
10956                 d.parentNode.insertBefore(replacement, d);
10957                 d.parentNode.removeChild(d);
10958             }
10959             this.elements.splice(index, 1, replacement);
10960         }
10961         return this;
10962     }
10963 });
10964 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10965
10966 /*
10967  * Based on:
10968  * Ext JS Library 1.1.1
10969  * Copyright(c) 2006-2007, Ext JS, LLC.
10970  *
10971  * Originally Released Under LGPL - original licence link has changed is not relivant.
10972  *
10973  * Fork - LGPL
10974  * <script type="text/javascript">
10975  */
10976
10977  
10978
10979 /**
10980  * @class Roo.data.Connection
10981  * @extends Roo.util.Observable
10982  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10983  * either to a configured URL, or to a URL specified at request time.<br><br>
10984  * <p>
10985  * Requests made by this class are asynchronous, and will return immediately. No data from
10986  * the server will be available to the statement immediately following the {@link #request} call.
10987  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10988  * <p>
10989  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10990  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10991  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10992  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10993  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10994  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10995  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10996  * standard DOM methods.
10997  * @constructor
10998  * @param {Object} config a configuration object.
10999  */
11000 Roo.data.Connection = function(config){
11001     Roo.apply(this, config);
11002     this.addEvents({
11003         /**
11004          * @event beforerequest
11005          * Fires before a network request is made to retrieve a data object.
11006          * @param {Connection} conn This Connection object.
11007          * @param {Object} options The options config object passed to the {@link #request} method.
11008          */
11009         "beforerequest" : true,
11010         /**
11011          * @event requestcomplete
11012          * Fires if the request was successfully completed.
11013          * @param {Connection} conn This Connection object.
11014          * @param {Object} response The XHR object containing the response data.
11015          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11016          * @param {Object} options The options config object passed to the {@link #request} method.
11017          */
11018         "requestcomplete" : true,
11019         /**
11020          * @event requestexception
11021          * Fires if an error HTTP status was returned from the server.
11022          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11023          * @param {Connection} conn This Connection object.
11024          * @param {Object} response The XHR object containing the response data.
11025          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11026          * @param {Object} options The options config object passed to the {@link #request} method.
11027          */
11028         "requestexception" : true
11029     });
11030     Roo.data.Connection.superclass.constructor.call(this);
11031 };
11032
11033 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11034     /**
11035      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11036      */
11037     /**
11038      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11039      * extra parameters to each request made by this object. (defaults to undefined)
11040      */
11041     /**
11042      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11043      *  to each request made by this object. (defaults to undefined)
11044      */
11045     /**
11046      * @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)
11047      */
11048     /**
11049      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11050      */
11051     timeout : 30000,
11052     /**
11053      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11054      * @type Boolean
11055      */
11056     autoAbort:false,
11057
11058     /**
11059      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11060      * @type Boolean
11061      */
11062     disableCaching: true,
11063
11064     /**
11065      * Sends an HTTP request to a remote server.
11066      * @param {Object} options An object which may contain the following properties:<ul>
11067      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11068      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11069      * request, a url encoded string or a function to call to get either.</li>
11070      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11071      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11072      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11073      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11074      * <li>options {Object} The parameter to the request call.</li>
11075      * <li>success {Boolean} True if the request succeeded.</li>
11076      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11077      * </ul></li>
11078      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11079      * The callback is passed the following parameters:<ul>
11080      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11081      * <li>options {Object} The parameter to the request call.</li>
11082      * </ul></li>
11083      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11084      * The callback is passed the following parameters:<ul>
11085      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11086      * <li>options {Object} The parameter to the request call.</li>
11087      * </ul></li>
11088      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11089      * for the callback function. Defaults to the browser window.</li>
11090      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11091      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11092      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11093      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11094      * params for the post data. Any params will be appended to the URL.</li>
11095      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11096      * </ul>
11097      * @return {Number} transactionId
11098      */
11099     request : function(o){
11100         if(this.fireEvent("beforerequest", this, o) !== false){
11101             var p = o.params;
11102
11103             if(typeof p == "function"){
11104                 p = p.call(o.scope||window, o);
11105             }
11106             if(typeof p == "object"){
11107                 p = Roo.urlEncode(o.params);
11108             }
11109             if(this.extraParams){
11110                 var extras = Roo.urlEncode(this.extraParams);
11111                 p = p ? (p + '&' + extras) : extras;
11112             }
11113
11114             var url = o.url || this.url;
11115             if(typeof url == 'function'){
11116                 url = url.call(o.scope||window, o);
11117             }
11118
11119             if(o.form){
11120                 var form = Roo.getDom(o.form);
11121                 url = url || form.action;
11122
11123                 var enctype = form.getAttribute("enctype");
11124                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11125                     return this.doFormUpload(o, p, url);
11126                 }
11127                 var f = Roo.lib.Ajax.serializeForm(form);
11128                 p = p ? (p + '&' + f) : f;
11129             }
11130
11131             var hs = o.headers;
11132             if(this.defaultHeaders){
11133                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11134                 if(!o.headers){
11135                     o.headers = hs;
11136                 }
11137             }
11138
11139             var cb = {
11140                 success: this.handleResponse,
11141                 failure: this.handleFailure,
11142                 scope: this,
11143                 argument: {options: o},
11144                 timeout : this.timeout
11145             };
11146
11147             var method = o.method||this.method||(p ? "POST" : "GET");
11148
11149             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11150                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11151             }
11152
11153             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11154                 if(o.autoAbort){
11155                     this.abort();
11156                 }
11157             }else if(this.autoAbort !== false){
11158                 this.abort();
11159             }
11160
11161             if((method == 'GET' && p) || o.xmlData){
11162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11163                 p = '';
11164             }
11165             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11166             return this.transId;
11167         }else{
11168             Roo.callback(o.callback, o.scope, [o, null, null]);
11169             return null;
11170         }
11171     },
11172
11173     /**
11174      * Determine whether this object has a request outstanding.
11175      * @param {Number} transactionId (Optional) defaults to the last transaction
11176      * @return {Boolean} True if there is an outstanding request.
11177      */
11178     isLoading : function(transId){
11179         if(transId){
11180             return Roo.lib.Ajax.isCallInProgress(transId);
11181         }else{
11182             return this.transId ? true : false;
11183         }
11184     },
11185
11186     /**
11187      * Aborts any outstanding request.
11188      * @param {Number} transactionId (Optional) defaults to the last transaction
11189      */
11190     abort : function(transId){
11191         if(transId || this.isLoading()){
11192             Roo.lib.Ajax.abort(transId || this.transId);
11193         }
11194     },
11195
11196     // private
11197     handleResponse : function(response){
11198         this.transId = false;
11199         var options = response.argument.options;
11200         response.argument = options ? options.argument : null;
11201         this.fireEvent("requestcomplete", this, response, options);
11202         Roo.callback(options.success, options.scope, [response, options]);
11203         Roo.callback(options.callback, options.scope, [options, true, response]);
11204     },
11205
11206     // private
11207     handleFailure : function(response, e){
11208         this.transId = false;
11209         var options = response.argument.options;
11210         response.argument = options ? options.argument : null;
11211         this.fireEvent("requestexception", this, response, options, e);
11212         Roo.callback(options.failure, options.scope, [response, options]);
11213         Roo.callback(options.callback, options.scope, [options, false, response]);
11214     },
11215
11216     // private
11217     doFormUpload : function(o, ps, url){
11218         var id = Roo.id();
11219         var frame = document.createElement('iframe');
11220         frame.id = id;
11221         frame.name = id;
11222         frame.className = 'x-hidden';
11223         if(Roo.isIE){
11224             frame.src = Roo.SSL_SECURE_URL;
11225         }
11226         document.body.appendChild(frame);
11227
11228         if(Roo.isIE){
11229            document.frames[id].name = id;
11230         }
11231
11232         var form = Roo.getDom(o.form);
11233         form.target = id;
11234         form.method = 'POST';
11235         form.enctype = form.encoding = 'multipart/form-data';
11236         if(url){
11237             form.action = url;
11238         }
11239
11240         var hiddens, hd;
11241         if(ps){ // add dynamic params
11242             hiddens = [];
11243             ps = Roo.urlDecode(ps, false);
11244             for(var k in ps){
11245                 if(ps.hasOwnProperty(k)){
11246                     hd = document.createElement('input');
11247                     hd.type = 'hidden';
11248                     hd.name = k;
11249                     hd.value = ps[k];
11250                     form.appendChild(hd);
11251                     hiddens.push(hd);
11252                 }
11253             }
11254         }
11255
11256         function cb(){
11257             var r = {  // bogus response object
11258                 responseText : '',
11259                 responseXML : null
11260             };
11261
11262             r.argument = o ? o.argument : null;
11263
11264             try { //
11265                 var doc;
11266                 if(Roo.isIE){
11267                     doc = frame.contentWindow.document;
11268                 }else {
11269                     doc = (frame.contentDocument || window.frames[id].document);
11270                 }
11271                 if(doc && doc.body){
11272                     r.responseText = doc.body.innerHTML;
11273                 }
11274                 if(doc && doc.XMLDocument){
11275                     r.responseXML = doc.XMLDocument;
11276                 }else {
11277                     r.responseXML = doc;
11278                 }
11279             }
11280             catch(e) {
11281                 // ignore
11282             }
11283
11284             Roo.EventManager.removeListener(frame, 'load', cb, this);
11285
11286             this.fireEvent("requestcomplete", this, r, o);
11287             Roo.callback(o.success, o.scope, [r, o]);
11288             Roo.callback(o.callback, o.scope, [o, true, r]);
11289
11290             setTimeout(function(){document.body.removeChild(frame);}, 100);
11291         }
11292
11293         Roo.EventManager.on(frame, 'load', cb, this);
11294         form.submit();
11295
11296         if(hiddens){ // remove dynamic params
11297             for(var i = 0, len = hiddens.length; i < len; i++){
11298                 form.removeChild(hiddens[i]);
11299             }
11300         }
11301     }
11302 });
11303
11304 /**
11305  * @class Roo.Ajax
11306  * @extends Roo.data.Connection
11307  * Global Ajax request class.
11308  *
11309  * @singleton
11310  */
11311 Roo.Ajax = new Roo.data.Connection({
11312     // fix up the docs
11313    /**
11314      * @cfg {String} url @hide
11315      */
11316     /**
11317      * @cfg {Object} extraParams @hide
11318      */
11319     /**
11320      * @cfg {Object} defaultHeaders @hide
11321      */
11322     /**
11323      * @cfg {String} method (Optional) @hide
11324      */
11325     /**
11326      * @cfg {Number} timeout (Optional) @hide
11327      */
11328     /**
11329      * @cfg {Boolean} autoAbort (Optional) @hide
11330      */
11331
11332     /**
11333      * @cfg {Boolean} disableCaching (Optional) @hide
11334      */
11335
11336     /**
11337      * @property  disableCaching
11338      * True to add a unique cache-buster param to GET requests. (defaults to true)
11339      * @type Boolean
11340      */
11341     /**
11342      * @property  url
11343      * The default URL to be used for requests to the server. (defaults to undefined)
11344      * @type String
11345      */
11346     /**
11347      * @property  extraParams
11348      * An object containing properties which are used as
11349      * extra parameters to each request made by this object. (defaults to undefined)
11350      * @type Object
11351      */
11352     /**
11353      * @property  defaultHeaders
11354      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11355      * @type Object
11356      */
11357     /**
11358      * @property  method
11359      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11360      * @type String
11361      */
11362     /**
11363      * @property  timeout
11364      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11365      * @type Number
11366      */
11367
11368     /**
11369      * @property  autoAbort
11370      * Whether a new request should abort any pending requests. (defaults to false)
11371      * @type Boolean
11372      */
11373     autoAbort : false,
11374
11375     /**
11376      * Serialize the passed form into a url encoded string
11377      * @param {String/HTMLElement} form
11378      * @return {String}
11379      */
11380     serializeForm : function(form){
11381         return Roo.lib.Ajax.serializeForm(form);
11382     }
11383 });/*
11384  * Based on:
11385  * Ext JS Library 1.1.1
11386  * Copyright(c) 2006-2007, Ext JS, LLC.
11387  *
11388  * Originally Released Under LGPL - original licence link has changed is not relivant.
11389  *
11390  * Fork - LGPL
11391  * <script type="text/javascript">
11392  */
11393  
11394 /**
11395  * @class Roo.Ajax
11396  * @extends Roo.data.Connection
11397  * Global Ajax request class.
11398  *
11399  * @instanceOf  Roo.data.Connection
11400  */
11401 Roo.Ajax = new Roo.data.Connection({
11402     // fix up the docs
11403     
11404     /**
11405      * fix up scoping
11406      * @scope Roo.Ajax
11407      */
11408     
11409    /**
11410      * @cfg {String} url @hide
11411      */
11412     /**
11413      * @cfg {Object} extraParams @hide
11414      */
11415     /**
11416      * @cfg {Object} defaultHeaders @hide
11417      */
11418     /**
11419      * @cfg {String} method (Optional) @hide
11420      */
11421     /**
11422      * @cfg {Number} timeout (Optional) @hide
11423      */
11424     /**
11425      * @cfg {Boolean} autoAbort (Optional) @hide
11426      */
11427
11428     /**
11429      * @cfg {Boolean} disableCaching (Optional) @hide
11430      */
11431
11432     /**
11433      * @property  disableCaching
11434      * True to add a unique cache-buster param to GET requests. (defaults to true)
11435      * @type Boolean
11436      */
11437     /**
11438      * @property  url
11439      * The default URL to be used for requests to the server. (defaults to undefined)
11440      * @type String
11441      */
11442     /**
11443      * @property  extraParams
11444      * An object containing properties which are used as
11445      * extra parameters to each request made by this object. (defaults to undefined)
11446      * @type Object
11447      */
11448     /**
11449      * @property  defaultHeaders
11450      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11451      * @type Object
11452      */
11453     /**
11454      * @property  method
11455      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11456      * @type String
11457      */
11458     /**
11459      * @property  timeout
11460      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11461      * @type Number
11462      */
11463
11464     /**
11465      * @property  autoAbort
11466      * Whether a new request should abort any pending requests. (defaults to false)
11467      * @type Boolean
11468      */
11469     autoAbort : false,
11470
11471     /**
11472      * Serialize the passed form into a url encoded string
11473      * @param {String/HTMLElement} form
11474      * @return {String}
11475      */
11476     serializeForm : function(form){
11477         return Roo.lib.Ajax.serializeForm(form);
11478     }
11479 });/*
11480  * Based on:
11481  * Ext JS Library 1.1.1
11482  * Copyright(c) 2006-2007, Ext JS, LLC.
11483  *
11484  * Originally Released Under LGPL - original licence link has changed is not relivant.
11485  *
11486  * Fork - LGPL
11487  * <script type="text/javascript">
11488  */
11489
11490  
11491 /**
11492  * @class Roo.UpdateManager
11493  * @extends Roo.util.Observable
11494  * Provides AJAX-style update for Element object.<br><br>
11495  * Usage:<br>
11496  * <pre><code>
11497  * // Get it from a Roo.Element object
11498  * var el = Roo.get("foo");
11499  * var mgr = el.getUpdateManager();
11500  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11501  * ...
11502  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11503  * <br>
11504  * // or directly (returns the same UpdateManager instance)
11505  * var mgr = new Roo.UpdateManager("myElementId");
11506  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11507  * mgr.on("update", myFcnNeedsToKnow);
11508  * <br>
11509    // short handed call directly from the element object
11510    Roo.get("foo").load({
11511         url: "bar.php",
11512         scripts:true,
11513         params: "for=bar",
11514         text: "Loading Foo..."
11515    });
11516  * </code></pre>
11517  * @constructor
11518  * Create new UpdateManager directly.
11519  * @param {String/HTMLElement/Roo.Element} el The element to update
11520  * @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).
11521  */
11522 Roo.UpdateManager = function(el, forceNew){
11523     el = Roo.get(el);
11524     if(!forceNew && el.updateManager){
11525         return el.updateManager;
11526     }
11527     /**
11528      * The Element object
11529      * @type Roo.Element
11530      */
11531     this.el = el;
11532     /**
11533      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11534      * @type String
11535      */
11536     this.defaultUrl = null;
11537
11538     this.addEvents({
11539         /**
11540          * @event beforeupdate
11541          * Fired before an update is made, return false from your handler and the update is cancelled.
11542          * @param {Roo.Element} el
11543          * @param {String/Object/Function} url
11544          * @param {String/Object} params
11545          */
11546         "beforeupdate": true,
11547         /**
11548          * @event update
11549          * Fired after successful update is made.
11550          * @param {Roo.Element} el
11551          * @param {Object} oResponseObject The response Object
11552          */
11553         "update": true,
11554         /**
11555          * @event failure
11556          * Fired on update failure.
11557          * @param {Roo.Element} el
11558          * @param {Object} oResponseObject The response Object
11559          */
11560         "failure": true
11561     });
11562     var d = Roo.UpdateManager.defaults;
11563     /**
11564      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11565      * @type String
11566      */
11567     this.sslBlankUrl = d.sslBlankUrl;
11568     /**
11569      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11570      * @type Boolean
11571      */
11572     this.disableCaching = d.disableCaching;
11573     /**
11574      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11575      * @type String
11576      */
11577     this.indicatorText = d.indicatorText;
11578     /**
11579      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11580      * @type String
11581      */
11582     this.showLoadIndicator = d.showLoadIndicator;
11583     /**
11584      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11585      * @type Number
11586      */
11587     this.timeout = d.timeout;
11588
11589     /**
11590      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11591      * @type Boolean
11592      */
11593     this.loadScripts = d.loadScripts;
11594
11595     /**
11596      * Transaction object of current executing transaction
11597      */
11598     this.transaction = null;
11599
11600     /**
11601      * @private
11602      */
11603     this.autoRefreshProcId = null;
11604     /**
11605      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11606      * @type Function
11607      */
11608     this.refreshDelegate = this.refresh.createDelegate(this);
11609     /**
11610      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11611      * @type Function
11612      */
11613     this.updateDelegate = this.update.createDelegate(this);
11614     /**
11615      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11616      * @type Function
11617      */
11618     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11619     /**
11620      * @private
11621      */
11622     this.successDelegate = this.processSuccess.createDelegate(this);
11623     /**
11624      * @private
11625      */
11626     this.failureDelegate = this.processFailure.createDelegate(this);
11627
11628     if(!this.renderer){
11629      /**
11630       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11631       */
11632     this.renderer = new Roo.UpdateManager.BasicRenderer();
11633     }
11634     
11635     Roo.UpdateManager.superclass.constructor.call(this);
11636 };
11637
11638 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11639     /**
11640      * Get the Element this UpdateManager is bound to
11641      * @return {Roo.Element} The element
11642      */
11643     getEl : function(){
11644         return this.el;
11645     },
11646     /**
11647      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11648      * @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:
11649 <pre><code>
11650 um.update({<br/>
11651     url: "your-url.php",<br/>
11652     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11653     callback: yourFunction,<br/>
11654     scope: yourObject, //(optional scope)  <br/>
11655     discardUrl: false, <br/>
11656     nocache: false,<br/>
11657     text: "Loading...",<br/>
11658     timeout: 30,<br/>
11659     scripts: false<br/>
11660 });
11661 </code></pre>
11662      * The only required property is url. The optional properties nocache, text and scripts
11663      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11664      * @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}
11665      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11666      * @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.
11667      */
11668     update : function(url, params, callback, discardUrl){
11669         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11670             var method = this.method, cfg;
11671             if(typeof url == "object"){ // must be config object
11672                 cfg = url;
11673                 url = cfg.url;
11674                 params = params || cfg.params;
11675                 callback = callback || cfg.callback;
11676                 discardUrl = discardUrl || cfg.discardUrl;
11677                 if(callback && cfg.scope){
11678                     callback = callback.createDelegate(cfg.scope);
11679                 }
11680                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11681                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11682                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11683                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11684                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11685             }
11686             this.showLoading();
11687             if(!discardUrl){
11688                 this.defaultUrl = url;
11689             }
11690             if(typeof url == "function"){
11691                 url = url.call(this);
11692             }
11693
11694             method = method || (params ? "POST" : "GET");
11695             if(method == "GET"){
11696                 url = this.prepareUrl(url);
11697             }
11698
11699             var o = Roo.apply(cfg ||{}, {
11700                 url : url,
11701                 params: params,
11702                 success: this.successDelegate,
11703                 failure: this.failureDelegate,
11704                 callback: undefined,
11705                 timeout: (this.timeout*1000),
11706                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11707             });
11708
11709             this.transaction = Roo.Ajax.request(o);
11710         }
11711     },
11712
11713     /**
11714      * 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.
11715      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11716      * @param {String/HTMLElement} form The form Id or form element
11717      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11718      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11719      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11720      */
11721     formUpdate : function(form, url, reset, callback){
11722         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11723             if(typeof url == "function"){
11724                 url = url.call(this);
11725             }
11726             form = Roo.getDom(form);
11727             this.transaction = Roo.Ajax.request({
11728                 form: form,
11729                 url:url,
11730                 success: this.successDelegate,
11731                 failure: this.failureDelegate,
11732                 timeout: (this.timeout*1000),
11733                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11734             });
11735             this.showLoading.defer(1, this);
11736         }
11737     },
11738
11739     /**
11740      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11741      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11742      */
11743     refresh : function(callback){
11744         if(this.defaultUrl == null){
11745             return;
11746         }
11747         this.update(this.defaultUrl, null, callback, true);
11748     },
11749
11750     /**
11751      * Set this element to auto refresh.
11752      * @param {Number} interval How often to update (in seconds).
11753      * @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)
11754      * @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}
11755      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11756      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11757      */
11758     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11759         if(refreshNow){
11760             this.update(url || this.defaultUrl, params, callback, true);
11761         }
11762         if(this.autoRefreshProcId){
11763             clearInterval(this.autoRefreshProcId);
11764         }
11765         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11766     },
11767
11768     /**
11769      * Stop auto refresh on this element.
11770      */
11771      stopAutoRefresh : function(){
11772         if(this.autoRefreshProcId){
11773             clearInterval(this.autoRefreshProcId);
11774             delete this.autoRefreshProcId;
11775         }
11776     },
11777
11778     isAutoRefreshing : function(){
11779        return this.autoRefreshProcId ? true : false;
11780     },
11781     /**
11782      * Called to update the element to "Loading" state. Override to perform custom action.
11783      */
11784     showLoading : function(){
11785         if(this.showLoadIndicator){
11786             this.el.update(this.indicatorText);
11787         }
11788     },
11789
11790     /**
11791      * Adds unique parameter to query string if disableCaching = true
11792      * @private
11793      */
11794     prepareUrl : function(url){
11795         if(this.disableCaching){
11796             var append = "_dc=" + (new Date().getTime());
11797             if(url.indexOf("?") !== -1){
11798                 url += "&" + append;
11799             }else{
11800                 url += "?" + append;
11801             }
11802         }
11803         return url;
11804     },
11805
11806     /**
11807      * @private
11808      */
11809     processSuccess : function(response){
11810         this.transaction = null;
11811         if(response.argument.form && response.argument.reset){
11812             try{ // put in try/catch since some older FF releases had problems with this
11813                 response.argument.form.reset();
11814             }catch(e){}
11815         }
11816         if(this.loadScripts){
11817             this.renderer.render(this.el, response, this,
11818                 this.updateComplete.createDelegate(this, [response]));
11819         }else{
11820             this.renderer.render(this.el, response, this);
11821             this.updateComplete(response);
11822         }
11823     },
11824
11825     updateComplete : function(response){
11826         this.fireEvent("update", this.el, response);
11827         if(typeof response.argument.callback == "function"){
11828             response.argument.callback(this.el, true, response);
11829         }
11830     },
11831
11832     /**
11833      * @private
11834      */
11835     processFailure : function(response){
11836         this.transaction = null;
11837         this.fireEvent("failure", this.el, response);
11838         if(typeof response.argument.callback == "function"){
11839             response.argument.callback(this.el, false, response);
11840         }
11841     },
11842
11843     /**
11844      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11845      * @param {Object} renderer The object implementing the render() method
11846      */
11847     setRenderer : function(renderer){
11848         this.renderer = renderer;
11849     },
11850
11851     getRenderer : function(){
11852        return this.renderer;
11853     },
11854
11855     /**
11856      * Set the defaultUrl used for updates
11857      * @param {String/Function} defaultUrl The url or a function to call to get the url
11858      */
11859     setDefaultUrl : function(defaultUrl){
11860         this.defaultUrl = defaultUrl;
11861     },
11862
11863     /**
11864      * Aborts the executing transaction
11865      */
11866     abort : function(){
11867         if(this.transaction){
11868             Roo.Ajax.abort(this.transaction);
11869         }
11870     },
11871
11872     /**
11873      * Returns true if an update is in progress
11874      * @return {Boolean}
11875      */
11876     isUpdating : function(){
11877         if(this.transaction){
11878             return Roo.Ajax.isLoading(this.transaction);
11879         }
11880         return false;
11881     }
11882 });
11883
11884 /**
11885  * @class Roo.UpdateManager.defaults
11886  * @static (not really - but it helps the doc tool)
11887  * The defaults collection enables customizing the default properties of UpdateManager
11888  */
11889    Roo.UpdateManager.defaults = {
11890        /**
11891          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11892          * @type Number
11893          */
11894          timeout : 30,
11895
11896          /**
11897          * True to process scripts by default (Defaults to false).
11898          * @type Boolean
11899          */
11900         loadScripts : false,
11901
11902         /**
11903         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11904         * @type String
11905         */
11906         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11907         /**
11908          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11909          * @type Boolean
11910          */
11911         disableCaching : false,
11912         /**
11913          * Whether to show indicatorText when loading (Defaults to true).
11914          * @type Boolean
11915          */
11916         showLoadIndicator : true,
11917         /**
11918          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11919          * @type String
11920          */
11921         indicatorText : '<div class="loading-indicator">Loading...</div>'
11922    };
11923
11924 /**
11925  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11926  *Usage:
11927  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11928  * @param {String/HTMLElement/Roo.Element} el The element to update
11929  * @param {String} url The url
11930  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11931  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11932  * @static
11933  * @deprecated
11934  * @member Roo.UpdateManager
11935  */
11936 Roo.UpdateManager.updateElement = function(el, url, params, options){
11937     var um = Roo.get(el, true).getUpdateManager();
11938     Roo.apply(um, options);
11939     um.update(url, params, options ? options.callback : null);
11940 };
11941 // alias for backwards compat
11942 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11943 /**
11944  * @class Roo.UpdateManager.BasicRenderer
11945  * Default Content renderer. Updates the elements innerHTML with the responseText.
11946  */
11947 Roo.UpdateManager.BasicRenderer = function(){};
11948
11949 Roo.UpdateManager.BasicRenderer.prototype = {
11950     /**
11951      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11952      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11953      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11954      * @param {Roo.Element} el The element being rendered
11955      * @param {Object} response The YUI Connect response object
11956      * @param {UpdateManager} updateManager The calling update manager
11957      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11958      */
11959      render : function(el, response, updateManager, callback){
11960         el.update(response.responseText, updateManager.loadScripts, callback);
11961     }
11962 };
11963 /*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973
11974 /**
11975  * @class Roo.util.DelayedTask
11976  * Provides a convenient method of performing setTimeout where a new
11977  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11978  * You can use this class to buffer
11979  * the keypress events for a certain number of milliseconds, and perform only if they stop
11980  * for that amount of time.
11981  * @constructor The parameters to this constructor serve as defaults and are not required.
11982  * @param {Function} fn (optional) The default function to timeout
11983  * @param {Object} scope (optional) The default scope of that timeout
11984  * @param {Array} args (optional) The default Array of arguments
11985  */
11986 Roo.util.DelayedTask = function(fn, scope, args){
11987     var id = null, d, t;
11988
11989     var call = function(){
11990         var now = new Date().getTime();
11991         if(now - t >= d){
11992             clearInterval(id);
11993             id = null;
11994             fn.apply(scope, args || []);
11995         }
11996     };
11997     /**
11998      * Cancels any pending timeout and queues a new one
11999      * @param {Number} delay The milliseconds to delay
12000      * @param {Function} newFn (optional) Overrides function passed to constructor
12001      * @param {Object} newScope (optional) Overrides scope passed to constructor
12002      * @param {Array} newArgs (optional) Overrides args passed to constructor
12003      */
12004     this.delay = function(delay, newFn, newScope, newArgs){
12005         if(id && delay != d){
12006             this.cancel();
12007         }
12008         d = delay;
12009         t = new Date().getTime();
12010         fn = newFn || fn;
12011         scope = newScope || scope;
12012         args = newArgs || args;
12013         if(!id){
12014             id = setInterval(call, d);
12015         }
12016     };
12017
12018     /**
12019      * Cancel the last queued timeout
12020      */
12021     this.cancel = function(){
12022         if(id){
12023             clearInterval(id);
12024             id = null;
12025         }
12026     };
12027 };/*
12028  * Based on:
12029  * Ext JS Library 1.1.1
12030  * Copyright(c) 2006-2007, Ext JS, LLC.
12031  *
12032  * Originally Released Under LGPL - original licence link has changed is not relivant.
12033  *
12034  * Fork - LGPL
12035  * <script type="text/javascript">
12036  */
12037  
12038  
12039 Roo.util.TaskRunner = function(interval){
12040     interval = interval || 10;
12041     var tasks = [], removeQueue = [];
12042     var id = 0;
12043     var running = false;
12044
12045     var stopThread = function(){
12046         running = false;
12047         clearInterval(id);
12048         id = 0;
12049     };
12050
12051     var startThread = function(){
12052         if(!running){
12053             running = true;
12054             id = setInterval(runTasks, interval);
12055         }
12056     };
12057
12058     var removeTask = function(task){
12059         removeQueue.push(task);
12060         if(task.onStop){
12061             task.onStop();
12062         }
12063     };
12064
12065     var runTasks = function(){
12066         if(removeQueue.length > 0){
12067             for(var i = 0, len = removeQueue.length; i < len; i++){
12068                 tasks.remove(removeQueue[i]);
12069             }
12070             removeQueue = [];
12071             if(tasks.length < 1){
12072                 stopThread();
12073                 return;
12074             }
12075         }
12076         var now = new Date().getTime();
12077         for(var i = 0, len = tasks.length; i < len; ++i){
12078             var t = tasks[i];
12079             var itime = now - t.taskRunTime;
12080             if(t.interval <= itime){
12081                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12082                 t.taskRunTime = now;
12083                 if(rt === false || t.taskRunCount === t.repeat){
12084                     removeTask(t);
12085                     return;
12086                 }
12087             }
12088             if(t.duration && t.duration <= (now - t.taskStartTime)){
12089                 removeTask(t);
12090             }
12091         }
12092     };
12093
12094     /**
12095      * Queues a new task.
12096      * @param {Object} task
12097      */
12098     this.start = function(task){
12099         tasks.push(task);
12100         task.taskStartTime = new Date().getTime();
12101         task.taskRunTime = 0;
12102         task.taskRunCount = 0;
12103         startThread();
12104         return task;
12105     };
12106
12107     this.stop = function(task){
12108         removeTask(task);
12109         return task;
12110     };
12111
12112     this.stopAll = function(){
12113         stopThread();
12114         for(var i = 0, len = tasks.length; i < len; i++){
12115             if(tasks[i].onStop){
12116                 tasks[i].onStop();
12117             }
12118         }
12119         tasks = [];
12120         removeQueue = [];
12121     };
12122 };
12123
12124 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12125  * Based on:
12126  * Ext JS Library 1.1.1
12127  * Copyright(c) 2006-2007, Ext JS, LLC.
12128  *
12129  * Originally Released Under LGPL - original licence link has changed is not relivant.
12130  *
12131  * Fork - LGPL
12132  * <script type="text/javascript">
12133  */
12134
12135  
12136 /**
12137  * @class Roo.util.MixedCollection
12138  * @extends Roo.util.Observable
12139  * A Collection class that maintains both numeric indexes and keys and exposes events.
12140  * @constructor
12141  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12142  * collection (defaults to false)
12143  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12144  * and return the key value for that item.  This is used when available to look up the key on items that
12145  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12146  * equivalent to providing an implementation for the {@link #getKey} method.
12147  */
12148 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12149     this.items = [];
12150     this.map = {};
12151     this.keys = [];
12152     this.length = 0;
12153     this.addEvents({
12154         /**
12155          * @event clear
12156          * Fires when the collection is cleared.
12157          */
12158         "clear" : true,
12159         /**
12160          * @event add
12161          * Fires when an item is added to the collection.
12162          * @param {Number} index The index at which the item was added.
12163          * @param {Object} o The item added.
12164          * @param {String} key The key associated with the added item.
12165          */
12166         "add" : true,
12167         /**
12168          * @event replace
12169          * Fires when an item is replaced in the collection.
12170          * @param {String} key he key associated with the new added.
12171          * @param {Object} old The item being replaced.
12172          * @param {Object} new The new item.
12173          */
12174         "replace" : true,
12175         /**
12176          * @event remove
12177          * Fires when an item is removed from the collection.
12178          * @param {Object} o The item being removed.
12179          * @param {String} key (optional) The key associated with the removed item.
12180          */
12181         "remove" : true,
12182         "sort" : true
12183     });
12184     this.allowFunctions = allowFunctions === true;
12185     if(keyFn){
12186         this.getKey = keyFn;
12187     }
12188     Roo.util.MixedCollection.superclass.constructor.call(this);
12189 };
12190
12191 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12192     allowFunctions : false,
12193     
12194 /**
12195  * Adds an item to the collection.
12196  * @param {String} key The key to associate with the item
12197  * @param {Object} o The item to add.
12198  * @return {Object} The item added.
12199  */
12200     add : function(key, o){
12201         if(arguments.length == 1){
12202             o = arguments[0];
12203             key = this.getKey(o);
12204         }
12205         if(typeof key == "undefined" || key === null){
12206             this.length++;
12207             this.items.push(o);
12208             this.keys.push(null);
12209         }else{
12210             var old = this.map[key];
12211             if(old){
12212                 return this.replace(key, o);
12213             }
12214             this.length++;
12215             this.items.push(o);
12216             this.map[key] = o;
12217             this.keys.push(key);
12218         }
12219         this.fireEvent("add", this.length-1, o, key);
12220         return o;
12221     },
12222    
12223 /**
12224   * MixedCollection has a generic way to fetch keys if you implement getKey.
12225 <pre><code>
12226 // normal way
12227 var mc = new Roo.util.MixedCollection();
12228 mc.add(someEl.dom.id, someEl);
12229 mc.add(otherEl.dom.id, otherEl);
12230 //and so on
12231
12232 // using getKey
12233 var mc = new Roo.util.MixedCollection();
12234 mc.getKey = function(el){
12235    return el.dom.id;
12236 };
12237 mc.add(someEl);
12238 mc.add(otherEl);
12239
12240 // or via the constructor
12241 var mc = new Roo.util.MixedCollection(false, function(el){
12242    return el.dom.id;
12243 });
12244 mc.add(someEl);
12245 mc.add(otherEl);
12246 </code></pre>
12247  * @param o {Object} The item for which to find the key.
12248  * @return {Object} The key for the passed item.
12249  */
12250     getKey : function(o){
12251          return o.id; 
12252     },
12253    
12254 /**
12255  * Replaces an item in the collection.
12256  * @param {String} key The key associated with the item to replace, or the item to replace.
12257  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12258  * @return {Object}  The new item.
12259  */
12260     replace : function(key, o){
12261         if(arguments.length == 1){
12262             o = arguments[0];
12263             key = this.getKey(o);
12264         }
12265         var old = this.item(key);
12266         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12267              return this.add(key, o);
12268         }
12269         var index = this.indexOfKey(key);
12270         this.items[index] = o;
12271         this.map[key] = o;
12272         this.fireEvent("replace", key, old, o);
12273         return o;
12274     },
12275    
12276 /**
12277  * Adds all elements of an Array or an Object to the collection.
12278  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12279  * an Array of values, each of which are added to the collection.
12280  */
12281     addAll : function(objs){
12282         if(arguments.length > 1 || objs instanceof Array){
12283             var args = arguments.length > 1 ? arguments : objs;
12284             for(var i = 0, len = args.length; i < len; i++){
12285                 this.add(args[i]);
12286             }
12287         }else{
12288             for(var key in objs){
12289                 if(this.allowFunctions || typeof objs[key] != "function"){
12290                     this.add(key, objs[key]);
12291                 }
12292             }
12293         }
12294     },
12295    
12296 /**
12297  * Executes the specified function once for every item in the collection, passing each
12298  * item as the first and only parameter. returning false from the function will stop the iteration.
12299  * @param {Function} fn The function to execute for each item.
12300  * @param {Object} scope (optional) The scope in which to execute the function.
12301  */
12302     each : function(fn, scope){
12303         var items = [].concat(this.items); // each safe for removal
12304         for(var i = 0, len = items.length; i < len; i++){
12305             if(fn.call(scope || items[i], items[i], i, len) === false){
12306                 break;
12307             }
12308         }
12309     },
12310    
12311 /**
12312  * Executes the specified function once for every key in the collection, passing each
12313  * key, and its associated item as the first two parameters.
12314  * @param {Function} fn The function to execute for each item.
12315  * @param {Object} scope (optional) The scope in which to execute the function.
12316  */
12317     eachKey : function(fn, scope){
12318         for(var i = 0, len = this.keys.length; i < len; i++){
12319             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12320         }
12321     },
12322    
12323 /**
12324  * Returns the first item in the collection which elicits a true return value from the
12325  * passed selection function.
12326  * @param {Function} fn The selection function to execute for each item.
12327  * @param {Object} scope (optional) The scope in which to execute the function.
12328  * @return {Object} The first item in the collection which returned true from the selection function.
12329  */
12330     find : function(fn, scope){
12331         for(var i = 0, len = this.items.length; i < len; i++){
12332             if(fn.call(scope || window, this.items[i], this.keys[i])){
12333                 return this.items[i];
12334             }
12335         }
12336         return null;
12337     },
12338    
12339 /**
12340  * Inserts an item at the specified index in the collection.
12341  * @param {Number} index The index to insert the item at.
12342  * @param {String} key The key to associate with the new item, or the item itself.
12343  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12344  * @return {Object} The item inserted.
12345  */
12346     insert : function(index, key, o){
12347         if(arguments.length == 2){
12348             o = arguments[1];
12349             key = this.getKey(o);
12350         }
12351         if(index >= this.length){
12352             return this.add(key, o);
12353         }
12354         this.length++;
12355         this.items.splice(index, 0, o);
12356         if(typeof key != "undefined" && key != null){
12357             this.map[key] = o;
12358         }
12359         this.keys.splice(index, 0, key);
12360         this.fireEvent("add", index, o, key);
12361         return o;
12362     },
12363    
12364 /**
12365  * Removed an item from the collection.
12366  * @param {Object} o The item to remove.
12367  * @return {Object} The item removed.
12368  */
12369     remove : function(o){
12370         return this.removeAt(this.indexOf(o));
12371     },
12372    
12373 /**
12374  * Remove an item from a specified index in the collection.
12375  * @param {Number} index The index within the collection of the item to remove.
12376  */
12377     removeAt : function(index){
12378         if(index < this.length && index >= 0){
12379             this.length--;
12380             var o = this.items[index];
12381             this.items.splice(index, 1);
12382             var key = this.keys[index];
12383             if(typeof key != "undefined"){
12384                 delete this.map[key];
12385             }
12386             this.keys.splice(index, 1);
12387             this.fireEvent("remove", o, key);
12388         }
12389     },
12390    
12391 /**
12392  * Removed an item associated with the passed key fom the collection.
12393  * @param {String} key The key of the item to remove.
12394  */
12395     removeKey : function(key){
12396         return this.removeAt(this.indexOfKey(key));
12397     },
12398    
12399 /**
12400  * Returns the number of items in the collection.
12401  * @return {Number} the number of items in the collection.
12402  */
12403     getCount : function(){
12404         return this.length; 
12405     },
12406    
12407 /**
12408  * Returns index within the collection of the passed Object.
12409  * @param {Object} o The item to find the index of.
12410  * @return {Number} index of the item.
12411  */
12412     indexOf : function(o){
12413         if(!this.items.indexOf){
12414             for(var i = 0, len = this.items.length; i < len; i++){
12415                 if(this.items[i] == o) return i;
12416             }
12417             return -1;
12418         }else{
12419             return this.items.indexOf(o);
12420         }
12421     },
12422    
12423 /**
12424  * Returns index within the collection of the passed key.
12425  * @param {String} key The key to find the index of.
12426  * @return {Number} index of the key.
12427  */
12428     indexOfKey : function(key){
12429         if(!this.keys.indexOf){
12430             for(var i = 0, len = this.keys.length; i < len; i++){
12431                 if(this.keys[i] == key) return i;
12432             }
12433             return -1;
12434         }else{
12435             return this.keys.indexOf(key);
12436         }
12437     },
12438    
12439 /**
12440  * Returns the item associated with the passed key OR index. Key has priority over index.
12441  * @param {String/Number} key The key or index of the item.
12442  * @return {Object} The item associated with the passed key.
12443  */
12444     item : function(key){
12445         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12446         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12447     },
12448     
12449 /**
12450  * Returns the item at the specified index.
12451  * @param {Number} index The index of the item.
12452  * @return {Object}
12453  */
12454     itemAt : function(index){
12455         return this.items[index];
12456     },
12457     
12458 /**
12459  * Returns the item associated with the passed key.
12460  * @param {String/Number} key The key of the item.
12461  * @return {Object} The item associated with the passed key.
12462  */
12463     key : function(key){
12464         return this.map[key];
12465     },
12466    
12467 /**
12468  * Returns true if the collection contains the passed Object as an item.
12469  * @param {Object} o  The Object to look for in the collection.
12470  * @return {Boolean} True if the collection contains the Object as an item.
12471  */
12472     contains : function(o){
12473         return this.indexOf(o) != -1;
12474     },
12475    
12476 /**
12477  * Returns true if the collection contains the passed Object as a key.
12478  * @param {String} key The key to look for in the collection.
12479  * @return {Boolean} True if the collection contains the Object as a key.
12480  */
12481     containsKey : function(key){
12482         return typeof this.map[key] != "undefined";
12483     },
12484    
12485 /**
12486  * Removes all items from the collection.
12487  */
12488     clear : function(){
12489         this.length = 0;
12490         this.items = [];
12491         this.keys = [];
12492         this.map = {};
12493         this.fireEvent("clear");
12494     },
12495    
12496 /**
12497  * Returns the first item in the collection.
12498  * @return {Object} the first item in the collection..
12499  */
12500     first : function(){
12501         return this.items[0]; 
12502     },
12503    
12504 /**
12505  * Returns the last item in the collection.
12506  * @return {Object} the last item in the collection..
12507  */
12508     last : function(){
12509         return this.items[this.length-1];   
12510     },
12511     
12512     _sort : function(property, dir, fn){
12513         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12514         fn = fn || function(a, b){
12515             return a-b;
12516         };
12517         var c = [], k = this.keys, items = this.items;
12518         for(var i = 0, len = items.length; i < len; i++){
12519             c[c.length] = {key: k[i], value: items[i], index: i};
12520         }
12521         c.sort(function(a, b){
12522             var v = fn(a[property], b[property]) * dsc;
12523             if(v == 0){
12524                 v = (a.index < b.index ? -1 : 1);
12525             }
12526             return v;
12527         });
12528         for(var i = 0, len = c.length; i < len; i++){
12529             items[i] = c[i].value;
12530             k[i] = c[i].key;
12531         }
12532         this.fireEvent("sort", this);
12533     },
12534     
12535     /**
12536      * Sorts this collection with the passed comparison function
12537      * @param {String} direction (optional) "ASC" or "DESC"
12538      * @param {Function} fn (optional) comparison function
12539      */
12540     sort : function(dir, fn){
12541         this._sort("value", dir, fn);
12542     },
12543     
12544     /**
12545      * Sorts this collection by keys
12546      * @param {String} direction (optional) "ASC" or "DESC"
12547      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12548      */
12549     keySort : function(dir, fn){
12550         this._sort("key", dir, fn || function(a, b){
12551             return String(a).toUpperCase()-String(b).toUpperCase();
12552         });
12553     },
12554     
12555     /**
12556      * Returns a range of items in this collection
12557      * @param {Number} startIndex (optional) defaults to 0
12558      * @param {Number} endIndex (optional) default to the last item
12559      * @return {Array} An array of items
12560      */
12561     getRange : function(start, end){
12562         var items = this.items;
12563         if(items.length < 1){
12564             return [];
12565         }
12566         start = start || 0;
12567         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12568         var r = [];
12569         if(start <= end){
12570             for(var i = start; i <= end; i++) {
12571                     r[r.length] = items[i];
12572             }
12573         }else{
12574             for(var i = start; i >= end; i--) {
12575                     r[r.length] = items[i];
12576             }
12577         }
12578         return r;
12579     },
12580         
12581     /**
12582      * Filter the <i>objects</i> in this collection by a specific property. 
12583      * Returns a new collection that has been filtered.
12584      * @param {String} property A property on your objects
12585      * @param {String/RegExp} value Either string that the property values 
12586      * should start with or a RegExp to test against the property
12587      * @return {MixedCollection} The new filtered collection
12588      */
12589     filter : function(property, value){
12590         if(!value.exec){ // not a regex
12591             value = String(value);
12592             if(value.length == 0){
12593                 return this.clone();
12594             }
12595             value = new RegExp("^" + Roo.escapeRe(value), "i");
12596         }
12597         return this.filterBy(function(o){
12598             return o && value.test(o[property]);
12599         });
12600         },
12601     
12602     /**
12603      * Filter by a function. * Returns a new collection that has been filtered.
12604      * The passed function will be called with each 
12605      * object in the collection. If the function returns true, the value is included 
12606      * otherwise it is filtered.
12607      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12608      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12609      * @return {MixedCollection} The new filtered collection
12610      */
12611     filterBy : function(fn, scope){
12612         var r = new Roo.util.MixedCollection();
12613         r.getKey = this.getKey;
12614         var k = this.keys, it = this.items;
12615         for(var i = 0, len = it.length; i < len; i++){
12616             if(fn.call(scope||this, it[i], k[i])){
12617                                 r.add(k[i], it[i]);
12618                         }
12619         }
12620         return r;
12621     },
12622     
12623     /**
12624      * Creates a duplicate of this collection
12625      * @return {MixedCollection}
12626      */
12627     clone : function(){
12628         var r = new Roo.util.MixedCollection();
12629         var k = this.keys, it = this.items;
12630         for(var i = 0, len = it.length; i < len; i++){
12631             r.add(k[i], it[i]);
12632         }
12633         r.getKey = this.getKey;
12634         return r;
12635     }
12636 });
12637 /**
12638  * Returns the item associated with the passed key or index.
12639  * @method
12640  * @param {String/Number} key The key or index of the item.
12641  * @return {Object} The item associated with the passed key.
12642  */
12643 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12644  * Based on:
12645  * Ext JS Library 1.1.1
12646  * Copyright(c) 2006-2007, Ext JS, LLC.
12647  *
12648  * Originally Released Under LGPL - original licence link has changed is not relivant.
12649  *
12650  * Fork - LGPL
12651  * <script type="text/javascript">
12652  */
12653 /**
12654  * @class Roo.util.JSON
12655  * Modified version of Douglas Crockford"s json.js that doesn"t
12656  * mess with the Object prototype 
12657  * http://www.json.org/js.html
12658  * @singleton
12659  */
12660 Roo.util.JSON = new (function(){
12661     var useHasOwn = {}.hasOwnProperty ? true : false;
12662     
12663     // crashes Safari in some instances
12664     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12665     
12666     var pad = function(n) {
12667         return n < 10 ? "0" + n : n;
12668     };
12669     
12670     var m = {
12671         "\b": '\\b',
12672         "\t": '\\t',
12673         "\n": '\\n',
12674         "\f": '\\f',
12675         "\r": '\\r',
12676         '"' : '\\"',
12677         "\\": '\\\\'
12678     };
12679
12680     var encodeString = function(s){
12681         if (/["\\\x00-\x1f]/.test(s)) {
12682             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12683                 var c = m[b];
12684                 if(c){
12685                     return c;
12686                 }
12687                 c = b.charCodeAt();
12688                 return "\\u00" +
12689                     Math.floor(c / 16).toString(16) +
12690                     (c % 16).toString(16);
12691             }) + '"';
12692         }
12693         return '"' + s + '"';
12694     };
12695     
12696     var encodeArray = function(o){
12697         var a = ["["], b, i, l = o.length, v;
12698             for (i = 0; i < l; i += 1) {
12699                 v = o[i];
12700                 switch (typeof v) {
12701                     case "undefined":
12702                     case "function":
12703                     case "unknown":
12704                         break;
12705                     default:
12706                         if (b) {
12707                             a.push(',');
12708                         }
12709                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12710                         b = true;
12711                 }
12712             }
12713             a.push("]");
12714             return a.join("");
12715     };
12716     
12717     var encodeDate = function(o){
12718         return '"' + o.getFullYear() + "-" +
12719                 pad(o.getMonth() + 1) + "-" +
12720                 pad(o.getDate()) + "T" +
12721                 pad(o.getHours()) + ":" +
12722                 pad(o.getMinutes()) + ":" +
12723                 pad(o.getSeconds()) + '"';
12724     };
12725     
12726     /**
12727      * Encodes an Object, Array or other value
12728      * @param {Mixed} o The variable to encode
12729      * @return {String} The JSON string
12730      */
12731     this.encode = function(o){
12732         if(typeof o == "undefined" || o === null){
12733             return "null";
12734         }else if(o instanceof Array){
12735             return encodeArray(o);
12736         }else if(o instanceof Date){
12737             return encodeDate(o);
12738         }else if(typeof o == "string"){
12739             return encodeString(o);
12740         }else if(typeof o == "number"){
12741             return isFinite(o) ? String(o) : "null";
12742         }else if(typeof o == "boolean"){
12743             return String(o);
12744         }else {
12745             var a = ["{"], b, i, v;
12746             for (i in o) {
12747                 if(!useHasOwn || o.hasOwnProperty(i)) {
12748                     v = o[i];
12749                     switch (typeof v) {
12750                     case "undefined":
12751                     case "function":
12752                     case "unknown":
12753                         break;
12754                     default:
12755                         if(b){
12756                             a.push(',');
12757                         }
12758                         a.push(this.encode(i), ":",
12759                                 v === null ? "null" : this.encode(v));
12760                         b = true;
12761                     }
12762                 }
12763             }
12764             a.push("}");
12765             return a.join("");
12766         }
12767     };
12768     
12769     /**
12770      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12771      * @param {String} json The JSON string
12772      * @return {Object} The resulting object
12773      */
12774     this.decode = function(json){
12775         /**
12776          * eval:var:json
12777          */
12778         return eval("(" + json + ')');
12779     };
12780 })();
12781 /** 
12782  * Shorthand for {@link Roo.util.JSON#encode}
12783  * @member Roo encode 
12784  * @method */
12785 Roo.encode = Roo.util.JSON.encode;
12786 /** 
12787  * Shorthand for {@link Roo.util.JSON#decode}
12788  * @member Roo decode 
12789  * @method */
12790 Roo.decode = Roo.util.JSON.decode;
12791 /*
12792  * Based on:
12793  * Ext JS Library 1.1.1
12794  * Copyright(c) 2006-2007, Ext JS, LLC.
12795  *
12796  * Originally Released Under LGPL - original licence link has changed is not relivant.
12797  *
12798  * Fork - LGPL
12799  * <script type="text/javascript">
12800  */
12801  
12802 /**
12803  * @class Roo.util.Format
12804  * Reusable data formatting functions
12805  * @singleton
12806  */
12807 Roo.util.Format = function(){
12808     var trimRe = /^\s+|\s+$/g;
12809     return {
12810         /**
12811          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12812          * @param {String} value The string to truncate
12813          * @param {Number} length The maximum length to allow before truncating
12814          * @return {String} The converted text
12815          */
12816         ellipsis : function(value, len){
12817             if(value && value.length > len){
12818                 return value.substr(0, len-3)+"...";
12819             }
12820             return value;
12821         },
12822
12823         /**
12824          * Checks a reference and converts it to empty string if it is undefined
12825          * @param {Mixed} value Reference to check
12826          * @return {Mixed} Empty string if converted, otherwise the original value
12827          */
12828         undef : function(value){
12829             return typeof value != "undefined" ? value : "";
12830         },
12831
12832         /**
12833          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12834          * @param {String} value The string to encode
12835          * @return {String} The encoded text
12836          */
12837         htmlEncode : function(value){
12838             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12839         },
12840
12841         /**
12842          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12843          * @param {String} value The string to decode
12844          * @return {String} The decoded text
12845          */
12846         htmlDecode : function(value){
12847             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12848         },
12849
12850         /**
12851          * Trims any whitespace from either side of a string
12852          * @param {String} value The text to trim
12853          * @return {String} The trimmed text
12854          */
12855         trim : function(value){
12856             return String(value).replace(trimRe, "");
12857         },
12858
12859         /**
12860          * Returns a substring from within an original string
12861          * @param {String} value The original text
12862          * @param {Number} start The start index of the substring
12863          * @param {Number} length The length of the substring
12864          * @return {String} The substring
12865          */
12866         substr : function(value, start, length){
12867             return String(value).substr(start, length);
12868         },
12869
12870         /**
12871          * Converts a string to all lower case letters
12872          * @param {String} value The text to convert
12873          * @return {String} The converted text
12874          */
12875         lowercase : function(value){
12876             return String(value).toLowerCase();
12877         },
12878
12879         /**
12880          * Converts a string to all upper case letters
12881          * @param {String} value The text to convert
12882          * @return {String} The converted text
12883          */
12884         uppercase : function(value){
12885             return String(value).toUpperCase();
12886         },
12887
12888         /**
12889          * Converts the first character only of a string to upper case
12890          * @param {String} value The text to convert
12891          * @return {String} The converted text
12892          */
12893         capitalize : function(value){
12894             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12895         },
12896
12897         // private
12898         call : function(value, fn){
12899             if(arguments.length > 2){
12900                 var args = Array.prototype.slice.call(arguments, 2);
12901                 args.unshift(value);
12902                  
12903                 return /** eval:var:value */  eval(fn).apply(window, args);
12904             }else{
12905                 /** eval:var:value */
12906                 return /** eval:var:value */ eval(fn).call(window, value);
12907             }
12908         },
12909
12910         /**
12911          * Format a number as US currency
12912          * @param {Number/String} value The numeric value to format
12913          * @return {String} The formatted currency string
12914          */
12915         usMoney : function(v){
12916             v = (Math.round((v-0)*100))/100;
12917             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12918             v = String(v);
12919             var ps = v.split('.');
12920             var whole = ps[0];
12921             var sub = ps[1] ? '.'+ ps[1] : '.00';
12922             var r = /(\d+)(\d{3})/;
12923             while (r.test(whole)) {
12924                 whole = whole.replace(r, '$1' + ',' + '$2');
12925             }
12926             return "$" + whole + sub ;
12927         },
12928
12929         /**
12930          * Parse a value into a formatted date using the specified format pattern.
12931          * @param {Mixed} value The value to format
12932          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12933          * @return {String} The formatted date string
12934          */
12935         date : function(v, format){
12936             if(!v){
12937                 return "";
12938             }
12939             if(!(v instanceof Date)){
12940                 v = new Date(Date.parse(v));
12941             }
12942             return v.dateFormat(format || "m/d/Y");
12943         },
12944
12945         /**
12946          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12947          * @param {String} format Any valid date format string
12948          * @return {Function} The date formatting function
12949          */
12950         dateRenderer : function(format){
12951             return function(v){
12952                 return Roo.util.Format.date(v, format);  
12953             };
12954         },
12955
12956         // private
12957         stripTagsRE : /<\/?[^>]+>/gi,
12958         
12959         /**
12960          * Strips all HTML tags
12961          * @param {Mixed} value The text from which to strip tags
12962          * @return {String} The stripped text
12963          */
12964         stripTags : function(v){
12965             return !v ? v : String(v).replace(this.stripTagsRE, "");
12966         }
12967     };
12968 }();/*
12969  * Based on:
12970  * Ext JS Library 1.1.1
12971  * Copyright(c) 2006-2007, Ext JS, LLC.
12972  *
12973  * Originally Released Under LGPL - original licence link has changed is not relivant.
12974  *
12975  * Fork - LGPL
12976  * <script type="text/javascript">
12977  */
12978
12979
12980  
12981
12982 /**
12983  * @class Roo.MasterTemplate
12984  * @extends Roo.Template
12985  * Provides a template that can have child templates. The syntax is:
12986 <pre><code>
12987 var t = new Roo.MasterTemplate(
12988         '&lt;select name="{name}"&gt;',
12989                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12990         '&lt;/select&gt;'
12991 );
12992 t.add('options', {value: 'foo', text: 'bar'});
12993 // or you can add multiple child elements in one shot
12994 t.addAll('options', [
12995     {value: 'foo', text: 'bar'},
12996     {value: 'foo2', text: 'bar2'},
12997     {value: 'foo3', text: 'bar3'}
12998 ]);
12999 // then append, applying the master template values
13000 t.append('my-form', {name: 'my-select'});
13001 </code></pre>
13002 * A name attribute for the child template is not required if you have only one child
13003 * template or you want to refer to them by index.
13004  */
13005 Roo.MasterTemplate = function(){
13006     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13007     this.originalHtml = this.html;
13008     var st = {};
13009     var m, re = this.subTemplateRe;
13010     re.lastIndex = 0;
13011     var subIndex = 0;
13012     while(m = re.exec(this.html)){
13013         var name = m[1], content = m[2];
13014         st[subIndex] = {
13015             name: name,
13016             index: subIndex,
13017             buffer: [],
13018             tpl : new Roo.Template(content)
13019         };
13020         if(name){
13021             st[name] = st[subIndex];
13022         }
13023         st[subIndex].tpl.compile();
13024         st[subIndex].tpl.call = this.call.createDelegate(this);
13025         subIndex++;
13026     }
13027     this.subCount = subIndex;
13028     this.subs = st;
13029 };
13030 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13031     /**
13032     * The regular expression used to match sub templates
13033     * @type RegExp
13034     * @property
13035     */
13036     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13037
13038     /**
13039      * Applies the passed values to a child template.
13040      * @param {String/Number} name (optional) The name or index of the child template
13041      * @param {Array/Object} values The values to be applied to the template
13042      * @return {MasterTemplate} this
13043      */
13044      add : function(name, values){
13045         if(arguments.length == 1){
13046             values = arguments[0];
13047             name = 0;
13048         }
13049         var s = this.subs[name];
13050         s.buffer[s.buffer.length] = s.tpl.apply(values);
13051         return this;
13052     },
13053
13054     /**
13055      * Applies all the passed values to a child template.
13056      * @param {String/Number} name (optional) The name or index of the child template
13057      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13058      * @param {Boolean} reset (optional) True to reset the template first
13059      * @return {MasterTemplate} this
13060      */
13061     fill : function(name, values, reset){
13062         var a = arguments;
13063         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13064             values = a[0];
13065             name = 0;
13066             reset = a[1];
13067         }
13068         if(reset){
13069             this.reset();
13070         }
13071         for(var i = 0, len = values.length; i < len; i++){
13072             this.add(name, values[i]);
13073         }
13074         return this;
13075     },
13076
13077     /**
13078      * Resets the template for reuse
13079      * @return {MasterTemplate} this
13080      */
13081      reset : function(){
13082         var s = this.subs;
13083         for(var i = 0; i < this.subCount; i++){
13084             s[i].buffer = [];
13085         }
13086         return this;
13087     },
13088
13089     applyTemplate : function(values){
13090         var s = this.subs;
13091         var replaceIndex = -1;
13092         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13093             return s[++replaceIndex].buffer.join("");
13094         });
13095         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13096     },
13097
13098     apply : function(){
13099         return this.applyTemplate.apply(this, arguments);
13100     },
13101
13102     compile : function(){return this;}
13103 });
13104
13105 /**
13106  * Alias for fill().
13107  * @method
13108  */
13109 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13110  /**
13111  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13112  * var tpl = Roo.MasterTemplate.from('element-id');
13113  * @param {String/HTMLElement} el
13114  * @param {Object} config
13115  * @static
13116  */
13117 Roo.MasterTemplate.from = function(el, config){
13118     el = Roo.getDom(el);
13119     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13120 };/*
13121  * Based on:
13122  * Ext JS Library 1.1.1
13123  * Copyright(c) 2006-2007, Ext JS, LLC.
13124  *
13125  * Originally Released Under LGPL - original licence link has changed is not relivant.
13126  *
13127  * Fork - LGPL
13128  * <script type="text/javascript">
13129  */
13130
13131  
13132 /**
13133  * @class Roo.util.CSS
13134  * Utility class for manipulating CSS rules
13135  * @singleton
13136  */
13137 Roo.util.CSS = function(){
13138         var rules = null;
13139         var doc = document;
13140
13141     var camelRe = /(-[a-z])/gi;
13142     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13143
13144    return {
13145    /**
13146     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13147     * tag and appended to the HEAD of the document.
13148     * @param {String} cssText The text containing the css rules
13149     * @param {String} id An id to add to the stylesheet for later removal
13150     * @return {StyleSheet}
13151     */
13152    createStyleSheet : function(cssText, id){
13153        var ss;
13154        var head = doc.getElementsByTagName("head")[0];
13155        var rules = doc.createElement("style");
13156        rules.setAttribute("type", "text/css");
13157        if(id){
13158            rules.setAttribute("id", id);
13159        }
13160        if(Roo.isIE){
13161            head.appendChild(rules);
13162            ss = rules.styleSheet;
13163            ss.cssText = cssText;
13164        }else{
13165            try{
13166                 rules.appendChild(doc.createTextNode(cssText));
13167            }catch(e){
13168                rules.cssText = cssText; 
13169            }
13170            head.appendChild(rules);
13171            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13172        }
13173        this.cacheStyleSheet(ss);
13174        return ss;
13175    },
13176
13177    /**
13178     * Removes a style or link tag by id
13179     * @param {String} id The id of the tag
13180     */
13181    removeStyleSheet : function(id){
13182        var existing = doc.getElementById(id);
13183        if(existing){
13184            existing.parentNode.removeChild(existing);
13185        }
13186    },
13187
13188    /**
13189     * Dynamically swaps an existing stylesheet reference for a new one
13190     * @param {String} id The id of an existing link tag to remove
13191     * @param {String} url The href of the new stylesheet to include
13192     */
13193    swapStyleSheet : function(id, url){
13194        this.removeStyleSheet(id);
13195        var ss = doc.createElement("link");
13196        ss.setAttribute("rel", "stylesheet");
13197        ss.setAttribute("type", "text/css");
13198        ss.setAttribute("id", id);
13199        ss.setAttribute("href", url);
13200        doc.getElementsByTagName("head")[0].appendChild(ss);
13201    },
13202    
13203    /**
13204     * Refresh the rule cache if you have dynamically added stylesheets
13205     * @return {Object} An object (hash) of rules indexed by selector
13206     */
13207    refreshCache : function(){
13208        return this.getRules(true);
13209    },
13210
13211    // private
13212    cacheStyleSheet : function(ss){
13213        if(!rules){
13214            rules = {};
13215        }
13216        try{// try catch for cross domain access issue
13217            var ssRules = ss.cssRules || ss.rules;
13218            for(var j = ssRules.length-1; j >= 0; --j){
13219                rules[ssRules[j].selectorText] = ssRules[j];
13220            }
13221        }catch(e){}
13222    },
13223    
13224    /**
13225     * Gets all css rules for the document
13226     * @param {Boolean} refreshCache true to refresh the internal cache
13227     * @return {Object} An object (hash) of rules indexed by selector
13228     */
13229    getRules : function(refreshCache){
13230                 if(rules == null || refreshCache){
13231                         rules = {};
13232                         var ds = doc.styleSheets;
13233                         for(var i =0, len = ds.length; i < len; i++){
13234                             try{
13235                         this.cacheStyleSheet(ds[i]);
13236                     }catch(e){} 
13237                 }
13238                 }
13239                 return rules;
13240         },
13241         
13242         /**
13243     * Gets an an individual CSS rule by selector(s)
13244     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13245     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13246     * @return {CSSRule} The CSS rule or null if one is not found
13247     */
13248    getRule : function(selector, refreshCache){
13249                 var rs = this.getRules(refreshCache);
13250                 if(!(selector instanceof Array)){
13251                     return rs[selector];
13252                 }
13253                 for(var i = 0; i < selector.length; i++){
13254                         if(rs[selector[i]]){
13255                                 return rs[selector[i]];
13256                         }
13257                 }
13258                 return null;
13259         },
13260         
13261         
13262         /**
13263     * Updates a rule property
13264     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13265     * @param {String} property The css property
13266     * @param {String} value The new value for the property
13267     * @return {Boolean} true If a rule was found and updated
13268     */
13269    updateRule : function(selector, property, value){
13270                 if(!(selector instanceof Array)){
13271                         var rule = this.getRule(selector);
13272                         if(rule){
13273                                 rule.style[property.replace(camelRe, camelFn)] = value;
13274                                 return true;
13275                         }
13276                 }else{
13277                         for(var i = 0; i < selector.length; i++){
13278                                 if(this.updateRule(selector[i], property, value)){
13279                                         return true;
13280                                 }
13281                         }
13282                 }
13283                 return false;
13284         }
13285    };   
13286 }();/*
13287  * Based on:
13288  * Ext JS Library 1.1.1
13289  * Copyright(c) 2006-2007, Ext JS, LLC.
13290  *
13291  * Originally Released Under LGPL - original licence link has changed is not relivant.
13292  *
13293  * Fork - LGPL
13294  * <script type="text/javascript">
13295  */
13296
13297  
13298
13299 /**
13300  * @class Roo.util.ClickRepeater
13301  * @extends Roo.util.Observable
13302  * 
13303  * A wrapper class which can be applied to any element. Fires a "click" event while the
13304  * mouse is pressed. The interval between firings may be specified in the config but
13305  * defaults to 10 milliseconds.
13306  * 
13307  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13308  * 
13309  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13310  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13311  * Similar to an autorepeat key delay.
13312  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13313  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13314  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13315  *           "interval" and "delay" are ignored. "immediate" is honored.
13316  * @cfg {Boolean} preventDefault True to prevent the default click event
13317  * @cfg {Boolean} stopDefault True to stop the default click event
13318  * 
13319  * @history
13320  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13321  *     2007-02-02 jvs Renamed to ClickRepeater
13322  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13323  *
13324  *  @constructor
13325  * @param {String/HTMLElement/Element} el The element to listen on
13326  * @param {Object} config
13327  **/
13328 Roo.util.ClickRepeater = function(el, config)
13329 {
13330     this.el = Roo.get(el);
13331     this.el.unselectable();
13332
13333     Roo.apply(this, config);
13334
13335     this.addEvents({
13336     /**
13337      * @event mousedown
13338      * Fires when the mouse button is depressed.
13339      * @param {Roo.util.ClickRepeater} this
13340      */
13341         "mousedown" : true,
13342     /**
13343      * @event click
13344      * Fires on a specified interval during the time the element is pressed.
13345      * @param {Roo.util.ClickRepeater} this
13346      */
13347         "click" : true,
13348     /**
13349      * @event mouseup
13350      * Fires when the mouse key is released.
13351      * @param {Roo.util.ClickRepeater} this
13352      */
13353         "mouseup" : true
13354     });
13355
13356     this.el.on("mousedown", this.handleMouseDown, this);
13357     if(this.preventDefault || this.stopDefault){
13358         this.el.on("click", function(e){
13359             if(this.preventDefault){
13360                 e.preventDefault();
13361             }
13362             if(this.stopDefault){
13363                 e.stopEvent();
13364             }
13365         }, this);
13366     }
13367
13368     // allow inline handler
13369     if(this.handler){
13370         this.on("click", this.handler,  this.scope || this);
13371     }
13372
13373     Roo.util.ClickRepeater.superclass.constructor.call(this);
13374 };
13375
13376 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13377     interval : 20,
13378     delay: 250,
13379     preventDefault : true,
13380     stopDefault : false,
13381     timer : 0,
13382
13383     // private
13384     handleMouseDown : function(){
13385         clearTimeout(this.timer);
13386         this.el.blur();
13387         if(this.pressClass){
13388             this.el.addClass(this.pressClass);
13389         }
13390         this.mousedownTime = new Date();
13391
13392         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13393         this.el.on("mouseout", this.handleMouseOut, this);
13394
13395         this.fireEvent("mousedown", this);
13396         this.fireEvent("click", this);
13397         
13398         this.timer = this.click.defer(this.delay || this.interval, this);
13399     },
13400
13401     // private
13402     click : function(){
13403         this.fireEvent("click", this);
13404         this.timer = this.click.defer(this.getInterval(), this);
13405     },
13406
13407     // private
13408     getInterval: function(){
13409         if(!this.accelerate){
13410             return this.interval;
13411         }
13412         var pressTime = this.mousedownTime.getElapsed();
13413         if(pressTime < 500){
13414             return 400;
13415         }else if(pressTime < 1700){
13416             return 320;
13417         }else if(pressTime < 2600){
13418             return 250;
13419         }else if(pressTime < 3500){
13420             return 180;
13421         }else if(pressTime < 4400){
13422             return 140;
13423         }else if(pressTime < 5300){
13424             return 80;
13425         }else if(pressTime < 6200){
13426             return 50;
13427         }else{
13428             return 10;
13429         }
13430     },
13431
13432     // private
13433     handleMouseOut : function(){
13434         clearTimeout(this.timer);
13435         if(this.pressClass){
13436             this.el.removeClass(this.pressClass);
13437         }
13438         this.el.on("mouseover", this.handleMouseReturn, this);
13439     },
13440
13441     // private
13442     handleMouseReturn : function(){
13443         this.el.un("mouseover", this.handleMouseReturn);
13444         if(this.pressClass){
13445             this.el.addClass(this.pressClass);
13446         }
13447         this.click();
13448     },
13449
13450     // private
13451     handleMouseUp : function(){
13452         clearTimeout(this.timer);
13453         this.el.un("mouseover", this.handleMouseReturn);
13454         this.el.un("mouseout", this.handleMouseOut);
13455         Roo.get(document).un("mouseup", this.handleMouseUp);
13456         this.el.removeClass(this.pressClass);
13457         this.fireEvent("mouseup", this);
13458     }
13459 });/*
13460  * Based on:
13461  * Ext JS Library 1.1.1
13462  * Copyright(c) 2006-2007, Ext JS, LLC.
13463  *
13464  * Originally Released Under LGPL - original licence link has changed is not relivant.
13465  *
13466  * Fork - LGPL
13467  * <script type="text/javascript">
13468  */
13469
13470  
13471 /**
13472  * @class Roo.KeyNav
13473  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13474  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13475  * way to implement custom navigation schemes for any UI component.</p>
13476  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13477  * pageUp, pageDown, del, home, end.  Usage:</p>
13478  <pre><code>
13479 var nav = new Roo.KeyNav("my-element", {
13480     "left" : function(e){
13481         this.moveLeft(e.ctrlKey);
13482     },
13483     "right" : function(e){
13484         this.moveRight(e.ctrlKey);
13485     },
13486     "enter" : function(e){
13487         this.save();
13488     },
13489     scope : this
13490 });
13491 </code></pre>
13492  * @constructor
13493  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13494  * @param {Object} config The config
13495  */
13496 Roo.KeyNav = function(el, config){
13497     this.el = Roo.get(el);
13498     Roo.apply(this, config);
13499     if(!this.disabled){
13500         this.disabled = true;
13501         this.enable();
13502     }
13503 };
13504
13505 Roo.KeyNav.prototype = {
13506     /**
13507      * @cfg {Boolean} disabled
13508      * True to disable this KeyNav instance (defaults to false)
13509      */
13510     disabled : false,
13511     /**
13512      * @cfg {String} defaultEventAction
13513      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13514      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13515      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13516      */
13517     defaultEventAction: "stopEvent",
13518     /**
13519      * @cfg {Boolean} forceKeyDown
13520      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13521      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13522      * handle keydown instead of keypress.
13523      */
13524     forceKeyDown : false,
13525
13526     // private
13527     prepareEvent : function(e){
13528         var k = e.getKey();
13529         var h = this.keyToHandler[k];
13530         //if(h && this[h]){
13531         //    e.stopPropagation();
13532         //}
13533         if(Roo.isSafari && h && k >= 37 && k <= 40){
13534             e.stopEvent();
13535         }
13536     },
13537
13538     // private
13539     relay : function(e){
13540         var k = e.getKey();
13541         var h = this.keyToHandler[k];
13542         if(h && this[h]){
13543             if(this.doRelay(e, this[h], h) !== true){
13544                 e[this.defaultEventAction]();
13545             }
13546         }
13547     },
13548
13549     // private
13550     doRelay : function(e, h, hname){
13551         return h.call(this.scope || this, e);
13552     },
13553
13554     // possible handlers
13555     enter : false,
13556     left : false,
13557     right : false,
13558     up : false,
13559     down : false,
13560     tab : false,
13561     esc : false,
13562     pageUp : false,
13563     pageDown : false,
13564     del : false,
13565     home : false,
13566     end : false,
13567
13568     // quick lookup hash
13569     keyToHandler : {
13570         37 : "left",
13571         39 : "right",
13572         38 : "up",
13573         40 : "down",
13574         33 : "pageUp",
13575         34 : "pageDown",
13576         46 : "del",
13577         36 : "home",
13578         35 : "end",
13579         13 : "enter",
13580         27 : "esc",
13581         9  : "tab"
13582     },
13583
13584         /**
13585          * Enable this KeyNav
13586          */
13587         enable: function(){
13588                 if(this.disabled){
13589             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13590             // the EventObject will normalize Safari automatically
13591             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13592                 this.el.on("keydown", this.relay,  this);
13593             }else{
13594                 this.el.on("keydown", this.prepareEvent,  this);
13595                 this.el.on("keypress", this.relay,  this);
13596             }
13597                     this.disabled = false;
13598                 }
13599         },
13600
13601         /**
13602          * Disable this KeyNav
13603          */
13604         disable: function(){
13605                 if(!this.disabled){
13606                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13607                 this.el.un("keydown", this.relay);
13608             }else{
13609                 this.el.un("keydown", this.prepareEvent);
13610                 this.el.un("keypress", this.relay);
13611             }
13612                     this.disabled = true;
13613                 }
13614         }
13615 };/*
13616  * Based on:
13617  * Ext JS Library 1.1.1
13618  * Copyright(c) 2006-2007, Ext JS, LLC.
13619  *
13620  * Originally Released Under LGPL - original licence link has changed is not relivant.
13621  *
13622  * Fork - LGPL
13623  * <script type="text/javascript">
13624  */
13625
13626  
13627 /**
13628  * @class Roo.KeyMap
13629  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13630  * The constructor accepts the same config object as defined by {@link #addBinding}.
13631  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13632  * combination it will call the function with this signature (if the match is a multi-key
13633  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13634  * A KeyMap can also handle a string representation of keys.<br />
13635  * Usage:
13636  <pre><code>
13637 // map one key by key code
13638 var map = new Roo.KeyMap("my-element", {
13639     key: 13, // or Roo.EventObject.ENTER
13640     fn: myHandler,
13641     scope: myObject
13642 });
13643
13644 // map multiple keys to one action by string
13645 var map = new Roo.KeyMap("my-element", {
13646     key: "a\r\n\t",
13647     fn: myHandler,
13648     scope: myObject
13649 });
13650
13651 // map multiple keys to multiple actions by strings and array of codes
13652 var map = new Roo.KeyMap("my-element", [
13653     {
13654         key: [10,13],
13655         fn: function(){ alert("Return was pressed"); }
13656     }, {
13657         key: "abc",
13658         fn: function(){ alert('a, b or c was pressed'); }
13659     }, {
13660         key: "\t",
13661         ctrl:true,
13662         shift:true,
13663         fn: function(){ alert('Control + shift + tab was pressed.'); }
13664     }
13665 ]);
13666 </code></pre>
13667  * <b>Note: A KeyMap starts enabled</b>
13668  * @constructor
13669  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13670  * @param {Object} config The config (see {@link #addBinding})
13671  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13672  */
13673 Roo.KeyMap = function(el, config, eventName){
13674     this.el  = Roo.get(el);
13675     this.eventName = eventName || "keydown";
13676     this.bindings = [];
13677     if(config){
13678         this.addBinding(config);
13679     }
13680     this.enable();
13681 };
13682
13683 Roo.KeyMap.prototype = {
13684     /**
13685      * True to stop the event from bubbling and prevent the default browser action if the
13686      * key was handled by the KeyMap (defaults to false)
13687      * @type Boolean
13688      */
13689     stopEvent : false,
13690
13691     /**
13692      * Add a new binding to this KeyMap. The following config object properties are supported:
13693      * <pre>
13694 Property    Type             Description
13695 ----------  ---------------  ----------------------------------------------------------------------
13696 key         String/Array     A single keycode or an array of keycodes to handle
13697 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13698 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13699 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13700 fn          Function         The function to call when KeyMap finds the expected key combination
13701 scope       Object           The scope of the callback function
13702 </pre>
13703      *
13704      * Usage:
13705      * <pre><code>
13706 // Create a KeyMap
13707 var map = new Roo.KeyMap(document, {
13708     key: Roo.EventObject.ENTER,
13709     fn: handleKey,
13710     scope: this
13711 });
13712
13713 //Add a new binding to the existing KeyMap later
13714 map.addBinding({
13715     key: 'abc',
13716     shift: true,
13717     fn: handleKey,
13718     scope: this
13719 });
13720 </code></pre>
13721      * @param {Object/Array} config A single KeyMap config or an array of configs
13722      */
13723         addBinding : function(config){
13724         if(config instanceof Array){
13725             for(var i = 0, len = config.length; i < len; i++){
13726                 this.addBinding(config[i]);
13727             }
13728             return;
13729         }
13730         var keyCode = config.key,
13731             shift = config.shift, 
13732             ctrl = config.ctrl, 
13733             alt = config.alt,
13734             fn = config.fn,
13735             scope = config.scope;
13736         if(typeof keyCode == "string"){
13737             var ks = [];
13738             var keyString = keyCode.toUpperCase();
13739             for(var j = 0, len = keyString.length; j < len; j++){
13740                 ks.push(keyString.charCodeAt(j));
13741             }
13742             keyCode = ks;
13743         }
13744         var keyArray = keyCode instanceof Array;
13745         var handler = function(e){
13746             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13747                 var k = e.getKey();
13748                 if(keyArray){
13749                     for(var i = 0, len = keyCode.length; i < len; i++){
13750                         if(keyCode[i] == k){
13751                           if(this.stopEvent){
13752                               e.stopEvent();
13753                           }
13754                           fn.call(scope || window, k, e);
13755                           return;
13756                         }
13757                     }
13758                 }else{
13759                     if(k == keyCode){
13760                         if(this.stopEvent){
13761                            e.stopEvent();
13762                         }
13763                         fn.call(scope || window, k, e);
13764                     }
13765                 }
13766             }
13767         };
13768         this.bindings.push(handler);  
13769         },
13770
13771     /**
13772      * Shorthand for adding a single key listener
13773      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13774      * following options:
13775      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13776      * @param {Function} fn The function to call
13777      * @param {Object} scope (optional) The scope of the function
13778      */
13779     on : function(key, fn, scope){
13780         var keyCode, shift, ctrl, alt;
13781         if(typeof key == "object" && !(key instanceof Array)){
13782             keyCode = key.key;
13783             shift = key.shift;
13784             ctrl = key.ctrl;
13785             alt = key.alt;
13786         }else{
13787             keyCode = key;
13788         }
13789         this.addBinding({
13790             key: keyCode,
13791             shift: shift,
13792             ctrl: ctrl,
13793             alt: alt,
13794             fn: fn,
13795             scope: scope
13796         })
13797     },
13798
13799     // private
13800     handleKeyDown : function(e){
13801             if(this.enabled){ //just in case
13802             var b = this.bindings;
13803             for(var i = 0, len = b.length; i < len; i++){
13804                 b[i].call(this, e);
13805             }
13806             }
13807         },
13808         
13809         /**
13810          * Returns true if this KeyMap is enabled
13811          * @return {Boolean} 
13812          */
13813         isEnabled : function(){
13814             return this.enabled;  
13815         },
13816         
13817         /**
13818          * Enables this KeyMap
13819          */
13820         enable: function(){
13821                 if(!this.enabled){
13822                     this.el.on(this.eventName, this.handleKeyDown, this);
13823                     this.enabled = true;
13824                 }
13825         },
13826
13827         /**
13828          * Disable this KeyMap
13829          */
13830         disable: function(){
13831                 if(this.enabled){
13832                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13833                     this.enabled = false;
13834                 }
13835         }
13836 };/*
13837  * Based on:
13838  * Ext JS Library 1.1.1
13839  * Copyright(c) 2006-2007, Ext JS, LLC.
13840  *
13841  * Originally Released Under LGPL - original licence link has changed is not relivant.
13842  *
13843  * Fork - LGPL
13844  * <script type="text/javascript">
13845  */
13846
13847  
13848 /**
13849  * @class Roo.util.TextMetrics
13850  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13851  * wide, in pixels, a given block of text will be.
13852  * @singleton
13853  */
13854 Roo.util.TextMetrics = function(){
13855     var shared;
13856     return {
13857         /**
13858          * Measures the size of the specified text
13859          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13860          * that can affect the size of the rendered text
13861          * @param {String} text The text to measure
13862          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13863          * in order to accurately measure the text height
13864          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13865          */
13866         measure : function(el, text, fixedWidth){
13867             if(!shared){
13868                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13869             }
13870             shared.bind(el);
13871             shared.setFixedWidth(fixedWidth || 'auto');
13872             return shared.getSize(text);
13873         },
13874
13875         /**
13876          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13877          * the overhead of multiple calls to initialize the style properties on each measurement.
13878          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13879          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13880          * in order to accurately measure the text height
13881          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13882          */
13883         createInstance : function(el, fixedWidth){
13884             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13885         }
13886     };
13887 }();
13888
13889  
13890
13891 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13892     var ml = new Roo.Element(document.createElement('div'));
13893     document.body.appendChild(ml.dom);
13894     ml.position('absolute');
13895     ml.setLeftTop(-1000, -1000);
13896     ml.hide();
13897
13898     if(fixedWidth){
13899         ml.setWidth(fixedWidth);
13900     }
13901      
13902     var instance = {
13903         /**
13904          * Returns the size of the specified text based on the internal element's style and width properties
13905          * @memberOf Roo.util.TextMetrics.Instance#
13906          * @param {String} text The text to measure
13907          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13908          */
13909         getSize : function(text){
13910             ml.update(text);
13911             var s = ml.getSize();
13912             ml.update('');
13913             return s;
13914         },
13915
13916         /**
13917          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13918          * that can affect the size of the rendered text
13919          * @memberOf Roo.util.TextMetrics.Instance#
13920          * @param {String/HTMLElement} el The element, dom node or id
13921          */
13922         bind : function(el){
13923             ml.setStyle(
13924                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13925             );
13926         },
13927
13928         /**
13929          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13930          * to set a fixed width in order to accurately measure the text height.
13931          * @memberOf Roo.util.TextMetrics.Instance#
13932          * @param {Number} width The width to set on the element
13933          */
13934         setFixedWidth : function(width){
13935             ml.setWidth(width);
13936         },
13937
13938         /**
13939          * Returns the measured width of the specified text
13940          * @memberOf Roo.util.TextMetrics.Instance#
13941          * @param {String} text The text to measure
13942          * @return {Number} width The width in pixels
13943          */
13944         getWidth : function(text){
13945             ml.dom.style.width = 'auto';
13946             return this.getSize(text).width;
13947         },
13948
13949         /**
13950          * Returns the measured height of the specified text.  For multiline text, be sure to call
13951          * {@link #setFixedWidth} if necessary.
13952          * @memberOf Roo.util.TextMetrics.Instance#
13953          * @param {String} text The text to measure
13954          * @return {Number} height The height in pixels
13955          */
13956         getHeight : function(text){
13957             return this.getSize(text).height;
13958         }
13959     };
13960
13961     instance.bind(bindTo);
13962
13963     return instance;
13964 };
13965
13966 // backwards compat
13967 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13968  * Based on:
13969  * Ext JS Library 1.1.1
13970  * Copyright(c) 2006-2007, Ext JS, LLC.
13971  *
13972  * Originally Released Under LGPL - original licence link has changed is not relivant.
13973  *
13974  * Fork - LGPL
13975  * <script type="text/javascript">
13976  */
13977
13978 /**
13979  * @class Roo.state.Provider
13980  * Abstract base class for state provider implementations. This class provides methods
13981  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13982  * Provider interface.
13983  */
13984 Roo.state.Provider = function(){
13985     /**
13986      * @event statechange
13987      * Fires when a state change occurs.
13988      * @param {Provider} this This state provider
13989      * @param {String} key The state key which was changed
13990      * @param {String} value The encoded value for the state
13991      */
13992     this.addEvents({
13993         "statechange": true
13994     });
13995     this.state = {};
13996     Roo.state.Provider.superclass.constructor.call(this);
13997 };
13998 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13999     /**
14000      * Returns the current value for a key
14001      * @param {String} name The key name
14002      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14003      * @return {Mixed} The state data
14004      */
14005     get : function(name, defaultValue){
14006         return typeof this.state[name] == "undefined" ?
14007             defaultValue : this.state[name];
14008     },
14009     
14010     /**
14011      * Clears a value from the state
14012      * @param {String} name The key name
14013      */
14014     clear : function(name){
14015         delete this.state[name];
14016         this.fireEvent("statechange", this, name, null);
14017     },
14018     
14019     /**
14020      * Sets the value for a key
14021      * @param {String} name The key name
14022      * @param {Mixed} value The value to set
14023      */
14024     set : function(name, value){
14025         this.state[name] = value;
14026         this.fireEvent("statechange", this, name, value);
14027     },
14028     
14029     /**
14030      * Decodes a string previously encoded with {@link #encodeValue}.
14031      * @param {String} value The value to decode
14032      * @return {Mixed} The decoded value
14033      */
14034     decodeValue : function(cookie){
14035         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14036         var matches = re.exec(unescape(cookie));
14037         if(!matches || !matches[1]) return; // non state cookie
14038         var type = matches[1];
14039         var v = matches[2];
14040         switch(type){
14041             case "n":
14042                 return parseFloat(v);
14043             case "d":
14044                 return new Date(Date.parse(v));
14045             case "b":
14046                 return (v == "1");
14047             case "a":
14048                 var all = [];
14049                 var values = v.split("^");
14050                 for(var i = 0, len = values.length; i < len; i++){
14051                     all.push(this.decodeValue(values[i]));
14052                 }
14053                 return all;
14054            case "o":
14055                 var all = {};
14056                 var values = v.split("^");
14057                 for(var i = 0, len = values.length; i < len; i++){
14058                     var kv = values[i].split("=");
14059                     all[kv[0]] = this.decodeValue(kv[1]);
14060                 }
14061                 return all;
14062            default:
14063                 return v;
14064         }
14065     },
14066     
14067     /**
14068      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14069      * @param {Mixed} value The value to encode
14070      * @return {String} The encoded value
14071      */
14072     encodeValue : function(v){
14073         var enc;
14074         if(typeof v == "number"){
14075             enc = "n:" + v;
14076         }else if(typeof v == "boolean"){
14077             enc = "b:" + (v ? "1" : "0");
14078         }else if(v instanceof Date){
14079             enc = "d:" + v.toGMTString();
14080         }else if(v instanceof Array){
14081             var flat = "";
14082             for(var i = 0, len = v.length; i < len; i++){
14083                 flat += this.encodeValue(v[i]);
14084                 if(i != len-1) flat += "^";
14085             }
14086             enc = "a:" + flat;
14087         }else if(typeof v == "object"){
14088             var flat = "";
14089             for(var key in v){
14090                 if(typeof v[key] != "function"){
14091                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14092                 }
14093             }
14094             enc = "o:" + flat.substring(0, flat.length-1);
14095         }else{
14096             enc = "s:" + v;
14097         }
14098         return escape(enc);        
14099     }
14100 });
14101
14102 /*
14103  * Based on:
14104  * Ext JS Library 1.1.1
14105  * Copyright(c) 2006-2007, Ext JS, LLC.
14106  *
14107  * Originally Released Under LGPL - original licence link has changed is not relivant.
14108  *
14109  * Fork - LGPL
14110  * <script type="text/javascript">
14111  */
14112 /**
14113  * @class Roo.state.Manager
14114  * This is the global state manager. By default all components that are "state aware" check this class
14115  * for state information if you don't pass them a custom state provider. In order for this class
14116  * to be useful, it must be initialized with a provider when your application initializes.
14117  <pre><code>
14118 // in your initialization function
14119 init : function(){
14120    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14121    ...
14122    // supposed you have a {@link Roo.BorderLayout}
14123    var layout = new Roo.BorderLayout(...);
14124    layout.restoreState();
14125    // or a {Roo.BasicDialog}
14126    var dialog = new Roo.BasicDialog(...);
14127    dialog.restoreState();
14128  </code></pre>
14129  * @singleton
14130  */
14131 Roo.state.Manager = function(){
14132     var provider = new Roo.state.Provider();
14133     
14134     return {
14135         /**
14136          * Configures the default state provider for your application
14137          * @param {Provider} stateProvider The state provider to set
14138          */
14139         setProvider : function(stateProvider){
14140             provider = stateProvider;
14141         },
14142         
14143         /**
14144          * Returns the current value for a key
14145          * @param {String} name The key name
14146          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14147          * @return {Mixed} The state data
14148          */
14149         get : function(key, defaultValue){
14150             return provider.get(key, defaultValue);
14151         },
14152         
14153         /**
14154          * Sets the value for a key
14155          * @param {String} name The key name
14156          * @param {Mixed} value The state data
14157          */
14158          set : function(key, value){
14159             provider.set(key, value);
14160         },
14161         
14162         /**
14163          * Clears a value from the state
14164          * @param {String} name The key name
14165          */
14166         clear : function(key){
14167             provider.clear(key);
14168         },
14169         
14170         /**
14171          * Gets the currently configured state provider
14172          * @return {Provider} The state provider
14173          */
14174         getProvider : function(){
14175             return provider;
14176         }
14177     };
14178 }();
14179 /*
14180  * Based on:
14181  * Ext JS Library 1.1.1
14182  * Copyright(c) 2006-2007, Ext JS, LLC.
14183  *
14184  * Originally Released Under LGPL - original licence link has changed is not relivant.
14185  *
14186  * Fork - LGPL
14187  * <script type="text/javascript">
14188  */
14189 /**
14190  * @class Roo.state.CookieProvider
14191  * @extends Roo.state.Provider
14192  * The default Provider implementation which saves state via cookies.
14193  * <br />Usage:
14194  <pre><code>
14195    var cp = new Roo.state.CookieProvider({
14196        path: "/cgi-bin/",
14197        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14198        domain: "roojs.com"
14199    })
14200    Roo.state.Manager.setProvider(cp);
14201  </code></pre>
14202  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14203  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14204  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14205  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14206  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14207  * domain the page is running on including the 'www' like 'www.roojs.com')
14208  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14209  * @constructor
14210  * Create a new CookieProvider
14211  * @param {Object} config The configuration object
14212  */
14213 Roo.state.CookieProvider = function(config){
14214     Roo.state.CookieProvider.superclass.constructor.call(this);
14215     this.path = "/";
14216     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14217     this.domain = null;
14218     this.secure = false;
14219     Roo.apply(this, config);
14220     this.state = this.readCookies();
14221 };
14222
14223 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14224     // private
14225     set : function(name, value){
14226         if(typeof value == "undefined" || value === null){
14227             this.clear(name);
14228             return;
14229         }
14230         this.setCookie(name, value);
14231         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14232     },
14233
14234     // private
14235     clear : function(name){
14236         this.clearCookie(name);
14237         Roo.state.CookieProvider.superclass.clear.call(this, name);
14238     },
14239
14240     // private
14241     readCookies : function(){
14242         var cookies = {};
14243         var c = document.cookie + ";";
14244         var re = /\s?(.*?)=(.*?);/g;
14245         var matches;
14246         while((matches = re.exec(c)) != null){
14247             var name = matches[1];
14248             var value = matches[2];
14249             if(name && name.substring(0,3) == "ys-"){
14250                 cookies[name.substr(3)] = this.decodeValue(value);
14251             }
14252         }
14253         return cookies;
14254     },
14255
14256     // private
14257     setCookie : function(name, value){
14258         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14259            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14260            ((this.path == null) ? "" : ("; path=" + this.path)) +
14261            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14262            ((this.secure == true) ? "; secure" : "");
14263     },
14264
14265     // private
14266     clearCookie : function(name){
14267         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14268            ((this.path == null) ? "" : ("; path=" + this.path)) +
14269            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14270            ((this.secure == true) ? "; secure" : "");
14271     }
14272 });/*
14273  * Based on:
14274  * Ext JS Library 1.1.1
14275  * Copyright(c) 2006-2007, Ext JS, LLC.
14276  *
14277  * Originally Released Under LGPL - original licence link has changed is not relivant.
14278  *
14279  * Fork - LGPL
14280  * <script type="text/javascript">
14281  */
14282
14283
14284
14285 /*
14286  * These classes are derivatives of the similarly named classes in the YUI Library.
14287  * The original license:
14288  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14289  * Code licensed under the BSD License:
14290  * http://developer.yahoo.net/yui/license.txt
14291  */
14292
14293 (function() {
14294
14295 var Event=Roo.EventManager;
14296 var Dom=Roo.lib.Dom;
14297
14298 /**
14299  * @class Roo.dd.DragDrop
14300  * Defines the interface and base operation of items that that can be
14301  * dragged or can be drop targets.  It was designed to be extended, overriding
14302  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14303  * Up to three html elements can be associated with a DragDrop instance:
14304  * <ul>
14305  * <li>linked element: the element that is passed into the constructor.
14306  * This is the element which defines the boundaries for interaction with
14307  * other DragDrop objects.</li>
14308  * <li>handle element(s): The drag operation only occurs if the element that
14309  * was clicked matches a handle element.  By default this is the linked
14310  * element, but there are times that you will want only a portion of the
14311  * linked element to initiate the drag operation, and the setHandleElId()
14312  * method provides a way to define this.</li>
14313  * <li>drag element: this represents the element that would be moved along
14314  * with the cursor during a drag operation.  By default, this is the linked
14315  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14316  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14317  * </li>
14318  * </ul>
14319  * This class should not be instantiated until the onload event to ensure that
14320  * the associated elements are available.
14321  * The following would define a DragDrop obj that would interact with any
14322  * other DragDrop obj in the "group1" group:
14323  * <pre>
14324  *  dd = new Roo.dd.DragDrop("div1", "group1");
14325  * </pre>
14326  * Since none of the event handlers have been implemented, nothing would
14327  * actually happen if you were to run the code above.  Normally you would
14328  * override this class or one of the default implementations, but you can
14329  * also override the methods you want on an instance of the class...
14330  * <pre>
14331  *  dd.onDragDrop = function(e, id) {
14332  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14333  *  }
14334  * </pre>
14335  * @constructor
14336  * @param {String} id of the element that is linked to this instance
14337  * @param {String} sGroup the group of related DragDrop objects
14338  * @param {object} config an object containing configurable attributes
14339  *                Valid properties for DragDrop:
14340  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14341  */
14342 Roo.dd.DragDrop = function(id, sGroup, config) {
14343     if (id) {
14344         this.init(id, sGroup, config);
14345     }
14346 };
14347
14348 Roo.dd.DragDrop.prototype = {
14349
14350     /**
14351      * The id of the element associated with this object.  This is what we
14352      * refer to as the "linked element" because the size and position of
14353      * this element is used to determine when the drag and drop objects have
14354      * interacted.
14355      * @property id
14356      * @type String
14357      */
14358     id: null,
14359
14360     /**
14361      * Configuration attributes passed into the constructor
14362      * @property config
14363      * @type object
14364      */
14365     config: null,
14366
14367     /**
14368      * The id of the element that will be dragged.  By default this is same
14369      * as the linked element , but could be changed to another element. Ex:
14370      * Roo.dd.DDProxy
14371      * @property dragElId
14372      * @type String
14373      * @private
14374      */
14375     dragElId: null,
14376
14377     /**
14378      * the id of the element that initiates the drag operation.  By default
14379      * this is the linked element, but could be changed to be a child of this
14380      * element.  This lets us do things like only starting the drag when the
14381      * header element within the linked html element is clicked.
14382      * @property handleElId
14383      * @type String
14384      * @private
14385      */
14386     handleElId: null,
14387
14388     /**
14389      * An associative array of HTML tags that will be ignored if clicked.
14390      * @property invalidHandleTypes
14391      * @type {string: string}
14392      */
14393     invalidHandleTypes: null,
14394
14395     /**
14396      * An associative array of ids for elements that will be ignored if clicked
14397      * @property invalidHandleIds
14398      * @type {string: string}
14399      */
14400     invalidHandleIds: null,
14401
14402     /**
14403      * An indexted array of css class names for elements that will be ignored
14404      * if clicked.
14405      * @property invalidHandleClasses
14406      * @type string[]
14407      */
14408     invalidHandleClasses: null,
14409
14410     /**
14411      * The linked element's absolute X position at the time the drag was
14412      * started
14413      * @property startPageX
14414      * @type int
14415      * @private
14416      */
14417     startPageX: 0,
14418
14419     /**
14420      * The linked element's absolute X position at the time the drag was
14421      * started
14422      * @property startPageY
14423      * @type int
14424      * @private
14425      */
14426     startPageY: 0,
14427
14428     /**
14429      * The group defines a logical collection of DragDrop objects that are
14430      * related.  Instances only get events when interacting with other
14431      * DragDrop object in the same group.  This lets us define multiple
14432      * groups using a single DragDrop subclass if we want.
14433      * @property groups
14434      * @type {string: string}
14435      */
14436     groups: null,
14437
14438     /**
14439      * Individual drag/drop instances can be locked.  This will prevent
14440      * onmousedown start drag.
14441      * @property locked
14442      * @type boolean
14443      * @private
14444      */
14445     locked: false,
14446
14447     /**
14448      * Lock this instance
14449      * @method lock
14450      */
14451     lock: function() { this.locked = true; },
14452
14453     /**
14454      * Unlock this instace
14455      * @method unlock
14456      */
14457     unlock: function() { this.locked = false; },
14458
14459     /**
14460      * By default, all insances can be a drop target.  This can be disabled by
14461      * setting isTarget to false.
14462      * @method isTarget
14463      * @type boolean
14464      */
14465     isTarget: true,
14466
14467     /**
14468      * The padding configured for this drag and drop object for calculating
14469      * the drop zone intersection with this object.
14470      * @method padding
14471      * @type int[]
14472      */
14473     padding: null,
14474
14475     /**
14476      * Cached reference to the linked element
14477      * @property _domRef
14478      * @private
14479      */
14480     _domRef: null,
14481
14482     /**
14483      * Internal typeof flag
14484      * @property __ygDragDrop
14485      * @private
14486      */
14487     __ygDragDrop: true,
14488
14489     /**
14490      * Set to true when horizontal contraints are applied
14491      * @property constrainX
14492      * @type boolean
14493      * @private
14494      */
14495     constrainX: false,
14496
14497     /**
14498      * Set to true when vertical contraints are applied
14499      * @property constrainY
14500      * @type boolean
14501      * @private
14502      */
14503     constrainY: false,
14504
14505     /**
14506      * The left constraint
14507      * @property minX
14508      * @type int
14509      * @private
14510      */
14511     minX: 0,
14512
14513     /**
14514      * The right constraint
14515      * @property maxX
14516      * @type int
14517      * @private
14518      */
14519     maxX: 0,
14520
14521     /**
14522      * The up constraint
14523      * @property minY
14524      * @type int
14525      * @type int
14526      * @private
14527      */
14528     minY: 0,
14529
14530     /**
14531      * The down constraint
14532      * @property maxY
14533      * @type int
14534      * @private
14535      */
14536     maxY: 0,
14537
14538     /**
14539      * Maintain offsets when we resetconstraints.  Set to true when you want
14540      * the position of the element relative to its parent to stay the same
14541      * when the page changes
14542      *
14543      * @property maintainOffset
14544      * @type boolean
14545      */
14546     maintainOffset: false,
14547
14548     /**
14549      * Array of pixel locations the element will snap to if we specified a
14550      * horizontal graduation/interval.  This array is generated automatically
14551      * when you define a tick interval.
14552      * @property xTicks
14553      * @type int[]
14554      */
14555     xTicks: null,
14556
14557     /**
14558      * Array of pixel locations the element will snap to if we specified a
14559      * vertical graduation/interval.  This array is generated automatically
14560      * when you define a tick interval.
14561      * @property yTicks
14562      * @type int[]
14563      */
14564     yTicks: null,
14565
14566     /**
14567      * By default the drag and drop instance will only respond to the primary
14568      * button click (left button for a right-handed mouse).  Set to true to
14569      * allow drag and drop to start with any mouse click that is propogated
14570      * by the browser
14571      * @property primaryButtonOnly
14572      * @type boolean
14573      */
14574     primaryButtonOnly: true,
14575
14576     /**
14577      * The availabe property is false until the linked dom element is accessible.
14578      * @property available
14579      * @type boolean
14580      */
14581     available: false,
14582
14583     /**
14584      * By default, drags can only be initiated if the mousedown occurs in the
14585      * region the linked element is.  This is done in part to work around a
14586      * bug in some browsers that mis-report the mousedown if the previous
14587      * mouseup happened outside of the window.  This property is set to true
14588      * if outer handles are defined.
14589      *
14590      * @property hasOuterHandles
14591      * @type boolean
14592      * @default false
14593      */
14594     hasOuterHandles: false,
14595
14596     /**
14597      * Code that executes immediately before the startDrag event
14598      * @method b4StartDrag
14599      * @private
14600      */
14601     b4StartDrag: function(x, y) { },
14602
14603     /**
14604      * Abstract method called after a drag/drop object is clicked
14605      * and the drag or mousedown time thresholds have beeen met.
14606      * @method startDrag
14607      * @param {int} X click location
14608      * @param {int} Y click location
14609      */
14610     startDrag: function(x, y) { /* override this */ },
14611
14612     /**
14613      * Code that executes immediately before the onDrag event
14614      * @method b4Drag
14615      * @private
14616      */
14617     b4Drag: function(e) { },
14618
14619     /**
14620      * Abstract method called during the onMouseMove event while dragging an
14621      * object.
14622      * @method onDrag
14623      * @param {Event} e the mousemove event
14624      */
14625     onDrag: function(e) { /* override this */ },
14626
14627     /**
14628      * Abstract method called when this element fist begins hovering over
14629      * another DragDrop obj
14630      * @method onDragEnter
14631      * @param {Event} e the mousemove event
14632      * @param {String|DragDrop[]} id In POINT mode, the element
14633      * id this is hovering over.  In INTERSECT mode, an array of one or more
14634      * dragdrop items being hovered over.
14635      */
14636     onDragEnter: function(e, id) { /* override this */ },
14637
14638     /**
14639      * Code that executes immediately before the onDragOver event
14640      * @method b4DragOver
14641      * @private
14642      */
14643     b4DragOver: function(e) { },
14644
14645     /**
14646      * Abstract method called when this element is hovering over another
14647      * DragDrop obj
14648      * @method onDragOver
14649      * @param {Event} e the mousemove event
14650      * @param {String|DragDrop[]} id In POINT mode, the element
14651      * id this is hovering over.  In INTERSECT mode, an array of dd items
14652      * being hovered over.
14653      */
14654     onDragOver: function(e, id) { /* override this */ },
14655
14656     /**
14657      * Code that executes immediately before the onDragOut event
14658      * @method b4DragOut
14659      * @private
14660      */
14661     b4DragOut: function(e) { },
14662
14663     /**
14664      * Abstract method called when we are no longer hovering over an element
14665      * @method onDragOut
14666      * @param {Event} e the mousemove event
14667      * @param {String|DragDrop[]} id In POINT mode, the element
14668      * id this was hovering over.  In INTERSECT mode, an array of dd items
14669      * that the mouse is no longer over.
14670      */
14671     onDragOut: function(e, id) { /* override this */ },
14672
14673     /**
14674      * Code that executes immediately before the onDragDrop event
14675      * @method b4DragDrop
14676      * @private
14677      */
14678     b4DragDrop: function(e) { },
14679
14680     /**
14681      * Abstract method called when this item is dropped on another DragDrop
14682      * obj
14683      * @method onDragDrop
14684      * @param {Event} e the mouseup event
14685      * @param {String|DragDrop[]} id In POINT mode, the element
14686      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14687      * was dropped on.
14688      */
14689     onDragDrop: function(e, id) { /* override this */ },
14690
14691     /**
14692      * Abstract method called when this item is dropped on an area with no
14693      * drop target
14694      * @method onInvalidDrop
14695      * @param {Event} e the mouseup event
14696      */
14697     onInvalidDrop: function(e) { /* override this */ },
14698
14699     /**
14700      * Code that executes immediately before the endDrag event
14701      * @method b4EndDrag
14702      * @private
14703      */
14704     b4EndDrag: function(e) { },
14705
14706     /**
14707      * Fired when we are done dragging the object
14708      * @method endDrag
14709      * @param {Event} e the mouseup event
14710      */
14711     endDrag: function(e) { /* override this */ },
14712
14713     /**
14714      * Code executed immediately before the onMouseDown event
14715      * @method b4MouseDown
14716      * @param {Event} e the mousedown event
14717      * @private
14718      */
14719     b4MouseDown: function(e) {  },
14720
14721     /**
14722      * Event handler that fires when a drag/drop obj gets a mousedown
14723      * @method onMouseDown
14724      * @param {Event} e the mousedown event
14725      */
14726     onMouseDown: function(e) { /* override this */ },
14727
14728     /**
14729      * Event handler that fires when a drag/drop obj gets a mouseup
14730      * @method onMouseUp
14731      * @param {Event} e the mouseup event
14732      */
14733     onMouseUp: function(e) { /* override this */ },
14734
14735     /**
14736      * Override the onAvailable method to do what is needed after the initial
14737      * position was determined.
14738      * @method onAvailable
14739      */
14740     onAvailable: function () {
14741     },
14742
14743     /*
14744      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14745      * @type Object
14746      */
14747     defaultPadding : {left:0, right:0, top:0, bottom:0},
14748
14749     /*
14750      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14751  *
14752  * Usage:
14753  <pre><code>
14754  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14755                 { dragElId: "existingProxyDiv" });
14756  dd.startDrag = function(){
14757      this.constrainTo("parent-id");
14758  };
14759  </code></pre>
14760  * Or you can initalize it using the {@link Roo.Element} object:
14761  <pre><code>
14762  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14763      startDrag : function(){
14764          this.constrainTo("parent-id");
14765      }
14766  });
14767  </code></pre>
14768      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14769      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14770      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14771      * an object containing the sides to pad. For example: {right:10, bottom:10}
14772      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14773      */
14774     constrainTo : function(constrainTo, pad, inContent){
14775         if(typeof pad == "number"){
14776             pad = {left: pad, right:pad, top:pad, bottom:pad};
14777         }
14778         pad = pad || this.defaultPadding;
14779         var b = Roo.get(this.getEl()).getBox();
14780         var ce = Roo.get(constrainTo);
14781         var s = ce.getScroll();
14782         var c, cd = ce.dom;
14783         if(cd == document.body){
14784             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14785         }else{
14786             xy = ce.getXY();
14787             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14788         }
14789
14790
14791         var topSpace = b.y - c.y;
14792         var leftSpace = b.x - c.x;
14793
14794         this.resetConstraints();
14795         this.setXConstraint(leftSpace - (pad.left||0), // left
14796                 c.width - leftSpace - b.width - (pad.right||0) //right
14797         );
14798         this.setYConstraint(topSpace - (pad.top||0), //top
14799                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14800         );
14801     },
14802
14803     /**
14804      * Returns a reference to the linked element
14805      * @method getEl
14806      * @return {HTMLElement} the html element
14807      */
14808     getEl: function() {
14809         if (!this._domRef) {
14810             this._domRef = Roo.getDom(this.id);
14811         }
14812
14813         return this._domRef;
14814     },
14815
14816     /**
14817      * Returns a reference to the actual element to drag.  By default this is
14818      * the same as the html element, but it can be assigned to another
14819      * element. An example of this can be found in Roo.dd.DDProxy
14820      * @method getDragEl
14821      * @return {HTMLElement} the html element
14822      */
14823     getDragEl: function() {
14824         return Roo.getDom(this.dragElId);
14825     },
14826
14827     /**
14828      * Sets up the DragDrop object.  Must be called in the constructor of any
14829      * Roo.dd.DragDrop subclass
14830      * @method init
14831      * @param id the id of the linked element
14832      * @param {String} sGroup the group of related items
14833      * @param {object} config configuration attributes
14834      */
14835     init: function(id, sGroup, config) {
14836         this.initTarget(id, sGroup, config);
14837         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14838         // Event.on(this.id, "selectstart", Event.preventDefault);
14839     },
14840
14841     /**
14842      * Initializes Targeting functionality only... the object does not
14843      * get a mousedown handler.
14844      * @method initTarget
14845      * @param id the id of the linked element
14846      * @param {String} sGroup the group of related items
14847      * @param {object} config configuration attributes
14848      */
14849     initTarget: function(id, sGroup, config) {
14850
14851         // configuration attributes
14852         this.config = config || {};
14853
14854         // create a local reference to the drag and drop manager
14855         this.DDM = Roo.dd.DDM;
14856         // initialize the groups array
14857         this.groups = {};
14858
14859         // assume that we have an element reference instead of an id if the
14860         // parameter is not a string
14861         if (typeof id !== "string") {
14862             id = Roo.id(id);
14863         }
14864
14865         // set the id
14866         this.id = id;
14867
14868         // add to an interaction group
14869         this.addToGroup((sGroup) ? sGroup : "default");
14870
14871         // We don't want to register this as the handle with the manager
14872         // so we just set the id rather than calling the setter.
14873         this.handleElId = id;
14874
14875         // the linked element is the element that gets dragged by default
14876         this.setDragElId(id);
14877
14878         // by default, clicked anchors will not start drag operations.
14879         this.invalidHandleTypes = { A: "A" };
14880         this.invalidHandleIds = {};
14881         this.invalidHandleClasses = [];
14882
14883         this.applyConfig();
14884
14885         this.handleOnAvailable();
14886     },
14887
14888     /**
14889      * Applies the configuration parameters that were passed into the constructor.
14890      * This is supposed to happen at each level through the inheritance chain.  So
14891      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14892      * DragDrop in order to get all of the parameters that are available in
14893      * each object.
14894      * @method applyConfig
14895      */
14896     applyConfig: function() {
14897
14898         // configurable properties:
14899         //    padding, isTarget, maintainOffset, primaryButtonOnly
14900         this.padding           = this.config.padding || [0, 0, 0, 0];
14901         this.isTarget          = (this.config.isTarget !== false);
14902         this.maintainOffset    = (this.config.maintainOffset);
14903         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14904
14905     },
14906
14907     /**
14908      * Executed when the linked element is available
14909      * @method handleOnAvailable
14910      * @private
14911      */
14912     handleOnAvailable: function() {
14913         this.available = true;
14914         this.resetConstraints();
14915         this.onAvailable();
14916     },
14917
14918      /**
14919      * Configures the padding for the target zone in px.  Effectively expands
14920      * (or reduces) the virtual object size for targeting calculations.
14921      * Supports css-style shorthand; if only one parameter is passed, all sides
14922      * will have that padding, and if only two are passed, the top and bottom
14923      * will have the first param, the left and right the second.
14924      * @method setPadding
14925      * @param {int} iTop    Top pad
14926      * @param {int} iRight  Right pad
14927      * @param {int} iBot    Bot pad
14928      * @param {int} iLeft   Left pad
14929      */
14930     setPadding: function(iTop, iRight, iBot, iLeft) {
14931         // this.padding = [iLeft, iRight, iTop, iBot];
14932         if (!iRight && 0 !== iRight) {
14933             this.padding = [iTop, iTop, iTop, iTop];
14934         } else if (!iBot && 0 !== iBot) {
14935             this.padding = [iTop, iRight, iTop, iRight];
14936         } else {
14937             this.padding = [iTop, iRight, iBot, iLeft];
14938         }
14939     },
14940
14941     /**
14942      * Stores the initial placement of the linked element.
14943      * @method setInitialPosition
14944      * @param {int} diffX   the X offset, default 0
14945      * @param {int} diffY   the Y offset, default 0
14946      */
14947     setInitPosition: function(diffX, diffY) {
14948         var el = this.getEl();
14949
14950         if (!this.DDM.verifyEl(el)) {
14951             return;
14952         }
14953
14954         var dx = diffX || 0;
14955         var dy = diffY || 0;
14956
14957         var p = Dom.getXY( el );
14958
14959         this.initPageX = p[0] - dx;
14960         this.initPageY = p[1] - dy;
14961
14962         this.lastPageX = p[0];
14963         this.lastPageY = p[1];
14964
14965
14966         this.setStartPosition(p);
14967     },
14968
14969     /**
14970      * Sets the start position of the element.  This is set when the obj
14971      * is initialized, the reset when a drag is started.
14972      * @method setStartPosition
14973      * @param pos current position (from previous lookup)
14974      * @private
14975      */
14976     setStartPosition: function(pos) {
14977         var p = pos || Dom.getXY( this.getEl() );
14978         this.deltaSetXY = null;
14979
14980         this.startPageX = p[0];
14981         this.startPageY = p[1];
14982     },
14983
14984     /**
14985      * Add this instance to a group of related drag/drop objects.  All
14986      * instances belong to at least one group, and can belong to as many
14987      * groups as needed.
14988      * @method addToGroup
14989      * @param sGroup {string} the name of the group
14990      */
14991     addToGroup: function(sGroup) {
14992         this.groups[sGroup] = true;
14993         this.DDM.regDragDrop(this, sGroup);
14994     },
14995
14996     /**
14997      * Remove's this instance from the supplied interaction group
14998      * @method removeFromGroup
14999      * @param {string}  sGroup  The group to drop
15000      */
15001     removeFromGroup: function(sGroup) {
15002         if (this.groups[sGroup]) {
15003             delete this.groups[sGroup];
15004         }
15005
15006         this.DDM.removeDDFromGroup(this, sGroup);
15007     },
15008
15009     /**
15010      * Allows you to specify that an element other than the linked element
15011      * will be moved with the cursor during a drag
15012      * @method setDragElId
15013      * @param id {string} the id of the element that will be used to initiate the drag
15014      */
15015     setDragElId: function(id) {
15016         this.dragElId = id;
15017     },
15018
15019     /**
15020      * Allows you to specify a child of the linked element that should be
15021      * used to initiate the drag operation.  An example of this would be if
15022      * you have a content div with text and links.  Clicking anywhere in the
15023      * content area would normally start the drag operation.  Use this method
15024      * to specify that an element inside of the content div is the element
15025      * that starts the drag operation.
15026      * @method setHandleElId
15027      * @param id {string} the id of the element that will be used to
15028      * initiate the drag.
15029      */
15030     setHandleElId: function(id) {
15031         if (typeof id !== "string") {
15032             id = Roo.id(id);
15033         }
15034         this.handleElId = id;
15035         this.DDM.regHandle(this.id, id);
15036     },
15037
15038     /**
15039      * Allows you to set an element outside of the linked element as a drag
15040      * handle
15041      * @method setOuterHandleElId
15042      * @param id the id of the element that will be used to initiate the drag
15043      */
15044     setOuterHandleElId: function(id) {
15045         if (typeof id !== "string") {
15046             id = Roo.id(id);
15047         }
15048         Event.on(id, "mousedown",
15049                 this.handleMouseDown, this);
15050         this.setHandleElId(id);
15051
15052         this.hasOuterHandles = true;
15053     },
15054
15055     /**
15056      * Remove all drag and drop hooks for this element
15057      * @method unreg
15058      */
15059     unreg: function() {
15060         Event.un(this.id, "mousedown",
15061                 this.handleMouseDown);
15062         this._domRef = null;
15063         this.DDM._remove(this);
15064     },
15065
15066     destroy : function(){
15067         this.unreg();
15068     },
15069
15070     /**
15071      * Returns true if this instance is locked, or the drag drop mgr is locked
15072      * (meaning that all drag/drop is disabled on the page.)
15073      * @method isLocked
15074      * @return {boolean} true if this obj or all drag/drop is locked, else
15075      * false
15076      */
15077     isLocked: function() {
15078         return (this.DDM.isLocked() || this.locked);
15079     },
15080
15081     /**
15082      * Fired when this object is clicked
15083      * @method handleMouseDown
15084      * @param {Event} e
15085      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15086      * @private
15087      */
15088     handleMouseDown: function(e, oDD){
15089         if (this.primaryButtonOnly && e.button != 0) {
15090             return;
15091         }
15092
15093         if (this.isLocked()) {
15094             return;
15095         }
15096
15097         this.DDM.refreshCache(this.groups);
15098
15099         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15100         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15101         } else {
15102             if (this.clickValidator(e)) {
15103
15104                 // set the initial element position
15105                 this.setStartPosition();
15106
15107
15108                 this.b4MouseDown(e);
15109                 this.onMouseDown(e);
15110
15111                 this.DDM.handleMouseDown(e, this);
15112
15113                 this.DDM.stopEvent(e);
15114             } else {
15115
15116
15117             }
15118         }
15119     },
15120
15121     clickValidator: function(e) {
15122         var target = e.getTarget();
15123         return ( this.isValidHandleChild(target) &&
15124                     (this.id == this.handleElId ||
15125                         this.DDM.handleWasClicked(target, this.id)) );
15126     },
15127
15128     /**
15129      * Allows you to specify a tag name that should not start a drag operation
15130      * when clicked.  This is designed to facilitate embedding links within a
15131      * drag handle that do something other than start the drag.
15132      * @method addInvalidHandleType
15133      * @param {string} tagName the type of element to exclude
15134      */
15135     addInvalidHandleType: function(tagName) {
15136         var type = tagName.toUpperCase();
15137         this.invalidHandleTypes[type] = type;
15138     },
15139
15140     /**
15141      * Lets you to specify an element id for a child of a drag handle
15142      * that should not initiate a drag
15143      * @method addInvalidHandleId
15144      * @param {string} id the element id of the element you wish to ignore
15145      */
15146     addInvalidHandleId: function(id) {
15147         if (typeof id !== "string") {
15148             id = Roo.id(id);
15149         }
15150         this.invalidHandleIds[id] = id;
15151     },
15152
15153     /**
15154      * Lets you specify a css class of elements that will not initiate a drag
15155      * @method addInvalidHandleClass
15156      * @param {string} cssClass the class of the elements you wish to ignore
15157      */
15158     addInvalidHandleClass: function(cssClass) {
15159         this.invalidHandleClasses.push(cssClass);
15160     },
15161
15162     /**
15163      * Unsets an excluded tag name set by addInvalidHandleType
15164      * @method removeInvalidHandleType
15165      * @param {string} tagName the type of element to unexclude
15166      */
15167     removeInvalidHandleType: function(tagName) {
15168         var type = tagName.toUpperCase();
15169         // this.invalidHandleTypes[type] = null;
15170         delete this.invalidHandleTypes[type];
15171     },
15172
15173     /**
15174      * Unsets an invalid handle id
15175      * @method removeInvalidHandleId
15176      * @param {string} id the id of the element to re-enable
15177      */
15178     removeInvalidHandleId: function(id) {
15179         if (typeof id !== "string") {
15180             id = Roo.id(id);
15181         }
15182         delete this.invalidHandleIds[id];
15183     },
15184
15185     /**
15186      * Unsets an invalid css class
15187      * @method removeInvalidHandleClass
15188      * @param {string} cssClass the class of the element(s) you wish to
15189      * re-enable
15190      */
15191     removeInvalidHandleClass: function(cssClass) {
15192         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15193             if (this.invalidHandleClasses[i] == cssClass) {
15194                 delete this.invalidHandleClasses[i];
15195             }
15196         }
15197     },
15198
15199     /**
15200      * Checks the tag exclusion list to see if this click should be ignored
15201      * @method isValidHandleChild
15202      * @param {HTMLElement} node the HTMLElement to evaluate
15203      * @return {boolean} true if this is a valid tag type, false if not
15204      */
15205     isValidHandleChild: function(node) {
15206
15207         var valid = true;
15208         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15209         var nodeName;
15210         try {
15211             nodeName = node.nodeName.toUpperCase();
15212         } catch(e) {
15213             nodeName = node.nodeName;
15214         }
15215         valid = valid && !this.invalidHandleTypes[nodeName];
15216         valid = valid && !this.invalidHandleIds[node.id];
15217
15218         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15219             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15220         }
15221
15222
15223         return valid;
15224
15225     },
15226
15227     /**
15228      * Create the array of horizontal tick marks if an interval was specified
15229      * in setXConstraint().
15230      * @method setXTicks
15231      * @private
15232      */
15233     setXTicks: function(iStartX, iTickSize) {
15234         this.xTicks = [];
15235         this.xTickSize = iTickSize;
15236
15237         var tickMap = {};
15238
15239         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15240             if (!tickMap[i]) {
15241                 this.xTicks[this.xTicks.length] = i;
15242                 tickMap[i] = true;
15243             }
15244         }
15245
15246         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15247             if (!tickMap[i]) {
15248                 this.xTicks[this.xTicks.length] = i;
15249                 tickMap[i] = true;
15250             }
15251         }
15252
15253         this.xTicks.sort(this.DDM.numericSort) ;
15254     },
15255
15256     /**
15257      * Create the array of vertical tick marks if an interval was specified in
15258      * setYConstraint().
15259      * @method setYTicks
15260      * @private
15261      */
15262     setYTicks: function(iStartY, iTickSize) {
15263         this.yTicks = [];
15264         this.yTickSize = iTickSize;
15265
15266         var tickMap = {};
15267
15268         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15269             if (!tickMap[i]) {
15270                 this.yTicks[this.yTicks.length] = i;
15271                 tickMap[i] = true;
15272             }
15273         }
15274
15275         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15276             if (!tickMap[i]) {
15277                 this.yTicks[this.yTicks.length] = i;
15278                 tickMap[i] = true;
15279             }
15280         }
15281
15282         this.yTicks.sort(this.DDM.numericSort) ;
15283     },
15284
15285     /**
15286      * By default, the element can be dragged any place on the screen.  Use
15287      * this method to limit the horizontal travel of the element.  Pass in
15288      * 0,0 for the parameters if you want to lock the drag to the y axis.
15289      * @method setXConstraint
15290      * @param {int} iLeft the number of pixels the element can move to the left
15291      * @param {int} iRight the number of pixels the element can move to the
15292      * right
15293      * @param {int} iTickSize optional parameter for specifying that the
15294      * element
15295      * should move iTickSize pixels at a time.
15296      */
15297     setXConstraint: function(iLeft, iRight, iTickSize) {
15298         this.leftConstraint = iLeft;
15299         this.rightConstraint = iRight;
15300
15301         this.minX = this.initPageX - iLeft;
15302         this.maxX = this.initPageX + iRight;
15303         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15304
15305         this.constrainX = true;
15306     },
15307
15308     /**
15309      * Clears any constraints applied to this instance.  Also clears ticks
15310      * since they can't exist independent of a constraint at this time.
15311      * @method clearConstraints
15312      */
15313     clearConstraints: function() {
15314         this.constrainX = false;
15315         this.constrainY = false;
15316         this.clearTicks();
15317     },
15318
15319     /**
15320      * Clears any tick interval defined for this instance
15321      * @method clearTicks
15322      */
15323     clearTicks: function() {
15324         this.xTicks = null;
15325         this.yTicks = null;
15326         this.xTickSize = 0;
15327         this.yTickSize = 0;
15328     },
15329
15330     /**
15331      * By default, the element can be dragged any place on the screen.  Set
15332      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15333      * parameters if you want to lock the drag to the x axis.
15334      * @method setYConstraint
15335      * @param {int} iUp the number of pixels the element can move up
15336      * @param {int} iDown the number of pixels the element can move down
15337      * @param {int} iTickSize optional parameter for specifying that the
15338      * element should move iTickSize pixels at a time.
15339      */
15340     setYConstraint: function(iUp, iDown, iTickSize) {
15341         this.topConstraint = iUp;
15342         this.bottomConstraint = iDown;
15343
15344         this.minY = this.initPageY - iUp;
15345         this.maxY = this.initPageY + iDown;
15346         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15347
15348         this.constrainY = true;
15349
15350     },
15351
15352     /**
15353      * resetConstraints must be called if you manually reposition a dd element.
15354      * @method resetConstraints
15355      * @param {boolean} maintainOffset
15356      */
15357     resetConstraints: function() {
15358
15359
15360         // Maintain offsets if necessary
15361         if (this.initPageX || this.initPageX === 0) {
15362             // figure out how much this thing has moved
15363             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15364             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15365
15366             this.setInitPosition(dx, dy);
15367
15368         // This is the first time we have detected the element's position
15369         } else {
15370             this.setInitPosition();
15371         }
15372
15373         if (this.constrainX) {
15374             this.setXConstraint( this.leftConstraint,
15375                                  this.rightConstraint,
15376                                  this.xTickSize        );
15377         }
15378
15379         if (this.constrainY) {
15380             this.setYConstraint( this.topConstraint,
15381                                  this.bottomConstraint,
15382                                  this.yTickSize         );
15383         }
15384     },
15385
15386     /**
15387      * Normally the drag element is moved pixel by pixel, but we can specify
15388      * that it move a number of pixels at a time.  This method resolves the
15389      * location when we have it set up like this.
15390      * @method getTick
15391      * @param {int} val where we want to place the object
15392      * @param {int[]} tickArray sorted array of valid points
15393      * @return {int} the closest tick
15394      * @private
15395      */
15396     getTick: function(val, tickArray) {
15397
15398         if (!tickArray) {
15399             // If tick interval is not defined, it is effectively 1 pixel,
15400             // so we return the value passed to us.
15401             return val;
15402         } else if (tickArray[0] >= val) {
15403             // The value is lower than the first tick, so we return the first
15404             // tick.
15405             return tickArray[0];
15406         } else {
15407             for (var i=0, len=tickArray.length; i<len; ++i) {
15408                 var next = i + 1;
15409                 if (tickArray[next] && tickArray[next] >= val) {
15410                     var diff1 = val - tickArray[i];
15411                     var diff2 = tickArray[next] - val;
15412                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15413                 }
15414             }
15415
15416             // The value is larger than the last tick, so we return the last
15417             // tick.
15418             return tickArray[tickArray.length - 1];
15419         }
15420     },
15421
15422     /**
15423      * toString method
15424      * @method toString
15425      * @return {string} string representation of the dd obj
15426      */
15427     toString: function() {
15428         return ("DragDrop " + this.id);
15429     }
15430
15431 };
15432
15433 })();
15434 /*
15435  * Based on:
15436  * Ext JS Library 1.1.1
15437  * Copyright(c) 2006-2007, Ext JS, LLC.
15438  *
15439  * Originally Released Under LGPL - original licence link has changed is not relivant.
15440  *
15441  * Fork - LGPL
15442  * <script type="text/javascript">
15443  */
15444
15445
15446 /**
15447  * The drag and drop utility provides a framework for building drag and drop
15448  * applications.  In addition to enabling drag and drop for specific elements,
15449  * the drag and drop elements are tracked by the manager class, and the
15450  * interactions between the various elements are tracked during the drag and
15451  * the implementing code is notified about these important moments.
15452  */
15453
15454 // Only load the library once.  Rewriting the manager class would orphan
15455 // existing drag and drop instances.
15456 if (!Roo.dd.DragDropMgr) {
15457
15458 /**
15459  * @class Roo.dd.DragDropMgr
15460  * DragDropMgr is a singleton that tracks the element interaction for
15461  * all DragDrop items in the window.  Generally, you will not call
15462  * this class directly, but it does have helper methods that could
15463  * be useful in your DragDrop implementations.
15464  * @singleton
15465  */
15466 Roo.dd.DragDropMgr = function() {
15467
15468     var Event = Roo.EventManager;
15469
15470     return {
15471
15472         /**
15473          * Two dimensional Array of registered DragDrop objects.  The first
15474          * dimension is the DragDrop item group, the second the DragDrop
15475          * object.
15476          * @property ids
15477          * @type {string: string}
15478          * @private
15479          * @static
15480          */
15481         ids: {},
15482
15483         /**
15484          * Array of element ids defined as drag handles.  Used to determine
15485          * if the element that generated the mousedown event is actually the
15486          * handle and not the html element itself.
15487          * @property handleIds
15488          * @type {string: string}
15489          * @private
15490          * @static
15491          */
15492         handleIds: {},
15493
15494         /**
15495          * the DragDrop object that is currently being dragged
15496          * @property dragCurrent
15497          * @type DragDrop
15498          * @private
15499          * @static
15500          **/
15501         dragCurrent: null,
15502
15503         /**
15504          * the DragDrop object(s) that are being hovered over
15505          * @property dragOvers
15506          * @type Array
15507          * @private
15508          * @static
15509          */
15510         dragOvers: {},
15511
15512         /**
15513          * the X distance between the cursor and the object being dragged
15514          * @property deltaX
15515          * @type int
15516          * @private
15517          * @static
15518          */
15519         deltaX: 0,
15520
15521         /**
15522          * the Y distance between the cursor and the object being dragged
15523          * @property deltaY
15524          * @type int
15525          * @private
15526          * @static
15527          */
15528         deltaY: 0,
15529
15530         /**
15531          * Flag to determine if we should prevent the default behavior of the
15532          * events we define. By default this is true, but this can be set to
15533          * false if you need the default behavior (not recommended)
15534          * @property preventDefault
15535          * @type boolean
15536          * @static
15537          */
15538         preventDefault: true,
15539
15540         /**
15541          * Flag to determine if we should stop the propagation of the events
15542          * we generate. This is true by default but you may want to set it to
15543          * false if the html element contains other features that require the
15544          * mouse click.
15545          * @property stopPropagation
15546          * @type boolean
15547          * @static
15548          */
15549         stopPropagation: true,
15550
15551         /**
15552          * Internal flag that is set to true when drag and drop has been
15553          * intialized
15554          * @property initialized
15555          * @private
15556          * @static
15557          */
15558         initalized: false,
15559
15560         /**
15561          * All drag and drop can be disabled.
15562          * @property locked
15563          * @private
15564          * @static
15565          */
15566         locked: false,
15567
15568         /**
15569          * Called the first time an element is registered.
15570          * @method init
15571          * @private
15572          * @static
15573          */
15574         init: function() {
15575             this.initialized = true;
15576         },
15577
15578         /**
15579          * In point mode, drag and drop interaction is defined by the
15580          * location of the cursor during the drag/drop
15581          * @property POINT
15582          * @type int
15583          * @static
15584          */
15585         POINT: 0,
15586
15587         /**
15588          * In intersect mode, drag and drop interactio nis defined by the
15589          * overlap of two or more drag and drop objects.
15590          * @property INTERSECT
15591          * @type int
15592          * @static
15593          */
15594         INTERSECT: 1,
15595
15596         /**
15597          * The current drag and drop mode.  Default: POINT
15598          * @property mode
15599          * @type int
15600          * @static
15601          */
15602         mode: 0,
15603
15604         /**
15605          * Runs method on all drag and drop objects
15606          * @method _execOnAll
15607          * @private
15608          * @static
15609          */
15610         _execOnAll: function(sMethod, args) {
15611             for (var i in this.ids) {
15612                 for (var j in this.ids[i]) {
15613                     var oDD = this.ids[i][j];
15614                     if (! this.isTypeOfDD(oDD)) {
15615                         continue;
15616                     }
15617                     oDD[sMethod].apply(oDD, args);
15618                 }
15619             }
15620         },
15621
15622         /**
15623          * Drag and drop initialization.  Sets up the global event handlers
15624          * @method _onLoad
15625          * @private
15626          * @static
15627          */
15628         _onLoad: function() {
15629
15630             this.init();
15631
15632
15633             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15634             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15635             Event.on(window,   "unload",    this._onUnload, this, true);
15636             Event.on(window,   "resize",    this._onResize, this, true);
15637             // Event.on(window,   "mouseout",    this._test);
15638
15639         },
15640
15641         /**
15642          * Reset constraints on all drag and drop objs
15643          * @method _onResize
15644          * @private
15645          * @static
15646          */
15647         _onResize: function(e) {
15648             this._execOnAll("resetConstraints", []);
15649         },
15650
15651         /**
15652          * Lock all drag and drop functionality
15653          * @method lock
15654          * @static
15655          */
15656         lock: function() { this.locked = true; },
15657
15658         /**
15659          * Unlock all drag and drop functionality
15660          * @method unlock
15661          * @static
15662          */
15663         unlock: function() { this.locked = false; },
15664
15665         /**
15666          * Is drag and drop locked?
15667          * @method isLocked
15668          * @return {boolean} True if drag and drop is locked, false otherwise.
15669          * @static
15670          */
15671         isLocked: function() { return this.locked; },
15672
15673         /**
15674          * Location cache that is set for all drag drop objects when a drag is
15675          * initiated, cleared when the drag is finished.
15676          * @property locationCache
15677          * @private
15678          * @static
15679          */
15680         locationCache: {},
15681
15682         /**
15683          * Set useCache to false if you want to force object the lookup of each
15684          * drag and drop linked element constantly during a drag.
15685          * @property useCache
15686          * @type boolean
15687          * @static
15688          */
15689         useCache: true,
15690
15691         /**
15692          * The number of pixels that the mouse needs to move after the
15693          * mousedown before the drag is initiated.  Default=3;
15694          * @property clickPixelThresh
15695          * @type int
15696          * @static
15697          */
15698         clickPixelThresh: 3,
15699
15700         /**
15701          * The number of milliseconds after the mousedown event to initiate the
15702          * drag if we don't get a mouseup event. Default=1000
15703          * @property clickTimeThresh
15704          * @type int
15705          * @static
15706          */
15707         clickTimeThresh: 350,
15708
15709         /**
15710          * Flag that indicates that either the drag pixel threshold or the
15711          * mousdown time threshold has been met
15712          * @property dragThreshMet
15713          * @type boolean
15714          * @private
15715          * @static
15716          */
15717         dragThreshMet: false,
15718
15719         /**
15720          * Timeout used for the click time threshold
15721          * @property clickTimeout
15722          * @type Object
15723          * @private
15724          * @static
15725          */
15726         clickTimeout: null,
15727
15728         /**
15729          * The X position of the mousedown event stored for later use when a
15730          * drag threshold is met.
15731          * @property startX
15732          * @type int
15733          * @private
15734          * @static
15735          */
15736         startX: 0,
15737
15738         /**
15739          * The Y position of the mousedown event stored for later use when a
15740          * drag threshold is met.
15741          * @property startY
15742          * @type int
15743          * @private
15744          * @static
15745          */
15746         startY: 0,
15747
15748         /**
15749          * Each DragDrop instance must be registered with the DragDropMgr.
15750          * This is executed in DragDrop.init()
15751          * @method regDragDrop
15752          * @param {DragDrop} oDD the DragDrop object to register
15753          * @param {String} sGroup the name of the group this element belongs to
15754          * @static
15755          */
15756         regDragDrop: function(oDD, sGroup) {
15757             if (!this.initialized) { this.init(); }
15758
15759             if (!this.ids[sGroup]) {
15760                 this.ids[sGroup] = {};
15761             }
15762             this.ids[sGroup][oDD.id] = oDD;
15763         },
15764
15765         /**
15766          * Removes the supplied dd instance from the supplied group. Executed
15767          * by DragDrop.removeFromGroup, so don't call this function directly.
15768          * @method removeDDFromGroup
15769          * @private
15770          * @static
15771          */
15772         removeDDFromGroup: function(oDD, sGroup) {
15773             if (!this.ids[sGroup]) {
15774                 this.ids[sGroup] = {};
15775             }
15776
15777             var obj = this.ids[sGroup];
15778             if (obj && obj[oDD.id]) {
15779                 delete obj[oDD.id];
15780             }
15781         },
15782
15783         /**
15784          * Unregisters a drag and drop item.  This is executed in
15785          * DragDrop.unreg, use that method instead of calling this directly.
15786          * @method _remove
15787          * @private
15788          * @static
15789          */
15790         _remove: function(oDD) {
15791             for (var g in oDD.groups) {
15792                 if (g && this.ids[g][oDD.id]) {
15793                     delete this.ids[g][oDD.id];
15794                 }
15795             }
15796             delete this.handleIds[oDD.id];
15797         },
15798
15799         /**
15800          * Each DragDrop handle element must be registered.  This is done
15801          * automatically when executing DragDrop.setHandleElId()
15802          * @method regHandle
15803          * @param {String} sDDId the DragDrop id this element is a handle for
15804          * @param {String} sHandleId the id of the element that is the drag
15805          * handle
15806          * @static
15807          */
15808         regHandle: function(sDDId, sHandleId) {
15809             if (!this.handleIds[sDDId]) {
15810                 this.handleIds[sDDId] = {};
15811             }
15812             this.handleIds[sDDId][sHandleId] = sHandleId;
15813         },
15814
15815         /**
15816          * Utility function to determine if a given element has been
15817          * registered as a drag drop item.
15818          * @method isDragDrop
15819          * @param {String} id the element id to check
15820          * @return {boolean} true if this element is a DragDrop item,
15821          * false otherwise
15822          * @static
15823          */
15824         isDragDrop: function(id) {
15825             return ( this.getDDById(id) ) ? true : false;
15826         },
15827
15828         /**
15829          * Returns the drag and drop instances that are in all groups the
15830          * passed in instance belongs to.
15831          * @method getRelated
15832          * @param {DragDrop} p_oDD the obj to get related data for
15833          * @param {boolean} bTargetsOnly if true, only return targetable objs
15834          * @return {DragDrop[]} the related instances
15835          * @static
15836          */
15837         getRelated: function(p_oDD, bTargetsOnly) {
15838             var oDDs = [];
15839             for (var i in p_oDD.groups) {
15840                 for (j in this.ids[i]) {
15841                     var dd = this.ids[i][j];
15842                     if (! this.isTypeOfDD(dd)) {
15843                         continue;
15844                     }
15845                     if (!bTargetsOnly || dd.isTarget) {
15846                         oDDs[oDDs.length] = dd;
15847                     }
15848                 }
15849             }
15850
15851             return oDDs;
15852         },
15853
15854         /**
15855          * Returns true if the specified dd target is a legal target for
15856          * the specifice drag obj
15857          * @method isLegalTarget
15858          * @param {DragDrop} the drag obj
15859          * @param {DragDrop} the target
15860          * @return {boolean} true if the target is a legal target for the
15861          * dd obj
15862          * @static
15863          */
15864         isLegalTarget: function (oDD, oTargetDD) {
15865             var targets = this.getRelated(oDD, true);
15866             for (var i=0, len=targets.length;i<len;++i) {
15867                 if (targets[i].id == oTargetDD.id) {
15868                     return true;
15869                 }
15870             }
15871
15872             return false;
15873         },
15874
15875         /**
15876          * My goal is to be able to transparently determine if an object is
15877          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15878          * returns "object", oDD.constructor.toString() always returns
15879          * "DragDrop" and not the name of the subclass.  So for now it just
15880          * evaluates a well-known variable in DragDrop.
15881          * @method isTypeOfDD
15882          * @param {Object} the object to evaluate
15883          * @return {boolean} true if typeof oDD = DragDrop
15884          * @static
15885          */
15886         isTypeOfDD: function (oDD) {
15887             return (oDD && oDD.__ygDragDrop);
15888         },
15889
15890         /**
15891          * Utility function to determine if a given element has been
15892          * registered as a drag drop handle for the given Drag Drop object.
15893          * @method isHandle
15894          * @param {String} id the element id to check
15895          * @return {boolean} true if this element is a DragDrop handle, false
15896          * otherwise
15897          * @static
15898          */
15899         isHandle: function(sDDId, sHandleId) {
15900             return ( this.handleIds[sDDId] &&
15901                             this.handleIds[sDDId][sHandleId] );
15902         },
15903
15904         /**
15905          * Returns the DragDrop instance for a given id
15906          * @method getDDById
15907          * @param {String} id the id of the DragDrop object
15908          * @return {DragDrop} the drag drop object, null if it is not found
15909          * @static
15910          */
15911         getDDById: function(id) {
15912             for (var i in this.ids) {
15913                 if (this.ids[i][id]) {
15914                     return this.ids[i][id];
15915                 }
15916             }
15917             return null;
15918         },
15919
15920         /**
15921          * Fired after a registered DragDrop object gets the mousedown event.
15922          * Sets up the events required to track the object being dragged
15923          * @method handleMouseDown
15924          * @param {Event} e the event
15925          * @param oDD the DragDrop object being dragged
15926          * @private
15927          * @static
15928          */
15929         handleMouseDown: function(e, oDD) {
15930             if(Roo.QuickTips){
15931                 Roo.QuickTips.disable();
15932             }
15933             this.currentTarget = e.getTarget();
15934
15935             this.dragCurrent = oDD;
15936
15937             var el = oDD.getEl();
15938
15939             // track start position
15940             this.startX = e.getPageX();
15941             this.startY = e.getPageY();
15942
15943             this.deltaX = this.startX - el.offsetLeft;
15944             this.deltaY = this.startY - el.offsetTop;
15945
15946             this.dragThreshMet = false;
15947
15948             this.clickTimeout = setTimeout(
15949                     function() {
15950                         var DDM = Roo.dd.DDM;
15951                         DDM.startDrag(DDM.startX, DDM.startY);
15952                     },
15953                     this.clickTimeThresh );
15954         },
15955
15956         /**
15957          * Fired when either the drag pixel threshol or the mousedown hold
15958          * time threshold has been met.
15959          * @method startDrag
15960          * @param x {int} the X position of the original mousedown
15961          * @param y {int} the Y position of the original mousedown
15962          * @static
15963          */
15964         startDrag: function(x, y) {
15965             clearTimeout(this.clickTimeout);
15966             if (this.dragCurrent) {
15967                 this.dragCurrent.b4StartDrag(x, y);
15968                 this.dragCurrent.startDrag(x, y);
15969             }
15970             this.dragThreshMet = true;
15971         },
15972
15973         /**
15974          * Internal function to handle the mouseup event.  Will be invoked
15975          * from the context of the document.
15976          * @method handleMouseUp
15977          * @param {Event} e the event
15978          * @private
15979          * @static
15980          */
15981         handleMouseUp: function(e) {
15982
15983             if(Roo.QuickTips){
15984                 Roo.QuickTips.enable();
15985             }
15986             if (! this.dragCurrent) {
15987                 return;
15988             }
15989
15990             clearTimeout(this.clickTimeout);
15991
15992             if (this.dragThreshMet) {
15993                 this.fireEvents(e, true);
15994             } else {
15995             }
15996
15997             this.stopDrag(e);
15998
15999             this.stopEvent(e);
16000         },
16001
16002         /**
16003          * Utility to stop event propagation and event default, if these
16004          * features are turned on.
16005          * @method stopEvent
16006          * @param {Event} e the event as returned by this.getEvent()
16007          * @static
16008          */
16009         stopEvent: function(e){
16010             if(this.stopPropagation) {
16011                 e.stopPropagation();
16012             }
16013
16014             if (this.preventDefault) {
16015                 e.preventDefault();
16016             }
16017         },
16018
16019         /**
16020          * Internal function to clean up event handlers after the drag
16021          * operation is complete
16022          * @method stopDrag
16023          * @param {Event} e the event
16024          * @private
16025          * @static
16026          */
16027         stopDrag: function(e) {
16028             // Fire the drag end event for the item that was dragged
16029             if (this.dragCurrent) {
16030                 if (this.dragThreshMet) {
16031                     this.dragCurrent.b4EndDrag(e);
16032                     this.dragCurrent.endDrag(e);
16033                 }
16034
16035                 this.dragCurrent.onMouseUp(e);
16036             }
16037
16038             this.dragCurrent = null;
16039             this.dragOvers = {};
16040         },
16041
16042         /**
16043          * Internal function to handle the mousemove event.  Will be invoked
16044          * from the context of the html element.
16045          *
16046          * @TODO figure out what we can do about mouse events lost when the
16047          * user drags objects beyond the window boundary.  Currently we can
16048          * detect this in internet explorer by verifying that the mouse is
16049          * down during the mousemove event.  Firefox doesn't give us the
16050          * button state on the mousemove event.
16051          * @method handleMouseMove
16052          * @param {Event} e the event
16053          * @private
16054          * @static
16055          */
16056         handleMouseMove: function(e) {
16057             if (! this.dragCurrent) {
16058                 return true;
16059             }
16060
16061             // var button = e.which || e.button;
16062
16063             // check for IE mouseup outside of page boundary
16064             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16065                 this.stopEvent(e);
16066                 return this.handleMouseUp(e);
16067             }
16068
16069             if (!this.dragThreshMet) {
16070                 var diffX = Math.abs(this.startX - e.getPageX());
16071                 var diffY = Math.abs(this.startY - e.getPageY());
16072                 if (diffX > this.clickPixelThresh ||
16073                             diffY > this.clickPixelThresh) {
16074                     this.startDrag(this.startX, this.startY);
16075                 }
16076             }
16077
16078             if (this.dragThreshMet) {
16079                 this.dragCurrent.b4Drag(e);
16080                 this.dragCurrent.onDrag(e);
16081                 if(!this.dragCurrent.moveOnly){
16082                     this.fireEvents(e, false);
16083                 }
16084             }
16085
16086             this.stopEvent(e);
16087
16088             return true;
16089         },
16090
16091         /**
16092          * Iterates over all of the DragDrop elements to find ones we are
16093          * hovering over or dropping on
16094          * @method fireEvents
16095          * @param {Event} e the event
16096          * @param {boolean} isDrop is this a drop op or a mouseover op?
16097          * @private
16098          * @static
16099          */
16100         fireEvents: function(e, isDrop) {
16101             var dc = this.dragCurrent;
16102
16103             // If the user did the mouse up outside of the window, we could
16104             // get here even though we have ended the drag.
16105             if (!dc || dc.isLocked()) {
16106                 return;
16107             }
16108
16109             var pt = e.getPoint();
16110
16111             // cache the previous dragOver array
16112             var oldOvers = [];
16113
16114             var outEvts   = [];
16115             var overEvts  = [];
16116             var dropEvts  = [];
16117             var enterEvts = [];
16118
16119             // Check to see if the object(s) we were hovering over is no longer
16120             // being hovered over so we can fire the onDragOut event
16121             for (var i in this.dragOvers) {
16122
16123                 var ddo = this.dragOvers[i];
16124
16125                 if (! this.isTypeOfDD(ddo)) {
16126                     continue;
16127                 }
16128
16129                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16130                     outEvts.push( ddo );
16131                 }
16132
16133                 oldOvers[i] = true;
16134                 delete this.dragOvers[i];
16135             }
16136
16137             for (var sGroup in dc.groups) {
16138
16139                 if ("string" != typeof sGroup) {
16140                     continue;
16141                 }
16142
16143                 for (i in this.ids[sGroup]) {
16144                     var oDD = this.ids[sGroup][i];
16145                     if (! this.isTypeOfDD(oDD)) {
16146                         continue;
16147                     }
16148
16149                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16150                         if (this.isOverTarget(pt, oDD, this.mode)) {
16151                             // look for drop interactions
16152                             if (isDrop) {
16153                                 dropEvts.push( oDD );
16154                             // look for drag enter and drag over interactions
16155                             } else {
16156
16157                                 // initial drag over: dragEnter fires
16158                                 if (!oldOvers[oDD.id]) {
16159                                     enterEvts.push( oDD );
16160                                 // subsequent drag overs: dragOver fires
16161                                 } else {
16162                                     overEvts.push( oDD );
16163                                 }
16164
16165                                 this.dragOvers[oDD.id] = oDD;
16166                             }
16167                         }
16168                     }
16169                 }
16170             }
16171
16172             if (this.mode) {
16173                 if (outEvts.length) {
16174                     dc.b4DragOut(e, outEvts);
16175                     dc.onDragOut(e, outEvts);
16176                 }
16177
16178                 if (enterEvts.length) {
16179                     dc.onDragEnter(e, enterEvts);
16180                 }
16181
16182                 if (overEvts.length) {
16183                     dc.b4DragOver(e, overEvts);
16184                     dc.onDragOver(e, overEvts);
16185                 }
16186
16187                 if (dropEvts.length) {
16188                     dc.b4DragDrop(e, dropEvts);
16189                     dc.onDragDrop(e, dropEvts);
16190                 }
16191
16192             } else {
16193                 // fire dragout events
16194                 var len = 0;
16195                 for (i=0, len=outEvts.length; i<len; ++i) {
16196                     dc.b4DragOut(e, outEvts[i].id);
16197                     dc.onDragOut(e, outEvts[i].id);
16198                 }
16199
16200                 // fire enter events
16201                 for (i=0,len=enterEvts.length; i<len; ++i) {
16202                     // dc.b4DragEnter(e, oDD.id);
16203                     dc.onDragEnter(e, enterEvts[i].id);
16204                 }
16205
16206                 // fire over events
16207                 for (i=0,len=overEvts.length; i<len; ++i) {
16208                     dc.b4DragOver(e, overEvts[i].id);
16209                     dc.onDragOver(e, overEvts[i].id);
16210                 }
16211
16212                 // fire drop events
16213                 for (i=0, len=dropEvts.length; i<len; ++i) {
16214                     dc.b4DragDrop(e, dropEvts[i].id);
16215                     dc.onDragDrop(e, dropEvts[i].id);
16216                 }
16217
16218             }
16219
16220             // notify about a drop that did not find a target
16221             if (isDrop && !dropEvts.length) {
16222                 dc.onInvalidDrop(e);
16223             }
16224
16225         },
16226
16227         /**
16228          * Helper function for getting the best match from the list of drag
16229          * and drop objects returned by the drag and drop events when we are
16230          * in INTERSECT mode.  It returns either the first object that the
16231          * cursor is over, or the object that has the greatest overlap with
16232          * the dragged element.
16233          * @method getBestMatch
16234          * @param  {DragDrop[]} dds The array of drag and drop objects
16235          * targeted
16236          * @return {DragDrop}       The best single match
16237          * @static
16238          */
16239         getBestMatch: function(dds) {
16240             var winner = null;
16241             // Return null if the input is not what we expect
16242             //if (!dds || !dds.length || dds.length == 0) {
16243                // winner = null;
16244             // If there is only one item, it wins
16245             //} else if (dds.length == 1) {
16246
16247             var len = dds.length;
16248
16249             if (len == 1) {
16250                 winner = dds[0];
16251             } else {
16252                 // Loop through the targeted items
16253                 for (var i=0; i<len; ++i) {
16254                     var dd = dds[i];
16255                     // If the cursor is over the object, it wins.  If the
16256                     // cursor is over multiple matches, the first one we come
16257                     // to wins.
16258                     if (dd.cursorIsOver) {
16259                         winner = dd;
16260                         break;
16261                     // Otherwise the object with the most overlap wins
16262                     } else {
16263                         if (!winner ||
16264                             winner.overlap.getArea() < dd.overlap.getArea()) {
16265                             winner = dd;
16266                         }
16267                     }
16268                 }
16269             }
16270
16271             return winner;
16272         },
16273
16274         /**
16275          * Refreshes the cache of the top-left and bottom-right points of the
16276          * drag and drop objects in the specified group(s).  This is in the
16277          * format that is stored in the drag and drop instance, so typical
16278          * usage is:
16279          * <code>
16280          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16281          * </code>
16282          * Alternatively:
16283          * <code>
16284          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16285          * </code>
16286          * @TODO this really should be an indexed array.  Alternatively this
16287          * method could accept both.
16288          * @method refreshCache
16289          * @param {Object} groups an associative array of groups to refresh
16290          * @static
16291          */
16292         refreshCache: function(groups) {
16293             for (var sGroup in groups) {
16294                 if ("string" != typeof sGroup) {
16295                     continue;
16296                 }
16297                 for (var i in this.ids[sGroup]) {
16298                     var oDD = this.ids[sGroup][i];
16299
16300                     if (this.isTypeOfDD(oDD)) {
16301                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16302                         var loc = this.getLocation(oDD);
16303                         if (loc) {
16304                             this.locationCache[oDD.id] = loc;
16305                         } else {
16306                             delete this.locationCache[oDD.id];
16307                             // this will unregister the drag and drop object if
16308                             // the element is not in a usable state
16309                             // oDD.unreg();
16310                         }
16311                     }
16312                 }
16313             }
16314         },
16315
16316         /**
16317          * This checks to make sure an element exists and is in the DOM.  The
16318          * main purpose is to handle cases where innerHTML is used to remove
16319          * drag and drop objects from the DOM.  IE provides an 'unspecified
16320          * error' when trying to access the offsetParent of such an element
16321          * @method verifyEl
16322          * @param {HTMLElement} el the element to check
16323          * @return {boolean} true if the element looks usable
16324          * @static
16325          */
16326         verifyEl: function(el) {
16327             if (el) {
16328                 var parent;
16329                 if(Roo.isIE){
16330                     try{
16331                         parent = el.offsetParent;
16332                     }catch(e){}
16333                 }else{
16334                     parent = el.offsetParent;
16335                 }
16336                 if (parent) {
16337                     return true;
16338                 }
16339             }
16340
16341             return false;
16342         },
16343
16344         /**
16345          * Returns a Region object containing the drag and drop element's position
16346          * and size, including the padding configured for it
16347          * @method getLocation
16348          * @param {DragDrop} oDD the drag and drop object to get the
16349          *                       location for
16350          * @return {Roo.lib.Region} a Region object representing the total area
16351          *                             the element occupies, including any padding
16352          *                             the instance is configured for.
16353          * @static
16354          */
16355         getLocation: function(oDD) {
16356             if (! this.isTypeOfDD(oDD)) {
16357                 return null;
16358             }
16359
16360             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16361
16362             try {
16363                 pos= Roo.lib.Dom.getXY(el);
16364             } catch (e) { }
16365
16366             if (!pos) {
16367                 return null;
16368             }
16369
16370             x1 = pos[0];
16371             x2 = x1 + el.offsetWidth;
16372             y1 = pos[1];
16373             y2 = y1 + el.offsetHeight;
16374
16375             t = y1 - oDD.padding[0];
16376             r = x2 + oDD.padding[1];
16377             b = y2 + oDD.padding[2];
16378             l = x1 - oDD.padding[3];
16379
16380             return new Roo.lib.Region( t, r, b, l );
16381         },
16382
16383         /**
16384          * Checks the cursor location to see if it over the target
16385          * @method isOverTarget
16386          * @param {Roo.lib.Point} pt The point to evaluate
16387          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16388          * @return {boolean} true if the mouse is over the target
16389          * @private
16390          * @static
16391          */
16392         isOverTarget: function(pt, oTarget, intersect) {
16393             // use cache if available
16394             var loc = this.locationCache[oTarget.id];
16395             if (!loc || !this.useCache) {
16396                 loc = this.getLocation(oTarget);
16397                 this.locationCache[oTarget.id] = loc;
16398
16399             }
16400
16401             if (!loc) {
16402                 return false;
16403             }
16404
16405             oTarget.cursorIsOver = loc.contains( pt );
16406
16407             // DragDrop is using this as a sanity check for the initial mousedown
16408             // in this case we are done.  In POINT mode, if the drag obj has no
16409             // contraints, we are also done. Otherwise we need to evaluate the
16410             // location of the target as related to the actual location of the
16411             // dragged element.
16412             var dc = this.dragCurrent;
16413             if (!dc || !dc.getTargetCoord ||
16414                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16415                 return oTarget.cursorIsOver;
16416             }
16417
16418             oTarget.overlap = null;
16419
16420             // Get the current location of the drag element, this is the
16421             // location of the mouse event less the delta that represents
16422             // where the original mousedown happened on the element.  We
16423             // need to consider constraints and ticks as well.
16424             var pos = dc.getTargetCoord(pt.x, pt.y);
16425
16426             var el = dc.getDragEl();
16427             var curRegion = new Roo.lib.Region( pos.y,
16428                                                    pos.x + el.offsetWidth,
16429                                                    pos.y + el.offsetHeight,
16430                                                    pos.x );
16431
16432             var overlap = curRegion.intersect(loc);
16433
16434             if (overlap) {
16435                 oTarget.overlap = overlap;
16436                 return (intersect) ? true : oTarget.cursorIsOver;
16437             } else {
16438                 return false;
16439             }
16440         },
16441
16442         /**
16443          * unload event handler
16444          * @method _onUnload
16445          * @private
16446          * @static
16447          */
16448         _onUnload: function(e, me) {
16449             Roo.dd.DragDropMgr.unregAll();
16450         },
16451
16452         /**
16453          * Cleans up the drag and drop events and objects.
16454          * @method unregAll
16455          * @private
16456          * @static
16457          */
16458         unregAll: function() {
16459
16460             if (this.dragCurrent) {
16461                 this.stopDrag();
16462                 this.dragCurrent = null;
16463             }
16464
16465             this._execOnAll("unreg", []);
16466
16467             for (i in this.elementCache) {
16468                 delete this.elementCache[i];
16469             }
16470
16471             this.elementCache = {};
16472             this.ids = {};
16473         },
16474
16475         /**
16476          * A cache of DOM elements
16477          * @property elementCache
16478          * @private
16479          * @static
16480          */
16481         elementCache: {},
16482
16483         /**
16484          * Get the wrapper for the DOM element specified
16485          * @method getElWrapper
16486          * @param {String} id the id of the element to get
16487          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16488          * @private
16489          * @deprecated This wrapper isn't that useful
16490          * @static
16491          */
16492         getElWrapper: function(id) {
16493             var oWrapper = this.elementCache[id];
16494             if (!oWrapper || !oWrapper.el) {
16495                 oWrapper = this.elementCache[id] =
16496                     new this.ElementWrapper(Roo.getDom(id));
16497             }
16498             return oWrapper;
16499         },
16500
16501         /**
16502          * Returns the actual DOM element
16503          * @method getElement
16504          * @param {String} id the id of the elment to get
16505          * @return {Object} The element
16506          * @deprecated use Roo.getDom instead
16507          * @static
16508          */
16509         getElement: function(id) {
16510             return Roo.getDom(id);
16511         },
16512
16513         /**
16514          * Returns the style property for the DOM element (i.e.,
16515          * document.getElById(id).style)
16516          * @method getCss
16517          * @param {String} id the id of the elment to get
16518          * @return {Object} The style property of the element
16519          * @deprecated use Roo.getDom instead
16520          * @static
16521          */
16522         getCss: function(id) {
16523             var el = Roo.getDom(id);
16524             return (el) ? el.style : null;
16525         },
16526
16527         /**
16528          * Inner class for cached elements
16529          * @class DragDropMgr.ElementWrapper
16530          * @for DragDropMgr
16531          * @private
16532          * @deprecated
16533          */
16534         ElementWrapper: function(el) {
16535                 /**
16536                  * The element
16537                  * @property el
16538                  */
16539                 this.el = el || null;
16540                 /**
16541                  * The element id
16542                  * @property id
16543                  */
16544                 this.id = this.el && el.id;
16545                 /**
16546                  * A reference to the style property
16547                  * @property css
16548                  */
16549                 this.css = this.el && el.style;
16550             },
16551
16552         /**
16553          * Returns the X position of an html element
16554          * @method getPosX
16555          * @param el the element for which to get the position
16556          * @return {int} the X coordinate
16557          * @for DragDropMgr
16558          * @deprecated use Roo.lib.Dom.getX instead
16559          * @static
16560          */
16561         getPosX: function(el) {
16562             return Roo.lib.Dom.getX(el);
16563         },
16564
16565         /**
16566          * Returns the Y position of an html element
16567          * @method getPosY
16568          * @param el the element for which to get the position
16569          * @return {int} the Y coordinate
16570          * @deprecated use Roo.lib.Dom.getY instead
16571          * @static
16572          */
16573         getPosY: function(el) {
16574             return Roo.lib.Dom.getY(el);
16575         },
16576
16577         /**
16578          * Swap two nodes.  In IE, we use the native method, for others we
16579          * emulate the IE behavior
16580          * @method swapNode
16581          * @param n1 the first node to swap
16582          * @param n2 the other node to swap
16583          * @static
16584          */
16585         swapNode: function(n1, n2) {
16586             if (n1.swapNode) {
16587                 n1.swapNode(n2);
16588             } else {
16589                 var p = n2.parentNode;
16590                 var s = n2.nextSibling;
16591
16592                 if (s == n1) {
16593                     p.insertBefore(n1, n2);
16594                 } else if (n2 == n1.nextSibling) {
16595                     p.insertBefore(n2, n1);
16596                 } else {
16597                     n1.parentNode.replaceChild(n2, n1);
16598                     p.insertBefore(n1, s);
16599                 }
16600             }
16601         },
16602
16603         /**
16604          * Returns the current scroll position
16605          * @method getScroll
16606          * @private
16607          * @static
16608          */
16609         getScroll: function () {
16610             var t, l, dde=document.documentElement, db=document.body;
16611             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16612                 t = dde.scrollTop;
16613                 l = dde.scrollLeft;
16614             } else if (db) {
16615                 t = db.scrollTop;
16616                 l = db.scrollLeft;
16617             } else {
16618
16619             }
16620             return { top: t, left: l };
16621         },
16622
16623         /**
16624          * Returns the specified element style property
16625          * @method getStyle
16626          * @param {HTMLElement} el          the element
16627          * @param {string}      styleProp   the style property
16628          * @return {string} The value of the style property
16629          * @deprecated use Roo.lib.Dom.getStyle
16630          * @static
16631          */
16632         getStyle: function(el, styleProp) {
16633             return Roo.fly(el).getStyle(styleProp);
16634         },
16635
16636         /**
16637          * Gets the scrollTop
16638          * @method getScrollTop
16639          * @return {int} the document's scrollTop
16640          * @static
16641          */
16642         getScrollTop: function () { return this.getScroll().top; },
16643
16644         /**
16645          * Gets the scrollLeft
16646          * @method getScrollLeft
16647          * @return {int} the document's scrollTop
16648          * @static
16649          */
16650         getScrollLeft: function () { return this.getScroll().left; },
16651
16652         /**
16653          * Sets the x/y position of an element to the location of the
16654          * target element.
16655          * @method moveToEl
16656          * @param {HTMLElement} moveEl      The element to move
16657          * @param {HTMLElement} targetEl    The position reference element
16658          * @static
16659          */
16660         moveToEl: function (moveEl, targetEl) {
16661             var aCoord = Roo.lib.Dom.getXY(targetEl);
16662             Roo.lib.Dom.setXY(moveEl, aCoord);
16663         },
16664
16665         /**
16666          * Numeric array sort function
16667          * @method numericSort
16668          * @static
16669          */
16670         numericSort: function(a, b) { return (a - b); },
16671
16672         /**
16673          * Internal counter
16674          * @property _timeoutCount
16675          * @private
16676          * @static
16677          */
16678         _timeoutCount: 0,
16679
16680         /**
16681          * Trying to make the load order less important.  Without this we get
16682          * an error if this file is loaded before the Event Utility.
16683          * @method _addListeners
16684          * @private
16685          * @static
16686          */
16687         _addListeners: function() {
16688             var DDM = Roo.dd.DDM;
16689             if ( Roo.lib.Event && document ) {
16690                 DDM._onLoad();
16691             } else {
16692                 if (DDM._timeoutCount > 2000) {
16693                 } else {
16694                     setTimeout(DDM._addListeners, 10);
16695                     if (document && document.body) {
16696                         DDM._timeoutCount += 1;
16697                     }
16698                 }
16699             }
16700         },
16701
16702         /**
16703          * Recursively searches the immediate parent and all child nodes for
16704          * the handle element in order to determine wheter or not it was
16705          * clicked.
16706          * @method handleWasClicked
16707          * @param node the html element to inspect
16708          * @static
16709          */
16710         handleWasClicked: function(node, id) {
16711             if (this.isHandle(id, node.id)) {
16712                 return true;
16713             } else {
16714                 // check to see if this is a text node child of the one we want
16715                 var p = node.parentNode;
16716
16717                 while (p) {
16718                     if (this.isHandle(id, p.id)) {
16719                         return true;
16720                     } else {
16721                         p = p.parentNode;
16722                     }
16723                 }
16724             }
16725
16726             return false;
16727         }
16728
16729     };
16730
16731 }();
16732
16733 // shorter alias, save a few bytes
16734 Roo.dd.DDM = Roo.dd.DragDropMgr;
16735 Roo.dd.DDM._addListeners();
16736
16737 }/*
16738  * Based on:
16739  * Ext JS Library 1.1.1
16740  * Copyright(c) 2006-2007, Ext JS, LLC.
16741  *
16742  * Originally Released Under LGPL - original licence link has changed is not relivant.
16743  *
16744  * Fork - LGPL
16745  * <script type="text/javascript">
16746  */
16747
16748 /**
16749  * @class Roo.dd.DD
16750  * A DragDrop implementation where the linked element follows the
16751  * mouse cursor during a drag.
16752  * @extends Roo.dd.DragDrop
16753  * @constructor
16754  * @param {String} id the id of the linked element
16755  * @param {String} sGroup the group of related DragDrop items
16756  * @param {object} config an object containing configurable attributes
16757  *                Valid properties for DD:
16758  *                    scroll
16759  */
16760 Roo.dd.DD = function(id, sGroup, config) {
16761     if (id) {
16762         this.init(id, sGroup, config);
16763     }
16764 };
16765
16766 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16767
16768     /**
16769      * When set to true, the utility automatically tries to scroll the browser
16770      * window wehn a drag and drop element is dragged near the viewport boundary.
16771      * Defaults to true.
16772      * @property scroll
16773      * @type boolean
16774      */
16775     scroll: true,
16776
16777     /**
16778      * Sets the pointer offset to the distance between the linked element's top
16779      * left corner and the location the element was clicked
16780      * @method autoOffset
16781      * @param {int} iPageX the X coordinate of the click
16782      * @param {int} iPageY the Y coordinate of the click
16783      */
16784     autoOffset: function(iPageX, iPageY) {
16785         var x = iPageX - this.startPageX;
16786         var y = iPageY - this.startPageY;
16787         this.setDelta(x, y);
16788     },
16789
16790     /**
16791      * Sets the pointer offset.  You can call this directly to force the
16792      * offset to be in a particular location (e.g., pass in 0,0 to set it
16793      * to the center of the object)
16794      * @method setDelta
16795      * @param {int} iDeltaX the distance from the left
16796      * @param {int} iDeltaY the distance from the top
16797      */
16798     setDelta: function(iDeltaX, iDeltaY) {
16799         this.deltaX = iDeltaX;
16800         this.deltaY = iDeltaY;
16801     },
16802
16803     /**
16804      * Sets the drag element to the location of the mousedown or click event,
16805      * maintaining the cursor location relative to the location on the element
16806      * that was clicked.  Override this if you want to place the element in a
16807      * location other than where the cursor is.
16808      * @method setDragElPos
16809      * @param {int} iPageX the X coordinate of the mousedown or drag event
16810      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16811      */
16812     setDragElPos: function(iPageX, iPageY) {
16813         // the first time we do this, we are going to check to make sure
16814         // the element has css positioning
16815
16816         var el = this.getDragEl();
16817         this.alignElWithMouse(el, iPageX, iPageY);
16818     },
16819
16820     /**
16821      * Sets the element to the location of the mousedown or click event,
16822      * maintaining the cursor location relative to the location on the element
16823      * that was clicked.  Override this if you want to place the element in a
16824      * location other than where the cursor is.
16825      * @method alignElWithMouse
16826      * @param {HTMLElement} el the element to move
16827      * @param {int} iPageX the X coordinate of the mousedown or drag event
16828      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16829      */
16830     alignElWithMouse: function(el, iPageX, iPageY) {
16831         var oCoord = this.getTargetCoord(iPageX, iPageY);
16832         var fly = el.dom ? el : Roo.fly(el);
16833         if (!this.deltaSetXY) {
16834             var aCoord = [oCoord.x, oCoord.y];
16835             fly.setXY(aCoord);
16836             var newLeft = fly.getLeft(true);
16837             var newTop  = fly.getTop(true);
16838             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16839         } else {
16840             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16841         }
16842
16843         this.cachePosition(oCoord.x, oCoord.y);
16844         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16845         return oCoord;
16846     },
16847
16848     /**
16849      * Saves the most recent position so that we can reset the constraints and
16850      * tick marks on-demand.  We need to know this so that we can calculate the
16851      * number of pixels the element is offset from its original position.
16852      * @method cachePosition
16853      * @param iPageX the current x position (optional, this just makes it so we
16854      * don't have to look it up again)
16855      * @param iPageY the current y position (optional, this just makes it so we
16856      * don't have to look it up again)
16857      */
16858     cachePosition: function(iPageX, iPageY) {
16859         if (iPageX) {
16860             this.lastPageX = iPageX;
16861             this.lastPageY = iPageY;
16862         } else {
16863             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16864             this.lastPageX = aCoord[0];
16865             this.lastPageY = aCoord[1];
16866         }
16867     },
16868
16869     /**
16870      * Auto-scroll the window if the dragged object has been moved beyond the
16871      * visible window boundary.
16872      * @method autoScroll
16873      * @param {int} x the drag element's x position
16874      * @param {int} y the drag element's y position
16875      * @param {int} h the height of the drag element
16876      * @param {int} w the width of the drag element
16877      * @private
16878      */
16879     autoScroll: function(x, y, h, w) {
16880
16881         if (this.scroll) {
16882             // The client height
16883             var clientH = Roo.lib.Dom.getViewWidth();
16884
16885             // The client width
16886             var clientW = Roo.lib.Dom.getViewHeight();
16887
16888             // The amt scrolled down
16889             var st = this.DDM.getScrollTop();
16890
16891             // The amt scrolled right
16892             var sl = this.DDM.getScrollLeft();
16893
16894             // Location of the bottom of the element
16895             var bot = h + y;
16896
16897             // Location of the right of the element
16898             var right = w + x;
16899
16900             // The distance from the cursor to the bottom of the visible area,
16901             // adjusted so that we don't scroll if the cursor is beyond the
16902             // element drag constraints
16903             var toBot = (clientH + st - y - this.deltaY);
16904
16905             // The distance from the cursor to the right of the visible area
16906             var toRight = (clientW + sl - x - this.deltaX);
16907
16908
16909             // How close to the edge the cursor must be before we scroll
16910             // var thresh = (document.all) ? 100 : 40;
16911             var thresh = 40;
16912
16913             // How many pixels to scroll per autoscroll op.  This helps to reduce
16914             // clunky scrolling. IE is more sensitive about this ... it needs this
16915             // value to be higher.
16916             var scrAmt = (document.all) ? 80 : 30;
16917
16918             // Scroll down if we are near the bottom of the visible page and the
16919             // obj extends below the crease
16920             if ( bot > clientH && toBot < thresh ) {
16921                 window.scrollTo(sl, st + scrAmt);
16922             }
16923
16924             // Scroll up if the window is scrolled down and the top of the object
16925             // goes above the top border
16926             if ( y < st && st > 0 && y - st < thresh ) {
16927                 window.scrollTo(sl, st - scrAmt);
16928             }
16929
16930             // Scroll right if the obj is beyond the right border and the cursor is
16931             // near the border.
16932             if ( right > clientW && toRight < thresh ) {
16933                 window.scrollTo(sl + scrAmt, st);
16934             }
16935
16936             // Scroll left if the window has been scrolled to the right and the obj
16937             // extends past the left border
16938             if ( x < sl && sl > 0 && x - sl < thresh ) {
16939                 window.scrollTo(sl - scrAmt, st);
16940             }
16941         }
16942     },
16943
16944     /**
16945      * Finds the location the element should be placed if we want to move
16946      * it to where the mouse location less the click offset would place us.
16947      * @method getTargetCoord
16948      * @param {int} iPageX the X coordinate of the click
16949      * @param {int} iPageY the Y coordinate of the click
16950      * @return an object that contains the coordinates (Object.x and Object.y)
16951      * @private
16952      */
16953     getTargetCoord: function(iPageX, iPageY) {
16954
16955
16956         var x = iPageX - this.deltaX;
16957         var y = iPageY - this.deltaY;
16958
16959         if (this.constrainX) {
16960             if (x < this.minX) { x = this.minX; }
16961             if (x > this.maxX) { x = this.maxX; }
16962         }
16963
16964         if (this.constrainY) {
16965             if (y < this.minY) { y = this.minY; }
16966             if (y > this.maxY) { y = this.maxY; }
16967         }
16968
16969         x = this.getTick(x, this.xTicks);
16970         y = this.getTick(y, this.yTicks);
16971
16972
16973         return {x:x, y:y};
16974     },
16975
16976     /*
16977      * Sets up config options specific to this class. Overrides
16978      * Roo.dd.DragDrop, but all versions of this method through the
16979      * inheritance chain are called
16980      */
16981     applyConfig: function() {
16982         Roo.dd.DD.superclass.applyConfig.call(this);
16983         this.scroll = (this.config.scroll !== false);
16984     },
16985
16986     /*
16987      * Event that fires prior to the onMouseDown event.  Overrides
16988      * Roo.dd.DragDrop.
16989      */
16990     b4MouseDown: function(e) {
16991         // this.resetConstraints();
16992         this.autoOffset(e.getPageX(),
16993                             e.getPageY());
16994     },
16995
16996     /*
16997      * Event that fires prior to the onDrag event.  Overrides
16998      * Roo.dd.DragDrop.
16999      */
17000     b4Drag: function(e) {
17001         this.setDragElPos(e.getPageX(),
17002                             e.getPageY());
17003     },
17004
17005     toString: function() {
17006         return ("DD " + this.id);
17007     }
17008
17009     //////////////////////////////////////////////////////////////////////////
17010     // Debugging ygDragDrop events that can be overridden
17011     //////////////////////////////////////////////////////////////////////////
17012     /*
17013     startDrag: function(x, y) {
17014     },
17015
17016     onDrag: function(e) {
17017     },
17018
17019     onDragEnter: function(e, id) {
17020     },
17021
17022     onDragOver: function(e, id) {
17023     },
17024
17025     onDragOut: function(e, id) {
17026     },
17027
17028     onDragDrop: function(e, id) {
17029     },
17030
17031     endDrag: function(e) {
17032     }
17033
17034     */
17035
17036 });/*
17037  * Based on:
17038  * Ext JS Library 1.1.1
17039  * Copyright(c) 2006-2007, Ext JS, LLC.
17040  *
17041  * Originally Released Under LGPL - original licence link has changed is not relivant.
17042  *
17043  * Fork - LGPL
17044  * <script type="text/javascript">
17045  */
17046
17047 /**
17048  * @class Roo.dd.DDProxy
17049  * A DragDrop implementation that inserts an empty, bordered div into
17050  * the document that follows the cursor during drag operations.  At the time of
17051  * the click, the frame div is resized to the dimensions of the linked html
17052  * element, and moved to the exact location of the linked element.
17053  *
17054  * References to the "frame" element refer to the single proxy element that
17055  * was created to be dragged in place of all DDProxy elements on the
17056  * page.
17057  *
17058  * @extends Roo.dd.DD
17059  * @constructor
17060  * @param {String} id the id of the linked html element
17061  * @param {String} sGroup the group of related DragDrop objects
17062  * @param {object} config an object containing configurable attributes
17063  *                Valid properties for DDProxy in addition to those in DragDrop:
17064  *                   resizeFrame, centerFrame, dragElId
17065  */
17066 Roo.dd.DDProxy = function(id, sGroup, config) {
17067     if (id) {
17068         this.init(id, sGroup, config);
17069         this.initFrame();
17070     }
17071 };
17072
17073 /**
17074  * The default drag frame div id
17075  * @property Roo.dd.DDProxy.dragElId
17076  * @type String
17077  * @static
17078  */
17079 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17080
17081 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17082
17083     /**
17084      * By default we resize the drag frame to be the same size as the element
17085      * we want to drag (this is to get the frame effect).  We can turn it off
17086      * if we want a different behavior.
17087      * @property resizeFrame
17088      * @type boolean
17089      */
17090     resizeFrame: true,
17091
17092     /**
17093      * By default the frame is positioned exactly where the drag element is, so
17094      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17095      * you do not have constraints on the obj is to have the drag frame centered
17096      * around the cursor.  Set centerFrame to true for this effect.
17097      * @property centerFrame
17098      * @type boolean
17099      */
17100     centerFrame: false,
17101
17102     /**
17103      * Creates the proxy element if it does not yet exist
17104      * @method createFrame
17105      */
17106     createFrame: function() {
17107         var self = this;
17108         var body = document.body;
17109
17110         if (!body || !body.firstChild) {
17111             setTimeout( function() { self.createFrame(); }, 50 );
17112             return;
17113         }
17114
17115         var div = this.getDragEl();
17116
17117         if (!div) {
17118             div    = document.createElement("div");
17119             div.id = this.dragElId;
17120             var s  = div.style;
17121
17122             s.position   = "absolute";
17123             s.visibility = "hidden";
17124             s.cursor     = "move";
17125             s.border     = "2px solid #aaa";
17126             s.zIndex     = 999;
17127
17128             // appendChild can blow up IE if invoked prior to the window load event
17129             // while rendering a table.  It is possible there are other scenarios
17130             // that would cause this to happen as well.
17131             body.insertBefore(div, body.firstChild);
17132         }
17133     },
17134
17135     /**
17136      * Initialization for the drag frame element.  Must be called in the
17137      * constructor of all subclasses
17138      * @method initFrame
17139      */
17140     initFrame: function() {
17141         this.createFrame();
17142     },
17143
17144     applyConfig: function() {
17145         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17146
17147         this.resizeFrame = (this.config.resizeFrame !== false);
17148         this.centerFrame = (this.config.centerFrame);
17149         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17150     },
17151
17152     /**
17153      * Resizes the drag frame to the dimensions of the clicked object, positions
17154      * it over the object, and finally displays it
17155      * @method showFrame
17156      * @param {int} iPageX X click position
17157      * @param {int} iPageY Y click position
17158      * @private
17159      */
17160     showFrame: function(iPageX, iPageY) {
17161         var el = this.getEl();
17162         var dragEl = this.getDragEl();
17163         var s = dragEl.style;
17164
17165         this._resizeProxy();
17166
17167         if (this.centerFrame) {
17168             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17169                            Math.round(parseInt(s.height, 10)/2) );
17170         }
17171
17172         this.setDragElPos(iPageX, iPageY);
17173
17174         Roo.fly(dragEl).show();
17175     },
17176
17177     /**
17178      * The proxy is automatically resized to the dimensions of the linked
17179      * element when a drag is initiated, unless resizeFrame is set to false
17180      * @method _resizeProxy
17181      * @private
17182      */
17183     _resizeProxy: function() {
17184         if (this.resizeFrame) {
17185             var el = this.getEl();
17186             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17187         }
17188     },
17189
17190     // overrides Roo.dd.DragDrop
17191     b4MouseDown: function(e) {
17192         var x = e.getPageX();
17193         var y = e.getPageY();
17194         this.autoOffset(x, y);
17195         this.setDragElPos(x, y);
17196     },
17197
17198     // overrides Roo.dd.DragDrop
17199     b4StartDrag: function(x, y) {
17200         // show the drag frame
17201         this.showFrame(x, y);
17202     },
17203
17204     // overrides Roo.dd.DragDrop
17205     b4EndDrag: function(e) {
17206         Roo.fly(this.getDragEl()).hide();
17207     },
17208
17209     // overrides Roo.dd.DragDrop
17210     // By default we try to move the element to the last location of the frame.
17211     // This is so that the default behavior mirrors that of Roo.dd.DD.
17212     endDrag: function(e) {
17213
17214         var lel = this.getEl();
17215         var del = this.getDragEl();
17216
17217         // Show the drag frame briefly so we can get its position
17218         del.style.visibility = "";
17219
17220         this.beforeMove();
17221         // Hide the linked element before the move to get around a Safari
17222         // rendering bug.
17223         lel.style.visibility = "hidden";
17224         Roo.dd.DDM.moveToEl(lel, del);
17225         del.style.visibility = "hidden";
17226         lel.style.visibility = "";
17227
17228         this.afterDrag();
17229     },
17230
17231     beforeMove : function(){
17232
17233     },
17234
17235     afterDrag : function(){
17236
17237     },
17238
17239     toString: function() {
17240         return ("DDProxy " + this.id);
17241     }
17242
17243 });
17244 /*
17245  * Based on:
17246  * Ext JS Library 1.1.1
17247  * Copyright(c) 2006-2007, Ext JS, LLC.
17248  *
17249  * Originally Released Under LGPL - original licence link has changed is not relivant.
17250  *
17251  * Fork - LGPL
17252  * <script type="text/javascript">
17253  */
17254
17255  /**
17256  * @class Roo.dd.DDTarget
17257  * A DragDrop implementation that does not move, but can be a drop
17258  * target.  You would get the same result by simply omitting implementation
17259  * for the event callbacks, but this way we reduce the processing cost of the
17260  * event listener and the callbacks.
17261  * @extends Roo.dd.DragDrop
17262  * @constructor
17263  * @param {String} id the id of the element that is a drop target
17264  * @param {String} sGroup the group of related DragDrop objects
17265  * @param {object} config an object containing configurable attributes
17266  *                 Valid properties for DDTarget in addition to those in
17267  *                 DragDrop:
17268  *                    none
17269  */
17270 Roo.dd.DDTarget = function(id, sGroup, config) {
17271     if (id) {
17272         this.initTarget(id, sGroup, config);
17273     }
17274 };
17275
17276 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17277 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17278     toString: function() {
17279         return ("DDTarget " + this.id);
17280     }
17281 });
17282 /*
17283  * Based on:
17284  * Ext JS Library 1.1.1
17285  * Copyright(c) 2006-2007, Ext JS, LLC.
17286  *
17287  * Originally Released Under LGPL - original licence link has changed is not relivant.
17288  *
17289  * Fork - LGPL
17290  * <script type="text/javascript">
17291  */
17292  
17293
17294 /**
17295  * @class Roo.dd.ScrollManager
17296  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17297  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17298  * @singleton
17299  */
17300 Roo.dd.ScrollManager = function(){
17301     var ddm = Roo.dd.DragDropMgr;
17302     var els = {};
17303     var dragEl = null;
17304     var proc = {};
17305     
17306     var onStop = function(e){
17307         dragEl = null;
17308         clearProc();
17309     };
17310     
17311     var triggerRefresh = function(){
17312         if(ddm.dragCurrent){
17313              ddm.refreshCache(ddm.dragCurrent.groups);
17314         }
17315     };
17316     
17317     var doScroll = function(){
17318         if(ddm.dragCurrent){
17319             var dds = Roo.dd.ScrollManager;
17320             if(!dds.animate){
17321                 if(proc.el.scroll(proc.dir, dds.increment)){
17322                     triggerRefresh();
17323                 }
17324             }else{
17325                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17326             }
17327         }
17328     };
17329     
17330     var clearProc = function(){
17331         if(proc.id){
17332             clearInterval(proc.id);
17333         }
17334         proc.id = 0;
17335         proc.el = null;
17336         proc.dir = "";
17337     };
17338     
17339     var startProc = function(el, dir){
17340         clearProc();
17341         proc.el = el;
17342         proc.dir = dir;
17343         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17344     };
17345     
17346     var onFire = function(e, isDrop){
17347         if(isDrop || !ddm.dragCurrent){ return; }
17348         var dds = Roo.dd.ScrollManager;
17349         if(!dragEl || dragEl != ddm.dragCurrent){
17350             dragEl = ddm.dragCurrent;
17351             // refresh regions on drag start
17352             dds.refreshCache();
17353         }
17354         
17355         var xy = Roo.lib.Event.getXY(e);
17356         var pt = new Roo.lib.Point(xy[0], xy[1]);
17357         for(var id in els){
17358             var el = els[id], r = el._region;
17359             if(r && r.contains(pt) && el.isScrollable()){
17360                 if(r.bottom - pt.y <= dds.thresh){
17361                     if(proc.el != el){
17362                         startProc(el, "down");
17363                     }
17364                     return;
17365                 }else if(r.right - pt.x <= dds.thresh){
17366                     if(proc.el != el){
17367                         startProc(el, "left");
17368                     }
17369                     return;
17370                 }else if(pt.y - r.top <= dds.thresh){
17371                     if(proc.el != el){
17372                         startProc(el, "up");
17373                     }
17374                     return;
17375                 }else if(pt.x - r.left <= dds.thresh){
17376                     if(proc.el != el){
17377                         startProc(el, "right");
17378                     }
17379                     return;
17380                 }
17381             }
17382         }
17383         clearProc();
17384     };
17385     
17386     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17387     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17388     
17389     return {
17390         /**
17391          * Registers new overflow element(s) to auto scroll
17392          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17393          */
17394         register : function(el){
17395             if(el instanceof Array){
17396                 for(var i = 0, len = el.length; i < len; i++) {
17397                         this.register(el[i]);
17398                 }
17399             }else{
17400                 el = Roo.get(el);
17401                 els[el.id] = el;
17402             }
17403         },
17404         
17405         /**
17406          * Unregisters overflow element(s) so they are no longer scrolled
17407          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17408          */
17409         unregister : function(el){
17410             if(el instanceof Array){
17411                 for(var i = 0, len = el.length; i < len; i++) {
17412                         this.unregister(el[i]);
17413                 }
17414             }else{
17415                 el = Roo.get(el);
17416                 delete els[el.id];
17417             }
17418         },
17419         
17420         /**
17421          * The number of pixels from the edge of a container the pointer needs to be to 
17422          * trigger scrolling (defaults to 25)
17423          * @type Number
17424          */
17425         thresh : 25,
17426         
17427         /**
17428          * The number of pixels to scroll in each scroll increment (defaults to 50)
17429          * @type Number
17430          */
17431         increment : 100,
17432         
17433         /**
17434          * The frequency of scrolls in milliseconds (defaults to 500)
17435          * @type Number
17436          */
17437         frequency : 500,
17438         
17439         /**
17440          * True to animate the scroll (defaults to true)
17441          * @type Boolean
17442          */
17443         animate: true,
17444         
17445         /**
17446          * The animation duration in seconds - 
17447          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17448          * @type Number
17449          */
17450         animDuration: .4,
17451         
17452         /**
17453          * Manually trigger a cache refresh.
17454          */
17455         refreshCache : function(){
17456             for(var id in els){
17457                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17458                     els[id]._region = els[id].getRegion();
17459                 }
17460             }
17461         }
17462     };
17463 }();/*
17464  * Based on:
17465  * Ext JS Library 1.1.1
17466  * Copyright(c) 2006-2007, Ext JS, LLC.
17467  *
17468  * Originally Released Under LGPL - original licence link has changed is not relivant.
17469  *
17470  * Fork - LGPL
17471  * <script type="text/javascript">
17472  */
17473  
17474
17475 /**
17476  * @class Roo.dd.Registry
17477  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17478  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17479  * @singleton
17480  */
17481 Roo.dd.Registry = function(){
17482     var elements = {}; 
17483     var handles = {}; 
17484     var autoIdSeed = 0;
17485
17486     var getId = function(el, autogen){
17487         if(typeof el == "string"){
17488             return el;
17489         }
17490         var id = el.id;
17491         if(!id && autogen !== false){
17492             id = "roodd-" + (++autoIdSeed);
17493             el.id = id;
17494         }
17495         return id;
17496     };
17497     
17498     return {
17499     /**
17500      * Register a drag drop element
17501      * @param {String|HTMLElement} element The id or DOM node to register
17502      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17503      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17504      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17505      * populated in the data object (if applicable):
17506      * <pre>
17507 Value      Description<br />
17508 ---------  ------------------------------------------<br />
17509 handles    Array of DOM nodes that trigger dragging<br />
17510            for the element being registered<br />
17511 isHandle   True if the element passed in triggers<br />
17512            dragging itself, else false
17513 </pre>
17514      */
17515         register : function(el, data){
17516             data = data || {};
17517             if(typeof el == "string"){
17518                 el = document.getElementById(el);
17519             }
17520             data.ddel = el;
17521             elements[getId(el)] = data;
17522             if(data.isHandle !== false){
17523                 handles[data.ddel.id] = data;
17524             }
17525             if(data.handles){
17526                 var hs = data.handles;
17527                 for(var i = 0, len = hs.length; i < len; i++){
17528                         handles[getId(hs[i])] = data;
17529                 }
17530             }
17531         },
17532
17533     /**
17534      * Unregister a drag drop element
17535      * @param {String|HTMLElement}  element The id or DOM node to unregister
17536      */
17537         unregister : function(el){
17538             var id = getId(el, false);
17539             var data = elements[id];
17540             if(data){
17541                 delete elements[id];
17542                 if(data.handles){
17543                     var hs = data.handles;
17544                     for(var i = 0, len = hs.length; i < len; i++){
17545                         delete handles[getId(hs[i], false)];
17546                     }
17547                 }
17548             }
17549         },
17550
17551     /**
17552      * Returns the handle registered for a DOM Node by id
17553      * @param {String|HTMLElement} id The DOM node or id to look up
17554      * @return {Object} handle The custom handle data
17555      */
17556         getHandle : function(id){
17557             if(typeof id != "string"){ // must be element?
17558                 id = id.id;
17559             }
17560             return handles[id];
17561         },
17562
17563     /**
17564      * Returns the handle that is registered for the DOM node that is the target of the event
17565      * @param {Event} e The event
17566      * @return {Object} handle The custom handle data
17567      */
17568         getHandleFromEvent : function(e){
17569             var t = Roo.lib.Event.getTarget(e);
17570             return t ? handles[t.id] : null;
17571         },
17572
17573     /**
17574      * Returns a custom data object that is registered for a DOM node by id
17575      * @param {String|HTMLElement} id The DOM node or id to look up
17576      * @return {Object} data The custom data
17577      */
17578         getTarget : function(id){
17579             if(typeof id != "string"){ // must be element?
17580                 id = id.id;
17581             }
17582             return elements[id];
17583         },
17584
17585     /**
17586      * Returns a custom data object that is registered for the DOM node that is the target of the event
17587      * @param {Event} e The event
17588      * @return {Object} data The custom data
17589      */
17590         getTargetFromEvent : function(e){
17591             var t = Roo.lib.Event.getTarget(e);
17592             return t ? elements[t.id] || handles[t.id] : null;
17593         }
17594     };
17595 }();/*
17596  * Based on:
17597  * Ext JS Library 1.1.1
17598  * Copyright(c) 2006-2007, Ext JS, LLC.
17599  *
17600  * Originally Released Under LGPL - original licence link has changed is not relivant.
17601  *
17602  * Fork - LGPL
17603  * <script type="text/javascript">
17604  */
17605  
17606
17607 /**
17608  * @class Roo.dd.StatusProxy
17609  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17610  * default drag proxy used by all Roo.dd components.
17611  * @constructor
17612  * @param {Object} config
17613  */
17614 Roo.dd.StatusProxy = function(config){
17615     Roo.apply(this, config);
17616     this.id = this.id || Roo.id();
17617     this.el = new Roo.Layer({
17618         dh: {
17619             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17620                 {tag: "div", cls: "x-dd-drop-icon"},
17621                 {tag: "div", cls: "x-dd-drag-ghost"}
17622             ]
17623         }, 
17624         shadow: !config || config.shadow !== false
17625     });
17626     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17627     this.dropStatus = this.dropNotAllowed;
17628 };
17629
17630 Roo.dd.StatusProxy.prototype = {
17631     /**
17632      * @cfg {String} dropAllowed
17633      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17634      */
17635     dropAllowed : "x-dd-drop-ok",
17636     /**
17637      * @cfg {String} dropNotAllowed
17638      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17639      */
17640     dropNotAllowed : "x-dd-drop-nodrop",
17641
17642     /**
17643      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17644      * over the current target element.
17645      * @param {String} cssClass The css class for the new drop status indicator image
17646      */
17647     setStatus : function(cssClass){
17648         cssClass = cssClass || this.dropNotAllowed;
17649         if(this.dropStatus != cssClass){
17650             this.el.replaceClass(this.dropStatus, cssClass);
17651             this.dropStatus = cssClass;
17652         }
17653     },
17654
17655     /**
17656      * Resets the status indicator to the default dropNotAllowed value
17657      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17658      */
17659     reset : function(clearGhost){
17660         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17661         this.dropStatus = this.dropNotAllowed;
17662         if(clearGhost){
17663             this.ghost.update("");
17664         }
17665     },
17666
17667     /**
17668      * Updates the contents of the ghost element
17669      * @param {String} html The html that will replace the current innerHTML of the ghost element
17670      */
17671     update : function(html){
17672         if(typeof html == "string"){
17673             this.ghost.update(html);
17674         }else{
17675             this.ghost.update("");
17676             html.style.margin = "0";
17677             this.ghost.dom.appendChild(html);
17678         }
17679         // ensure float = none set?? cant remember why though.
17680         var el = this.ghost.dom.firstChild;
17681                 if(el){
17682                         Roo.fly(el).setStyle('float', 'none');
17683                 }
17684     },
17685     
17686     /**
17687      * Returns the underlying proxy {@link Roo.Layer}
17688      * @return {Roo.Layer} el
17689     */
17690     getEl : function(){
17691         return this.el;
17692     },
17693
17694     /**
17695      * Returns the ghost element
17696      * @return {Roo.Element} el
17697      */
17698     getGhost : function(){
17699         return this.ghost;
17700     },
17701
17702     /**
17703      * Hides the proxy
17704      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17705      */
17706     hide : function(clear){
17707         this.el.hide();
17708         if(clear){
17709             this.reset(true);
17710         }
17711     },
17712
17713     /**
17714      * Stops the repair animation if it's currently running
17715      */
17716     stop : function(){
17717         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17718             this.anim.stop();
17719         }
17720     },
17721
17722     /**
17723      * Displays this proxy
17724      */
17725     show : function(){
17726         this.el.show();
17727     },
17728
17729     /**
17730      * Force the Layer to sync its shadow and shim positions to the element
17731      */
17732     sync : function(){
17733         this.el.sync();
17734     },
17735
17736     /**
17737      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17738      * invalid drop operation by the item being dragged.
17739      * @param {Array} xy The XY position of the element ([x, y])
17740      * @param {Function} callback The function to call after the repair is complete
17741      * @param {Object} scope The scope in which to execute the callback
17742      */
17743     repair : function(xy, callback, scope){
17744         this.callback = callback;
17745         this.scope = scope;
17746         if(xy && this.animRepair !== false){
17747             this.el.addClass("x-dd-drag-repair");
17748             this.el.hideUnders(true);
17749             this.anim = this.el.shift({
17750                 duration: this.repairDuration || .5,
17751                 easing: 'easeOut',
17752                 xy: xy,
17753                 stopFx: true,
17754                 callback: this.afterRepair,
17755                 scope: this
17756             });
17757         }else{
17758             this.afterRepair();
17759         }
17760     },
17761
17762     // private
17763     afterRepair : function(){
17764         this.hide(true);
17765         if(typeof this.callback == "function"){
17766             this.callback.call(this.scope || this);
17767         }
17768         this.callback = null;
17769         this.scope = null;
17770     }
17771 };/*
17772  * Based on:
17773  * Ext JS Library 1.1.1
17774  * Copyright(c) 2006-2007, Ext JS, LLC.
17775  *
17776  * Originally Released Under LGPL - original licence link has changed is not relivant.
17777  *
17778  * Fork - LGPL
17779  * <script type="text/javascript">
17780  */
17781
17782 /**
17783  * @class Roo.dd.DragSource
17784  * @extends Roo.dd.DDProxy
17785  * A simple class that provides the basic implementation needed to make any element draggable.
17786  * @constructor
17787  * @param {String/HTMLElement/Element} el The container element
17788  * @param {Object} config
17789  */
17790 Roo.dd.DragSource = function(el, config){
17791     this.el = Roo.get(el);
17792     this.dragData = {};
17793     
17794     Roo.apply(this, config);
17795     
17796     if(!this.proxy){
17797         this.proxy = new Roo.dd.StatusProxy();
17798     }
17799
17800     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17801           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17802     
17803     this.dragging = false;
17804 };
17805
17806 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17807     /**
17808      * @cfg {String} dropAllowed
17809      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17810      */
17811     dropAllowed : "x-dd-drop-ok",
17812     /**
17813      * @cfg {String} dropNotAllowed
17814      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17815      */
17816     dropNotAllowed : "x-dd-drop-nodrop",
17817
17818     /**
17819      * Returns the data object associated with this drag source
17820      * @return {Object} data An object containing arbitrary data
17821      */
17822     getDragData : function(e){
17823         return this.dragData;
17824     },
17825
17826     // private
17827     onDragEnter : function(e, id){
17828         var target = Roo.dd.DragDropMgr.getDDById(id);
17829         this.cachedTarget = target;
17830         if(this.beforeDragEnter(target, e, id) !== false){
17831             if(target.isNotifyTarget){
17832                 var status = target.notifyEnter(this, e, this.dragData);
17833                 this.proxy.setStatus(status);
17834             }else{
17835                 this.proxy.setStatus(this.dropAllowed);
17836             }
17837             
17838             if(this.afterDragEnter){
17839                 /**
17840                  * An empty function by default, but provided so that you can perform a custom action
17841                  * when the dragged item enters the drop target by providing an implementation.
17842                  * @param {Roo.dd.DragDrop} target The drop target
17843                  * @param {Event} e The event object
17844                  * @param {String} id The id of the dragged element
17845                  * @method afterDragEnter
17846                  */
17847                 this.afterDragEnter(target, e, id);
17848             }
17849         }
17850     },
17851
17852     /**
17853      * An empty function by default, but provided so that you can perform a custom action
17854      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17855      * @param {Roo.dd.DragDrop} target The drop target
17856      * @param {Event} e The event object
17857      * @param {String} id The id of the dragged element
17858      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17859      */
17860     beforeDragEnter : function(target, e, id){
17861         return true;
17862     },
17863
17864     // private
17865     alignElWithMouse: function() {
17866         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17867         this.proxy.sync();
17868     },
17869
17870     // private
17871     onDragOver : function(e, id){
17872         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17873         if(this.beforeDragOver(target, e, id) !== false){
17874             if(target.isNotifyTarget){
17875                 var status = target.notifyOver(this, e, this.dragData);
17876                 this.proxy.setStatus(status);
17877             }
17878
17879             if(this.afterDragOver){
17880                 /**
17881                  * An empty function by default, but provided so that you can perform a custom action
17882                  * while the dragged item is over the drop target by providing an implementation.
17883                  * @param {Roo.dd.DragDrop} target The drop target
17884                  * @param {Event} e The event object
17885                  * @param {String} id The id of the dragged element
17886                  * @method afterDragOver
17887                  */
17888                 this.afterDragOver(target, e, id);
17889             }
17890         }
17891     },
17892
17893     /**
17894      * An empty function by default, but provided so that you can perform a custom action
17895      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17896      * @param {Roo.dd.DragDrop} target The drop target
17897      * @param {Event} e The event object
17898      * @param {String} id The id of the dragged element
17899      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17900      */
17901     beforeDragOver : function(target, e, id){
17902         return true;
17903     },
17904
17905     // private
17906     onDragOut : function(e, id){
17907         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17908         if(this.beforeDragOut(target, e, id) !== false){
17909             if(target.isNotifyTarget){
17910                 target.notifyOut(this, e, this.dragData);
17911             }
17912             this.proxy.reset();
17913             if(this.afterDragOut){
17914                 /**
17915                  * An empty function by default, but provided so that you can perform a custom action
17916                  * after the dragged item is dragged out of the target without dropping.
17917                  * @param {Roo.dd.DragDrop} target The drop target
17918                  * @param {Event} e The event object
17919                  * @param {String} id The id of the dragged element
17920                  * @method afterDragOut
17921                  */
17922                 this.afterDragOut(target, e, id);
17923             }
17924         }
17925         this.cachedTarget = null;
17926     },
17927
17928     /**
17929      * An empty function by default, but provided so that you can perform a custom action before the dragged
17930      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17931      * @param {Roo.dd.DragDrop} target The drop target
17932      * @param {Event} e The event object
17933      * @param {String} id The id of the dragged element
17934      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17935      */
17936     beforeDragOut : function(target, e, id){
17937         return true;
17938     },
17939     
17940     // private
17941     onDragDrop : function(e, id){
17942         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17943         if(this.beforeDragDrop(target, e, id) !== false){
17944             if(target.isNotifyTarget){
17945                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17946                     this.onValidDrop(target, e, id);
17947                 }else{
17948                     this.onInvalidDrop(target, e, id);
17949                 }
17950             }else{
17951                 this.onValidDrop(target, e, id);
17952             }
17953             
17954             if(this.afterDragDrop){
17955                 /**
17956                  * An empty function by default, but provided so that you can perform a custom action
17957                  * after a valid drag drop has occurred by providing an implementation.
17958                  * @param {Roo.dd.DragDrop} target The drop target
17959                  * @param {Event} e The event object
17960                  * @param {String} id The id of the dropped element
17961                  * @method afterDragDrop
17962                  */
17963                 this.afterDragDrop(target, e, id);
17964             }
17965         }
17966         delete this.cachedTarget;
17967     },
17968
17969     /**
17970      * An empty function by default, but provided so that you can perform a custom action before the dragged
17971      * item is dropped onto the target and optionally cancel the onDragDrop.
17972      * @param {Roo.dd.DragDrop} target The drop target
17973      * @param {Event} e The event object
17974      * @param {String} id The id of the dragged element
17975      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17976      */
17977     beforeDragDrop : function(target, e, id){
17978         return true;
17979     },
17980
17981     // private
17982     onValidDrop : function(target, e, id){
17983         this.hideProxy();
17984         if(this.afterValidDrop){
17985             /**
17986              * An empty function by default, but provided so that you can perform a custom action
17987              * after a valid drop has occurred by providing an implementation.
17988              * @param {Object} target The target DD 
17989              * @param {Event} e The event object
17990              * @param {String} id The id of the dropped element
17991              * @method afterInvalidDrop
17992              */
17993             this.afterValidDrop(target, e, id);
17994         }
17995     },
17996
17997     // private
17998     getRepairXY : function(e, data){
17999         return this.el.getXY();  
18000     },
18001
18002     // private
18003     onInvalidDrop : function(target, e, id){
18004         this.beforeInvalidDrop(target, e, id);
18005         if(this.cachedTarget){
18006             if(this.cachedTarget.isNotifyTarget){
18007                 this.cachedTarget.notifyOut(this, e, this.dragData);
18008             }
18009             this.cacheTarget = null;
18010         }
18011         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18012
18013         if(this.afterInvalidDrop){
18014             /**
18015              * An empty function by default, but provided so that you can perform a custom action
18016              * after an invalid drop has occurred by providing an implementation.
18017              * @param {Event} e The event object
18018              * @param {String} id The id of the dropped element
18019              * @method afterInvalidDrop
18020              */
18021             this.afterInvalidDrop(e, id);
18022         }
18023     },
18024
18025     // private
18026     afterRepair : function(){
18027         if(Roo.enableFx){
18028             this.el.highlight(this.hlColor || "c3daf9");
18029         }
18030         this.dragging = false;
18031     },
18032
18033     /**
18034      * An empty function by default, but provided so that you can perform a custom action after an invalid
18035      * drop has occurred.
18036      * @param {Roo.dd.DragDrop} target The drop target
18037      * @param {Event} e The event object
18038      * @param {String} id The id of the dragged element
18039      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18040      */
18041     beforeInvalidDrop : function(target, e, id){
18042         return true;
18043     },
18044
18045     // private
18046     handleMouseDown : function(e){
18047         if(this.dragging) {
18048             return;
18049         }
18050         var data = this.getDragData(e);
18051         if(data && this.onBeforeDrag(data, e) !== false){
18052             this.dragData = data;
18053             this.proxy.stop();
18054             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18055         } 
18056     },
18057
18058     /**
18059      * An empty function by default, but provided so that you can perform a custom action before the initial
18060      * drag event begins and optionally cancel it.
18061      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18062      * @param {Event} e The event object
18063      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18064      */
18065     onBeforeDrag : function(data, e){
18066         return true;
18067     },
18068
18069     /**
18070      * An empty function by default, but provided so that you can perform a custom action once the initial
18071      * drag event has begun.  The drag cannot be canceled from this function.
18072      * @param {Number} x The x position of the click on the dragged object
18073      * @param {Number} y The y position of the click on the dragged object
18074      */
18075     onStartDrag : Roo.emptyFn,
18076
18077     // private - YUI override
18078     startDrag : function(x, y){
18079         this.proxy.reset();
18080         this.dragging = true;
18081         this.proxy.update("");
18082         this.onInitDrag(x, y);
18083         this.proxy.show();
18084     },
18085
18086     // private
18087     onInitDrag : function(x, y){
18088         var clone = this.el.dom.cloneNode(true);
18089         clone.id = Roo.id(); // prevent duplicate ids
18090         this.proxy.update(clone);
18091         this.onStartDrag(x, y);
18092         return true;
18093     },
18094
18095     /**
18096      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18097      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18098      */
18099     getProxy : function(){
18100         return this.proxy;  
18101     },
18102
18103     /**
18104      * Hides the drag source's {@link Roo.dd.StatusProxy}
18105      */
18106     hideProxy : function(){
18107         this.proxy.hide();  
18108         this.proxy.reset(true);
18109         this.dragging = false;
18110     },
18111
18112     // private
18113     triggerCacheRefresh : function(){
18114         Roo.dd.DDM.refreshCache(this.groups);
18115     },
18116
18117     // private - override to prevent hiding
18118     b4EndDrag: function(e) {
18119     },
18120
18121     // private - override to prevent moving
18122     endDrag : function(e){
18123         this.onEndDrag(this.dragData, e);
18124     },
18125
18126     // private
18127     onEndDrag : function(data, e){
18128     },
18129     
18130     // private - pin to cursor
18131     autoOffset : function(x, y) {
18132         this.setDelta(-12, -20);
18133     }    
18134 });/*
18135  * Based on:
18136  * Ext JS Library 1.1.1
18137  * Copyright(c) 2006-2007, Ext JS, LLC.
18138  *
18139  * Originally Released Under LGPL - original licence link has changed is not relivant.
18140  *
18141  * Fork - LGPL
18142  * <script type="text/javascript">
18143  */
18144
18145
18146 /**
18147  * @class Roo.dd.DropTarget
18148  * @extends Roo.dd.DDTarget
18149  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18150  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18151  * @constructor
18152  * @param {String/HTMLElement/Element} el The container element
18153  * @param {Object} config
18154  */
18155 Roo.dd.DropTarget = function(el, config){
18156     this.el = Roo.get(el);
18157     
18158     Roo.apply(this, config);
18159     
18160     if(this.containerScroll){
18161         Roo.dd.ScrollManager.register(this.el);
18162     }
18163     
18164     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18165           {isTarget: true});
18166
18167 };
18168
18169 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18170     /**
18171      * @cfg {String} overClass
18172      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18173      */
18174     /**
18175      * @cfg {String} dropAllowed
18176      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18177      */
18178     dropAllowed : "x-dd-drop-ok",
18179     /**
18180      * @cfg {String} dropNotAllowed
18181      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18182      */
18183     dropNotAllowed : "x-dd-drop-nodrop",
18184
18185     // private
18186     isTarget : true,
18187
18188     // private
18189     isNotifyTarget : true,
18190
18191     /**
18192      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18193      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18194      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18195      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18196      * @param {Event} e The event
18197      * @param {Object} data An object containing arbitrary data supplied by the drag source
18198      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18199      * underlying {@link Roo.dd.StatusProxy} can be updated
18200      */
18201     notifyEnter : function(dd, e, data){
18202         if(this.overClass){
18203             this.el.addClass(this.overClass);
18204         }
18205         return this.dropAllowed;
18206     },
18207
18208     /**
18209      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18210      * This method will be called on every mouse movement while the drag source is over the drop target.
18211      * This default implementation simply returns the dropAllowed config value.
18212      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18213      * @param {Event} e The event
18214      * @param {Object} data An object containing arbitrary data supplied by the drag source
18215      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18216      * underlying {@link Roo.dd.StatusProxy} can be updated
18217      */
18218     notifyOver : function(dd, e, data){
18219         return this.dropAllowed;
18220     },
18221
18222     /**
18223      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18224      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18225      * overClass (if any) from the drop element.
18226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18227      * @param {Event} e The event
18228      * @param {Object} data An object containing arbitrary data supplied by the drag source
18229      */
18230     notifyOut : function(dd, e, data){
18231         if(this.overClass){
18232             this.el.removeClass(this.overClass);
18233         }
18234     },
18235
18236     /**
18237      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18238      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18239      * implementation that does something to process the drop event and returns true so that the drag source's
18240      * repair action does not run.
18241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18242      * @param {Event} e The event
18243      * @param {Object} data An object containing arbitrary data supplied by the drag source
18244      * @return {Boolean} True if the drop was valid, else false
18245      */
18246     notifyDrop : function(dd, e, data){
18247         return false;
18248     }
18249 });/*
18250  * Based on:
18251  * Ext JS Library 1.1.1
18252  * Copyright(c) 2006-2007, Ext JS, LLC.
18253  *
18254  * Originally Released Under LGPL - original licence link has changed is not relivant.
18255  *
18256  * Fork - LGPL
18257  * <script type="text/javascript">
18258  */
18259
18260
18261 /**
18262  * @class Roo.dd.DragZone
18263  * @extends Roo.dd.DragSource
18264  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18265  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18266  * @constructor
18267  * @param {String/HTMLElement/Element} el The container element
18268  * @param {Object} config
18269  */
18270 Roo.dd.DragZone = function(el, config){
18271     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18272     if(this.containerScroll){
18273         Roo.dd.ScrollManager.register(this.el);
18274     }
18275 };
18276
18277 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18278     /**
18279      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18280      * for auto scrolling during drag operations.
18281      */
18282     /**
18283      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18284      * method after a failed drop (defaults to "c3daf9" - light blue)
18285      */
18286
18287     /**
18288      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18289      * for a valid target to drag based on the mouse down. Override this method
18290      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18291      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18292      * @param {EventObject} e The mouse down event
18293      * @return {Object} The dragData
18294      */
18295     getDragData : function(e){
18296         return Roo.dd.Registry.getHandleFromEvent(e);
18297     },
18298     
18299     /**
18300      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18301      * this.dragData.ddel
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      * @return {Boolean} true to continue the drag, false to cancel
18305      */
18306     onInitDrag : function(x, y){
18307         this.proxy.update(this.dragData.ddel.cloneNode(true));
18308         this.onStartDrag(x, y);
18309         return true;
18310     },
18311     
18312     /**
18313      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18314      */
18315     afterRepair : function(){
18316         if(Roo.enableFx){
18317             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18318         }
18319         this.dragging = false;
18320     },
18321
18322     /**
18323      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18324      * the XY of this.dragData.ddel
18325      * @param {EventObject} e The mouse up event
18326      * @return {Array} The xy location (e.g. [100, 200])
18327      */
18328     getRepairXY : function(e){
18329         return Roo.Element.fly(this.dragData.ddel).getXY();  
18330     }
18331 });/*
18332  * Based on:
18333  * Ext JS Library 1.1.1
18334  * Copyright(c) 2006-2007, Ext JS, LLC.
18335  *
18336  * Originally Released Under LGPL - original licence link has changed is not relivant.
18337  *
18338  * Fork - LGPL
18339  * <script type="text/javascript">
18340  */
18341 /**
18342  * @class Roo.dd.DropZone
18343  * @extends Roo.dd.DropTarget
18344  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18345  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18346  * @constructor
18347  * @param {String/HTMLElement/Element} el The container element
18348  * @param {Object} config
18349  */
18350 Roo.dd.DropZone = function(el, config){
18351     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18352 };
18353
18354 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18355     /**
18356      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18357      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18358      * provide your own custom lookup.
18359      * @param {Event} e The event
18360      * @return {Object} data The custom data
18361      */
18362     getTargetFromEvent : function(e){
18363         return Roo.dd.Registry.getTargetFromEvent(e);
18364     },
18365
18366     /**
18367      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18368      * that it has registered.  This method has no default implementation and should be overridden to provide
18369      * node-specific processing if necessary.
18370      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18371      * {@link #getTargetFromEvent} for this node)
18372      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18373      * @param {Event} e The event
18374      * @param {Object} data An object containing arbitrary data supplied by the drag source
18375      */
18376     onNodeEnter : function(n, dd, e, data){
18377         
18378     },
18379
18380     /**
18381      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18382      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18383      * overridden to provide the proper feedback.
18384      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18385      * {@link #getTargetFromEvent} for this node)
18386      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18387      * @param {Event} e The event
18388      * @param {Object} data An object containing arbitrary data supplied by the drag source
18389      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18390      * underlying {@link Roo.dd.StatusProxy} can be updated
18391      */
18392     onNodeOver : function(n, dd, e, data){
18393         return this.dropAllowed;
18394     },
18395
18396     /**
18397      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18398      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18399      * node-specific processing if necessary.
18400      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18401      * {@link #getTargetFromEvent} for this node)
18402      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18403      * @param {Event} e The event
18404      * @param {Object} data An object containing arbitrary data supplied by the drag source
18405      */
18406     onNodeOut : function(n, dd, e, data){
18407         
18408     },
18409
18410     /**
18411      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18412      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18413      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18414      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18415      * {@link #getTargetFromEvent} for this node)
18416      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18417      * @param {Event} e The event
18418      * @param {Object} data An object containing arbitrary data supplied by the drag source
18419      * @return {Boolean} True if the drop was valid, else false
18420      */
18421     onNodeDrop : function(n, dd, e, data){
18422         return false;
18423     },
18424
18425     /**
18426      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18427      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18428      * it should be overridden to provide the proper feedback if necessary.
18429      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18430      * @param {Event} e The event
18431      * @param {Object} data An object containing arbitrary data supplied by the drag source
18432      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18433      * underlying {@link Roo.dd.StatusProxy} can be updated
18434      */
18435     onContainerOver : function(dd, e, data){
18436         return this.dropNotAllowed;
18437     },
18438
18439     /**
18440      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18441      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18442      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18443      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18444      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18445      * @param {Event} e The event
18446      * @param {Object} data An object containing arbitrary data supplied by the drag source
18447      * @return {Boolean} True if the drop was valid, else false
18448      */
18449     onContainerDrop : function(dd, e, data){
18450         return false;
18451     },
18452
18453     /**
18454      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18455      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18456      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18457      * you should override this method and provide a custom implementation.
18458      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18459      * @param {Event} e The event
18460      * @param {Object} data An object containing arbitrary data supplied by the drag source
18461      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18462      * underlying {@link Roo.dd.StatusProxy} can be updated
18463      */
18464     notifyEnter : function(dd, e, data){
18465         return this.dropNotAllowed;
18466     },
18467
18468     /**
18469      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18470      * This method will be called on every mouse movement while the drag source is over the drop zone.
18471      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18472      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18473      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18474      * registered node, it will call {@link #onContainerOver}.
18475      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18476      * @param {Event} e The event
18477      * @param {Object} data An object containing arbitrary data supplied by the drag source
18478      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18479      * underlying {@link Roo.dd.StatusProxy} can be updated
18480      */
18481     notifyOver : function(dd, e, data){
18482         var n = this.getTargetFromEvent(e);
18483         if(!n){ // not over valid drop target
18484             if(this.lastOverNode){
18485                 this.onNodeOut(this.lastOverNode, dd, e, data);
18486                 this.lastOverNode = null;
18487             }
18488             return this.onContainerOver(dd, e, data);
18489         }
18490         if(this.lastOverNode != n){
18491             if(this.lastOverNode){
18492                 this.onNodeOut(this.lastOverNode, dd, e, data);
18493             }
18494             this.onNodeEnter(n, dd, e, data);
18495             this.lastOverNode = n;
18496         }
18497         return this.onNodeOver(n, dd, e, data);
18498     },
18499
18500     /**
18501      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18502      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18503      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18504      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18505      * @param {Event} e The event
18506      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18507      */
18508     notifyOut : function(dd, e, data){
18509         if(this.lastOverNode){
18510             this.onNodeOut(this.lastOverNode, dd, e, data);
18511             this.lastOverNode = null;
18512         }
18513     },
18514
18515     /**
18516      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18517      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18518      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18519      * otherwise it will call {@link #onContainerDrop}.
18520      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18521      * @param {Event} e The event
18522      * @param {Object} data An object containing arbitrary data supplied by the drag source
18523      * @return {Boolean} True if the drop was valid, else false
18524      */
18525     notifyDrop : function(dd, e, data){
18526         if(this.lastOverNode){
18527             this.onNodeOut(this.lastOverNode, dd, e, data);
18528             this.lastOverNode = null;
18529         }
18530         var n = this.getTargetFromEvent(e);
18531         return n ?
18532             this.onNodeDrop(n, dd, e, data) :
18533             this.onContainerDrop(dd, e, data);
18534     },
18535
18536     // private
18537     triggerCacheRefresh : function(){
18538         Roo.dd.DDM.refreshCache(this.groups);
18539     }  
18540 });/*
18541  * Based on:
18542  * Ext JS Library 1.1.1
18543  * Copyright(c) 2006-2007, Ext JS, LLC.
18544  *
18545  * Originally Released Under LGPL - original licence link has changed is not relivant.
18546  *
18547  * Fork - LGPL
18548  * <script type="text/javascript">
18549  */
18550
18551
18552 /**
18553  * @class Roo.data.SortTypes
18554  * @singleton
18555  * Defines the default sorting (casting?) comparison functions used when sorting data.
18556  */
18557 Roo.data.SortTypes = {
18558     /**
18559      * Default sort that does nothing
18560      * @param {Mixed} s The value being converted
18561      * @return {Mixed} The comparison value
18562      */
18563     none : function(s){
18564         return s;
18565     },
18566     
18567     /**
18568      * The regular expression used to strip tags
18569      * @type {RegExp}
18570      * @property
18571      */
18572     stripTagsRE : /<\/?[^>]+>/gi,
18573     
18574     /**
18575      * Strips all HTML tags to sort on text only
18576      * @param {Mixed} s The value being converted
18577      * @return {String} The comparison value
18578      */
18579     asText : function(s){
18580         return String(s).replace(this.stripTagsRE, "");
18581     },
18582     
18583     /**
18584      * Strips all HTML tags to sort on text only - Case insensitive
18585      * @param {Mixed} s The value being converted
18586      * @return {String} The comparison value
18587      */
18588     asUCText : function(s){
18589         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18590     },
18591     
18592     /**
18593      * Case insensitive string
18594      * @param {Mixed} s The value being converted
18595      * @return {String} The comparison value
18596      */
18597     asUCString : function(s) {
18598         return String(s).toUpperCase();
18599     },
18600     
18601     /**
18602      * Date sorting
18603      * @param {Mixed} s The value being converted
18604      * @return {Number} The comparison value
18605      */
18606     asDate : function(s) {
18607         if(!s){
18608             return 0;
18609         }
18610         if(s instanceof Date){
18611             return s.getTime();
18612         }
18613         return Date.parse(String(s));
18614     },
18615     
18616     /**
18617      * Float sorting
18618      * @param {Mixed} s The value being converted
18619      * @return {Float} The comparison value
18620      */
18621     asFloat : function(s) {
18622         var val = parseFloat(String(s).replace(/,/g, ""));
18623         if(isNaN(val)) val = 0;
18624         return val;
18625     },
18626     
18627     /**
18628      * Integer sorting
18629      * @param {Mixed} s The value being converted
18630      * @return {Number} The comparison value
18631      */
18632     asInt : function(s) {
18633         var val = parseInt(String(s).replace(/,/g, ""));
18634         if(isNaN(val)) val = 0;
18635         return val;
18636     }
18637 };/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647
18648 /**
18649 * @class Roo.data.Record
18650  * Instances of this class encapsulate both record <em>definition</em> information, and record
18651  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18652  * to access Records cached in an {@link Roo.data.Store} object.<br>
18653  * <p>
18654  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18655  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18656  * objects.<br>
18657  * <p>
18658  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18659  * @constructor
18660  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18661  * {@link #create}. The parameters are the same.
18662  * @param {Array} data An associative Array of data values keyed by the field name.
18663  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18664  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18665  * not specified an integer id is generated.
18666  */
18667 Roo.data.Record = function(data, id){
18668     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18669     this.data = data;
18670 };
18671
18672 /**
18673  * Generate a constructor for a specific record layout.
18674  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18675  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18676  * Each field definition object may contain the following properties: <ul>
18677  * <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,
18678  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18679  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18680  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18681  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18682  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18683  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18684  * this may be omitted.</p></li>
18685  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18686  * <ul><li>auto (Default, implies no conversion)</li>
18687  * <li>string</li>
18688  * <li>int</li>
18689  * <li>float</li>
18690  * <li>boolean</li>
18691  * <li>date</li></ul></p></li>
18692  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18693  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18694  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18695  * by the Reader into an object that will be stored in the Record. It is passed the
18696  * following parameters:<ul>
18697  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18698  * </ul></p></li>
18699  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18700  * </ul>
18701  * <br>usage:<br><pre><code>
18702 var TopicRecord = Roo.data.Record.create(
18703     {name: 'title', mapping: 'topic_title'},
18704     {name: 'author', mapping: 'username'},
18705     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18706     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18707     {name: 'lastPoster', mapping: 'user2'},
18708     {name: 'excerpt', mapping: 'post_text'}
18709 );
18710
18711 var myNewRecord = new TopicRecord({
18712     title: 'Do my job please',
18713     author: 'noobie',
18714     totalPosts: 1,
18715     lastPost: new Date(),
18716     lastPoster: 'Animal',
18717     excerpt: 'No way dude!'
18718 });
18719 myStore.add(myNewRecord);
18720 </code></pre>
18721  * @method create
18722  * @static
18723  */
18724 Roo.data.Record.create = function(o){
18725     var f = function(){
18726         f.superclass.constructor.apply(this, arguments);
18727     };
18728     Roo.extend(f, Roo.data.Record);
18729     var p = f.prototype;
18730     p.fields = new Roo.util.MixedCollection(false, function(field){
18731         return field.name;
18732     });
18733     for(var i = 0, len = o.length; i < len; i++){
18734         p.fields.add(new Roo.data.Field(o[i]));
18735     }
18736     f.getField = function(name){
18737         return p.fields.get(name);  
18738     };
18739     return f;
18740 };
18741
18742 Roo.data.Record.AUTO_ID = 1000;
18743 Roo.data.Record.EDIT = 'edit';
18744 Roo.data.Record.REJECT = 'reject';
18745 Roo.data.Record.COMMIT = 'commit';
18746
18747 Roo.data.Record.prototype = {
18748     /**
18749      * Readonly flag - true if this record has been modified.
18750      * @type Boolean
18751      */
18752     dirty : false,
18753     editing : false,
18754     error: null,
18755     modified: null,
18756
18757     // private
18758     join : function(store){
18759         this.store = store;
18760     },
18761
18762     /**
18763      * Set the named field to the specified value.
18764      * @param {String} name The name of the field to set.
18765      * @param {Object} value The value to set the field to.
18766      */
18767     set : function(name, value){
18768         if(this.data[name] == value){
18769             return;
18770         }
18771         this.dirty = true;
18772         if(!this.modified){
18773             this.modified = {};
18774         }
18775         if(typeof this.modified[name] == 'undefined'){
18776             this.modified[name] = this.data[name];
18777         }
18778         this.data[name] = value;
18779         if(!this.editing){
18780             this.store.afterEdit(this);
18781         }       
18782     },
18783
18784     /**
18785      * Get the value of the named field.
18786      * @param {String} name The name of the field to get the value of.
18787      * @return {Object} The value of the field.
18788      */
18789     get : function(name){
18790         return this.data[name]; 
18791     },
18792
18793     // private
18794     beginEdit : function(){
18795         this.editing = true;
18796         this.modified = {}; 
18797     },
18798
18799     // private
18800     cancelEdit : function(){
18801         this.editing = false;
18802         delete this.modified;
18803     },
18804
18805     // private
18806     endEdit : function(){
18807         this.editing = false;
18808         if(this.dirty && this.store){
18809             this.store.afterEdit(this);
18810         }
18811     },
18812
18813     /**
18814      * Usually called by the {@link Roo.data.Store} which owns the Record.
18815      * Rejects all changes made to the Record since either creation, or the last commit operation.
18816      * Modified fields are reverted to their original values.
18817      * <p>
18818      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18819      * of reject operations.
18820      */
18821     reject : function(){
18822         var m = this.modified;
18823         for(var n in m){
18824             if(typeof m[n] != "function"){
18825                 this.data[n] = m[n];
18826             }
18827         }
18828         this.dirty = false;
18829         delete this.modified;
18830         this.editing = false;
18831         if(this.store){
18832             this.store.afterReject(this);
18833         }
18834     },
18835
18836     /**
18837      * Usually called by the {@link Roo.data.Store} which owns the Record.
18838      * Commits all changes made to the Record since either creation, or the last commit operation.
18839      * <p>
18840      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18841      * of commit operations.
18842      */
18843     commit : function(){
18844         this.dirty = false;
18845         delete this.modified;
18846         this.editing = false;
18847         if(this.store){
18848             this.store.afterCommit(this);
18849         }
18850     },
18851
18852     // private
18853     hasError : function(){
18854         return this.error != null;
18855     },
18856
18857     // private
18858     clearError : function(){
18859         this.error = null;
18860     },
18861
18862     /**
18863      * Creates a copy of this record.
18864      * @param {String} id (optional) A new record id if you don't want to use this record's id
18865      * @return {Record}
18866      */
18867     copy : function(newId) {
18868         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18869     }
18870 };/*
18871  * Based on:
18872  * Ext JS Library 1.1.1
18873  * Copyright(c) 2006-2007, Ext JS, LLC.
18874  *
18875  * Originally Released Under LGPL - original licence link has changed is not relivant.
18876  *
18877  * Fork - LGPL
18878  * <script type="text/javascript">
18879  */
18880
18881
18882
18883 /**
18884  * @class Roo.data.Store
18885  * @extends Roo.util.Observable
18886  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18887  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18888  * <p>
18889  * 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
18890  * has no knowledge of the format of the data returned by the Proxy.<br>
18891  * <p>
18892  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18893  * instances from the data object. These records are cached and made available through accessor functions.
18894  * @constructor
18895  * Creates a new Store.
18896  * @param {Object} config A config object containing the objects needed for the Store to access data,
18897  * and read the data into Records.
18898  */
18899 Roo.data.Store = function(config){
18900     this.data = new Roo.util.MixedCollection(false);
18901     this.data.getKey = function(o){
18902         return o.id;
18903     };
18904     this.baseParams = {};
18905     // private
18906     this.paramNames = {
18907         "start" : "start",
18908         "limit" : "limit",
18909         "sort" : "sort",
18910         "dir" : "dir"
18911     };
18912
18913     if(config && config.data){
18914         this.inlineData = config.data;
18915         delete config.data;
18916     }
18917
18918     Roo.apply(this, config);
18919     
18920     if(this.reader){ // reader passed
18921         this.reader = Roo.factory(this.reader, Roo.data);
18922         this.reader.xmodule = this.xmodule || false;
18923         if(!this.recordType){
18924             this.recordType = this.reader.recordType;
18925         }
18926         if(this.reader.onMetaChange){
18927             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18928         }
18929     }
18930
18931     if(this.recordType){
18932         this.fields = this.recordType.prototype.fields;
18933     }
18934     this.modified = [];
18935
18936     this.addEvents({
18937         /**
18938          * @event datachanged
18939          * Fires when the data cache has changed, and a widget which is using this Store
18940          * as a Record cache should refresh its view.
18941          * @param {Store} this
18942          */
18943         datachanged : true,
18944         /**
18945          * @event metachange
18946          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18947          * @param {Store} this
18948          * @param {Object} meta The JSON metadata
18949          */
18950         metachange : true,
18951         /**
18952          * @event add
18953          * Fires when Records have been added to the Store
18954          * @param {Store} this
18955          * @param {Roo.data.Record[]} records The array of Records added
18956          * @param {Number} index The index at which the record(s) were added
18957          */
18958         add : true,
18959         /**
18960          * @event remove
18961          * Fires when a Record has been removed from the Store
18962          * @param {Store} this
18963          * @param {Roo.data.Record} record The Record that was removed
18964          * @param {Number} index The index at which the record was removed
18965          */
18966         remove : true,
18967         /**
18968          * @event update
18969          * Fires when a Record has been updated
18970          * @param {Store} this
18971          * @param {Roo.data.Record} record The Record that was updated
18972          * @param {String} operation The update operation being performed.  Value may be one of:
18973          * <pre><code>
18974  Roo.data.Record.EDIT
18975  Roo.data.Record.REJECT
18976  Roo.data.Record.COMMIT
18977          * </code></pre>
18978          */
18979         update : true,
18980         /**
18981          * @event clear
18982          * Fires when the data cache has been cleared.
18983          * @param {Store} this
18984          */
18985         clear : true,
18986         /**
18987          * @event beforeload
18988          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18989          * the load action will be canceled.
18990          * @param {Store} this
18991          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18992          */
18993         beforeload : true,
18994         /**
18995          * @event load
18996          * Fires after a new set of Records has been loaded.
18997          * @param {Store} this
18998          * @param {Roo.data.Record[]} records The Records that were loaded
18999          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19000          */
19001         load : true,
19002         /**
19003          * @event loadexception
19004          * Fires if an exception occurs in the Proxy during loading.
19005          * Called with the signature of the Proxy's "loadexception" event.
19006          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19007          * 
19008          * @param {Proxy} 
19009          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19010          * @param {Object} load options 
19011          * @param {Object} jsonData from your request (normally this contains the Exception)
19012          */
19013         loadexception : true
19014     });
19015     
19016     if(this.proxy){
19017         this.proxy = Roo.factory(this.proxy, Roo.data);
19018         this.proxy.xmodule = this.xmodule || false;
19019         this.relayEvents(this.proxy,  ["loadexception"]);
19020     }
19021     this.sortToggle = {};
19022
19023     Roo.data.Store.superclass.constructor.call(this);
19024
19025     if(this.inlineData){
19026         this.loadData(this.inlineData);
19027         delete this.inlineData;
19028     }
19029 };
19030 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19031      /**
19032     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19033     * without a remote query - used by combo/forms at present.
19034     */
19035     
19036     /**
19037     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19038     */
19039     /**
19040     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19041     */
19042     /**
19043     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19044     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19045     */
19046     /**
19047     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19048     * on any HTTP request
19049     */
19050     /**
19051     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19052     */
19053     /**
19054     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19055     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19056     */
19057     remoteSort : false,
19058
19059     /**
19060     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19061      * loaded or when a record is removed. (defaults to false).
19062     */
19063     pruneModifiedRecords : false,
19064
19065     // private
19066     lastOptions : null,
19067
19068     /**
19069      * Add Records to the Store and fires the add event.
19070      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19071      */
19072     add : function(records){
19073         records = [].concat(records);
19074         for(var i = 0, len = records.length; i < len; i++){
19075             records[i].join(this);
19076         }
19077         var index = this.data.length;
19078         this.data.addAll(records);
19079         this.fireEvent("add", this, records, index);
19080     },
19081
19082     /**
19083      * Remove a Record from the Store and fires the remove event.
19084      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19085      */
19086     remove : function(record){
19087         var index = this.data.indexOf(record);
19088         this.data.removeAt(index);
19089         if(this.pruneModifiedRecords){
19090             this.modified.remove(record);
19091         }
19092         this.fireEvent("remove", this, record, index);
19093     },
19094
19095     /**
19096      * Remove all Records from the Store and fires the clear event.
19097      */
19098     removeAll : function(){
19099         this.data.clear();
19100         if(this.pruneModifiedRecords){
19101             this.modified = [];
19102         }
19103         this.fireEvent("clear", this);
19104     },
19105
19106     /**
19107      * Inserts Records to the Store at the given index and fires the add event.
19108      * @param {Number} index The start index at which to insert the passed Records.
19109      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19110      */
19111     insert : function(index, records){
19112         records = [].concat(records);
19113         for(var i = 0, len = records.length; i < len; i++){
19114             this.data.insert(index, records[i]);
19115             records[i].join(this);
19116         }
19117         this.fireEvent("add", this, records, index);
19118     },
19119
19120     /**
19121      * Get the index within the cache of the passed Record.
19122      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19123      * @return {Number} The index of the passed Record. Returns -1 if not found.
19124      */
19125     indexOf : function(record){
19126         return this.data.indexOf(record);
19127     },
19128
19129     /**
19130      * Get the index within the cache of the Record with the passed id.
19131      * @param {String} id The id of the Record to find.
19132      * @return {Number} The index of the Record. Returns -1 if not found.
19133      */
19134     indexOfId : function(id){
19135         return this.data.indexOfKey(id);
19136     },
19137
19138     /**
19139      * Get the Record with the specified id.
19140      * @param {String} id The id of the Record to find.
19141      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19142      */
19143     getById : function(id){
19144         return this.data.key(id);
19145     },
19146
19147     /**
19148      * Get the Record at the specified index.
19149      * @param {Number} index The index of the Record to find.
19150      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19151      */
19152     getAt : function(index){
19153         return this.data.itemAt(index);
19154     },
19155
19156     /**
19157      * Returns a range of Records between specified indices.
19158      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19159      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19160      * @return {Roo.data.Record[]} An array of Records
19161      */
19162     getRange : function(start, end){
19163         return this.data.getRange(start, end);
19164     },
19165
19166     // private
19167     storeOptions : function(o){
19168         o = Roo.apply({}, o);
19169         delete o.callback;
19170         delete o.scope;
19171         this.lastOptions = o;
19172     },
19173
19174     /**
19175      * Loads the Record cache from the configured Proxy using the configured Reader.
19176      * <p>
19177      * If using remote paging, then the first load call must specify the <em>start</em>
19178      * and <em>limit</em> properties in the options.params property to establish the initial
19179      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19180      * <p>
19181      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19182      * and this call will return before the new data has been loaded. Perform any post-processing
19183      * in a callback function, or in a "load" event handler.</strong>
19184      * <p>
19185      * @param {Object} options An object containing properties which control loading options:<ul>
19186      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19187      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19188      * passed the following arguments:<ul>
19189      * <li>r : Roo.data.Record[]</li>
19190      * <li>options: Options object from the load call</li>
19191      * <li>success: Boolean success indicator</li></ul></li>
19192      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19193      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19194      * </ul>
19195      */
19196     load : function(options){
19197         options = options || {};
19198         if(this.fireEvent("beforeload", this, options) !== false){
19199             this.storeOptions(options);
19200             var p = Roo.apply(options.params || {}, this.baseParams);
19201             if(this.sortInfo && this.remoteSort){
19202                 var pn = this.paramNames;
19203                 p[pn["sort"]] = this.sortInfo.field;
19204                 p[pn["dir"]] = this.sortInfo.direction;
19205             }
19206             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19207         }
19208     },
19209
19210     /**
19211      * Reloads the Record cache from the configured Proxy using the configured Reader and
19212      * the options from the last load operation performed.
19213      * @param {Object} options (optional) An object containing properties which may override the options
19214      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19215      * the most recently used options are reused).
19216      */
19217     reload : function(options){
19218         this.load(Roo.applyIf(options||{}, this.lastOptions));
19219     },
19220
19221     // private
19222     // Called as a callback by the Reader during a load operation.
19223     loadRecords : function(o, options, success){
19224         if(!o || success === false){
19225             if(success !== false){
19226                 this.fireEvent("load", this, [], options);
19227             }
19228             if(options.callback){
19229                 options.callback.call(options.scope || this, [], options, false);
19230             }
19231             return;
19232         }
19233         // if data returned failure - throw an exception.
19234         if (o.success === false) {
19235             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19236             return;
19237         }
19238         var r = o.records, t = o.totalRecords || r.length;
19239         if(!options || options.add !== true){
19240             if(this.pruneModifiedRecords){
19241                 this.modified = [];
19242             }
19243             for(var i = 0, len = r.length; i < len; i++){
19244                 r[i].join(this);
19245             }
19246             if(this.snapshot){
19247                 this.data = this.snapshot;
19248                 delete this.snapshot;
19249             }
19250             this.data.clear();
19251             this.data.addAll(r);
19252             this.totalLength = t;
19253             this.applySort();
19254             this.fireEvent("datachanged", this);
19255         }else{
19256             this.totalLength = Math.max(t, this.data.length+r.length);
19257             this.add(r);
19258         }
19259         this.fireEvent("load", this, r, options);
19260         if(options.callback){
19261             options.callback.call(options.scope || this, r, options, true);
19262         }
19263     },
19264
19265     /**
19266      * Loads data from a passed data block. A Reader which understands the format of the data
19267      * must have been configured in the constructor.
19268      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19269      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19270      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19271      */
19272     loadData : function(o, append){
19273         var r = this.reader.readRecords(o);
19274         this.loadRecords(r, {add: append}, true);
19275     },
19276
19277     /**
19278      * Gets the number of cached records.
19279      * <p>
19280      * <em>If using paging, this may not be the total size of the dataset. If the data object
19281      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19282      * the data set size</em>
19283      */
19284     getCount : function(){
19285         return this.data.length || 0;
19286     },
19287
19288     /**
19289      * Gets the total number of records in the dataset as returned by the server.
19290      * <p>
19291      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19292      * the dataset size</em>
19293      */
19294     getTotalCount : function(){
19295         return this.totalLength || 0;
19296     },
19297
19298     /**
19299      * Returns the sort state of the Store as an object with two properties:
19300      * <pre><code>
19301  field {String} The name of the field by which the Records are sorted
19302  direction {String} The sort order, "ASC" or "DESC"
19303      * </code></pre>
19304      */
19305     getSortState : function(){
19306         return this.sortInfo;
19307     },
19308
19309     // private
19310     applySort : function(){
19311         if(this.sortInfo && !this.remoteSort){
19312             var s = this.sortInfo, f = s.field;
19313             var st = this.fields.get(f).sortType;
19314             var fn = function(r1, r2){
19315                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19316                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19317             };
19318             this.data.sort(s.direction, fn);
19319             if(this.snapshot && this.snapshot != this.data){
19320                 this.snapshot.sort(s.direction, fn);
19321             }
19322         }
19323     },
19324
19325     /**
19326      * Sets the default sort column and order to be used by the next load operation.
19327      * @param {String} fieldName The name of the field to sort by.
19328      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19329      */
19330     setDefaultSort : function(field, dir){
19331         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19332     },
19333
19334     /**
19335      * Sort the Records.
19336      * If remote sorting is used, the sort is performed on the server, and the cache is
19337      * reloaded. If local sorting is used, the cache is sorted internally.
19338      * @param {String} fieldName The name of the field to sort by.
19339      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19340      */
19341     sort : function(fieldName, dir){
19342         var f = this.fields.get(fieldName);
19343         if(!dir){
19344             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19345                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19346             }else{
19347                 dir = f.sortDir;
19348             }
19349         }
19350         this.sortToggle[f.name] = dir;
19351         this.sortInfo = {field: f.name, direction: dir};
19352         if(!this.remoteSort){
19353             this.applySort();
19354             this.fireEvent("datachanged", this);
19355         }else{
19356             this.load(this.lastOptions);
19357         }
19358     },
19359
19360     /**
19361      * Calls the specified function for each of the Records in the cache.
19362      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19363      * Returning <em>false</em> aborts and exits the iteration.
19364      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19365      */
19366     each : function(fn, scope){
19367         this.data.each(fn, scope);
19368     },
19369
19370     /**
19371      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19372      * (e.g., during paging).
19373      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19374      */
19375     getModifiedRecords : function(){
19376         return this.modified;
19377     },
19378
19379     // private
19380     createFilterFn : function(property, value, anyMatch){
19381         if(!value.exec){ // not a regex
19382             value = String(value);
19383             if(value.length == 0){
19384                 return false;
19385             }
19386             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19387         }
19388         return function(r){
19389             return value.test(r.data[property]);
19390         };
19391     },
19392
19393     /**
19394      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19395      * @param {String} property A field on your records
19396      * @param {Number} start The record index to start at (defaults to 0)
19397      * @param {Number} end The last record index to include (defaults to length - 1)
19398      * @return {Number} The sum
19399      */
19400     sum : function(property, start, end){
19401         var rs = this.data.items, v = 0;
19402         start = start || 0;
19403         end = (end || end === 0) ? end : rs.length-1;
19404
19405         for(var i = start; i <= end; i++){
19406             v += (rs[i].data[property] || 0);
19407         }
19408         return v;
19409     },
19410
19411     /**
19412      * Filter the records by a specified property.
19413      * @param {String} field A field on your records
19414      * @param {String/RegExp} value Either a string that the field
19415      * should start with or a RegExp to test against the field
19416      * @param {Boolean} anyMatch True to match any part not just the beginning
19417      */
19418     filter : function(property, value, anyMatch){
19419         var fn = this.createFilterFn(property, value, anyMatch);
19420         return fn ? this.filterBy(fn) : this.clearFilter();
19421     },
19422
19423     /**
19424      * Filter by a function. The specified function will be called with each
19425      * record in this data source. If the function returns true the record is included,
19426      * otherwise it is filtered.
19427      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19428      * @param {Object} scope (optional) The scope of the function (defaults to this)
19429      */
19430     filterBy : function(fn, scope){
19431         this.snapshot = this.snapshot || this.data;
19432         this.data = this.queryBy(fn, scope||this);
19433         this.fireEvent("datachanged", this);
19434     },
19435
19436     /**
19437      * Query the records by a specified property.
19438      * @param {String} field A field on your records
19439      * @param {String/RegExp} value Either a string that the field
19440      * should start with or a RegExp to test against the field
19441      * @param {Boolean} anyMatch True to match any part not just the beginning
19442      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19443      */
19444     query : function(property, value, anyMatch){
19445         var fn = this.createFilterFn(property, value, anyMatch);
19446         return fn ? this.queryBy(fn) : this.data.clone();
19447     },
19448
19449     /**
19450      * Query by a function. The specified function will be called with each
19451      * record in this data source. If the function returns true the record is included
19452      * in the results.
19453      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19454      * @param {Object} scope (optional) The scope of the function (defaults to this)
19455       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19456      **/
19457     queryBy : function(fn, scope){
19458         var data = this.snapshot || this.data;
19459         return data.filterBy(fn, scope||this);
19460     },
19461
19462     /**
19463      * Collects unique values for a particular dataIndex from this store.
19464      * @param {String} dataIndex The property to collect
19465      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19466      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19467      * @return {Array} An array of the unique values
19468      **/
19469     collect : function(dataIndex, allowNull, bypassFilter){
19470         var d = (bypassFilter === true && this.snapshot) ?
19471                 this.snapshot.items : this.data.items;
19472         var v, sv, r = [], l = {};
19473         for(var i = 0, len = d.length; i < len; i++){
19474             v = d[i].data[dataIndex];
19475             sv = String(v);
19476             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19477                 l[sv] = true;
19478                 r[r.length] = v;
19479             }
19480         }
19481         return r;
19482     },
19483
19484     /**
19485      * Revert to a view of the Record cache with no filtering applied.
19486      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19487      */
19488     clearFilter : function(suppressEvent){
19489         if(this.snapshot && this.snapshot != this.data){
19490             this.data = this.snapshot;
19491             delete this.snapshot;
19492             if(suppressEvent !== true){
19493                 this.fireEvent("datachanged", this);
19494             }
19495         }
19496     },
19497
19498     // private
19499     afterEdit : function(record){
19500         if(this.modified.indexOf(record) == -1){
19501             this.modified.push(record);
19502         }
19503         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19504     },
19505
19506     // private
19507     afterReject : function(record){
19508         this.modified.remove(record);
19509         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19510     },
19511
19512     // private
19513     afterCommit : function(record){
19514         this.modified.remove(record);
19515         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19516     },
19517
19518     /**
19519      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19520      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19521      */
19522     commitChanges : function(){
19523         var m = this.modified.slice(0);
19524         this.modified = [];
19525         for(var i = 0, len = m.length; i < len; i++){
19526             m[i].commit();
19527         }
19528     },
19529
19530     /**
19531      * Cancel outstanding changes on all changed records.
19532      */
19533     rejectChanges : function(){
19534         var m = this.modified.slice(0);
19535         this.modified = [];
19536         for(var i = 0, len = m.length; i < len; i++){
19537             m[i].reject();
19538         }
19539     },
19540
19541     onMetaChange : function(meta, rtype, o){
19542         this.recordType = rtype;
19543         this.fields = rtype.prototype.fields;
19544         delete this.snapshot;
19545         this.sortInfo = meta.sortInfo;
19546         this.modified = [];
19547         this.fireEvent('metachange', this, this.reader.meta);
19548     }
19549 });/*
19550  * Based on:
19551  * Ext JS Library 1.1.1
19552  * Copyright(c) 2006-2007, Ext JS, LLC.
19553  *
19554  * Originally Released Under LGPL - original licence link has changed is not relivant.
19555  *
19556  * Fork - LGPL
19557  * <script type="text/javascript">
19558  */
19559
19560 /**
19561  * @class Roo.data.SimpleStore
19562  * @extends Roo.data.Store
19563  * Small helper class to make creating Stores from Array data easier.
19564  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19565  * @cfg {Array} fields An array of field definition objects, or field name strings.
19566  * @cfg {Array} data The multi-dimensional array of data
19567  * @constructor
19568  * @param {Object} config
19569  */
19570 Roo.data.SimpleStore = function(config){
19571     Roo.data.SimpleStore.superclass.constructor.call(this, {
19572         isLocal : true,
19573         reader: new Roo.data.ArrayReader({
19574                 id: config.id
19575             },
19576             Roo.data.Record.create(config.fields)
19577         ),
19578         proxy : new Roo.data.MemoryProxy(config.data)
19579     });
19580     this.load();
19581 };
19582 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19583  * Based on:
19584  * Ext JS Library 1.1.1
19585  * Copyright(c) 2006-2007, Ext JS, LLC.
19586  *
19587  * Originally Released Under LGPL - original licence link has changed is not relivant.
19588  *
19589  * Fork - LGPL
19590  * <script type="text/javascript">
19591  */
19592
19593 /**
19594 /**
19595  * @extends Roo.data.Store
19596  * @class Roo.data.JsonStore
19597  * Small helper class to make creating Stores for JSON data easier. <br/>
19598 <pre><code>
19599 var store = new Roo.data.JsonStore({
19600     url: 'get-images.php',
19601     root: 'images',
19602     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19603 });
19604 </code></pre>
19605  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19606  * JsonReader and HttpProxy (unless inline data is provided).</b>
19607  * @cfg {Array} fields An array of field definition objects, or field name strings.
19608  * @constructor
19609  * @param {Object} config
19610  */
19611 Roo.data.JsonStore = function(c){
19612     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19613         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19614         reader: new Roo.data.JsonReader(c, c.fields)
19615     }));
19616 };
19617 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19618  * Based on:
19619  * Ext JS Library 1.1.1
19620  * Copyright(c) 2006-2007, Ext JS, LLC.
19621  *
19622  * Originally Released Under LGPL - original licence link has changed is not relivant.
19623  *
19624  * Fork - LGPL
19625  * <script type="text/javascript">
19626  */
19627
19628  
19629 Roo.data.Field = function(config){
19630     if(typeof config == "string"){
19631         config = {name: config};
19632     }
19633     Roo.apply(this, config);
19634     
19635     if(!this.type){
19636         this.type = "auto";
19637     }
19638     
19639     var st = Roo.data.SortTypes;
19640     // named sortTypes are supported, here we look them up
19641     if(typeof this.sortType == "string"){
19642         this.sortType = st[this.sortType];
19643     }
19644     
19645     // set default sortType for strings and dates
19646     if(!this.sortType){
19647         switch(this.type){
19648             case "string":
19649                 this.sortType = st.asUCString;
19650                 break;
19651             case "date":
19652                 this.sortType = st.asDate;
19653                 break;
19654             default:
19655                 this.sortType = st.none;
19656         }
19657     }
19658
19659     // define once
19660     var stripRe = /[\$,%]/g;
19661
19662     // prebuilt conversion function for this field, instead of
19663     // switching every time we're reading a value
19664     if(!this.convert){
19665         var cv, dateFormat = this.dateFormat;
19666         switch(this.type){
19667             case "":
19668             case "auto":
19669             case undefined:
19670                 cv = function(v){ return v; };
19671                 break;
19672             case "string":
19673                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19674                 break;
19675             case "int":
19676                 cv = function(v){
19677                     return v !== undefined && v !== null && v !== '' ?
19678                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19679                     };
19680                 break;
19681             case "float":
19682                 cv = function(v){
19683                     return v !== undefined && v !== null && v !== '' ?
19684                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19685                     };
19686                 break;
19687             case "bool":
19688             case "boolean":
19689                 cv = function(v){ return v === true || v === "true" || v == 1; };
19690                 break;
19691             case "date":
19692                 cv = function(v){
19693                     if(!v){
19694                         return '';
19695                     }
19696                     if(v instanceof Date){
19697                         return v;
19698                     }
19699                     if(dateFormat){
19700                         if(dateFormat == "timestamp"){
19701                             return new Date(v*1000);
19702                         }
19703                         return Date.parseDate(v, dateFormat);
19704                     }
19705                     var parsed = Date.parse(v);
19706                     return parsed ? new Date(parsed) : null;
19707                 };
19708              break;
19709             
19710         }
19711         this.convert = cv;
19712     }
19713 };
19714
19715 Roo.data.Field.prototype = {
19716     dateFormat: null,
19717     defaultValue: "",
19718     mapping: null,
19719     sortType : null,
19720     sortDir : "ASC"
19721 };/*
19722  * Based on:
19723  * Ext JS Library 1.1.1
19724  * Copyright(c) 2006-2007, Ext JS, LLC.
19725  *
19726  * Originally Released Under LGPL - original licence link has changed is not relivant.
19727  *
19728  * Fork - LGPL
19729  * <script type="text/javascript">
19730  */
19731  
19732 // Base class for reading structured data from a data source.  This class is intended to be
19733 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19734
19735 /**
19736  * @class Roo.data.DataReader
19737  * Base class for reading structured data from a data source.  This class is intended to be
19738  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19739  */
19740
19741 Roo.data.DataReader = function(meta, recordType){
19742     
19743     this.meta = meta;
19744     
19745     this.recordType = recordType instanceof Array ? 
19746         Roo.data.Record.create(recordType) : recordType;
19747 };
19748
19749 Roo.data.DataReader.prototype = {
19750      /**
19751      * Create an empty record
19752      * @param {Object} data (optional) - overlay some values
19753      * @return {Roo.data.Record} record created.
19754      */
19755     newRow :  function(d) {
19756         var da =  {};
19757         this.recordType.prototype.fields.each(function(c) {
19758             switch( c.type) {
19759                 case 'int' : da[c.name] = 0; break;
19760                 case 'date' : da[c.name] = new Date(); break;
19761                 case 'float' : da[c.name] = 0.0; break;
19762                 case 'boolean' : da[c.name] = false; break;
19763                 default : da[c.name] = ""; break;
19764             }
19765             
19766         });
19767         return new this.recordType(Roo.apply(da, d));
19768     }
19769     
19770 };/*
19771  * Based on:
19772  * Ext JS Library 1.1.1
19773  * Copyright(c) 2006-2007, Ext JS, LLC.
19774  *
19775  * Originally Released Under LGPL - original licence link has changed is not relivant.
19776  *
19777  * Fork - LGPL
19778  * <script type="text/javascript">
19779  */
19780
19781 /**
19782  * @class Roo.data.DataProxy
19783  * @extends Roo.data.Observable
19784  * This class is an abstract base class for implementations which provide retrieval of
19785  * unformatted data objects.<br>
19786  * <p>
19787  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19788  * (of the appropriate type which knows how to parse the data object) to provide a block of
19789  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19790  * <p>
19791  * Custom implementations must implement the load method as described in
19792  * {@link Roo.data.HttpProxy#load}.
19793  */
19794 Roo.data.DataProxy = function(){
19795     this.addEvents({
19796         /**
19797          * @event beforeload
19798          * Fires before a network request is made to retrieve a data object.
19799          * @param {Object} This DataProxy object.
19800          * @param {Object} params The params parameter to the load function.
19801          */
19802         beforeload : true,
19803         /**
19804          * @event load
19805          * Fires before the load method's callback is called.
19806          * @param {Object} This DataProxy object.
19807          * @param {Object} o The data object.
19808          * @param {Object} arg The callback argument object passed to the load function.
19809          */
19810         load : true,
19811         /**
19812          * @event loadexception
19813          * Fires if an Exception occurs during data retrieval.
19814          * @param {Object} This DataProxy object.
19815          * @param {Object} o The data object.
19816          * @param {Object} arg The callback argument object passed to the load function.
19817          * @param {Object} e The Exception.
19818          */
19819         loadexception : true
19820     });
19821     Roo.data.DataProxy.superclass.constructor.call(this);
19822 };
19823
19824 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19825
19826     /**
19827      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19828      */
19829 /*
19830  * Based on:
19831  * Ext JS Library 1.1.1
19832  * Copyright(c) 2006-2007, Ext JS, LLC.
19833  *
19834  * Originally Released Under LGPL - original licence link has changed is not relivant.
19835  *
19836  * Fork - LGPL
19837  * <script type="text/javascript">
19838  */
19839 /**
19840  * @class Roo.data.MemoryProxy
19841  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19842  * to the Reader when its load method is called.
19843  * @constructor
19844  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19845  */
19846 Roo.data.MemoryProxy = function(data){
19847     if (data.data) {
19848         data = data.data;
19849     }
19850     Roo.data.MemoryProxy.superclass.constructor.call(this);
19851     this.data = data;
19852 };
19853
19854 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19855     /**
19856      * Load data from the requested source (in this case an in-memory
19857      * data object passed to the constructor), read the data object into
19858      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19859      * process that block using the passed callback.
19860      * @param {Object} params This parameter is not used by the MemoryProxy class.
19861      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19862      * object into a block of Roo.data.Records.
19863      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19864      * The function must be passed <ul>
19865      * <li>The Record block object</li>
19866      * <li>The "arg" argument from the load function</li>
19867      * <li>A boolean success indicator</li>
19868      * </ul>
19869      * @param {Object} scope The scope in which to call the callback
19870      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19871      */
19872     load : function(params, reader, callback, scope, arg){
19873         params = params || {};
19874         var result;
19875         try {
19876             result = reader.readRecords(this.data);
19877         }catch(e){
19878             this.fireEvent("loadexception", this, arg, null, e);
19879             callback.call(scope, null, arg, false);
19880             return;
19881         }
19882         callback.call(scope, result, arg, true);
19883     },
19884     
19885     // private
19886     update : function(params, records){
19887         
19888     }
19889 });/*
19890  * Based on:
19891  * Ext JS Library 1.1.1
19892  * Copyright(c) 2006-2007, Ext JS, LLC.
19893  *
19894  * Originally Released Under LGPL - original licence link has changed is not relivant.
19895  *
19896  * Fork - LGPL
19897  * <script type="text/javascript">
19898  */
19899 /**
19900  * @class Roo.data.HttpProxy
19901  * @extends Roo.data.DataProxy
19902  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19903  * configured to reference a certain URL.<br><br>
19904  * <p>
19905  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19906  * from which the running page was served.<br><br>
19907  * <p>
19908  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19909  * <p>
19910  * Be aware that to enable the browser to parse an XML document, the server must set
19911  * the Content-Type header in the HTTP response to "text/xml".
19912  * @constructor
19913  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19914  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19915  * will be used to make the request.
19916  */
19917 Roo.data.HttpProxy = function(conn){
19918     Roo.data.HttpProxy.superclass.constructor.call(this);
19919     // is conn a conn config or a real conn?
19920     this.conn = conn;
19921     this.useAjax = !conn || !conn.events;
19922   
19923 };
19924
19925 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19926     // thse are take from connection...
19927     
19928     /**
19929      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19930      */
19931     /**
19932      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19933      * extra parameters to each request made by this object. (defaults to undefined)
19934      */
19935     /**
19936      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19937      *  to each request made by this object. (defaults to undefined)
19938      */
19939     /**
19940      * @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)
19941      */
19942     /**
19943      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19944      */
19945      /**
19946      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19947      * @type Boolean
19948      */
19949   
19950
19951     /**
19952      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19953      * @type Boolean
19954      */
19955     /**
19956      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19957      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19958      * a finer-grained basis than the DataProxy events.
19959      */
19960     getConnection : function(){
19961         return this.useAjax ? Roo.Ajax : this.conn;
19962     },
19963
19964     /**
19965      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19966      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19967      * process that block using the passed callback.
19968      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19969      * for the request to the remote server.
19970      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19971      * object into a block of Roo.data.Records.
19972      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19973      * The function must be passed <ul>
19974      * <li>The Record block object</li>
19975      * <li>The "arg" argument from the load function</li>
19976      * <li>A boolean success indicator</li>
19977      * </ul>
19978      * @param {Object} scope The scope in which to call the callback
19979      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19980      */
19981     load : function(params, reader, callback, scope, arg){
19982         if(this.fireEvent("beforeload", this, params) !== false){
19983             var  o = {
19984                 params : params || {},
19985                 request: {
19986                     callback : callback,
19987                     scope : scope,
19988                     arg : arg
19989                 },
19990                 reader: reader,
19991                 callback : this.loadResponse,
19992                 scope: this
19993             };
19994             if(this.useAjax){
19995                 Roo.applyIf(o, this.conn);
19996                 if(this.activeRequest){
19997                     Roo.Ajax.abort(this.activeRequest);
19998                 }
19999                 this.activeRequest = Roo.Ajax.request(o);
20000             }else{
20001                 this.conn.request(o);
20002             }
20003         }else{
20004             callback.call(scope||this, null, arg, false);
20005         }
20006     },
20007
20008     // private
20009     loadResponse : function(o, success, response){
20010         delete this.activeRequest;
20011         if(!success){
20012             this.fireEvent("loadexception", this, o, response);
20013             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20014             return;
20015         }
20016         var result;
20017         try {
20018             result = o.reader.read(response);
20019         }catch(e){
20020             this.fireEvent("loadexception", this, o, response, e);
20021             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20022             return;
20023         }
20024         
20025         this.fireEvent("load", this, o, o.request.arg);
20026         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20027     },
20028
20029     // private
20030     update : function(dataSet){
20031
20032     },
20033
20034     // private
20035     updateResponse : function(dataSet){
20036
20037     }
20038 });/*
20039  * Based on:
20040  * Ext JS Library 1.1.1
20041  * Copyright(c) 2006-2007, Ext JS, LLC.
20042  *
20043  * Originally Released Under LGPL - original licence link has changed is not relivant.
20044  *
20045  * Fork - LGPL
20046  * <script type="text/javascript">
20047  */
20048
20049 /**
20050  * @class Roo.data.ScriptTagProxy
20051  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20052  * other than the originating domain of the running page.<br><br>
20053  * <p>
20054  * <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
20055  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20056  * <p>
20057  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20058  * source code that is used as the source inside a &lt;script> tag.<br><br>
20059  * <p>
20060  * In order for the browser to process the returned data, the server must wrap the data object
20061  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20062  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20063  * depending on whether the callback name was passed:
20064  * <p>
20065  * <pre><code>
20066 boolean scriptTag = false;
20067 String cb = request.getParameter("callback");
20068 if (cb != null) {
20069     scriptTag = true;
20070     response.setContentType("text/javascript");
20071 } else {
20072     response.setContentType("application/x-json");
20073 }
20074 Writer out = response.getWriter();
20075 if (scriptTag) {
20076     out.write(cb + "(");
20077 }
20078 out.print(dataBlock.toJsonString());
20079 if (scriptTag) {
20080     out.write(");");
20081 }
20082 </pre></code>
20083  *
20084  * @constructor
20085  * @param {Object} config A configuration object.
20086  */
20087 Roo.data.ScriptTagProxy = function(config){
20088     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20089     Roo.apply(this, config);
20090     this.head = document.getElementsByTagName("head")[0];
20091 };
20092
20093 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20094
20095 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20096     /**
20097      * @cfg {String} url The URL from which to request the data object.
20098      */
20099     /**
20100      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20101      */
20102     timeout : 30000,
20103     /**
20104      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20105      * the server the name of the callback function set up by the load call to process the returned data object.
20106      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20107      * javascript output which calls this named function passing the data object as its only parameter.
20108      */
20109     callbackParam : "callback",
20110     /**
20111      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20112      * name to the request.
20113      */
20114     nocache : true,
20115
20116     /**
20117      * Load data from the configured URL, read the data object into
20118      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20119      * process that block using the passed callback.
20120      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20121      * for the request to the remote server.
20122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20123      * object into a block of Roo.data.Records.
20124      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20125      * The function must be passed <ul>
20126      * <li>The Record block object</li>
20127      * <li>The "arg" argument from the load function</li>
20128      * <li>A boolean success indicator</li>
20129      * </ul>
20130      * @param {Object} scope The scope in which to call the callback
20131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20132      */
20133     load : function(params, reader, callback, scope, arg){
20134         if(this.fireEvent("beforeload", this, params) !== false){
20135
20136             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20137
20138             var url = this.url;
20139             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20140             if(this.nocache){
20141                 url += "&_dc=" + (new Date().getTime());
20142             }
20143             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20144             var trans = {
20145                 id : transId,
20146                 cb : "stcCallback"+transId,
20147                 scriptId : "stcScript"+transId,
20148                 params : params,
20149                 arg : arg,
20150                 url : url,
20151                 callback : callback,
20152                 scope : scope,
20153                 reader : reader
20154             };
20155             var conn = this;
20156
20157             window[trans.cb] = function(o){
20158                 conn.handleResponse(o, trans);
20159             };
20160
20161             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20162
20163             if(this.autoAbort !== false){
20164                 this.abort();
20165             }
20166
20167             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20168
20169             var script = document.createElement("script");
20170             script.setAttribute("src", url);
20171             script.setAttribute("type", "text/javascript");
20172             script.setAttribute("id", trans.scriptId);
20173             this.head.appendChild(script);
20174
20175             this.trans = trans;
20176         }else{
20177             callback.call(scope||this, null, arg, false);
20178         }
20179     },
20180
20181     // private
20182     isLoading : function(){
20183         return this.trans ? true : false;
20184     },
20185
20186     /**
20187      * Abort the current server request.
20188      */
20189     abort : function(){
20190         if(this.isLoading()){
20191             this.destroyTrans(this.trans);
20192         }
20193     },
20194
20195     // private
20196     destroyTrans : function(trans, isLoaded){
20197         this.head.removeChild(document.getElementById(trans.scriptId));
20198         clearTimeout(trans.timeoutId);
20199         if(isLoaded){
20200             window[trans.cb] = undefined;
20201             try{
20202                 delete window[trans.cb];
20203             }catch(e){}
20204         }else{
20205             // if hasn't been loaded, wait for load to remove it to prevent script error
20206             window[trans.cb] = function(){
20207                 window[trans.cb] = undefined;
20208                 try{
20209                     delete window[trans.cb];
20210                 }catch(e){}
20211             };
20212         }
20213     },
20214
20215     // private
20216     handleResponse : function(o, trans){
20217         this.trans = false;
20218         this.destroyTrans(trans, true);
20219         var result;
20220         try {
20221             result = trans.reader.readRecords(o);
20222         }catch(e){
20223             this.fireEvent("loadexception", this, o, trans.arg, e);
20224             trans.callback.call(trans.scope||window, null, trans.arg, false);
20225             return;
20226         }
20227         this.fireEvent("load", this, o, trans.arg);
20228         trans.callback.call(trans.scope||window, result, trans.arg, true);
20229     },
20230
20231     // private
20232     handleFailure : function(trans){
20233         this.trans = false;
20234         this.destroyTrans(trans, false);
20235         this.fireEvent("loadexception", this, null, trans.arg);
20236         trans.callback.call(trans.scope||window, null, trans.arg, false);
20237     }
20238 });/*
20239  * Based on:
20240  * Ext JS Library 1.1.1
20241  * Copyright(c) 2006-2007, Ext JS, LLC.
20242  *
20243  * Originally Released Under LGPL - original licence link has changed is not relivant.
20244  *
20245  * Fork - LGPL
20246  * <script type="text/javascript">
20247  */
20248
20249 /**
20250  * @class Roo.data.JsonReader
20251  * @extends Roo.data.DataReader
20252  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20253  * based on mappings in a provided Roo.data.Record constructor.
20254  * <p>
20255  * Example code:
20256  * <pre><code>
20257 var RecordDef = Roo.data.Record.create([
20258     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20259     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20260 ]);
20261 var myReader = new Roo.data.JsonReader({
20262     totalProperty: "results",    // The property which contains the total dataset size (optional)
20263     root: "rows",                // The property which contains an Array of row objects
20264     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20265 }, RecordDef);
20266 </code></pre>
20267  * <p>
20268  * This would consume a JSON file like this:
20269  * <pre><code>
20270 { 'results': 2, 'rows': [
20271     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20272     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20273 }
20274 </code></pre>
20275  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20276  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20277  * paged from the remote server.
20278  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20279  * @cfg {String} root name of the property which contains the Array of row objects.
20280  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20281  * @constructor
20282  * Create a new JsonReader
20283  * @param {Object} meta Metadata configuration options
20284  * @param {Object} recordType Either an Array of field definition objects,
20285  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20286  */
20287 Roo.data.JsonReader = function(meta, recordType){
20288     
20289     meta = meta || {};
20290     // set some defaults:
20291     Roo.applyIf(meta, {
20292         totalProperty: 'total',
20293         successProperty : 'success',
20294         root : 'data',
20295         id : 'id'
20296     });
20297     
20298     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20299 };
20300 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20301     /**
20302      * This method is only used by a DataProxy which has retrieved data from a remote server.
20303      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20304      * @return {Object} data A data block which is used by an Roo.data.Store object as
20305      * a cache of Roo.data.Records.
20306      */
20307     read : function(response){
20308         var json = response.responseText;
20309         /* eval:var:o */
20310         var o = eval("("+json+")");
20311         if(!o) {
20312             throw {message: "JsonReader.read: Json object not found"};
20313         }
20314         
20315         if(o.metaData){
20316             delete this.ef;
20317             this.meta = o.metaData;
20318             this.recordType = Roo.data.Record.create(o.metaData.fields);
20319             this.onMetaChange(this.meta, this.recordType, o);
20320         }
20321         return this.readRecords(o);
20322     },
20323
20324     // private function a store will implement
20325     onMetaChange : function(meta, recordType, o){
20326
20327     },
20328
20329     /**
20330          * @ignore
20331          */
20332     simpleAccess: function(obj, subsc) {
20333         return obj[subsc];
20334     },
20335
20336         /**
20337          * @ignore
20338          */
20339     getJsonAccessor: function(){
20340         var re = /[\[\.]/;
20341         return function(expr) {
20342             try {
20343                 return(re.test(expr))
20344                     ? new Function("obj", "return obj." + expr)
20345                     : function(obj){
20346                         return obj[expr];
20347                     };
20348             } catch(e){}
20349             return Roo.emptyFn;
20350         };
20351     }(),
20352
20353     /**
20354      * Create a data block containing Roo.data.Records from an XML document.
20355      * @param {Object} o An object which contains an Array of row objects in the property specified
20356      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20357      * which contains the total size of the dataset.
20358      * @return {Object} data A data block which is used by an Roo.data.Store object as
20359      * a cache of Roo.data.Records.
20360      */
20361     readRecords : function(o){
20362         /**
20363          * After any data loads, the raw JSON data is available for further custom processing.
20364          * @type Object
20365          */
20366         this.jsonData = o;
20367         var s = this.meta, Record = this.recordType,
20368             f = Record.prototype.fields, fi = f.items, fl = f.length;
20369
20370 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20371         if (!this.ef) {
20372             if(s.totalProperty) {
20373                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20374                 }
20375                 if(s.successProperty) {
20376                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20377                 }
20378                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20379                 if (s.id) {
20380                         var g = this.getJsonAccessor(s.id);
20381                         this.getId = function(rec) {
20382                                 var r = g(rec);
20383                                 return (r === undefined || r === "") ? null : r;
20384                         };
20385                 } else {
20386                         this.getId = function(){return null;};
20387                 }
20388             this.ef = [];
20389             for(var i = 0; i < fl; i++){
20390                 f = fi[i];
20391                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20392                 this.ef[i] = this.getJsonAccessor(map);
20393             }
20394         }
20395
20396         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20397         if(s.totalProperty){
20398             var v = parseInt(this.getTotal(o), 10);
20399             if(!isNaN(v)){
20400                 totalRecords = v;
20401             }
20402         }
20403         if(s.successProperty){
20404             var v = this.getSuccess(o);
20405             if(v === false || v === 'false'){
20406                 success = false;
20407             }
20408         }
20409         var records = [];
20410             for(var i = 0; i < c; i++){
20411                     var n = root[i];
20412                 var values = {};
20413                 var id = this.getId(n);
20414                 for(var j = 0; j < fl; j++){
20415                     f = fi[j];
20416                 var v = this.ef[j](n);
20417                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20418                 }
20419                 var record = new Record(values, id);
20420                 record.json = n;
20421                 records[i] = record;
20422             }
20423             return {
20424                 success : success,
20425                 records : records,
20426                 totalRecords : totalRecords
20427             };
20428     }
20429 });/*
20430  * Based on:
20431  * Ext JS Library 1.1.1
20432  * Copyright(c) 2006-2007, Ext JS, LLC.
20433  *
20434  * Originally Released Under LGPL - original licence link has changed is not relivant.
20435  *
20436  * Fork - LGPL
20437  * <script type="text/javascript">
20438  */
20439
20440 /**
20441  * @class Roo.data.XmlReader
20442  * @extends Roo.data.DataReader
20443  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20444  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20445  * <p>
20446  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20447  * header in the HTTP response must be set to "text/xml".</em>
20448  * <p>
20449  * Example code:
20450  * <pre><code>
20451 var RecordDef = Roo.data.Record.create([
20452    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20453    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20454 ]);
20455 var myReader = new Roo.data.XmlReader({
20456    totalRecords: "results", // The element which contains the total dataset size (optional)
20457    record: "row",           // The repeated element which contains row information
20458    id: "id"                 // The element within the row that provides an ID for the record (optional)
20459 }, RecordDef);
20460 </code></pre>
20461  * <p>
20462  * This would consume an XML file like this:
20463  * <pre><code>
20464 &lt;?xml?>
20465 &lt;dataset>
20466  &lt;results>2&lt;/results>
20467  &lt;row>
20468    &lt;id>1&lt;/id>
20469    &lt;name>Bill&lt;/name>
20470    &lt;occupation>Gardener&lt;/occupation>
20471  &lt;/row>
20472  &lt;row>
20473    &lt;id>2&lt;/id>
20474    &lt;name>Ben&lt;/name>
20475    &lt;occupation>Horticulturalist&lt;/occupation>
20476  &lt;/row>
20477 &lt;/dataset>
20478 </code></pre>
20479  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20480  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20481  * paged from the remote server.
20482  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20483  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20484  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20485  * a record identifier value.
20486  * @constructor
20487  * Create a new XmlReader
20488  * @param {Object} meta Metadata configuration options
20489  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20490  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20491  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20492  */
20493 Roo.data.XmlReader = function(meta, recordType){
20494     meta = meta || {};
20495     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20496 };
20497 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20498     /**
20499      * This method is only used by a DataProxy which has retrieved data from a remote server.
20500          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20501          * to contain a method called 'responseXML' that returns an XML document object.
20502      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20503      * a cache of Roo.data.Records.
20504      */
20505     read : function(response){
20506         var doc = response.responseXML;
20507         if(!doc) {
20508             throw {message: "XmlReader.read: XML Document not available"};
20509         }
20510         return this.readRecords(doc);
20511     },
20512
20513     /**
20514      * Create a data block containing Roo.data.Records from an XML document.
20515          * @param {Object} doc A parsed XML document.
20516      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20517      * a cache of Roo.data.Records.
20518      */
20519     readRecords : function(doc){
20520         /**
20521          * After any data loads/reads, the raw XML Document is available for further custom processing.
20522          * @type XMLDocument
20523          */
20524         this.xmlData = doc;
20525         var root = doc.documentElement || doc;
20526         var q = Roo.DomQuery;
20527         var recordType = this.recordType, fields = recordType.prototype.fields;
20528         var sid = this.meta.id;
20529         var totalRecords = 0, success = true;
20530         if(this.meta.totalRecords){
20531             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20532         }
20533         
20534         if(this.meta.success){
20535             var sv = q.selectValue(this.meta.success, root, true);
20536             success = sv !== false && sv !== 'false';
20537         }
20538         var records = [];
20539         var ns = q.select(this.meta.record, root);
20540         for(var i = 0, len = ns.length; i < len; i++) {
20541                 var n = ns[i];
20542                 var values = {};
20543                 var id = sid ? q.selectValue(sid, n) : undefined;
20544                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20545                     var f = fields.items[j];
20546                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20547                     v = f.convert(v);
20548                     values[f.name] = v;
20549                 }
20550                 var record = new recordType(values, id);
20551                 record.node = n;
20552                 records[records.length] = record;
20553             }
20554
20555             return {
20556                 success : success,
20557                 records : records,
20558                 totalRecords : totalRecords || records.length
20559             };
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572 /**
20573  * @class Roo.data.ArrayReader
20574  * @extends Roo.data.DataReader
20575  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20576  * Each element of that Array represents a row of data fields. The
20577  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20578  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20579  * <p>
20580  * Example code:.
20581  * <pre><code>
20582 var RecordDef = Roo.data.Record.create([
20583     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20584     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20585 ]);
20586 var myReader = new Roo.data.ArrayReader({
20587     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20588 }, RecordDef);
20589 </code></pre>
20590  * <p>
20591  * This would consume an Array like this:
20592  * <pre><code>
20593 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20594   </code></pre>
20595  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20596  * @constructor
20597  * Create a new JsonReader
20598  * @param {Object} meta Metadata configuration options.
20599  * @param {Object} recordType Either an Array of field definition objects
20600  * as specified to {@link Roo.data.Record#create},
20601  * or an {@link Roo.data.Record} object
20602  * created using {@link Roo.data.Record#create}.
20603  */
20604 Roo.data.ArrayReader = function(meta, recordType){
20605     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20606 };
20607
20608 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20609     /**
20610      * Create a data block containing Roo.data.Records from an XML document.
20611      * @param {Object} o An Array of row objects which represents the dataset.
20612      * @return {Object} data A data block which is used by an Roo.data.Store object as
20613      * a cache of Roo.data.Records.
20614      */
20615     readRecords : function(o){
20616         var sid = this.meta ? this.meta.id : null;
20617         var recordType = this.recordType, fields = recordType.prototype.fields;
20618         var records = [];
20619         var root = o;
20620             for(var i = 0; i < root.length; i++){
20621                     var n = root[i];
20622                 var values = {};
20623                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20624                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20625                 var f = fields.items[j];
20626                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20627                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20628                 v = f.convert(v);
20629                 values[f.name] = v;
20630             }
20631                 var record = new recordType(values, id);
20632                 record.json = n;
20633                 records[records.length] = record;
20634             }
20635             return {
20636                 records : records,
20637                 totalRecords : records.length
20638             };
20639     }
20640 });/*
20641  * Based on:
20642  * Ext JS Library 1.1.1
20643  * Copyright(c) 2006-2007, Ext JS, LLC.
20644  *
20645  * Originally Released Under LGPL - original licence link has changed is not relivant.
20646  *
20647  * Fork - LGPL
20648  * <script type="text/javascript">
20649  */
20650
20651
20652 /**
20653  * @class Roo.data.Tree
20654  * @extends Roo.util.Observable
20655  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20656  * in the tree have most standard DOM functionality.
20657  * @constructor
20658  * @param {Node} root (optional) The root node
20659  */
20660 Roo.data.Tree = function(root){
20661    this.nodeHash = {};
20662    /**
20663     * The root node for this tree
20664     * @type Node
20665     */
20666    this.root = null;
20667    if(root){
20668        this.setRootNode(root);
20669    }
20670    this.addEvents({
20671        /**
20672         * @event append
20673         * Fires when a new child node is appended to a node in this tree.
20674         * @param {Tree} tree The owner tree
20675         * @param {Node} parent The parent node
20676         * @param {Node} node The newly appended node
20677         * @param {Number} index The index of the newly appended node
20678         */
20679        "append" : true,
20680        /**
20681         * @event remove
20682         * Fires when a child node is removed from a node in this tree.
20683         * @param {Tree} tree The owner tree
20684         * @param {Node} parent The parent node
20685         * @param {Node} node The child node removed
20686         */
20687        "remove" : true,
20688        /**
20689         * @event move
20690         * Fires when a node is moved to a new location in the tree
20691         * @param {Tree} tree The owner tree
20692         * @param {Node} node The node moved
20693         * @param {Node} oldParent The old parent of this node
20694         * @param {Node} newParent The new parent of this node
20695         * @param {Number} index The index it was moved to
20696         */
20697        "move" : true,
20698        /**
20699         * @event insert
20700         * Fires when a new child node is inserted in a node in this tree.
20701         * @param {Tree} tree The owner tree
20702         * @param {Node} parent The parent node
20703         * @param {Node} node The child node inserted
20704         * @param {Node} refNode The child node the node was inserted before
20705         */
20706        "insert" : true,
20707        /**
20708         * @event beforeappend
20709         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20710         * @param {Tree} tree The owner tree
20711         * @param {Node} parent The parent node
20712         * @param {Node} node The child node to be appended
20713         */
20714        "beforeappend" : true,
20715        /**
20716         * @event beforeremove
20717         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20718         * @param {Tree} tree The owner tree
20719         * @param {Node} parent The parent node
20720         * @param {Node} node The child node to be removed
20721         */
20722        "beforeremove" : true,
20723        /**
20724         * @event beforemove
20725         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20726         * @param {Tree} tree The owner tree
20727         * @param {Node} node The node being moved
20728         * @param {Node} oldParent The parent of the node
20729         * @param {Node} newParent The new parent the node is moving to
20730         * @param {Number} index The index it is being moved to
20731         */
20732        "beforemove" : true,
20733        /**
20734         * @event beforeinsert
20735         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20736         * @param {Tree} tree The owner tree
20737         * @param {Node} parent The parent node
20738         * @param {Node} node The child node to be inserted
20739         * @param {Node} refNode The child node the node is being inserted before
20740         */
20741        "beforeinsert" : true
20742    });
20743
20744     Roo.data.Tree.superclass.constructor.call(this);
20745 };
20746
20747 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20748     pathSeparator: "/",
20749
20750     proxyNodeEvent : function(){
20751         return this.fireEvent.apply(this, arguments);
20752     },
20753
20754     /**
20755      * Returns the root node for this tree.
20756      * @return {Node}
20757      */
20758     getRootNode : function(){
20759         return this.root;
20760     },
20761
20762     /**
20763      * Sets the root node for this tree.
20764      * @param {Node} node
20765      * @return {Node}
20766      */
20767     setRootNode : function(node){
20768         this.root = node;
20769         node.ownerTree = this;
20770         node.isRoot = true;
20771         this.registerNode(node);
20772         return node;
20773     },
20774
20775     /**
20776      * Gets a node in this tree by its id.
20777      * @param {String} id
20778      * @return {Node}
20779      */
20780     getNodeById : function(id){
20781         return this.nodeHash[id];
20782     },
20783
20784     registerNode : function(node){
20785         this.nodeHash[node.id] = node;
20786     },
20787
20788     unregisterNode : function(node){
20789         delete this.nodeHash[node.id];
20790     },
20791
20792     toString : function(){
20793         return "[Tree"+(this.id?" "+this.id:"")+"]";
20794     }
20795 });
20796
20797 /**
20798  * @class Roo.data.Node
20799  * @extends Roo.util.Observable
20800  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20801  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20802  * @constructor
20803  * @param {Object} attributes The attributes/config for the node
20804  */
20805 Roo.data.Node = function(attributes){
20806     /**
20807      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20808      * @type {Object}
20809      */
20810     this.attributes = attributes || {};
20811     this.leaf = this.attributes.leaf;
20812     /**
20813      * The node id. @type String
20814      */
20815     this.id = this.attributes.id;
20816     if(!this.id){
20817         this.id = Roo.id(null, "ynode-");
20818         this.attributes.id = this.id;
20819     }
20820     /**
20821      * All child nodes of this node. @type Array
20822      */
20823     this.childNodes = [];
20824     if(!this.childNodes.indexOf){ // indexOf is a must
20825         this.childNodes.indexOf = function(o){
20826             for(var i = 0, len = this.length; i < len; i++){
20827                 if(this[i] == o) return i;
20828             }
20829             return -1;
20830         };
20831     }
20832     /**
20833      * The parent node for this node. @type Node
20834      */
20835     this.parentNode = null;
20836     /**
20837      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20838      */
20839     this.firstChild = null;
20840     /**
20841      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20842      */
20843     this.lastChild = null;
20844     /**
20845      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20846      */
20847     this.previousSibling = null;
20848     /**
20849      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20850      */
20851     this.nextSibling = null;
20852
20853     this.addEvents({
20854        /**
20855         * @event append
20856         * Fires when a new child node is appended
20857         * @param {Tree} tree The owner tree
20858         * @param {Node} this This node
20859         * @param {Node} node The newly appended node
20860         * @param {Number} index The index of the newly appended node
20861         */
20862        "append" : true,
20863        /**
20864         * @event remove
20865         * Fires when a child node is removed
20866         * @param {Tree} tree The owner tree
20867         * @param {Node} this This node
20868         * @param {Node} node The removed node
20869         */
20870        "remove" : true,
20871        /**
20872         * @event move
20873         * Fires when this node is moved to a new location in the tree
20874         * @param {Tree} tree The owner tree
20875         * @param {Node} this This node
20876         * @param {Node} oldParent The old parent of this node
20877         * @param {Node} newParent The new parent of this node
20878         * @param {Number} index The index it was moved to
20879         */
20880        "move" : true,
20881        /**
20882         * @event insert
20883         * Fires when a new child node is inserted.
20884         * @param {Tree} tree The owner tree
20885         * @param {Node} this This node
20886         * @param {Node} node The child node inserted
20887         * @param {Node} refNode The child node the node was inserted before
20888         */
20889        "insert" : true,
20890        /**
20891         * @event beforeappend
20892         * Fires before a new child is appended, return false to cancel the append.
20893         * @param {Tree} tree The owner tree
20894         * @param {Node} this This node
20895         * @param {Node} node The child node to be appended
20896         */
20897        "beforeappend" : true,
20898        /**
20899         * @event beforeremove
20900         * Fires before a child is removed, return false to cancel the remove.
20901         * @param {Tree} tree The owner tree
20902         * @param {Node} this This node
20903         * @param {Node} node The child node to be removed
20904         */
20905        "beforeremove" : true,
20906        /**
20907         * @event beforemove
20908         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20909         * @param {Tree} tree The owner tree
20910         * @param {Node} this This node
20911         * @param {Node} oldParent The parent of this node
20912         * @param {Node} newParent The new parent this node is moving to
20913         * @param {Number} index The index it is being moved to
20914         */
20915        "beforemove" : true,
20916        /**
20917         * @event beforeinsert
20918         * Fires before a new child is inserted, return false to cancel the insert.
20919         * @param {Tree} tree The owner tree
20920         * @param {Node} this This node
20921         * @param {Node} node The child node to be inserted
20922         * @param {Node} refNode The child node the node is being inserted before
20923         */
20924        "beforeinsert" : true
20925    });
20926     this.listeners = this.attributes.listeners;
20927     Roo.data.Node.superclass.constructor.call(this);
20928 };
20929
20930 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20931     fireEvent : function(evtName){
20932         // first do standard event for this node
20933         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20934             return false;
20935         }
20936         // then bubble it up to the tree if the event wasn't cancelled
20937         var ot = this.getOwnerTree();
20938         if(ot){
20939             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20940                 return false;
20941             }
20942         }
20943         return true;
20944     },
20945
20946     /**
20947      * Returns true if this node is a leaf
20948      * @return {Boolean}
20949      */
20950     isLeaf : function(){
20951         return this.leaf === true;
20952     },
20953
20954     // private
20955     setFirstChild : function(node){
20956         this.firstChild = node;
20957     },
20958
20959     //private
20960     setLastChild : function(node){
20961         this.lastChild = node;
20962     },
20963
20964
20965     /**
20966      * Returns true if this node is the last child of its parent
20967      * @return {Boolean}
20968      */
20969     isLast : function(){
20970        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20971     },
20972
20973     /**
20974      * Returns true if this node is the first child of its parent
20975      * @return {Boolean}
20976      */
20977     isFirst : function(){
20978        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20979     },
20980
20981     hasChildNodes : function(){
20982         return !this.isLeaf() && this.childNodes.length > 0;
20983     },
20984
20985     /**
20986      * Insert node(s) as the last child node of this node.
20987      * @param {Node/Array} node The node or Array of nodes to append
20988      * @return {Node} The appended node if single append, or null if an array was passed
20989      */
20990     appendChild : function(node){
20991         var multi = false;
20992         if(node instanceof Array){
20993             multi = node;
20994         }else if(arguments.length > 1){
20995             multi = arguments;
20996         }
20997         // if passed an array or multiple args do them one by one
20998         if(multi){
20999             for(var i = 0, len = multi.length; i < len; i++) {
21000                 this.appendChild(multi[i]);
21001             }
21002         }else{
21003             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21004                 return false;
21005             }
21006             var index = this.childNodes.length;
21007             var oldParent = node.parentNode;
21008             // it's a move, make sure we move it cleanly
21009             if(oldParent){
21010                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21011                     return false;
21012                 }
21013                 oldParent.removeChild(node);
21014             }
21015             index = this.childNodes.length;
21016             if(index == 0){
21017                 this.setFirstChild(node);
21018             }
21019             this.childNodes.push(node);
21020             node.parentNode = this;
21021             var ps = this.childNodes[index-1];
21022             if(ps){
21023                 node.previousSibling = ps;
21024                 ps.nextSibling = node;
21025             }else{
21026                 node.previousSibling = null;
21027             }
21028             node.nextSibling = null;
21029             this.setLastChild(node);
21030             node.setOwnerTree(this.getOwnerTree());
21031             this.fireEvent("append", this.ownerTree, this, node, index);
21032             if(oldParent){
21033                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21034             }
21035             return node;
21036         }
21037     },
21038
21039     /**
21040      * Removes a child node from this node.
21041      * @param {Node} node The node to remove
21042      * @return {Node} The removed node
21043      */
21044     removeChild : function(node){
21045         var index = this.childNodes.indexOf(node);
21046         if(index == -1){
21047             return false;
21048         }
21049         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21050             return false;
21051         }
21052
21053         // remove it from childNodes collection
21054         this.childNodes.splice(index, 1);
21055
21056         // update siblings
21057         if(node.previousSibling){
21058             node.previousSibling.nextSibling = node.nextSibling;
21059         }
21060         if(node.nextSibling){
21061             node.nextSibling.previousSibling = node.previousSibling;
21062         }
21063
21064         // update child refs
21065         if(this.firstChild == node){
21066             this.setFirstChild(node.nextSibling);
21067         }
21068         if(this.lastChild == node){
21069             this.setLastChild(node.previousSibling);
21070         }
21071
21072         node.setOwnerTree(null);
21073         // clear any references from the node
21074         node.parentNode = null;
21075         node.previousSibling = null;
21076         node.nextSibling = null;
21077         this.fireEvent("remove", this.ownerTree, this, node);
21078         return node;
21079     },
21080
21081     /**
21082      * Inserts the first node before the second node in this nodes childNodes collection.
21083      * @param {Node} node The node to insert
21084      * @param {Node} refNode The node to insert before (if null the node is appended)
21085      * @return {Node} The inserted node
21086      */
21087     insertBefore : function(node, refNode){
21088         if(!refNode){ // like standard Dom, refNode can be null for append
21089             return this.appendChild(node);
21090         }
21091         // nothing to do
21092         if(node == refNode){
21093             return false;
21094         }
21095
21096         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21097             return false;
21098         }
21099         var index = this.childNodes.indexOf(refNode);
21100         var oldParent = node.parentNode;
21101         var refIndex = index;
21102
21103         // when moving internally, indexes will change after remove
21104         if(oldParent == this && this.childNodes.indexOf(node) < index){
21105             refIndex--;
21106         }
21107
21108         // it's a move, make sure we move it cleanly
21109         if(oldParent){
21110             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21111                 return false;
21112             }
21113             oldParent.removeChild(node);
21114         }
21115         if(refIndex == 0){
21116             this.setFirstChild(node);
21117         }
21118         this.childNodes.splice(refIndex, 0, node);
21119         node.parentNode = this;
21120         var ps = this.childNodes[refIndex-1];
21121         if(ps){
21122             node.previousSibling = ps;
21123             ps.nextSibling = node;
21124         }else{
21125             node.previousSibling = null;
21126         }
21127         node.nextSibling = refNode;
21128         refNode.previousSibling = node;
21129         node.setOwnerTree(this.getOwnerTree());
21130         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21131         if(oldParent){
21132             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21133         }
21134         return node;
21135     },
21136
21137     /**
21138      * Returns the child node at the specified index.
21139      * @param {Number} index
21140      * @return {Node}
21141      */
21142     item : function(index){
21143         return this.childNodes[index];
21144     },
21145
21146     /**
21147      * Replaces one child node in this node with another.
21148      * @param {Node} newChild The replacement node
21149      * @param {Node} oldChild The node to replace
21150      * @return {Node} The replaced node
21151      */
21152     replaceChild : function(newChild, oldChild){
21153         this.insertBefore(newChild, oldChild);
21154         this.removeChild(oldChild);
21155         return oldChild;
21156     },
21157
21158     /**
21159      * Returns the index of a child node
21160      * @param {Node} node
21161      * @return {Number} The index of the node or -1 if it was not found
21162      */
21163     indexOf : function(child){
21164         return this.childNodes.indexOf(child);
21165     },
21166
21167     /**
21168      * Returns the tree this node is in.
21169      * @return {Tree}
21170      */
21171     getOwnerTree : function(){
21172         // if it doesn't have one, look for one
21173         if(!this.ownerTree){
21174             var p = this;
21175             while(p){
21176                 if(p.ownerTree){
21177                     this.ownerTree = p.ownerTree;
21178                     break;
21179                 }
21180                 p = p.parentNode;
21181             }
21182         }
21183         return this.ownerTree;
21184     },
21185
21186     /**
21187      * Returns depth of this node (the root node has a depth of 0)
21188      * @return {Number}
21189      */
21190     getDepth : function(){
21191         var depth = 0;
21192         var p = this;
21193         while(p.parentNode){
21194             ++depth;
21195             p = p.parentNode;
21196         }
21197         return depth;
21198     },
21199
21200     // private
21201     setOwnerTree : function(tree){
21202         // if it's move, we need to update everyone
21203         if(tree != this.ownerTree){
21204             if(this.ownerTree){
21205                 this.ownerTree.unregisterNode(this);
21206             }
21207             this.ownerTree = tree;
21208             var cs = this.childNodes;
21209             for(var i = 0, len = cs.length; i < len; i++) {
21210                 cs[i].setOwnerTree(tree);
21211             }
21212             if(tree){
21213                 tree.registerNode(this);
21214             }
21215         }
21216     },
21217
21218     /**
21219      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21220      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21221      * @return {String} The path
21222      */
21223     getPath : function(attr){
21224         attr = attr || "id";
21225         var p = this.parentNode;
21226         var b = [this.attributes[attr]];
21227         while(p){
21228             b.unshift(p.attributes[attr]);
21229             p = p.parentNode;
21230         }
21231         var sep = this.getOwnerTree().pathSeparator;
21232         return sep + b.join(sep);
21233     },
21234
21235     /**
21236      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21237      * function call will be the scope provided or the current node. The arguments to the function
21238      * will be the args provided or the current node. If the function returns false at any point,
21239      * the bubble is stopped.
21240      * @param {Function} fn The function to call
21241      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21242      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21243      */
21244     bubble : function(fn, scope, args){
21245         var p = this;
21246         while(p){
21247             if(fn.call(scope || p, args || p) === false){
21248                 break;
21249             }
21250             p = p.parentNode;
21251         }
21252     },
21253
21254     /**
21255      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21256      * function call will be the scope provided or the current node. The arguments to the function
21257      * will be the args provided or the current node. If the function returns false at any point,
21258      * the cascade is stopped on that branch.
21259      * @param {Function} fn The function to call
21260      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21261      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21262      */
21263     cascade : function(fn, scope, args){
21264         if(fn.call(scope || this, args || this) !== false){
21265             var cs = this.childNodes;
21266             for(var i = 0, len = cs.length; i < len; i++) {
21267                 cs[i].cascade(fn, scope, args);
21268             }
21269         }
21270     },
21271
21272     /**
21273      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21274      * function call will be the scope provided or the current node. The arguments to the function
21275      * will be the args provided or the current node. If the function returns false at any point,
21276      * the iteration stops.
21277      * @param {Function} fn The function to call
21278      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21279      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21280      */
21281     eachChild : function(fn, scope, args){
21282         var cs = this.childNodes;
21283         for(var i = 0, len = cs.length; i < len; i++) {
21284                 if(fn.call(scope || this, args || cs[i]) === false){
21285                     break;
21286                 }
21287         }
21288     },
21289
21290     /**
21291      * Finds the first child that has the attribute with the specified value.
21292      * @param {String} attribute The attribute name
21293      * @param {Mixed} value The value to search for
21294      * @return {Node} The found child or null if none was found
21295      */
21296     findChild : function(attribute, value){
21297         var cs = this.childNodes;
21298         for(var i = 0, len = cs.length; i < len; i++) {
21299                 if(cs[i].attributes[attribute] == value){
21300                     return cs[i];
21301                 }
21302         }
21303         return null;
21304     },
21305
21306     /**
21307      * Finds the first child by a custom function. The child matches if the function passed
21308      * returns true.
21309      * @param {Function} fn
21310      * @param {Object} scope (optional)
21311      * @return {Node} The found child or null if none was found
21312      */
21313     findChildBy : function(fn, scope){
21314         var cs = this.childNodes;
21315         for(var i = 0, len = cs.length; i < len; i++) {
21316                 if(fn.call(scope||cs[i], cs[i]) === true){
21317                     return cs[i];
21318                 }
21319         }
21320         return null;
21321     },
21322
21323     /**
21324      * Sorts this nodes children using the supplied sort function
21325      * @param {Function} fn
21326      * @param {Object} scope (optional)
21327      */
21328     sort : function(fn, scope){
21329         var cs = this.childNodes;
21330         var len = cs.length;
21331         if(len > 0){
21332             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21333             cs.sort(sortFn);
21334             for(var i = 0; i < len; i++){
21335                 var n = cs[i];
21336                 n.previousSibling = cs[i-1];
21337                 n.nextSibling = cs[i+1];
21338                 if(i == 0){
21339                     this.setFirstChild(n);
21340                 }
21341                 if(i == len-1){
21342                     this.setLastChild(n);
21343                 }
21344             }
21345         }
21346     },
21347
21348     /**
21349      * Returns true if this node is an ancestor (at any point) of the passed node.
21350      * @param {Node} node
21351      * @return {Boolean}
21352      */
21353     contains : function(node){
21354         return node.isAncestor(this);
21355     },
21356
21357     /**
21358      * Returns true if the passed node is an ancestor (at any point) of this node.
21359      * @param {Node} node
21360      * @return {Boolean}
21361      */
21362     isAncestor : function(node){
21363         var p = this.parentNode;
21364         while(p){
21365             if(p == node){
21366                 return true;
21367             }
21368             p = p.parentNode;
21369         }
21370         return false;
21371     },
21372
21373     toString : function(){
21374         return "[Node"+(this.id?" "+this.id:"")+"]";
21375     }
21376 });/*
21377  * Based on:
21378  * Ext JS Library 1.1.1
21379  * Copyright(c) 2006-2007, Ext JS, LLC.
21380  *
21381  * Originally Released Under LGPL - original licence link has changed is not relivant.
21382  *
21383  * Fork - LGPL
21384  * <script type="text/javascript">
21385  */
21386  
21387
21388 /**
21389  * @class Roo.ComponentMgr
21390  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21391  * @singleton
21392  */
21393 Roo.ComponentMgr = function(){
21394     var all = new Roo.util.MixedCollection();
21395
21396     return {
21397         /**
21398          * Registers a component.
21399          * @param {Roo.Component} c The component
21400          */
21401         register : function(c){
21402             all.add(c);
21403         },
21404
21405         /**
21406          * Unregisters a component.
21407          * @param {Roo.Component} c The component
21408          */
21409         unregister : function(c){
21410             all.remove(c);
21411         },
21412
21413         /**
21414          * Returns a component by id
21415          * @param {String} id The component id
21416          */
21417         get : function(id){
21418             return all.get(id);
21419         },
21420
21421         /**
21422          * Registers a function that will be called when a specified component is added to ComponentMgr
21423          * @param {String} id The component id
21424          * @param {Funtction} fn The callback function
21425          * @param {Object} scope The scope of the callback
21426          */
21427         onAvailable : function(id, fn, scope){
21428             all.on("add", function(index, o){
21429                 if(o.id == id){
21430                     fn.call(scope || o, o);
21431                     all.un("add", fn, scope);
21432                 }
21433             });
21434         }
21435     };
21436 }();/*
21437  * Based on:
21438  * Ext JS Library 1.1.1
21439  * Copyright(c) 2006-2007, Ext JS, LLC.
21440  *
21441  * Originally Released Under LGPL - original licence link has changed is not relivant.
21442  *
21443  * Fork - LGPL
21444  * <script type="text/javascript">
21445  */
21446  
21447 /**
21448  * @class Roo.Component
21449  * @extends Roo.util.Observable
21450  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21451  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21452  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21453  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21454  * All visual components (widgets) that require rendering into a layout should subclass Component.
21455  * @constructor
21456  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21457  * 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
21458  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21459  */
21460 Roo.Component = function(config){
21461     config = config || {};
21462     if(config.tagName || config.dom || typeof config == "string"){ // element object
21463         config = {el: config, id: config.id || config};
21464     }
21465     this.initialConfig = config;
21466
21467     Roo.apply(this, config);
21468     this.addEvents({
21469         /**
21470          * @event disable
21471          * Fires after the component is disabled.
21472              * @param {Roo.Component} this
21473              */
21474         disable : true,
21475         /**
21476          * @event enable
21477          * Fires after the component is enabled.
21478              * @param {Roo.Component} this
21479              */
21480         enable : true,
21481         /**
21482          * @event beforeshow
21483          * Fires before the component is shown.  Return false to stop the show.
21484              * @param {Roo.Component} this
21485              */
21486         beforeshow : true,
21487         /**
21488          * @event show
21489          * Fires after the component is shown.
21490              * @param {Roo.Component} this
21491              */
21492         show : true,
21493         /**
21494          * @event beforehide
21495          * Fires before the component is hidden. Return false to stop the hide.
21496              * @param {Roo.Component} this
21497              */
21498         beforehide : true,
21499         /**
21500          * @event hide
21501          * Fires after the component is hidden.
21502              * @param {Roo.Component} this
21503              */
21504         hide : true,
21505         /**
21506          * @event beforerender
21507          * Fires before the component is rendered. Return false to stop the render.
21508              * @param {Roo.Component} this
21509              */
21510         beforerender : true,
21511         /**
21512          * @event render
21513          * Fires after the component is rendered.
21514              * @param {Roo.Component} this
21515              */
21516         render : true,
21517         /**
21518          * @event beforedestroy
21519          * Fires before the component is destroyed. Return false to stop the destroy.
21520              * @param {Roo.Component} this
21521              */
21522         beforedestroy : true,
21523         /**
21524          * @event destroy
21525          * Fires after the component is destroyed.
21526              * @param {Roo.Component} this
21527              */
21528         destroy : true
21529     });
21530     if(!this.id){
21531         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21532     }
21533     Roo.ComponentMgr.register(this);
21534     Roo.Component.superclass.constructor.call(this);
21535     this.initComponent();
21536     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21537         this.render(this.renderTo);
21538         delete this.renderTo;
21539     }
21540 };
21541
21542 // private
21543 Roo.Component.AUTO_ID = 1000;
21544
21545 Roo.extend(Roo.Component, Roo.util.Observable, {
21546     /**
21547      * @property {Boolean} hidden
21548      * true if this component is hidden. Read-only.
21549      */
21550     hidden : false,
21551     /**
21552      * true if this component is disabled. Read-only.
21553      */
21554     disabled : false,
21555     /**
21556      * true if this component has been rendered. Read-only.
21557      */
21558     rendered : false,
21559     
21560     /** @cfg {String} disableClass
21561      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21562      */
21563     disabledClass : "x-item-disabled",
21564         /** @cfg {Boolean} allowDomMove
21565          * Whether the component can move the Dom node when rendering (defaults to true).
21566          */
21567     allowDomMove : true,
21568     /** @cfg {String} hideMode
21569      * How this component should hidden. Supported values are
21570      * "visibility" (css visibility), "offsets" (negative offset position) and
21571      * "display" (css display) - defaults to "display".
21572      */
21573     hideMode: 'display',
21574
21575     // private
21576     ctype : "Roo.Component",
21577
21578     /** @cfg {String} actionMode 
21579      * which property holds the element that used for  hide() / show() / disable() / enable()
21580      * default is 'el' 
21581      */
21582     actionMode : "el",
21583
21584     // private
21585     getActionEl : function(){
21586         return this[this.actionMode];
21587     },
21588
21589     initComponent : Roo.emptyFn,
21590     /**
21591      * If this is a lazy rendering component, render it to its container element.
21592      * @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.
21593      */
21594     render : function(container, position){
21595         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21596             if(!container && this.el){
21597                 this.el = Roo.get(this.el);
21598                 container = this.el.dom.parentNode;
21599                 this.allowDomMove = false;
21600             }
21601             this.container = Roo.get(container);
21602             this.rendered = true;
21603             if(position !== undefined){
21604                 if(typeof position == 'number'){
21605                     position = this.container.dom.childNodes[position];
21606                 }else{
21607                     position = Roo.getDom(position);
21608                 }
21609             }
21610             this.onRender(this.container, position || null);
21611             if(this.cls){
21612                 this.el.addClass(this.cls);
21613                 delete this.cls;
21614             }
21615             if(this.style){
21616                 this.el.applyStyles(this.style);
21617                 delete this.style;
21618             }
21619             this.fireEvent("render", this);
21620             this.afterRender(this.container);
21621             if(this.hidden){
21622                 this.hide();
21623             }
21624             if(this.disabled){
21625                 this.disable();
21626             }
21627         }
21628         return this;
21629     },
21630
21631     // private
21632     // default function is not really useful
21633     onRender : function(ct, position){
21634         if(this.el){
21635             this.el = Roo.get(this.el);
21636             if(this.allowDomMove !== false){
21637                 ct.dom.insertBefore(this.el.dom, position);
21638             }
21639         }
21640     },
21641
21642     // private
21643     getAutoCreate : function(){
21644         var cfg = typeof this.autoCreate == "object" ?
21645                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21646         if(this.id && !cfg.id){
21647             cfg.id = this.id;
21648         }
21649         return cfg;
21650     },
21651
21652     // private
21653     afterRender : Roo.emptyFn,
21654
21655     /**
21656      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21657      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21658      */
21659     destroy : function(){
21660         if(this.fireEvent("beforedestroy", this) !== false){
21661             this.purgeListeners();
21662             this.beforeDestroy();
21663             if(this.rendered){
21664                 this.el.removeAllListeners();
21665                 this.el.remove();
21666                 if(this.actionMode == "container"){
21667                     this.container.remove();
21668                 }
21669             }
21670             this.onDestroy();
21671             Roo.ComponentMgr.unregister(this);
21672             this.fireEvent("destroy", this);
21673         }
21674     },
21675
21676         // private
21677     beforeDestroy : function(){
21678
21679     },
21680
21681         // private
21682         onDestroy : function(){
21683
21684     },
21685
21686     /**
21687      * Returns the underlying {@link Roo.Element}.
21688      * @return {Roo.Element} The element
21689      */
21690     getEl : function(){
21691         return this.el;
21692     },
21693
21694     /**
21695      * Returns the id of this component.
21696      * @return {String}
21697      */
21698     getId : function(){
21699         return this.id;
21700     },
21701
21702     /**
21703      * Try to focus this component.
21704      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21705      * @return {Roo.Component} this
21706      */
21707     focus : function(selectText){
21708         if(this.rendered){
21709             this.el.focus();
21710             if(selectText === true){
21711                 this.el.dom.select();
21712             }
21713         }
21714         return this;
21715     },
21716
21717     // private
21718     blur : function(){
21719         if(this.rendered){
21720             this.el.blur();
21721         }
21722         return this;
21723     },
21724
21725     /**
21726      * Disable this component.
21727      * @return {Roo.Component} this
21728      */
21729     disable : function(){
21730         if(this.rendered){
21731             this.onDisable();
21732         }
21733         this.disabled = true;
21734         this.fireEvent("disable", this);
21735         return this;
21736     },
21737
21738         // private
21739     onDisable : function(){
21740         this.getActionEl().addClass(this.disabledClass);
21741         this.el.dom.disabled = true;
21742     },
21743
21744     /**
21745      * Enable this component.
21746      * @return {Roo.Component} this
21747      */
21748     enable : function(){
21749         if(this.rendered){
21750             this.onEnable();
21751         }
21752         this.disabled = false;
21753         this.fireEvent("enable", this);
21754         return this;
21755     },
21756
21757         // private
21758     onEnable : function(){
21759         this.getActionEl().removeClass(this.disabledClass);
21760         this.el.dom.disabled = false;
21761     },
21762
21763     /**
21764      * Convenience function for setting disabled/enabled by boolean.
21765      * @param {Boolean} disabled
21766      */
21767     setDisabled : function(disabled){
21768         this[disabled ? "disable" : "enable"]();
21769     },
21770
21771     /**
21772      * Show this component.
21773      * @return {Roo.Component} this
21774      */
21775     show: function(){
21776         if(this.fireEvent("beforeshow", this) !== false){
21777             this.hidden = false;
21778             if(this.rendered){
21779                 this.onShow();
21780             }
21781             this.fireEvent("show", this);
21782         }
21783         return this;
21784     },
21785
21786     // private
21787     onShow : function(){
21788         var ae = this.getActionEl();
21789         if(this.hideMode == 'visibility'){
21790             ae.dom.style.visibility = "visible";
21791         }else if(this.hideMode == 'offsets'){
21792             ae.removeClass('x-hidden');
21793         }else{
21794             ae.dom.style.display = "";
21795         }
21796     },
21797
21798     /**
21799      * Hide this component.
21800      * @return {Roo.Component} this
21801      */
21802     hide: function(){
21803         if(this.fireEvent("beforehide", this) !== false){
21804             this.hidden = true;
21805             if(this.rendered){
21806                 this.onHide();
21807             }
21808             this.fireEvent("hide", this);
21809         }
21810         return this;
21811     },
21812
21813     // private
21814     onHide : function(){
21815         var ae = this.getActionEl();
21816         if(this.hideMode == 'visibility'){
21817             ae.dom.style.visibility = "hidden";
21818         }else if(this.hideMode == 'offsets'){
21819             ae.addClass('x-hidden');
21820         }else{
21821             ae.dom.style.display = "none";
21822         }
21823     },
21824
21825     /**
21826      * Convenience function to hide or show this component by boolean.
21827      * @param {Boolean} visible True to show, false to hide
21828      * @return {Roo.Component} this
21829      */
21830     setVisible: function(visible){
21831         if(visible) {
21832             this.show();
21833         }else{
21834             this.hide();
21835         }
21836         return this;
21837     },
21838
21839     /**
21840      * Returns true if this component is visible.
21841      */
21842     isVisible : function(){
21843         return this.getActionEl().isVisible();
21844     },
21845
21846     cloneConfig : function(overrides){
21847         overrides = overrides || {};
21848         var id = overrides.id || Roo.id();
21849         var cfg = Roo.applyIf(overrides, this.initialConfig);
21850         cfg.id = id; // prevent dup id
21851         return new this.constructor(cfg);
21852     }
21853 });/*
21854  * Based on:
21855  * Ext JS Library 1.1.1
21856  * Copyright(c) 2006-2007, Ext JS, LLC.
21857  *
21858  * Originally Released Under LGPL - original licence link has changed is not relivant.
21859  *
21860  * Fork - LGPL
21861  * <script type="text/javascript">
21862  */
21863  (function(){ 
21864 /**
21865  * @class Roo.Layer
21866  * @extends Roo.Element
21867  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21868  * automatic maintaining of shadow/shim positions.
21869  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21870  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21871  * you can pass a string with a CSS class name. False turns off the shadow.
21872  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21873  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21874  * @cfg {String} cls CSS class to add to the element
21875  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21876  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21877  * @constructor
21878  * @param {Object} config An object with config options.
21879  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21880  */
21881
21882 Roo.Layer = function(config, existingEl){
21883     config = config || {};
21884     var dh = Roo.DomHelper;
21885     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21886     if(existingEl){
21887         this.dom = Roo.getDom(existingEl);
21888     }
21889     if(!this.dom){
21890         var o = config.dh || {tag: "div", cls: "x-layer"};
21891         this.dom = dh.append(pel, o);
21892     }
21893     if(config.cls){
21894         this.addClass(config.cls);
21895     }
21896     this.constrain = config.constrain !== false;
21897     this.visibilityMode = Roo.Element.VISIBILITY;
21898     if(config.id){
21899         this.id = this.dom.id = config.id;
21900     }else{
21901         this.id = Roo.id(this.dom);
21902     }
21903     this.zindex = config.zindex || this.getZIndex();
21904     this.position("absolute", this.zindex);
21905     if(config.shadow){
21906         this.shadowOffset = config.shadowOffset || 4;
21907         this.shadow = new Roo.Shadow({
21908             offset : this.shadowOffset,
21909             mode : config.shadow
21910         });
21911     }else{
21912         this.shadowOffset = 0;
21913     }
21914     this.useShim = config.shim !== false && Roo.useShims;
21915     this.useDisplay = config.useDisplay;
21916     this.hide();
21917 };
21918
21919 var supr = Roo.Element.prototype;
21920
21921 // shims are shared among layer to keep from having 100 iframes
21922 var shims = [];
21923
21924 Roo.extend(Roo.Layer, Roo.Element, {
21925
21926     getZIndex : function(){
21927         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21928     },
21929
21930     getShim : function(){
21931         if(!this.useShim){
21932             return null;
21933         }
21934         if(this.shim){
21935             return this.shim;
21936         }
21937         var shim = shims.shift();
21938         if(!shim){
21939             shim = this.createShim();
21940             shim.enableDisplayMode('block');
21941             shim.dom.style.display = 'none';
21942             shim.dom.style.visibility = 'visible';
21943         }
21944         var pn = this.dom.parentNode;
21945         if(shim.dom.parentNode != pn){
21946             pn.insertBefore(shim.dom, this.dom);
21947         }
21948         shim.setStyle('z-index', this.getZIndex()-2);
21949         this.shim = shim;
21950         return shim;
21951     },
21952
21953     hideShim : function(){
21954         if(this.shim){
21955             this.shim.setDisplayed(false);
21956             shims.push(this.shim);
21957             delete this.shim;
21958         }
21959     },
21960
21961     disableShadow : function(){
21962         if(this.shadow){
21963             this.shadowDisabled = true;
21964             this.shadow.hide();
21965             this.lastShadowOffset = this.shadowOffset;
21966             this.shadowOffset = 0;
21967         }
21968     },
21969
21970     enableShadow : function(show){
21971         if(this.shadow){
21972             this.shadowDisabled = false;
21973             this.shadowOffset = this.lastShadowOffset;
21974             delete this.lastShadowOffset;
21975             if(show){
21976                 this.sync(true);
21977             }
21978         }
21979     },
21980
21981     // private
21982     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21983     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21984     sync : function(doShow){
21985         var sw = this.shadow;
21986         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21987             var sh = this.getShim();
21988
21989             var w = this.getWidth(),
21990                 h = this.getHeight();
21991
21992             var l = this.getLeft(true),
21993                 t = this.getTop(true);
21994
21995             if(sw && !this.shadowDisabled){
21996                 if(doShow && !sw.isVisible()){
21997                     sw.show(this);
21998                 }else{
21999                     sw.realign(l, t, w, h);
22000                 }
22001                 if(sh){
22002                     if(doShow){
22003                        sh.show();
22004                     }
22005                     // fit the shim behind the shadow, so it is shimmed too
22006                     var a = sw.adjusts, s = sh.dom.style;
22007                     s.left = (Math.min(l, l+a.l))+"px";
22008                     s.top = (Math.min(t, t+a.t))+"px";
22009                     s.width = (w+a.w)+"px";
22010                     s.height = (h+a.h)+"px";
22011                 }
22012             }else if(sh){
22013                 if(doShow){
22014                    sh.show();
22015                 }
22016                 sh.setSize(w, h);
22017                 sh.setLeftTop(l, t);
22018             }
22019             
22020         }
22021     },
22022
22023     // private
22024     destroy : function(){
22025         this.hideShim();
22026         if(this.shadow){
22027             this.shadow.hide();
22028         }
22029         this.removeAllListeners();
22030         var pn = this.dom.parentNode;
22031         if(pn){
22032             pn.removeChild(this.dom);
22033         }
22034         Roo.Element.uncache(this.id);
22035     },
22036
22037     remove : function(){
22038         this.destroy();
22039     },
22040
22041     // private
22042     beginUpdate : function(){
22043         this.updating = true;
22044     },
22045
22046     // private
22047     endUpdate : function(){
22048         this.updating = false;
22049         this.sync(true);
22050     },
22051
22052     // private
22053     hideUnders : function(negOffset){
22054         if(this.shadow){
22055             this.shadow.hide();
22056         }
22057         this.hideShim();
22058     },
22059
22060     // private
22061     constrainXY : function(){
22062         if(this.constrain){
22063             var vw = Roo.lib.Dom.getViewWidth(),
22064                 vh = Roo.lib.Dom.getViewHeight();
22065             var s = Roo.get(document).getScroll();
22066
22067             var xy = this.getXY();
22068             var x = xy[0], y = xy[1];   
22069             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22070             // only move it if it needs it
22071             var moved = false;
22072             // first validate right/bottom
22073             if((x + w) > vw+s.left){
22074                 x = vw - w - this.shadowOffset;
22075                 moved = true;
22076             }
22077             if((y + h) > vh+s.top){
22078                 y = vh - h - this.shadowOffset;
22079                 moved = true;
22080             }
22081             // then make sure top/left isn't negative
22082             if(x < s.left){
22083                 x = s.left;
22084                 moved = true;
22085             }
22086             if(y < s.top){
22087                 y = s.top;
22088                 moved = true;
22089             }
22090             if(moved){
22091                 if(this.avoidY){
22092                     var ay = this.avoidY;
22093                     if(y <= ay && (y+h) >= ay){
22094                         y = ay-h-5;   
22095                     }
22096                 }
22097                 xy = [x, y];
22098                 this.storeXY(xy);
22099                 supr.setXY.call(this, xy);
22100                 this.sync();
22101             }
22102         }
22103     },
22104
22105     isVisible : function(){
22106         return this.visible;    
22107     },
22108
22109     // private
22110     showAction : function(){
22111         this.visible = true; // track visibility to prevent getStyle calls
22112         if(this.useDisplay === true){
22113             this.setDisplayed("");
22114         }else if(this.lastXY){
22115             supr.setXY.call(this, this.lastXY);
22116         }else if(this.lastLT){
22117             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22118         }
22119     },
22120
22121     // private
22122     hideAction : function(){
22123         this.visible = false;
22124         if(this.useDisplay === true){
22125             this.setDisplayed(false);
22126         }else{
22127             this.setLeftTop(-10000,-10000);
22128         }
22129     },
22130
22131     // overridden Element method
22132     setVisible : function(v, a, d, c, e){
22133         if(v){
22134             this.showAction();
22135         }
22136         if(a && v){
22137             var cb = function(){
22138                 this.sync(true);
22139                 if(c){
22140                     c();
22141                 }
22142             }.createDelegate(this);
22143             supr.setVisible.call(this, true, true, d, cb, e);
22144         }else{
22145             if(!v){
22146                 this.hideUnders(true);
22147             }
22148             var cb = c;
22149             if(a){
22150                 cb = function(){
22151                     this.hideAction();
22152                     if(c){
22153                         c();
22154                     }
22155                 }.createDelegate(this);
22156             }
22157             supr.setVisible.call(this, v, a, d, cb, e);
22158             if(v){
22159                 this.sync(true);
22160             }else if(!a){
22161                 this.hideAction();
22162             }
22163         }
22164     },
22165
22166     storeXY : function(xy){
22167         delete this.lastLT;
22168         this.lastXY = xy;
22169     },
22170
22171     storeLeftTop : function(left, top){
22172         delete this.lastXY;
22173         this.lastLT = [left, top];
22174     },
22175
22176     // private
22177     beforeFx : function(){
22178         this.beforeAction();
22179         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22180     },
22181
22182     // private
22183     afterFx : function(){
22184         Roo.Layer.superclass.afterFx.apply(this, arguments);
22185         this.sync(this.isVisible());
22186     },
22187
22188     // private
22189     beforeAction : function(){
22190         if(!this.updating && this.shadow){
22191             this.shadow.hide();
22192         }
22193     },
22194
22195     // overridden Element method
22196     setLeft : function(left){
22197         this.storeLeftTop(left, this.getTop(true));
22198         supr.setLeft.apply(this, arguments);
22199         this.sync();
22200     },
22201
22202     setTop : function(top){
22203         this.storeLeftTop(this.getLeft(true), top);
22204         supr.setTop.apply(this, arguments);
22205         this.sync();
22206     },
22207
22208     setLeftTop : function(left, top){
22209         this.storeLeftTop(left, top);
22210         supr.setLeftTop.apply(this, arguments);
22211         this.sync();
22212     },
22213
22214     setXY : function(xy, a, d, c, e){
22215         this.fixDisplay();
22216         this.beforeAction();
22217         this.storeXY(xy);
22218         var cb = this.createCB(c);
22219         supr.setXY.call(this, xy, a, d, cb, e);
22220         if(!a){
22221             cb();
22222         }
22223     },
22224
22225     // private
22226     createCB : function(c){
22227         var el = this;
22228         return function(){
22229             el.constrainXY();
22230             el.sync(true);
22231             if(c){
22232                 c();
22233             }
22234         };
22235     },
22236
22237     // overridden Element method
22238     setX : function(x, a, d, c, e){
22239         this.setXY([x, this.getY()], a, d, c, e);
22240     },
22241
22242     // overridden Element method
22243     setY : function(y, a, d, c, e){
22244         this.setXY([this.getX(), y], a, d, c, e);
22245     },
22246
22247     // overridden Element method
22248     setSize : function(w, h, a, d, c, e){
22249         this.beforeAction();
22250         var cb = this.createCB(c);
22251         supr.setSize.call(this, w, h, a, d, cb, e);
22252         if(!a){
22253             cb();
22254         }
22255     },
22256
22257     // overridden Element method
22258     setWidth : function(w, a, d, c, e){
22259         this.beforeAction();
22260         var cb = this.createCB(c);
22261         supr.setWidth.call(this, w, a, d, cb, e);
22262         if(!a){
22263             cb();
22264         }
22265     },
22266
22267     // overridden Element method
22268     setHeight : function(h, a, d, c, e){
22269         this.beforeAction();
22270         var cb = this.createCB(c);
22271         supr.setHeight.call(this, h, a, d, cb, e);
22272         if(!a){
22273             cb();
22274         }
22275     },
22276
22277     // overridden Element method
22278     setBounds : function(x, y, w, h, a, d, c, e){
22279         this.beforeAction();
22280         var cb = this.createCB(c);
22281         if(!a){
22282             this.storeXY([x, y]);
22283             supr.setXY.call(this, [x, y]);
22284             supr.setSize.call(this, w, h, a, d, cb, e);
22285             cb();
22286         }else{
22287             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22288         }
22289         return this;
22290     },
22291     
22292     /**
22293      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22294      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22295      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22296      * @param {Number} zindex The new z-index to set
22297      * @return {this} The Layer
22298      */
22299     setZIndex : function(zindex){
22300         this.zindex = zindex;
22301         this.setStyle("z-index", zindex + 2);
22302         if(this.shadow){
22303             this.shadow.setZIndex(zindex + 1);
22304         }
22305         if(this.shim){
22306             this.shim.setStyle("z-index", zindex);
22307         }
22308     }
22309 });
22310 })();/*
22311  * Based on:
22312  * Ext JS Library 1.1.1
22313  * Copyright(c) 2006-2007, Ext JS, LLC.
22314  *
22315  * Originally Released Under LGPL - original licence link has changed is not relivant.
22316  *
22317  * Fork - LGPL
22318  * <script type="text/javascript">
22319  */
22320
22321
22322 /**
22323  * @class Roo.Shadow
22324  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22325  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22326  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22327  * @constructor
22328  * Create a new Shadow
22329  * @param {Object} config The config object
22330  */
22331 Roo.Shadow = function(config){
22332     Roo.apply(this, config);
22333     if(typeof this.mode != "string"){
22334         this.mode = this.defaultMode;
22335     }
22336     var o = this.offset, a = {h: 0};
22337     var rad = Math.floor(this.offset/2);
22338     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22339         case "drop":
22340             a.w = 0;
22341             a.l = a.t = o;
22342             a.t -= 1;
22343             if(Roo.isIE){
22344                 a.l -= this.offset + rad;
22345                 a.t -= this.offset + rad;
22346                 a.w -= rad;
22347                 a.h -= rad;
22348                 a.t += 1;
22349             }
22350         break;
22351         case "sides":
22352             a.w = (o*2);
22353             a.l = -o;
22354             a.t = o-1;
22355             if(Roo.isIE){
22356                 a.l -= (this.offset - rad);
22357                 a.t -= this.offset + rad;
22358                 a.l += 1;
22359                 a.w -= (this.offset - rad)*2;
22360                 a.w -= rad + 1;
22361                 a.h -= 1;
22362             }
22363         break;
22364         case "frame":
22365             a.w = a.h = (o*2);
22366             a.l = a.t = -o;
22367             a.t += 1;
22368             a.h -= 2;
22369             if(Roo.isIE){
22370                 a.l -= (this.offset - rad);
22371                 a.t -= (this.offset - rad);
22372                 a.l += 1;
22373                 a.w -= (this.offset + rad + 1);
22374                 a.h -= (this.offset + rad);
22375                 a.h += 1;
22376             }
22377         break;
22378     };
22379
22380     this.adjusts = a;
22381 };
22382
22383 Roo.Shadow.prototype = {
22384     /**
22385      * @cfg {String} mode
22386      * The shadow display mode.  Supports the following options:<br />
22387      * sides: Shadow displays on both sides and bottom only<br />
22388      * frame: Shadow displays equally on all four sides<br />
22389      * drop: Traditional bottom-right drop shadow (default)
22390      */
22391     /**
22392      * @cfg {String} offset
22393      * The number of pixels to offset the shadow from the element (defaults to 4)
22394      */
22395     offset: 4,
22396
22397     // private
22398     defaultMode: "drop",
22399
22400     /**
22401      * Displays the shadow under the target element
22402      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22403      */
22404     show : function(target){
22405         target = Roo.get(target);
22406         if(!this.el){
22407             this.el = Roo.Shadow.Pool.pull();
22408             if(this.el.dom.nextSibling != target.dom){
22409                 this.el.insertBefore(target);
22410             }
22411         }
22412         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22413         if(Roo.isIE){
22414             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22415         }
22416         this.realign(
22417             target.getLeft(true),
22418             target.getTop(true),
22419             target.getWidth(),
22420             target.getHeight()
22421         );
22422         this.el.dom.style.display = "block";
22423     },
22424
22425     /**
22426      * Returns true if the shadow is visible, else false
22427      */
22428     isVisible : function(){
22429         return this.el ? true : false;  
22430     },
22431
22432     /**
22433      * Direct alignment when values are already available. Show must be called at least once before
22434      * calling this method to ensure it is initialized.
22435      * @param {Number} left The target element left position
22436      * @param {Number} top The target element top position
22437      * @param {Number} width The target element width
22438      * @param {Number} height The target element height
22439      */
22440     realign : function(l, t, w, h){
22441         if(!this.el){
22442             return;
22443         }
22444         var a = this.adjusts, d = this.el.dom, s = d.style;
22445         var iea = 0;
22446         s.left = (l+a.l)+"px";
22447         s.top = (t+a.t)+"px";
22448         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22449         if(s.width != sws || s.height != shs){
22450             s.width = sws;
22451             s.height = shs;
22452             if(!Roo.isIE){
22453                 var cn = d.childNodes;
22454                 var sww = Math.max(0, (sw-12))+"px";
22455                 cn[0].childNodes[1].style.width = sww;
22456                 cn[1].childNodes[1].style.width = sww;
22457                 cn[2].childNodes[1].style.width = sww;
22458                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22459             }
22460         }
22461     },
22462
22463     /**
22464      * Hides this shadow
22465      */
22466     hide : function(){
22467         if(this.el){
22468             this.el.dom.style.display = "none";
22469             Roo.Shadow.Pool.push(this.el);
22470             delete this.el;
22471         }
22472     },
22473
22474     /**
22475      * Adjust the z-index of this shadow
22476      * @param {Number} zindex The new z-index
22477      */
22478     setZIndex : function(z){
22479         this.zIndex = z;
22480         if(this.el){
22481             this.el.setStyle("z-index", z);
22482         }
22483     }
22484 };
22485
22486 // Private utility class that manages the internal Shadow cache
22487 Roo.Shadow.Pool = function(){
22488     var p = [];
22489     var markup = Roo.isIE ?
22490                  '<div class="x-ie-shadow"></div>' :
22491                  '<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>';
22492     return {
22493         pull : function(){
22494             var sh = p.shift();
22495             if(!sh){
22496                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22497                 sh.autoBoxAdjust = false;
22498             }
22499             return sh;
22500         },
22501
22502         push : function(sh){
22503             p.push(sh);
22504         }
22505     };
22506 }();/*
22507  * Based on:
22508  * Ext JS Library 1.1.1
22509  * Copyright(c) 2006-2007, Ext JS, LLC.
22510  *
22511  * Originally Released Under LGPL - original licence link has changed is not relivant.
22512  *
22513  * Fork - LGPL
22514  * <script type="text/javascript">
22515  */
22516
22517 /**
22518  * @class Roo.BoxComponent
22519  * @extends Roo.Component
22520  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22521  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22522  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22523  * layout containers.
22524  * @constructor
22525  * @param {Roo.Element/String/Object} config The configuration options.
22526  */
22527 Roo.BoxComponent = function(config){
22528     Roo.Component.call(this, config);
22529     this.addEvents({
22530         /**
22531          * @event resize
22532          * Fires after the component is resized.
22533              * @param {Roo.Component} this
22534              * @param {Number} adjWidth The box-adjusted width that was set
22535              * @param {Number} adjHeight The box-adjusted height that was set
22536              * @param {Number} rawWidth The width that was originally specified
22537              * @param {Number} rawHeight The height that was originally specified
22538              */
22539         resize : true,
22540         /**
22541          * @event move
22542          * Fires after the component is moved.
22543              * @param {Roo.Component} this
22544              * @param {Number} x The new x position
22545              * @param {Number} y The new y position
22546              */
22547         move : true
22548     });
22549 };
22550
22551 Roo.extend(Roo.BoxComponent, Roo.Component, {
22552     // private, set in afterRender to signify that the component has been rendered
22553     boxReady : false,
22554     // private, used to defer height settings to subclasses
22555     deferHeight: false,
22556     /** @cfg {Number} width
22557      * width (optional) size of component
22558      */
22559      /** @cfg {Number} height
22560      * height (optional) size of component
22561      */
22562      
22563     /**
22564      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22565      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22566      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22567      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22568      * @return {Roo.BoxComponent} this
22569      */
22570     setSize : function(w, h){
22571         // support for standard size objects
22572         if(typeof w == 'object'){
22573             h = w.height;
22574             w = w.width;
22575         }
22576         // not rendered
22577         if(!this.boxReady){
22578             this.width = w;
22579             this.height = h;
22580             return this;
22581         }
22582
22583         // prevent recalcs when not needed
22584         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22585             return this;
22586         }
22587         this.lastSize = {width: w, height: h};
22588
22589         var adj = this.adjustSize(w, h);
22590         var aw = adj.width, ah = adj.height;
22591         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22592             var rz = this.getResizeEl();
22593             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22594                 rz.setSize(aw, ah);
22595             }else if(!this.deferHeight && ah !== undefined){
22596                 rz.setHeight(ah);
22597             }else if(aw !== undefined){
22598                 rz.setWidth(aw);
22599             }
22600             this.onResize(aw, ah, w, h);
22601             this.fireEvent('resize', this, aw, ah, w, h);
22602         }
22603         return this;
22604     },
22605
22606     /**
22607      * Gets the current size of the component's underlying element.
22608      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22609      */
22610     getSize : function(){
22611         return this.el.getSize();
22612     },
22613
22614     /**
22615      * Gets the current XY position of the component's underlying element.
22616      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22617      * @return {Array} The XY position of the element (e.g., [100, 200])
22618      */
22619     getPosition : function(local){
22620         if(local === true){
22621             return [this.el.getLeft(true), this.el.getTop(true)];
22622         }
22623         return this.xy || this.el.getXY();
22624     },
22625
22626     /**
22627      * Gets the current box measurements of the component's underlying element.
22628      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22629      * @returns {Object} box An object in the format {x, y, width, height}
22630      */
22631     getBox : function(local){
22632         var s = this.el.getSize();
22633         if(local){
22634             s.x = this.el.getLeft(true);
22635             s.y = this.el.getTop(true);
22636         }else{
22637             var xy = this.xy || this.el.getXY();
22638             s.x = xy[0];
22639             s.y = xy[1];
22640         }
22641         return s;
22642     },
22643
22644     /**
22645      * Sets the current box measurements of the component's underlying element.
22646      * @param {Object} box An object in the format {x, y, width, height}
22647      * @returns {Roo.BoxComponent} this
22648      */
22649     updateBox : function(box){
22650         this.setSize(box.width, box.height);
22651         this.setPagePosition(box.x, box.y);
22652         return this;
22653     },
22654
22655     // protected
22656     getResizeEl : function(){
22657         return this.resizeEl || this.el;
22658     },
22659
22660     // protected
22661     getPositionEl : function(){
22662         return this.positionEl || this.el;
22663     },
22664
22665     /**
22666      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22667      * This method fires the move event.
22668      * @param {Number} left The new left
22669      * @param {Number} top The new top
22670      * @returns {Roo.BoxComponent} this
22671      */
22672     setPosition : function(x, y){
22673         this.x = x;
22674         this.y = y;
22675         if(!this.boxReady){
22676             return this;
22677         }
22678         var adj = this.adjustPosition(x, y);
22679         var ax = adj.x, ay = adj.y;
22680
22681         var el = this.getPositionEl();
22682         if(ax !== undefined || ay !== undefined){
22683             if(ax !== undefined && ay !== undefined){
22684                 el.setLeftTop(ax, ay);
22685             }else if(ax !== undefined){
22686                 el.setLeft(ax);
22687             }else if(ay !== undefined){
22688                 el.setTop(ay);
22689             }
22690             this.onPosition(ax, ay);
22691             this.fireEvent('move', this, ax, ay);
22692         }
22693         return this;
22694     },
22695
22696     /**
22697      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22698      * This method fires the move event.
22699      * @param {Number} x The new x position
22700      * @param {Number} y The new y position
22701      * @returns {Roo.BoxComponent} this
22702      */
22703     setPagePosition : function(x, y){
22704         this.pageX = x;
22705         this.pageY = y;
22706         if(!this.boxReady){
22707             return;
22708         }
22709         if(x === undefined || y === undefined){ // cannot translate undefined points
22710             return;
22711         }
22712         var p = this.el.translatePoints(x, y);
22713         this.setPosition(p.left, p.top);
22714         return this;
22715     },
22716
22717     // private
22718     onRender : function(ct, position){
22719         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22720         if(this.resizeEl){
22721             this.resizeEl = Roo.get(this.resizeEl);
22722         }
22723         if(this.positionEl){
22724             this.positionEl = Roo.get(this.positionEl);
22725         }
22726     },
22727
22728     // private
22729     afterRender : function(){
22730         Roo.BoxComponent.superclass.afterRender.call(this);
22731         this.boxReady = true;
22732         this.setSize(this.width, this.height);
22733         if(this.x || this.y){
22734             this.setPosition(this.x, this.y);
22735         }
22736         if(this.pageX || this.pageY){
22737             this.setPagePosition(this.pageX, this.pageY);
22738         }
22739     },
22740
22741     /**
22742      * Force the component's size to recalculate based on the underlying element's current height and width.
22743      * @returns {Roo.BoxComponent} this
22744      */
22745     syncSize : function(){
22746         delete this.lastSize;
22747         this.setSize(this.el.getWidth(), this.el.getHeight());
22748         return this;
22749     },
22750
22751     /**
22752      * Called after the component is resized, this method is empty by default but can be implemented by any
22753      * subclass that needs to perform custom logic after a resize occurs.
22754      * @param {Number} adjWidth The box-adjusted width that was set
22755      * @param {Number} adjHeight The box-adjusted height that was set
22756      * @param {Number} rawWidth The width that was originally specified
22757      * @param {Number} rawHeight The height that was originally specified
22758      */
22759     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22760
22761     },
22762
22763     /**
22764      * Called after the component is moved, this method is empty by default but can be implemented by any
22765      * subclass that needs to perform custom logic after a move occurs.
22766      * @param {Number} x The new x position
22767      * @param {Number} y The new y position
22768      */
22769     onPosition : function(x, y){
22770
22771     },
22772
22773     // private
22774     adjustSize : function(w, h){
22775         if(this.autoWidth){
22776             w = 'auto';
22777         }
22778         if(this.autoHeight){
22779             h = 'auto';
22780         }
22781         return {width : w, height: h};
22782     },
22783
22784     // private
22785     adjustPosition : function(x, y){
22786         return {x : x, y: y};
22787     }
22788 });/*
22789  * Based on:
22790  * Ext JS Library 1.1.1
22791  * Copyright(c) 2006-2007, Ext JS, LLC.
22792  *
22793  * Originally Released Under LGPL - original licence link has changed is not relivant.
22794  *
22795  * Fork - LGPL
22796  * <script type="text/javascript">
22797  */
22798
22799
22800 /**
22801  * @class Roo.SplitBar
22802  * @extends Roo.util.Observable
22803  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22804  * <br><br>
22805  * Usage:
22806  * <pre><code>
22807 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22808                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22809 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22810 split.minSize = 100;
22811 split.maxSize = 600;
22812 split.animate = true;
22813 split.on('moved', splitterMoved);
22814 </code></pre>
22815  * @constructor
22816  * Create a new SplitBar
22817  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22818  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22819  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22820  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22821                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22822                         position of the SplitBar).
22823  */
22824 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22825     
22826     /** @private */
22827     this.el = Roo.get(dragElement, true);
22828     this.el.dom.unselectable = "on";
22829     /** @private */
22830     this.resizingEl = Roo.get(resizingElement, true);
22831
22832     /**
22833      * @private
22834      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22835      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22836      * @type Number
22837      */
22838     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22839     
22840     /**
22841      * The minimum size of the resizing element. (Defaults to 0)
22842      * @type Number
22843      */
22844     this.minSize = 0;
22845     
22846     /**
22847      * The maximum size of the resizing element. (Defaults to 2000)
22848      * @type Number
22849      */
22850     this.maxSize = 2000;
22851     
22852     /**
22853      * Whether to animate the transition to the new size
22854      * @type Boolean
22855      */
22856     this.animate = false;
22857     
22858     /**
22859      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22860      * @type Boolean
22861      */
22862     this.useShim = false;
22863     
22864     /** @private */
22865     this.shim = null;
22866     
22867     if(!existingProxy){
22868         /** @private */
22869         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22870     }else{
22871         this.proxy = Roo.get(existingProxy).dom;
22872     }
22873     /** @private */
22874     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22875     
22876     /** @private */
22877     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22878     
22879     /** @private */
22880     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22881     
22882     /** @private */
22883     this.dragSpecs = {};
22884     
22885     /**
22886      * @private The adapter to use to positon and resize elements
22887      */
22888     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22889     this.adapter.init(this);
22890     
22891     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22892         /** @private */
22893         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22894         this.el.addClass("x-splitbar-h");
22895     }else{
22896         /** @private */
22897         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22898         this.el.addClass("x-splitbar-v");
22899     }
22900     
22901     this.addEvents({
22902         /**
22903          * @event resize
22904          * Fires when the splitter is moved (alias for {@link #event-moved})
22905          * @param {Roo.SplitBar} this
22906          * @param {Number} newSize the new width or height
22907          */
22908         "resize" : true,
22909         /**
22910          * @event moved
22911          * Fires when the splitter is moved
22912          * @param {Roo.SplitBar} this
22913          * @param {Number} newSize the new width or height
22914          */
22915         "moved" : true,
22916         /**
22917          * @event beforeresize
22918          * Fires before the splitter is dragged
22919          * @param {Roo.SplitBar} this
22920          */
22921         "beforeresize" : true,
22922
22923         "beforeapply" : true
22924     });
22925
22926     Roo.util.Observable.call(this);
22927 };
22928
22929 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22930     onStartProxyDrag : function(x, y){
22931         this.fireEvent("beforeresize", this);
22932         if(!this.overlay){
22933             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22934             o.unselectable();
22935             o.enableDisplayMode("block");
22936             // all splitbars share the same overlay
22937             Roo.SplitBar.prototype.overlay = o;
22938         }
22939         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22940         this.overlay.show();
22941         Roo.get(this.proxy).setDisplayed("block");
22942         var size = this.adapter.getElementSize(this);
22943         this.activeMinSize = this.getMinimumSize();;
22944         this.activeMaxSize = this.getMaximumSize();;
22945         var c1 = size - this.activeMinSize;
22946         var c2 = Math.max(this.activeMaxSize - size, 0);
22947         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22948             this.dd.resetConstraints();
22949             this.dd.setXConstraint(
22950                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22951                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22952             );
22953             this.dd.setYConstraint(0, 0);
22954         }else{
22955             this.dd.resetConstraints();
22956             this.dd.setXConstraint(0, 0);
22957             this.dd.setYConstraint(
22958                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22959                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22960             );
22961          }
22962         this.dragSpecs.startSize = size;
22963         this.dragSpecs.startPoint = [x, y];
22964         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22965     },
22966     
22967     /** 
22968      * @private Called after the drag operation by the DDProxy
22969      */
22970     onEndProxyDrag : function(e){
22971         Roo.get(this.proxy).setDisplayed(false);
22972         var endPoint = Roo.lib.Event.getXY(e);
22973         if(this.overlay){
22974             this.overlay.hide();
22975         }
22976         var newSize;
22977         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22978             newSize = this.dragSpecs.startSize + 
22979                 (this.placement == Roo.SplitBar.LEFT ?
22980                     endPoint[0] - this.dragSpecs.startPoint[0] :
22981                     this.dragSpecs.startPoint[0] - endPoint[0]
22982                 );
22983         }else{
22984             newSize = this.dragSpecs.startSize + 
22985                 (this.placement == Roo.SplitBar.TOP ?
22986                     endPoint[1] - this.dragSpecs.startPoint[1] :
22987                     this.dragSpecs.startPoint[1] - endPoint[1]
22988                 );
22989         }
22990         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22991         if(newSize != this.dragSpecs.startSize){
22992             if(this.fireEvent('beforeapply', this, newSize) !== false){
22993                 this.adapter.setElementSize(this, newSize);
22994                 this.fireEvent("moved", this, newSize);
22995                 this.fireEvent("resize", this, newSize);
22996             }
22997         }
22998     },
22999     
23000     /**
23001      * Get the adapter this SplitBar uses
23002      * @return The adapter object
23003      */
23004     getAdapter : function(){
23005         return this.adapter;
23006     },
23007     
23008     /**
23009      * Set the adapter this SplitBar uses
23010      * @param {Object} adapter A SplitBar adapter object
23011      */
23012     setAdapter : function(adapter){
23013         this.adapter = adapter;
23014         this.adapter.init(this);
23015     },
23016     
23017     /**
23018      * Gets the minimum size for the resizing element
23019      * @return {Number} The minimum size
23020      */
23021     getMinimumSize : function(){
23022         return this.minSize;
23023     },
23024     
23025     /**
23026      * Sets the minimum size for the resizing element
23027      * @param {Number} minSize The minimum size
23028      */
23029     setMinimumSize : function(minSize){
23030         this.minSize = minSize;
23031     },
23032     
23033     /**
23034      * Gets the maximum size for the resizing element
23035      * @return {Number} The maximum size
23036      */
23037     getMaximumSize : function(){
23038         return this.maxSize;
23039     },
23040     
23041     /**
23042      * Sets the maximum size for the resizing element
23043      * @param {Number} maxSize The maximum size
23044      */
23045     setMaximumSize : function(maxSize){
23046         this.maxSize = maxSize;
23047     },
23048     
23049     /**
23050      * Sets the initialize size for the resizing element
23051      * @param {Number} size The initial size
23052      */
23053     setCurrentSize : function(size){
23054         var oldAnimate = this.animate;
23055         this.animate = false;
23056         this.adapter.setElementSize(this, size);
23057         this.animate = oldAnimate;
23058     },
23059     
23060     /**
23061      * Destroy this splitbar. 
23062      * @param {Boolean} removeEl True to remove the element
23063      */
23064     destroy : function(removeEl){
23065         if(this.shim){
23066             this.shim.remove();
23067         }
23068         this.dd.unreg();
23069         this.proxy.parentNode.removeChild(this.proxy);
23070         if(removeEl){
23071             this.el.remove();
23072         }
23073     }
23074 });
23075
23076 /**
23077  * @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.
23078  */
23079 Roo.SplitBar.createProxy = function(dir){
23080     var proxy = new Roo.Element(document.createElement("div"));
23081     proxy.unselectable();
23082     var cls = 'x-splitbar-proxy';
23083     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23084     document.body.appendChild(proxy.dom);
23085     return proxy.dom;
23086 };
23087
23088 /** 
23089  * @class Roo.SplitBar.BasicLayoutAdapter
23090  * Default Adapter. It assumes the splitter and resizing element are not positioned
23091  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23092  */
23093 Roo.SplitBar.BasicLayoutAdapter = function(){
23094 };
23095
23096 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23097     // do nothing for now
23098     init : function(s){
23099     
23100     },
23101     /**
23102      * Called before drag operations to get the current size of the resizing element. 
23103      * @param {Roo.SplitBar} s The SplitBar using this adapter
23104      */
23105      getElementSize : function(s){
23106         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23107             return s.resizingEl.getWidth();
23108         }else{
23109             return s.resizingEl.getHeight();
23110         }
23111     },
23112     
23113     /**
23114      * Called after drag operations to set the size of the resizing element.
23115      * @param {Roo.SplitBar} s The SplitBar using this adapter
23116      * @param {Number} newSize The new size to set
23117      * @param {Function} onComplete A function to be invoked when resizing is complete
23118      */
23119     setElementSize : function(s, newSize, onComplete){
23120         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23121             if(!s.animate){
23122                 s.resizingEl.setWidth(newSize);
23123                 if(onComplete){
23124                     onComplete(s, newSize);
23125                 }
23126             }else{
23127                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23128             }
23129         }else{
23130             
23131             if(!s.animate){
23132                 s.resizingEl.setHeight(newSize);
23133                 if(onComplete){
23134                     onComplete(s, newSize);
23135                 }
23136             }else{
23137                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23138             }
23139         }
23140     }
23141 };
23142
23143 /** 
23144  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23145  * @extends Roo.SplitBar.BasicLayoutAdapter
23146  * Adapter that  moves the splitter element to align with the resized sizing element. 
23147  * Used with an absolute positioned SplitBar.
23148  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23149  * document.body, make sure you assign an id to the body element.
23150  */
23151 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23152     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23153     this.container = Roo.get(container);
23154 };
23155
23156 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23157     init : function(s){
23158         this.basic.init(s);
23159     },
23160     
23161     getElementSize : function(s){
23162         return this.basic.getElementSize(s);
23163     },
23164     
23165     setElementSize : function(s, newSize, onComplete){
23166         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23167     },
23168     
23169     moveSplitter : function(s){
23170         var yes = Roo.SplitBar;
23171         switch(s.placement){
23172             case yes.LEFT:
23173                 s.el.setX(s.resizingEl.getRight());
23174                 break;
23175             case yes.RIGHT:
23176                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23177                 break;
23178             case yes.TOP:
23179                 s.el.setY(s.resizingEl.getBottom());
23180                 break;
23181             case yes.BOTTOM:
23182                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23183                 break;
23184         }
23185     }
23186 };
23187
23188 /**
23189  * Orientation constant - Create a vertical SplitBar
23190  * @static
23191  * @type Number
23192  */
23193 Roo.SplitBar.VERTICAL = 1;
23194
23195 /**
23196  * Orientation constant - Create a horizontal SplitBar
23197  * @static
23198  * @type Number
23199  */
23200 Roo.SplitBar.HORIZONTAL = 2;
23201
23202 /**
23203  * Placement constant - The resizing element is to the left of the splitter element
23204  * @static
23205  * @type Number
23206  */
23207 Roo.SplitBar.LEFT = 1;
23208
23209 /**
23210  * Placement constant - The resizing element is to the right of the splitter element
23211  * @static
23212  * @type Number
23213  */
23214 Roo.SplitBar.RIGHT = 2;
23215
23216 /**
23217  * Placement constant - The resizing element is positioned above the splitter element
23218  * @static
23219  * @type Number
23220  */
23221 Roo.SplitBar.TOP = 3;
23222
23223 /**
23224  * Placement constant - The resizing element is positioned under splitter element
23225  * @static
23226  * @type Number
23227  */
23228 Roo.SplitBar.BOTTOM = 4;
23229 /*
23230  * Based on:
23231  * Ext JS Library 1.1.1
23232  * Copyright(c) 2006-2007, Ext JS, LLC.
23233  *
23234  * Originally Released Under LGPL - original licence link has changed is not relivant.
23235  *
23236  * Fork - LGPL
23237  * <script type="text/javascript">
23238  */
23239
23240 /**
23241  * @class Roo.View
23242  * @extends Roo.util.Observable
23243  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23244  * This class also supports single and multi selection modes. <br>
23245  * Create a data model bound view:
23246  <pre><code>
23247  var store = new Roo.data.Store(...);
23248
23249  var view = new Roo.View({
23250     el : "my-element",
23251     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23252  
23253     singleSelect: true,
23254     selectedClass: "ydataview-selected",
23255     store: store
23256  });
23257
23258  // listen for node click?
23259  view.on("click", function(vw, index, node, e){
23260  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23261  });
23262
23263  // load XML data
23264  dataModel.load("foobar.xml");
23265  </code></pre>
23266  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23267  * <br><br>
23268  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23269  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23270  * 
23271  * Note: old style constructor is still suported (container, template, config)
23272  * 
23273  * @constructor
23274  * Create a new View
23275  * @param {Object} config The config object
23276  * 
23277  */
23278 Roo.View = function(config, depreciated_tpl, depreciated_config){
23279     
23280     if (typeof(depreciated_tpl) == 'undefined') {
23281         // new way.. - universal constructor.
23282         Roo.apply(this, config);
23283         this.el  = Roo.get(this.el);
23284     } else {
23285         // old format..
23286         this.el  = Roo.get(config);
23287         this.tpl = depreciated_tpl;
23288         Roo.apply(this, depreciated_config);
23289     }
23290      
23291     
23292     if(typeof(this.tpl) == "string"){
23293         this.tpl = new Roo.Template(this.tpl);
23294     } 
23295     
23296     
23297     this.tpl.compile();
23298    
23299
23300      
23301     /** @private */
23302     this.addEvents({
23303     /**
23304      * @event beforeclick
23305      * Fires before a click is processed. Returns false to cancel the default action.
23306      * @param {Roo.View} this
23307      * @param {Number} index The index of the target node
23308      * @param {HTMLElement} node The target node
23309      * @param {Roo.EventObject} e The raw event object
23310      */
23311         "beforeclick" : true,
23312     /**
23313      * @event click
23314      * Fires when a template node is clicked.
23315      * @param {Roo.View} this
23316      * @param {Number} index The index of the target node
23317      * @param {HTMLElement} node The target node
23318      * @param {Roo.EventObject} e The raw event object
23319      */
23320         "click" : true,
23321     /**
23322      * @event dblclick
23323      * Fires when a template node is double clicked.
23324      * @param {Roo.View} this
23325      * @param {Number} index The index of the target node
23326      * @param {HTMLElement} node The target node
23327      * @param {Roo.EventObject} e The raw event object
23328      */
23329         "dblclick" : true,
23330     /**
23331      * @event contextmenu
23332      * Fires when a template node is right clicked.
23333      * @param {Roo.View} this
23334      * @param {Number} index The index of the target node
23335      * @param {HTMLElement} node The target node
23336      * @param {Roo.EventObject} e The raw event object
23337      */
23338         "contextmenu" : true,
23339     /**
23340      * @event selectionchange
23341      * Fires when the selected nodes change.
23342      * @param {Roo.View} this
23343      * @param {Array} selections Array of the selected nodes
23344      */
23345         "selectionchange" : true,
23346
23347     /**
23348      * @event beforeselect
23349      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23350      * @param {Roo.View} this
23351      * @param {HTMLElement} node The node to be selected
23352      * @param {Array} selections Array of currently selected nodes
23353      */
23354         "beforeselect" : true
23355     });
23356
23357     this.el.on({
23358         "click": this.onClick,
23359         "dblclick": this.onDblClick,
23360         "contextmenu": this.onContextMenu,
23361         scope:this
23362     });
23363
23364     this.selections = [];
23365     this.nodes = [];
23366     this.cmp = new Roo.CompositeElementLite([]);
23367     if(this.store){
23368         this.store = Roo.factory(this.store, Roo.data);
23369         this.setStore(this.store, true);
23370     }
23371     Roo.View.superclass.constructor.call(this);
23372 };
23373
23374 Roo.extend(Roo.View, Roo.util.Observable, {
23375     
23376      /**
23377      * @cfg {Roo.data.Store} store Data store to load data from.
23378      */
23379     store : false,
23380     
23381     /**
23382      * @cfg {String|Roo.Element} el The container element.
23383      */
23384     el : '',
23385     
23386     /**
23387      * @cfg {String|Roo.Template} tpl The template used by this View 
23388      */
23389     tpl : false,
23390     
23391     /**
23392      * @cfg {String} selectedClass The css class to add to selected nodes
23393      */
23394     selectedClass : "x-view-selected",
23395      /**
23396      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23397      */
23398     emptyText : "",
23399     /**
23400      * Returns the element this view is bound to.
23401      * @return {Roo.Element}
23402      */
23403     getEl : function(){
23404         return this.el;
23405     },
23406
23407     /**
23408      * Refreshes the view.
23409      */
23410     refresh : function(){
23411         var t = this.tpl;
23412         this.clearSelections();
23413         this.el.update("");
23414         var html = [];
23415         var records = this.store.getRange();
23416         if(records.length < 1){
23417             this.el.update(this.emptyText);
23418             return;
23419         }
23420         for(var i = 0, len = records.length; i < len; i++){
23421             var data = this.prepareData(records[i].data, i, records[i]);
23422             html[html.length] = t.apply(data);
23423         }
23424         this.el.update(html.join(""));
23425         this.nodes = this.el.dom.childNodes;
23426         this.updateIndexes(0);
23427     },
23428
23429     /**
23430      * Function to override to reformat the data that is sent to
23431      * the template for each node.
23432      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23433      * a JSON object for an UpdateManager bound view).
23434      */
23435     prepareData : function(data){
23436         return data;
23437     },
23438
23439     onUpdate : function(ds, record){
23440         this.clearSelections();
23441         var index = this.store.indexOf(record);
23442         var n = this.nodes[index];
23443         this.tpl.insertBefore(n, this.prepareData(record.data));
23444         n.parentNode.removeChild(n);
23445         this.updateIndexes(index, index);
23446     },
23447
23448     onAdd : function(ds, records, index){
23449         this.clearSelections();
23450         if(this.nodes.length == 0){
23451             this.refresh();
23452             return;
23453         }
23454         var n = this.nodes[index];
23455         for(var i = 0, len = records.length; i < len; i++){
23456             var d = this.prepareData(records[i].data);
23457             if(n){
23458                 this.tpl.insertBefore(n, d);
23459             }else{
23460                 this.tpl.append(this.el, d);
23461             }
23462         }
23463         this.updateIndexes(index);
23464     },
23465
23466     onRemove : function(ds, record, index){
23467         this.clearSelections();
23468         this.el.dom.removeChild(this.nodes[index]);
23469         this.updateIndexes(index);
23470     },
23471
23472     /**
23473      * Refresh an individual node.
23474      * @param {Number} index
23475      */
23476     refreshNode : function(index){
23477         this.onUpdate(this.store, this.store.getAt(index));
23478     },
23479
23480     updateIndexes : function(startIndex, endIndex){
23481         var ns = this.nodes;
23482         startIndex = startIndex || 0;
23483         endIndex = endIndex || ns.length - 1;
23484         for(var i = startIndex; i <= endIndex; i++){
23485             ns[i].nodeIndex = i;
23486         }
23487     },
23488
23489     /**
23490      * Changes the data store this view uses and refresh the view.
23491      * @param {Store} store
23492      */
23493     setStore : function(store, initial){
23494         if(!initial && this.store){
23495             this.store.un("datachanged", this.refresh);
23496             this.store.un("add", this.onAdd);
23497             this.store.un("remove", this.onRemove);
23498             this.store.un("update", this.onUpdate);
23499             this.store.un("clear", this.refresh);
23500         }
23501         if(store){
23502           
23503             store.on("datachanged", this.refresh, this);
23504             store.on("add", this.onAdd, this);
23505             store.on("remove", this.onRemove, this);
23506             store.on("update", this.onUpdate, this);
23507             store.on("clear", this.refresh, this);
23508         }
23509         
23510         if(store){
23511             this.refresh();
23512         }
23513     },
23514
23515     /**
23516      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23517      * @param {HTMLElement} node
23518      * @return {HTMLElement} The template node
23519      */
23520     findItemFromChild : function(node){
23521         var el = this.el.dom;
23522         if(!node || node.parentNode == el){
23523                     return node;
23524             }
23525             var p = node.parentNode;
23526             while(p && p != el){
23527             if(p.parentNode == el){
23528                 return p;
23529             }
23530             p = p.parentNode;
23531         }
23532             return null;
23533     },
23534
23535     /** @ignore */
23536     onClick : function(e){
23537         var item = this.findItemFromChild(e.getTarget());
23538         if(item){
23539             var index = this.indexOf(item);
23540             if(this.onItemClick(item, index, e) !== false){
23541                 this.fireEvent("click", this, index, item, e);
23542             }
23543         }else{
23544             this.clearSelections();
23545         }
23546     },
23547
23548     /** @ignore */
23549     onContextMenu : function(e){
23550         var item = this.findItemFromChild(e.getTarget());
23551         if(item){
23552             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23553         }
23554     },
23555
23556     /** @ignore */
23557     onDblClick : function(e){
23558         var item = this.findItemFromChild(e.getTarget());
23559         if(item){
23560             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23561         }
23562     },
23563
23564     onItemClick : function(item, index, e){
23565         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23566             return false;
23567         }
23568         if(this.multiSelect || this.singleSelect){
23569             if(this.multiSelect && e.shiftKey && this.lastSelection){
23570                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23571             }else{
23572                 this.select(item, this.multiSelect && e.ctrlKey);
23573                 this.lastSelection = item;
23574             }
23575             e.preventDefault();
23576         }
23577         return true;
23578     },
23579
23580     /**
23581      * Get the number of selected nodes.
23582      * @return {Number}
23583      */
23584     getSelectionCount : function(){
23585         return this.selections.length;
23586     },
23587
23588     /**
23589      * Get the currently selected nodes.
23590      * @return {Array} An array of HTMLElements
23591      */
23592     getSelectedNodes : function(){
23593         return this.selections;
23594     },
23595
23596     /**
23597      * Get the indexes of the selected nodes.
23598      * @return {Array}
23599      */
23600     getSelectedIndexes : function(){
23601         var indexes = [], s = this.selections;
23602         for(var i = 0, len = s.length; i < len; i++){
23603             indexes.push(s[i].nodeIndex);
23604         }
23605         return indexes;
23606     },
23607
23608     /**
23609      * Clear all selections
23610      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23611      */
23612     clearSelections : function(suppressEvent){
23613         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23614             this.cmp.elements = this.selections;
23615             this.cmp.removeClass(this.selectedClass);
23616             this.selections = [];
23617             if(!suppressEvent){
23618                 this.fireEvent("selectionchange", this, this.selections);
23619             }
23620         }
23621     },
23622
23623     /**
23624      * Returns true if the passed node is selected
23625      * @param {HTMLElement/Number} node The node or node index
23626      * @return {Boolean}
23627      */
23628     isSelected : function(node){
23629         var s = this.selections;
23630         if(s.length < 1){
23631             return false;
23632         }
23633         node = this.getNode(node);
23634         return s.indexOf(node) !== -1;
23635     },
23636
23637     /**
23638      * Selects nodes.
23639      * @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
23640      * @param {Boolean} keepExisting (optional) true to keep existing selections
23641      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23642      */
23643     select : function(nodeInfo, keepExisting, suppressEvent){
23644         if(nodeInfo instanceof Array){
23645             if(!keepExisting){
23646                 this.clearSelections(true);
23647             }
23648             for(var i = 0, len = nodeInfo.length; i < len; i++){
23649                 this.select(nodeInfo[i], true, true);
23650             }
23651         } else{
23652             var node = this.getNode(nodeInfo);
23653             if(node && !this.isSelected(node)){
23654                 if(!keepExisting){
23655                     this.clearSelections(true);
23656                 }
23657                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23658                     Roo.fly(node).addClass(this.selectedClass);
23659                     this.selections.push(node);
23660                     if(!suppressEvent){
23661                         this.fireEvent("selectionchange", this, this.selections);
23662                     }
23663                 }
23664             }
23665         }
23666     },
23667
23668     /**
23669      * Gets a template node.
23670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23671      * @return {HTMLElement} The node or null if it wasn't found
23672      */
23673     getNode : function(nodeInfo){
23674         if(typeof nodeInfo == "string"){
23675             return document.getElementById(nodeInfo);
23676         }else if(typeof nodeInfo == "number"){
23677             return this.nodes[nodeInfo];
23678         }
23679         return nodeInfo;
23680     },
23681
23682     /**
23683      * Gets a range template nodes.
23684      * @param {Number} startIndex
23685      * @param {Number} endIndex
23686      * @return {Array} An array of nodes
23687      */
23688     getNodes : function(start, end){
23689         var ns = this.nodes;
23690         start = start || 0;
23691         end = typeof end == "undefined" ? ns.length - 1 : end;
23692         var nodes = [];
23693         if(start <= end){
23694             for(var i = start; i <= end; i++){
23695                 nodes.push(ns[i]);
23696             }
23697         } else{
23698             for(var i = start; i >= end; i--){
23699                 nodes.push(ns[i]);
23700             }
23701         }
23702         return nodes;
23703     },
23704
23705     /**
23706      * Finds the index of the passed node
23707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23708      * @return {Number} The index of the node or -1
23709      */
23710     indexOf : function(node){
23711         node = this.getNode(node);
23712         if(typeof node.nodeIndex == "number"){
23713             return node.nodeIndex;
23714         }
23715         var ns = this.nodes;
23716         for(var i = 0, len = ns.length; i < len; i++){
23717             if(ns[i] == node){
23718                 return i;
23719             }
23720         }
23721         return -1;
23722     }
23723 });
23724 /*
23725  * Based on:
23726  * Ext JS Library 1.1.1
23727  * Copyright(c) 2006-2007, Ext JS, LLC.
23728  *
23729  * Originally Released Under LGPL - original licence link has changed is not relivant.
23730  *
23731  * Fork - LGPL
23732  * <script type="text/javascript">
23733  */
23734
23735 /**
23736  * @class Roo.JsonView
23737  * @extends Roo.View
23738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23739 <pre><code>
23740 var view = new Roo.JsonView({
23741     container: "my-element",
23742     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23743     multiSelect: true, 
23744     jsonRoot: "data" 
23745 });
23746
23747 // listen for node click?
23748 view.on("click", function(vw, index, node, e){
23749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23750 });
23751
23752 // direct load of JSON data
23753 view.load("foobar.php");
23754
23755 // Example from my blog list
23756 var tpl = new Roo.Template(
23757     '&lt;div class="entry"&gt;' +
23758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23760     "&lt;/div&gt;&lt;hr /&gt;"
23761 );
23762
23763 var moreView = new Roo.JsonView({
23764     container :  "entry-list", 
23765     template : tpl,
23766     jsonRoot: "posts"
23767 });
23768 moreView.on("beforerender", this.sortEntries, this);
23769 moreView.load({
23770     url: "/blog/get-posts.php",
23771     params: "allposts=true",
23772     text: "Loading Blog Entries..."
23773 });
23774 </code></pre>
23775
23776 * Note: old code is supported with arguments : (container, template, config)
23777
23778
23779  * @constructor
23780  * Create a new JsonView
23781  * 
23782  * @param {Object} config The config object
23783  * 
23784  */
23785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23786     
23787     
23788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23789
23790     var um = this.el.getUpdateManager();
23791     um.setRenderer(this);
23792     um.on("update", this.onLoad, this);
23793     um.on("failure", this.onLoadException, this);
23794
23795     /**
23796      * @event beforerender
23797      * Fires before rendering of the downloaded JSON data.
23798      * @param {Roo.JsonView} this
23799      * @param {Object} data The JSON data loaded
23800      */
23801     /**
23802      * @event load
23803      * Fires when data is loaded.
23804      * @param {Roo.JsonView} this
23805      * @param {Object} data The JSON data loaded
23806      * @param {Object} response The raw Connect response object
23807      */
23808     /**
23809      * @event loadexception
23810      * Fires when loading fails.
23811      * @param {Roo.JsonView} this
23812      * @param {Object} response The raw Connect response object
23813      */
23814     this.addEvents({
23815         'beforerender' : true,
23816         'load' : true,
23817         'loadexception' : true
23818     });
23819 };
23820 Roo.extend(Roo.JsonView, Roo.View, {
23821     /**
23822      * @type {String} The root property in the loaded JSON object that contains the data
23823      */
23824     jsonRoot : "",
23825
23826     /**
23827      * Refreshes the view.
23828      */
23829     refresh : function(){
23830         this.clearSelections();
23831         this.el.update("");
23832         var html = [];
23833         var o = this.jsonData;
23834         if(o && o.length > 0){
23835             for(var i = 0, len = o.length; i < len; i++){
23836                 var data = this.prepareData(o[i], i, o);
23837                 html[html.length] = this.tpl.apply(data);
23838             }
23839         }else{
23840             html.push(this.emptyText);
23841         }
23842         this.el.update(html.join(""));
23843         this.nodes = this.el.dom.childNodes;
23844         this.updateIndexes(0);
23845     },
23846
23847     /**
23848      * 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.
23849      * @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:
23850      <pre><code>
23851      view.load({
23852          url: "your-url.php",
23853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23854          callback: yourFunction,
23855          scope: yourObject, //(optional scope)
23856          discardUrl: false,
23857          nocache: false,
23858          text: "Loading...",
23859          timeout: 30,
23860          scripts: false
23861      });
23862      </code></pre>
23863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23864      * 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.
23865      * @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}
23866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23867      * @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.
23868      */
23869     load : function(){
23870         var um = this.el.getUpdateManager();
23871         um.update.apply(um, arguments);
23872     },
23873
23874     render : function(el, response){
23875         this.clearSelections();
23876         this.el.update("");
23877         var o;
23878         try{
23879             o = Roo.util.JSON.decode(response.responseText);
23880             if(this.jsonRoot){
23881                 
23882                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23883             }
23884         } catch(e){
23885         }
23886         /**
23887          * The current JSON data or null
23888          */
23889         this.jsonData = o;
23890         this.beforeRender();
23891         this.refresh();
23892     },
23893
23894 /**
23895  * Get the number of records in the current JSON dataset
23896  * @return {Number}
23897  */
23898     getCount : function(){
23899         return this.jsonData ? this.jsonData.length : 0;
23900     },
23901
23902 /**
23903  * Returns the JSON object for the specified node(s)
23904  * @param {HTMLElement/Array} node The node or an array of nodes
23905  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23906  * you get the JSON object for the node
23907  */
23908     getNodeData : function(node){
23909         if(node instanceof Array){
23910             var data = [];
23911             for(var i = 0, len = node.length; i < len; i++){
23912                 data.push(this.getNodeData(node[i]));
23913             }
23914             return data;
23915         }
23916         return this.jsonData[this.indexOf(node)] || null;
23917     },
23918
23919     beforeRender : function(){
23920         this.snapshot = this.jsonData;
23921         if(this.sortInfo){
23922             this.sort.apply(this, this.sortInfo);
23923         }
23924         this.fireEvent("beforerender", this, this.jsonData);
23925     },
23926
23927     onLoad : function(el, o){
23928         this.fireEvent("load", this, this.jsonData, o);
23929     },
23930
23931     onLoadException : function(el, o){
23932         this.fireEvent("loadexception", this, o);
23933     },
23934
23935 /**
23936  * Filter the data by a specific property.
23937  * @param {String} property A property on your JSON objects
23938  * @param {String/RegExp} value Either string that the property values
23939  * should start with, or a RegExp to test against the property
23940  */
23941     filter : function(property, value){
23942         if(this.jsonData){
23943             var data = [];
23944             var ss = this.snapshot;
23945             if(typeof value == "string"){
23946                 var vlen = value.length;
23947                 if(vlen == 0){
23948                     this.clearFilter();
23949                     return;
23950                 }
23951                 value = value.toLowerCase();
23952                 for(var i = 0, len = ss.length; i < len; i++){
23953                     var o = ss[i];
23954                     if(o[property].substr(0, vlen).toLowerCase() == value){
23955                         data.push(o);
23956                     }
23957                 }
23958             } else if(value.exec){ // regex?
23959                 for(var i = 0, len = ss.length; i < len; i++){
23960                     var o = ss[i];
23961                     if(value.test(o[property])){
23962                         data.push(o);
23963                     }
23964                 }
23965             } else{
23966                 return;
23967             }
23968             this.jsonData = data;
23969             this.refresh();
23970         }
23971     },
23972
23973 /**
23974  * Filter by a function. The passed function will be called with each
23975  * object in the current dataset. If the function returns true the value is kept,
23976  * otherwise it is filtered.
23977  * @param {Function} fn
23978  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23979  */
23980     filterBy : function(fn, scope){
23981         if(this.jsonData){
23982             var data = [];
23983             var ss = this.snapshot;
23984             for(var i = 0, len = ss.length; i < len; i++){
23985                 var o = ss[i];
23986                 if(fn.call(scope || this, o)){
23987                     data.push(o);
23988                 }
23989             }
23990             this.jsonData = data;
23991             this.refresh();
23992         }
23993     },
23994
23995 /**
23996  * Clears the current filter.
23997  */
23998     clearFilter : function(){
23999         if(this.snapshot && this.jsonData != this.snapshot){
24000             this.jsonData = this.snapshot;
24001             this.refresh();
24002         }
24003     },
24004
24005
24006 /**
24007  * Sorts the data for this view and refreshes it.
24008  * @param {String} property A property on your JSON objects to sort on
24009  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24010  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24011  */
24012     sort : function(property, dir, sortType){
24013         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24014         if(this.jsonData){
24015             var p = property;
24016             var dsc = dir && dir.toLowerCase() == "desc";
24017             var f = function(o1, o2){
24018                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24019                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24020                 ;
24021                 if(v1 < v2){
24022                     return dsc ? +1 : -1;
24023                 } else if(v1 > v2){
24024                     return dsc ? -1 : +1;
24025                 } else{
24026                     return 0;
24027                 }
24028             };
24029             this.jsonData.sort(f);
24030             this.refresh();
24031             if(this.jsonData != this.snapshot){
24032                 this.snapshot.sort(f);
24033             }
24034         }
24035     }
24036 });/*
24037  * Based on:
24038  * Ext JS Library 1.1.1
24039  * Copyright(c) 2006-2007, Ext JS, LLC.
24040  *
24041  * Originally Released Under LGPL - original licence link has changed is not relivant.
24042  *
24043  * Fork - LGPL
24044  * <script type="text/javascript">
24045  */
24046  
24047
24048 /**
24049  * @class Roo.ColorPalette
24050  * @extends Roo.Component
24051  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24052  * Here's an example of typical usage:
24053  * <pre><code>
24054 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24055 cp.render('my-div');
24056
24057 cp.on('select', function(palette, selColor){
24058     // do something with selColor
24059 });
24060 </code></pre>
24061  * @constructor
24062  * Create a new ColorPalette
24063  * @param {Object} config The config object
24064  */
24065 Roo.ColorPalette = function(config){
24066     Roo.ColorPalette.superclass.constructor.call(this, config);
24067     this.addEvents({
24068         /**
24069              * @event select
24070              * Fires when a color is selected
24071              * @param {ColorPalette} this
24072              * @param {String} color The 6-digit color hex code (without the # symbol)
24073              */
24074         select: true
24075     });
24076
24077     if(this.handler){
24078         this.on("select", this.handler, this.scope, true);
24079     }
24080 };
24081 Roo.extend(Roo.ColorPalette, Roo.Component, {
24082     /**
24083      * @cfg {String} itemCls
24084      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24085      */
24086     itemCls : "x-color-palette",
24087     /**
24088      * @cfg {String} value
24089      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24090      * the hex codes are case-sensitive.
24091      */
24092     value : null,
24093     clickEvent:'click',
24094     // private
24095     ctype: "Roo.ColorPalette",
24096
24097     /**
24098      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24099      */
24100     allowReselect : false,
24101
24102     /**
24103      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24104      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24105      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24106      * of colors with the width setting until the box is symmetrical.</p>
24107      * <p>You can override individual colors if needed:</p>
24108      * <pre><code>
24109 var cp = new Roo.ColorPalette();
24110 cp.colors[0] = "FF0000";  // change the first box to red
24111 </code></pre>
24112
24113 Or you can provide a custom array of your own for complete control:
24114 <pre><code>
24115 var cp = new Roo.ColorPalette();
24116 cp.colors = ["000000", "993300", "333300"];
24117 </code></pre>
24118      * @type Array
24119      */
24120     colors : [
24121         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24122         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24123         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24124         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24125         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24126     ],
24127
24128     // private
24129     onRender : function(container, position){
24130         var t = new Roo.MasterTemplate(
24131             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24132         );
24133         var c = this.colors;
24134         for(var i = 0, len = c.length; i < len; i++){
24135             t.add([c[i]]);
24136         }
24137         var el = document.createElement("div");
24138         el.className = this.itemCls;
24139         t.overwrite(el);
24140         container.dom.insertBefore(el, position);
24141         this.el = Roo.get(el);
24142         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24143         if(this.clickEvent != 'click'){
24144             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24145         }
24146     },
24147
24148     // private
24149     afterRender : function(){
24150         Roo.ColorPalette.superclass.afterRender.call(this);
24151         if(this.value){
24152             var s = this.value;
24153             this.value = null;
24154             this.select(s);
24155         }
24156     },
24157
24158     // private
24159     handleClick : function(e, t){
24160         e.preventDefault();
24161         if(!this.disabled){
24162             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24163             this.select(c.toUpperCase());
24164         }
24165     },
24166
24167     /**
24168      * Selects the specified color in the palette (fires the select event)
24169      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24170      */
24171     select : function(color){
24172         color = color.replace("#", "");
24173         if(color != this.value || this.allowReselect){
24174             var el = this.el;
24175             if(this.value){
24176                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24177             }
24178             el.child("a.color-"+color).addClass("x-color-palette-sel");
24179             this.value = color;
24180             this.fireEvent("select", this, color);
24181         }
24182     }
24183 });/*
24184  * Based on:
24185  * Ext JS Library 1.1.1
24186  * Copyright(c) 2006-2007, Ext JS, LLC.
24187  *
24188  * Originally Released Under LGPL - original licence link has changed is not relivant.
24189  *
24190  * Fork - LGPL
24191  * <script type="text/javascript">
24192  */
24193  
24194 /**
24195  * @class Roo.DatePicker
24196  * @extends Roo.Component
24197  * Simple date picker class.
24198  * @constructor
24199  * Create a new DatePicker
24200  * @param {Object} config The config object
24201  */
24202 Roo.DatePicker = function(config){
24203     Roo.DatePicker.superclass.constructor.call(this, config);
24204
24205     this.value = config && config.value ?
24206                  config.value.clearTime() : new Date().clearTime();
24207
24208     this.addEvents({
24209         /**
24210              * @event select
24211              * Fires when a date is selected
24212              * @param {DatePicker} this
24213              * @param {Date} date The selected date
24214              */
24215         select: true
24216     });
24217
24218     if(this.handler){
24219         this.on("select", this.handler,  this.scope || this);
24220     }
24221     // build the disabledDatesRE
24222     if(!this.disabledDatesRE && this.disabledDates){
24223         var dd = this.disabledDates;
24224         var re = "(?:";
24225         for(var i = 0; i < dd.length; i++){
24226             re += dd[i];
24227             if(i != dd.length-1) re += "|";
24228         }
24229         this.disabledDatesRE = new RegExp(re + ")");
24230     }
24231 };
24232
24233 Roo.extend(Roo.DatePicker, Roo.Component, {
24234     /**
24235      * @cfg {String} todayText
24236      * The text to display on the button that selects the current date (defaults to "Today")
24237      */
24238     todayText : "Today",
24239     /**
24240      * @cfg {String} okText
24241      * The text to display on the ok button
24242      */
24243     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24244     /**
24245      * @cfg {String} cancelText
24246      * The text to display on the cancel button
24247      */
24248     cancelText : "Cancel",
24249     /**
24250      * @cfg {String} todayTip
24251      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24252      */
24253     todayTip : "{0} (Spacebar)",
24254     /**
24255      * @cfg {Date} minDate
24256      * Minimum allowable date (JavaScript date object, defaults to null)
24257      */
24258     minDate : null,
24259     /**
24260      * @cfg {Date} maxDate
24261      * Maximum allowable date (JavaScript date object, defaults to null)
24262      */
24263     maxDate : null,
24264     /**
24265      * @cfg {String} minText
24266      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24267      */
24268     minText : "This date is before the minimum date",
24269     /**
24270      * @cfg {String} maxText
24271      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24272      */
24273     maxText : "This date is after the maximum date",
24274     /**
24275      * @cfg {String} format
24276      * The default date format string which can be overriden for localization support.  The format must be
24277      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24278      */
24279     format : "m/d/y",
24280     /**
24281      * @cfg {Array} disabledDays
24282      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24283      */
24284     disabledDays : null,
24285     /**
24286      * @cfg {String} disabledDaysText
24287      * The tooltip to display when the date falls on a disabled day (defaults to "")
24288      */
24289     disabledDaysText : "",
24290     /**
24291      * @cfg {RegExp} disabledDatesRE
24292      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24293      */
24294     disabledDatesRE : null,
24295     /**
24296      * @cfg {String} disabledDatesText
24297      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24298      */
24299     disabledDatesText : "",
24300     /**
24301      * @cfg {Boolean} constrainToViewport
24302      * True to constrain the date picker to the viewport (defaults to true)
24303      */
24304     constrainToViewport : true,
24305     /**
24306      * @cfg {Array} monthNames
24307      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24308      */
24309     monthNames : Date.monthNames,
24310     /**
24311      * @cfg {Array} dayNames
24312      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24313      */
24314     dayNames : Date.dayNames,
24315     /**
24316      * @cfg {String} nextText
24317      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24318      */
24319     nextText: 'Next Month (Control+Right)',
24320     /**
24321      * @cfg {String} prevText
24322      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24323      */
24324     prevText: 'Previous Month (Control+Left)',
24325     /**
24326      * @cfg {String} monthYearText
24327      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24328      */
24329     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24330     /**
24331      * @cfg {Number} startDay
24332      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24333      */
24334     startDay : 0,
24335     /**
24336      * @cfg {Bool} showClear
24337      * Show a clear button (usefull for date form elements that can be blank.)
24338      */
24339     
24340     showClear: false,
24341     
24342     /**
24343      * Sets the value of the date field
24344      * @param {Date} value The date to set
24345      */
24346     setValue : function(value){
24347         var old = this.value;
24348         this.value = value.clearTime(true);
24349         if(this.el){
24350             this.update(this.value);
24351         }
24352     },
24353
24354     /**
24355      * Gets the current selected value of the date field
24356      * @return {Date} The selected date
24357      */
24358     getValue : function(){
24359         return this.value;
24360     },
24361
24362     // private
24363     focus : function(){
24364         if(this.el){
24365             this.update(this.activeDate);
24366         }
24367     },
24368
24369     // private
24370     onRender : function(container, position){
24371         var m = [
24372              '<table cellspacing="0">',
24373                 '<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>',
24374                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24375         var dn = this.dayNames;
24376         for(var i = 0; i < 7; i++){
24377             var d = this.startDay+i;
24378             if(d > 6){
24379                 d = d-7;
24380             }
24381             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24382         }
24383         m[m.length] = "</tr></thead><tbody><tr>";
24384         for(var i = 0; i < 42; i++) {
24385             if(i % 7 == 0 && i != 0){
24386                 m[m.length] = "</tr><tr>";
24387             }
24388             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24389         }
24390         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24391             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24392
24393         var el = document.createElement("div");
24394         el.className = "x-date-picker";
24395         el.innerHTML = m.join("");
24396
24397         container.dom.insertBefore(el, position);
24398
24399         this.el = Roo.get(el);
24400         this.eventEl = Roo.get(el.firstChild);
24401
24402         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24403             handler: this.showPrevMonth,
24404             scope: this,
24405             preventDefault:true,
24406             stopDefault:true
24407         });
24408
24409         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24410             handler: this.showNextMonth,
24411             scope: this,
24412             preventDefault:true,
24413             stopDefault:true
24414         });
24415
24416         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24417
24418         this.monthPicker = this.el.down('div.x-date-mp');
24419         this.monthPicker.enableDisplayMode('block');
24420         
24421         var kn = new Roo.KeyNav(this.eventEl, {
24422             "left" : function(e){
24423                 e.ctrlKey ?
24424                     this.showPrevMonth() :
24425                     this.update(this.activeDate.add("d", -1));
24426             },
24427
24428             "right" : function(e){
24429                 e.ctrlKey ?
24430                     this.showNextMonth() :
24431                     this.update(this.activeDate.add("d", 1));
24432             },
24433
24434             "up" : function(e){
24435                 e.ctrlKey ?
24436                     this.showNextYear() :
24437                     this.update(this.activeDate.add("d", -7));
24438             },
24439
24440             "down" : function(e){
24441                 e.ctrlKey ?
24442                     this.showPrevYear() :
24443                     this.update(this.activeDate.add("d", 7));
24444             },
24445
24446             "pageUp" : function(e){
24447                 this.showNextMonth();
24448             },
24449
24450             "pageDown" : function(e){
24451                 this.showPrevMonth();
24452             },
24453
24454             "enter" : function(e){
24455                 e.stopPropagation();
24456                 return true;
24457             },
24458
24459             scope : this
24460         });
24461
24462         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24463
24464         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24465
24466         this.el.unselectable();
24467         
24468         this.cells = this.el.select("table.x-date-inner tbody td");
24469         this.textNodes = this.el.query("table.x-date-inner tbody span");
24470
24471         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24472             text: "&#160;",
24473             tooltip: this.monthYearText
24474         });
24475
24476         this.mbtn.on('click', this.showMonthPicker, this);
24477         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24478
24479
24480         var today = (new Date()).dateFormat(this.format);
24481         
24482         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24483         baseTb.add({
24484             text: String.format(this.todayText, today),
24485             tooltip: String.format(this.todayTip, today),
24486             handler: this.selectToday,
24487             scope: this
24488         });
24489         
24490         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24491             
24492         //});
24493         if (this.showClear) {
24494             
24495             baseTb.add( new Roo.Toolbar.Fill());
24496             baseTb.add({
24497                 text: '&#160;',
24498                 cls: 'x-btn-icon x-btn-clear',
24499                 handler: function() {
24500                     //this.value = '';
24501                     this.fireEvent("select", this, '');
24502                 },
24503                 scope: this
24504             });
24505         }
24506         
24507         
24508         if(Roo.isIE){
24509             this.el.repaint();
24510         }
24511         this.update(this.value);
24512     },
24513
24514     createMonthPicker : function(){
24515         if(!this.monthPicker.dom.firstChild){
24516             var buf = ['<table border="0" cellspacing="0">'];
24517             for(var i = 0; i < 6; i++){
24518                 buf.push(
24519                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24520                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24521                     i == 0 ?
24522                     '<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>' :
24523                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24524                 );
24525             }
24526             buf.push(
24527                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24528                     this.okText,
24529                     '</button><button type="button" class="x-date-mp-cancel">',
24530                     this.cancelText,
24531                     '</button></td></tr>',
24532                 '</table>'
24533             );
24534             this.monthPicker.update(buf.join(''));
24535             this.monthPicker.on('click', this.onMonthClick, this);
24536             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24537
24538             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24539             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24540
24541             this.mpMonths.each(function(m, a, i){
24542                 i += 1;
24543                 if((i%2) == 0){
24544                     m.dom.xmonth = 5 + Math.round(i * .5);
24545                 }else{
24546                     m.dom.xmonth = Math.round((i-1) * .5);
24547                 }
24548             });
24549         }
24550     },
24551
24552     showMonthPicker : function(){
24553         this.createMonthPicker();
24554         var size = this.el.getSize();
24555         this.monthPicker.setSize(size);
24556         this.monthPicker.child('table').setSize(size);
24557
24558         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24559         this.updateMPMonth(this.mpSelMonth);
24560         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24561         this.updateMPYear(this.mpSelYear);
24562
24563         this.monthPicker.slideIn('t', {duration:.2});
24564     },
24565
24566     updateMPYear : function(y){
24567         this.mpyear = y;
24568         var ys = this.mpYears.elements;
24569         for(var i = 1; i <= 10; i++){
24570             var td = ys[i-1], y2;
24571             if((i%2) == 0){
24572                 y2 = y + Math.round(i * .5);
24573                 td.firstChild.innerHTML = y2;
24574                 td.xyear = y2;
24575             }else{
24576                 y2 = y - (5-Math.round(i * .5));
24577                 td.firstChild.innerHTML = y2;
24578                 td.xyear = y2;
24579             }
24580             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24581         }
24582     },
24583
24584     updateMPMonth : function(sm){
24585         this.mpMonths.each(function(m, a, i){
24586             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24587         });
24588     },
24589
24590     selectMPMonth: function(m){
24591         
24592     },
24593
24594     onMonthClick : function(e, t){
24595         e.stopEvent();
24596         var el = new Roo.Element(t), pn;
24597         if(el.is('button.x-date-mp-cancel')){
24598             this.hideMonthPicker();
24599         }
24600         else if(el.is('button.x-date-mp-ok')){
24601             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24602             this.hideMonthPicker();
24603         }
24604         else if(pn = el.up('td.x-date-mp-month', 2)){
24605             this.mpMonths.removeClass('x-date-mp-sel');
24606             pn.addClass('x-date-mp-sel');
24607             this.mpSelMonth = pn.dom.xmonth;
24608         }
24609         else if(pn = el.up('td.x-date-mp-year', 2)){
24610             this.mpYears.removeClass('x-date-mp-sel');
24611             pn.addClass('x-date-mp-sel');
24612             this.mpSelYear = pn.dom.xyear;
24613         }
24614         else if(el.is('a.x-date-mp-prev')){
24615             this.updateMPYear(this.mpyear-10);
24616         }
24617         else if(el.is('a.x-date-mp-next')){
24618             this.updateMPYear(this.mpyear+10);
24619         }
24620     },
24621
24622     onMonthDblClick : function(e, t){
24623         e.stopEvent();
24624         var el = new Roo.Element(t), pn;
24625         if(pn = el.up('td.x-date-mp-month', 2)){
24626             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24627             this.hideMonthPicker();
24628         }
24629         else if(pn = el.up('td.x-date-mp-year', 2)){
24630             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24631             this.hideMonthPicker();
24632         }
24633     },
24634
24635     hideMonthPicker : function(disableAnim){
24636         if(this.monthPicker){
24637             if(disableAnim === true){
24638                 this.monthPicker.hide();
24639             }else{
24640                 this.monthPicker.slideOut('t', {duration:.2});
24641             }
24642         }
24643     },
24644
24645     // private
24646     showPrevMonth : function(e){
24647         this.update(this.activeDate.add("mo", -1));
24648     },
24649
24650     // private
24651     showNextMonth : function(e){
24652         this.update(this.activeDate.add("mo", 1));
24653     },
24654
24655     // private
24656     showPrevYear : function(){
24657         this.update(this.activeDate.add("y", -1));
24658     },
24659
24660     // private
24661     showNextYear : function(){
24662         this.update(this.activeDate.add("y", 1));
24663     },
24664
24665     // private
24666     handleMouseWheel : function(e){
24667         var delta = e.getWheelDelta();
24668         if(delta > 0){
24669             this.showPrevMonth();
24670             e.stopEvent();
24671         } else if(delta < 0){
24672             this.showNextMonth();
24673             e.stopEvent();
24674         }
24675     },
24676
24677     // private
24678     handleDateClick : function(e, t){
24679         e.stopEvent();
24680         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24681             this.setValue(new Date(t.dateValue));
24682             this.fireEvent("select", this, this.value);
24683         }
24684     },
24685
24686     // private
24687     selectToday : function(){
24688         this.setValue(new Date().clearTime());
24689         this.fireEvent("select", this, this.value);
24690     },
24691
24692     // private
24693     update : function(date){
24694         var vd = this.activeDate;
24695         this.activeDate = date;
24696         if(vd && this.el){
24697             var t = date.getTime();
24698             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24699                 this.cells.removeClass("x-date-selected");
24700                 this.cells.each(function(c){
24701                    if(c.dom.firstChild.dateValue == t){
24702                        c.addClass("x-date-selected");
24703                        setTimeout(function(){
24704                             try{c.dom.firstChild.focus();}catch(e){}
24705                        }, 50);
24706                        return false;
24707                    }
24708                 });
24709                 return;
24710             }
24711         }
24712         var days = date.getDaysInMonth();
24713         var firstOfMonth = date.getFirstDateOfMonth();
24714         var startingPos = firstOfMonth.getDay()-this.startDay;
24715
24716         if(startingPos <= this.startDay){
24717             startingPos += 7;
24718         }
24719
24720         var pm = date.add("mo", -1);
24721         var prevStart = pm.getDaysInMonth()-startingPos;
24722
24723         var cells = this.cells.elements;
24724         var textEls = this.textNodes;
24725         days += startingPos;
24726
24727         // convert everything to numbers so it's fast
24728         var day = 86400000;
24729         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24730         var today = new Date().clearTime().getTime();
24731         var sel = date.clearTime().getTime();
24732         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24733         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24734         var ddMatch = this.disabledDatesRE;
24735         var ddText = this.disabledDatesText;
24736         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24737         var ddaysText = this.disabledDaysText;
24738         var format = this.format;
24739
24740         var setCellClass = function(cal, cell){
24741             cell.title = "";
24742             var t = d.getTime();
24743             cell.firstChild.dateValue = t;
24744             if(t == today){
24745                 cell.className += " x-date-today";
24746                 cell.title = cal.todayText;
24747             }
24748             if(t == sel){
24749                 cell.className += " x-date-selected";
24750                 setTimeout(function(){
24751                     try{cell.firstChild.focus();}catch(e){}
24752                 }, 50);
24753             }
24754             // disabling
24755             if(t < min) {
24756                 cell.className = " x-date-disabled";
24757                 cell.title = cal.minText;
24758                 return;
24759             }
24760             if(t > max) {
24761                 cell.className = " x-date-disabled";
24762                 cell.title = cal.maxText;
24763                 return;
24764             }
24765             if(ddays){
24766                 if(ddays.indexOf(d.getDay()) != -1){
24767                     cell.title = ddaysText;
24768                     cell.className = " x-date-disabled";
24769                 }
24770             }
24771             if(ddMatch && format){
24772                 var fvalue = d.dateFormat(format);
24773                 if(ddMatch.test(fvalue)){
24774                     cell.title = ddText.replace("%0", fvalue);
24775                     cell.className = " x-date-disabled";
24776                 }
24777             }
24778         };
24779
24780         var i = 0;
24781         for(; i < startingPos; i++) {
24782             textEls[i].innerHTML = (++prevStart);
24783             d.setDate(d.getDate()+1);
24784             cells[i].className = "x-date-prevday";
24785             setCellClass(this, cells[i]);
24786         }
24787         for(; i < days; i++){
24788             intDay = i - startingPos + 1;
24789             textEls[i].innerHTML = (intDay);
24790             d.setDate(d.getDate()+1);
24791             cells[i].className = "x-date-active";
24792             setCellClass(this, cells[i]);
24793         }
24794         var extraDays = 0;
24795         for(; i < 42; i++) {
24796              textEls[i].innerHTML = (++extraDays);
24797              d.setDate(d.getDate()+1);
24798              cells[i].className = "x-date-nextday";
24799              setCellClass(this, cells[i]);
24800         }
24801
24802         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24803
24804         if(!this.internalRender){
24805             var main = this.el.dom.firstChild;
24806             var w = main.offsetWidth;
24807             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24808             Roo.fly(main).setWidth(w);
24809             this.internalRender = true;
24810             // opera does not respect the auto grow header center column
24811             // then, after it gets a width opera refuses to recalculate
24812             // without a second pass
24813             if(Roo.isOpera && !this.secondPass){
24814                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24815                 this.secondPass = true;
24816                 this.update.defer(10, this, [date]);
24817             }
24818         }
24819     }
24820 });/*
24821  * Based on:
24822  * Ext JS Library 1.1.1
24823  * Copyright(c) 2006-2007, Ext JS, LLC.
24824  *
24825  * Originally Released Under LGPL - original licence link has changed is not relivant.
24826  *
24827  * Fork - LGPL
24828  * <script type="text/javascript">
24829  */
24830 /**
24831  * @class Roo.TabPanel
24832  * @extends Roo.util.Observable
24833  * A lightweight tab container.
24834  * <br><br>
24835  * Usage:
24836  * <pre><code>
24837 // basic tabs 1, built from existing content
24838 var tabs = new Roo.TabPanel("tabs1");
24839 tabs.addTab("script", "View Script");
24840 tabs.addTab("markup", "View Markup");
24841 tabs.activate("script");
24842
24843 // more advanced tabs, built from javascript
24844 var jtabs = new Roo.TabPanel("jtabs");
24845 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24846
24847 // set up the UpdateManager
24848 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24849 var updater = tab2.getUpdateManager();
24850 updater.setDefaultUrl("ajax1.htm");
24851 tab2.on('activate', updater.refresh, updater, true);
24852
24853 // Use setUrl for Ajax loading
24854 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24855 tab3.setUrl("ajax2.htm", null, true);
24856
24857 // Disabled tab
24858 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24859 tab4.disable();
24860
24861 jtabs.activate("jtabs-1");
24862  * </code></pre>
24863  * @constructor
24864  * Create a new TabPanel.
24865  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24866  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24867  */
24868 Roo.TabPanel = function(container, config){
24869     /**
24870     * The container element for this TabPanel.
24871     * @type Roo.Element
24872     */
24873     this.el = Roo.get(container, true);
24874     if(config){
24875         if(typeof config == "boolean"){
24876             this.tabPosition = config ? "bottom" : "top";
24877         }else{
24878             Roo.apply(this, config);
24879         }
24880     }
24881     if(this.tabPosition == "bottom"){
24882         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24883         this.el.addClass("x-tabs-bottom");
24884     }
24885     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24886     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24887     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24888     if(Roo.isIE){
24889         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24890     }
24891     if(this.tabPosition != "bottom"){
24892     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24893      * @type Roo.Element
24894      */
24895       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24896       this.el.addClass("x-tabs-top");
24897     }
24898     this.items = [];
24899
24900     this.bodyEl.setStyle("position", "relative");
24901
24902     this.active = null;
24903     this.activateDelegate = this.activate.createDelegate(this);
24904
24905     this.addEvents({
24906         /**
24907          * @event tabchange
24908          * Fires when the active tab changes
24909          * @param {Roo.TabPanel} this
24910          * @param {Roo.TabPanelItem} activePanel The new active tab
24911          */
24912         "tabchange": true,
24913         /**
24914          * @event beforetabchange
24915          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24916          * @param {Roo.TabPanel} this
24917          * @param {Object} e Set cancel to true on this object to cancel the tab change
24918          * @param {Roo.TabPanelItem} tab The tab being changed to
24919          */
24920         "beforetabchange" : true
24921     });
24922
24923     Roo.EventManager.onWindowResize(this.onResize, this);
24924     this.cpad = this.el.getPadding("lr");
24925     this.hiddenCount = 0;
24926
24927     Roo.TabPanel.superclass.constructor.call(this);
24928 };
24929
24930 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24931         /*
24932          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24933          */
24934     tabPosition : "top",
24935         /*
24936          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24937          */
24938     currentTabWidth : 0,
24939         /*
24940          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24941          */
24942     minTabWidth : 40,
24943         /*
24944          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24945          */
24946     maxTabWidth : 250,
24947         /*
24948          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24949          */
24950     preferredTabWidth : 175,
24951         /*
24952          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24953          */
24954     resizeTabs : false,
24955         /*
24956          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24957          */
24958     monitorResize : true,
24959
24960     /**
24961      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24962      * @param {String} id The id of the div to use <b>or create</b>
24963      * @param {String} text The text for the tab
24964      * @param {String} content (optional) Content to put in the TabPanelItem body
24965      * @param {Boolean} closable (optional) True to create a close icon on the tab
24966      * @return {Roo.TabPanelItem} The created TabPanelItem
24967      */
24968     addTab : function(id, text, content, closable){
24969         var item = new Roo.TabPanelItem(this, id, text, closable);
24970         this.addTabItem(item);
24971         if(content){
24972             item.setContent(content);
24973         }
24974         return item;
24975     },
24976
24977     /**
24978      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24979      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24980      * @return {Roo.TabPanelItem}
24981      */
24982     getTab : function(id){
24983         return this.items[id];
24984     },
24985
24986     /**
24987      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24988      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24989      */
24990     hideTab : function(id){
24991         var t = this.items[id];
24992         if(!t.isHidden()){
24993            t.setHidden(true);
24994            this.hiddenCount++;
24995            this.autoSizeTabs();
24996         }
24997     },
24998
24999     /**
25000      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25001      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25002      */
25003     unhideTab : function(id){
25004         var t = this.items[id];
25005         if(t.isHidden()){
25006            t.setHidden(false);
25007            this.hiddenCount--;
25008            this.autoSizeTabs();
25009         }
25010     },
25011
25012     /**
25013      * Adds an existing {@link Roo.TabPanelItem}.
25014      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25015      */
25016     addTabItem : function(item){
25017         this.items[item.id] = item;
25018         this.items.push(item);
25019         if(this.resizeTabs){
25020            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25021            this.autoSizeTabs();
25022         }else{
25023             item.autoSize();
25024         }
25025     },
25026
25027     /**
25028      * Removes a {@link Roo.TabPanelItem}.
25029      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25030      */
25031     removeTab : function(id){
25032         var items = this.items;
25033         var tab = items[id];
25034         if(!tab) return;
25035         var index = items.indexOf(tab);
25036         if(this.active == tab && items.length > 1){
25037             var newTab = this.getNextAvailable(index);
25038             if(newTab)newTab.activate();
25039         }
25040         this.stripEl.dom.removeChild(tab.pnode.dom);
25041         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25042             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25043         }
25044         items.splice(index, 1);
25045         delete this.items[tab.id];
25046         tab.fireEvent("close", tab);
25047         tab.purgeListeners();
25048         this.autoSizeTabs();
25049     },
25050
25051     getNextAvailable : function(start){
25052         var items = this.items;
25053         var index = start;
25054         // look for a next tab that will slide over to
25055         // replace the one being removed
25056         while(index < items.length){
25057             var item = items[++index];
25058             if(item && !item.isHidden()){
25059                 return item;
25060             }
25061         }
25062         // if one isn't found select the previous tab (on the left)
25063         index = start;
25064         while(index >= 0){
25065             var item = items[--index];
25066             if(item && !item.isHidden()){
25067                 return item;
25068             }
25069         }
25070         return null;
25071     },
25072
25073     /**
25074      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25075      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25076      */
25077     disableTab : function(id){
25078         var tab = this.items[id];
25079         if(tab && this.active != tab){
25080             tab.disable();
25081         }
25082     },
25083
25084     /**
25085      * Enables a {@link Roo.TabPanelItem} that is disabled.
25086      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25087      */
25088     enableTab : function(id){
25089         var tab = this.items[id];
25090         tab.enable();
25091     },
25092
25093     /**
25094      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25095      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25096      * @return {Roo.TabPanelItem} The TabPanelItem.
25097      */
25098     activate : function(id){
25099         var tab = this.items[id];
25100         if(!tab){
25101             return null;
25102         }
25103         if(tab == this.active || tab.disabled){
25104             return tab;
25105         }
25106         var e = {};
25107         this.fireEvent("beforetabchange", this, e, tab);
25108         if(e.cancel !== true && !tab.disabled){
25109             if(this.active){
25110                 this.active.hide();
25111             }
25112             this.active = this.items[id];
25113             this.active.show();
25114             this.fireEvent("tabchange", this, this.active);
25115         }
25116         return tab;
25117     },
25118
25119     /**
25120      * Gets the active {@link Roo.TabPanelItem}.
25121      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25122      */
25123     getActiveTab : function(){
25124         return this.active;
25125     },
25126
25127     /**
25128      * Updates the tab body element to fit the height of the container element
25129      * for overflow scrolling
25130      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25131      */
25132     syncHeight : function(targetHeight){
25133         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25134         var bm = this.bodyEl.getMargins();
25135         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25136         this.bodyEl.setHeight(newHeight);
25137         return newHeight;
25138     },
25139
25140     onResize : function(){
25141         if(this.monitorResize){
25142             this.autoSizeTabs();
25143         }
25144     },
25145
25146     /**
25147      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25148      */
25149     beginUpdate : function(){
25150         this.updating = true;
25151     },
25152
25153     /**
25154      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25155      */
25156     endUpdate : function(){
25157         this.updating = false;
25158         this.autoSizeTabs();
25159     },
25160
25161     /**
25162      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25163      */
25164     autoSizeTabs : function(){
25165         var count = this.items.length;
25166         var vcount = count - this.hiddenCount;
25167         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25168         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25169         var availWidth = Math.floor(w / vcount);
25170         var b = this.stripBody;
25171         if(b.getWidth() > w){
25172             var tabs = this.items;
25173             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25174             if(availWidth < this.minTabWidth){
25175                 /*if(!this.sleft){    // incomplete scrolling code
25176                     this.createScrollButtons();
25177                 }
25178                 this.showScroll();
25179                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25180             }
25181         }else{
25182             if(this.currentTabWidth < this.preferredTabWidth){
25183                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25184             }
25185         }
25186     },
25187
25188     /**
25189      * Returns the number of tabs in this TabPanel.
25190      * @return {Number}
25191      */
25192      getCount : function(){
25193          return this.items.length;
25194      },
25195
25196     /**
25197      * Resizes all the tabs to the passed width
25198      * @param {Number} The new width
25199      */
25200     setTabWidth : function(width){
25201         this.currentTabWidth = width;
25202         for(var i = 0, len = this.items.length; i < len; i++) {
25203                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25204         }
25205     },
25206
25207     /**
25208      * Destroys this TabPanel
25209      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25210      */
25211     destroy : function(removeEl){
25212         Roo.EventManager.removeResizeListener(this.onResize, this);
25213         for(var i = 0, len = this.items.length; i < len; i++){
25214             this.items[i].purgeListeners();
25215         }
25216         if(removeEl === true){
25217             this.el.update("");
25218             this.el.remove();
25219         }
25220     }
25221 });
25222
25223 /**
25224  * @class Roo.TabPanelItem
25225  * @extends Roo.util.Observable
25226  * Represents an individual item (tab plus body) in a TabPanel.
25227  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25228  * @param {String} id The id of this TabPanelItem
25229  * @param {String} text The text for the tab of this TabPanelItem
25230  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25231  */
25232 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25233     /**
25234      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25235      * @type Roo.TabPanel
25236      */
25237     this.tabPanel = tabPanel;
25238     /**
25239      * The id for this TabPanelItem
25240      * @type String
25241      */
25242     this.id = id;
25243     /** @private */
25244     this.disabled = false;
25245     /** @private */
25246     this.text = text;
25247     /** @private */
25248     this.loaded = false;
25249     this.closable = closable;
25250
25251     /**
25252      * The body element for this TabPanelItem.
25253      * @type Roo.Element
25254      */
25255     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25256     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25257     this.bodyEl.setStyle("display", "block");
25258     this.bodyEl.setStyle("zoom", "1");
25259     this.hideAction();
25260
25261     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25262     /** @private */
25263     this.el = Roo.get(els.el, true);
25264     this.inner = Roo.get(els.inner, true);
25265     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25266     this.pnode = Roo.get(els.el.parentNode, true);
25267     this.el.on("mousedown", this.onTabMouseDown, this);
25268     this.el.on("click", this.onTabClick, this);
25269     /** @private */
25270     if(closable){
25271         var c = Roo.get(els.close, true);
25272         c.dom.title = this.closeText;
25273         c.addClassOnOver("close-over");
25274         c.on("click", this.closeClick, this);
25275      }
25276
25277     this.addEvents({
25278          /**
25279          * @event activate
25280          * Fires when this tab becomes the active tab.
25281          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25282          * @param {Roo.TabPanelItem} this
25283          */
25284         "activate": true,
25285         /**
25286          * @event beforeclose
25287          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25288          * @param {Roo.TabPanelItem} this
25289          * @param {Object} e Set cancel to true on this object to cancel the close.
25290          */
25291         "beforeclose": true,
25292         /**
25293          * @event close
25294          * Fires when this tab is closed.
25295          * @param {Roo.TabPanelItem} this
25296          */
25297          "close": true,
25298         /**
25299          * @event deactivate
25300          * Fires when this tab is no longer the active tab.
25301          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25302          * @param {Roo.TabPanelItem} this
25303          */
25304          "deactivate" : true
25305     });
25306     this.hidden = false;
25307
25308     Roo.TabPanelItem.superclass.constructor.call(this);
25309 };
25310
25311 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25312     purgeListeners : function(){
25313        Roo.util.Observable.prototype.purgeListeners.call(this);
25314        this.el.removeAllListeners();
25315     },
25316     /**
25317      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25318      */
25319     show : function(){
25320         this.pnode.addClass("on");
25321         this.showAction();
25322         if(Roo.isOpera){
25323             this.tabPanel.stripWrap.repaint();
25324         }
25325         this.fireEvent("activate", this.tabPanel, this);
25326     },
25327
25328     /**
25329      * Returns true if this tab is the active tab.
25330      * @return {Boolean}
25331      */
25332     isActive : function(){
25333         return this.tabPanel.getActiveTab() == this;
25334     },
25335
25336     /**
25337      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25338      */
25339     hide : function(){
25340         this.pnode.removeClass("on");
25341         this.hideAction();
25342         this.fireEvent("deactivate", this.tabPanel, this);
25343     },
25344
25345     hideAction : function(){
25346         this.bodyEl.hide();
25347         this.bodyEl.setStyle("position", "absolute");
25348         this.bodyEl.setLeft("-20000px");
25349         this.bodyEl.setTop("-20000px");
25350     },
25351
25352     showAction : function(){
25353         this.bodyEl.setStyle("position", "relative");
25354         this.bodyEl.setTop("");
25355         this.bodyEl.setLeft("");
25356         this.bodyEl.show();
25357     },
25358
25359     /**
25360      * Set the tooltip for the tab.
25361      * @param {String} tooltip The tab's tooltip
25362      */
25363     setTooltip : function(text){
25364         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25365             this.textEl.dom.qtip = text;
25366             this.textEl.dom.removeAttribute('title');
25367         }else{
25368             this.textEl.dom.title = text;
25369         }
25370     },
25371
25372     onTabClick : function(e){
25373         e.preventDefault();
25374         this.tabPanel.activate(this.id);
25375     },
25376
25377     onTabMouseDown : function(e){
25378         e.preventDefault();
25379         this.tabPanel.activate(this.id);
25380     },
25381
25382     getWidth : function(){
25383         return this.inner.getWidth();
25384     },
25385
25386     setWidth : function(width){
25387         var iwidth = width - this.pnode.getPadding("lr");
25388         this.inner.setWidth(iwidth);
25389         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25390         this.pnode.setWidth(width);
25391     },
25392
25393     /**
25394      * Show or hide the tab
25395      * @param {Boolean} hidden True to hide or false to show.
25396      */
25397     setHidden : function(hidden){
25398         this.hidden = hidden;
25399         this.pnode.setStyle("display", hidden ? "none" : "");
25400     },
25401
25402     /**
25403      * Returns true if this tab is "hidden"
25404      * @return {Boolean}
25405      */
25406     isHidden : function(){
25407         return this.hidden;
25408     },
25409
25410     /**
25411      * Returns the text for this tab
25412      * @return {String}
25413      */
25414     getText : function(){
25415         return this.text;
25416     },
25417
25418     autoSize : function(){
25419         //this.el.beginMeasure();
25420         this.textEl.setWidth(1);
25421         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25422         //this.el.endMeasure();
25423     },
25424
25425     /**
25426      * Sets the text for the tab (Note: this also sets the tooltip text)
25427      * @param {String} text The tab's text and tooltip
25428      */
25429     setText : function(text){
25430         this.text = text;
25431         this.textEl.update(text);
25432         this.setTooltip(text);
25433         if(!this.tabPanel.resizeTabs){
25434             this.autoSize();
25435         }
25436     },
25437     /**
25438      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25439      */
25440     activate : function(){
25441         this.tabPanel.activate(this.id);
25442     },
25443
25444     /**
25445      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25446      */
25447     disable : function(){
25448         if(this.tabPanel.active != this){
25449             this.disabled = true;
25450             this.pnode.addClass("disabled");
25451         }
25452     },
25453
25454     /**
25455      * Enables this TabPanelItem if it was previously disabled.
25456      */
25457     enable : function(){
25458         this.disabled = false;
25459         this.pnode.removeClass("disabled");
25460     },
25461
25462     /**
25463      * Sets the content for this TabPanelItem.
25464      * @param {String} content The content
25465      * @param {Boolean} loadScripts true to look for and load scripts
25466      */
25467     setContent : function(content, loadScripts){
25468         this.bodyEl.update(content, loadScripts);
25469     },
25470
25471     /**
25472      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25473      * @return {Roo.UpdateManager} The UpdateManager
25474      */
25475     getUpdateManager : function(){
25476         return this.bodyEl.getUpdateManager();
25477     },
25478
25479     /**
25480      * Set a URL to be used to load the content for this TabPanelItem.
25481      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25482      * @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)
25483      * @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)
25484      * @return {Roo.UpdateManager} The UpdateManager
25485      */
25486     setUrl : function(url, params, loadOnce){
25487         if(this.refreshDelegate){
25488             this.un('activate', this.refreshDelegate);
25489         }
25490         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25491         this.on("activate", this.refreshDelegate);
25492         return this.bodyEl.getUpdateManager();
25493     },
25494
25495     /** @private */
25496     _handleRefresh : function(url, params, loadOnce){
25497         if(!loadOnce || !this.loaded){
25498             var updater = this.bodyEl.getUpdateManager();
25499             updater.update(url, params, this._setLoaded.createDelegate(this));
25500         }
25501     },
25502
25503     /**
25504      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25505      *   Will fail silently if the setUrl method has not been called.
25506      *   This does not activate the panel, just updates its content.
25507      */
25508     refresh : function(){
25509         if(this.refreshDelegate){
25510            this.loaded = false;
25511            this.refreshDelegate();
25512         }
25513     },
25514
25515     /** @private */
25516     _setLoaded : function(){
25517         this.loaded = true;
25518     },
25519
25520     /** @private */
25521     closeClick : function(e){
25522         var o = {};
25523         e.stopEvent();
25524         this.fireEvent("beforeclose", this, o);
25525         if(o.cancel !== true){
25526             this.tabPanel.removeTab(this.id);
25527         }
25528     },
25529     /**
25530      * The text displayed in the tooltip for the close icon.
25531      * @type String
25532      */
25533     closeText : "Close this tab"
25534 });
25535
25536 /** @private */
25537 Roo.TabPanel.prototype.createStrip = function(container){
25538     var strip = document.createElement("div");
25539     strip.className = "x-tabs-wrap";
25540     container.appendChild(strip);
25541     return strip;
25542 };
25543 /** @private */
25544 Roo.TabPanel.prototype.createStripList = function(strip){
25545     // div wrapper for retard IE
25546     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>';
25547     return strip.firstChild.firstChild.firstChild.firstChild;
25548 };
25549 /** @private */
25550 Roo.TabPanel.prototype.createBody = function(container){
25551     var body = document.createElement("div");
25552     Roo.id(body, "tab-body");
25553     Roo.fly(body).addClass("x-tabs-body");
25554     container.appendChild(body);
25555     return body;
25556 };
25557 /** @private */
25558 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25559     var body = Roo.getDom(id);
25560     if(!body){
25561         body = document.createElement("div");
25562         body.id = id;
25563     }
25564     Roo.fly(body).addClass("x-tabs-item-body");
25565     bodyEl.insertBefore(body, bodyEl.firstChild);
25566     return body;
25567 };
25568 /** @private */
25569 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25570     var td = document.createElement("td");
25571     stripEl.appendChild(td);
25572     if(closable){
25573         td.className = "x-tabs-closable";
25574         if(!this.closeTpl){
25575             this.closeTpl = new Roo.Template(
25576                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25577                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25578                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25579             );
25580         }
25581         var el = this.closeTpl.overwrite(td, {"text": text});
25582         var close = el.getElementsByTagName("div")[0];
25583         var inner = el.getElementsByTagName("em")[0];
25584         return {"el": el, "close": close, "inner": inner};
25585     } else {
25586         if(!this.tabTpl){
25587             this.tabTpl = new Roo.Template(
25588                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25589                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25590             );
25591         }
25592         var el = this.tabTpl.overwrite(td, {"text": text});
25593         var inner = el.getElementsByTagName("em")[0];
25594         return {"el": el, "inner": inner};
25595     }
25596 };/*
25597  * Based on:
25598  * Ext JS Library 1.1.1
25599  * Copyright(c) 2006-2007, Ext JS, LLC.
25600  *
25601  * Originally Released Under LGPL - original licence link has changed is not relivant.
25602  *
25603  * Fork - LGPL
25604  * <script type="text/javascript">
25605  */
25606
25607 /**
25608  * @class Roo.Button
25609  * @extends Roo.util.Observable
25610  * Simple Button class
25611  * @cfg {String} text The button text
25612  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25613  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25614  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25615  * @cfg {Object} scope The scope of the handler
25616  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25617  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25618  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25619  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25620  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25621  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25622    applies if enableToggle = true)
25623  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25624  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25625   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25626  * @constructor
25627  * Create a new button
25628  * @param {Object} config The config object
25629  */
25630 Roo.Button = function(renderTo, config)
25631 {
25632     if (!config) {
25633         config = renderTo;
25634         renderTo = config.renderTo || false;
25635     }
25636     
25637     Roo.apply(this, config);
25638     this.addEvents({
25639         /**
25640              * @event click
25641              * Fires when this button is clicked
25642              * @param {Button} this
25643              * @param {EventObject} e The click event
25644              */
25645             "click" : true,
25646         /**
25647              * @event toggle
25648              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25649              * @param {Button} this
25650              * @param {Boolean} pressed
25651              */
25652             "toggle" : true,
25653         /**
25654              * @event mouseover
25655              * Fires when the mouse hovers over the button
25656              * @param {Button} this
25657              * @param {Event} e The event object
25658              */
25659         'mouseover' : true,
25660         /**
25661              * @event mouseout
25662              * Fires when the mouse exits the button
25663              * @param {Button} this
25664              * @param {Event} e The event object
25665              */
25666         'mouseout': true,
25667          /**
25668              * @event render
25669              * Fires when the button is rendered
25670              * @param {Button} this
25671              */
25672         'render': true
25673     });
25674     if(this.menu){
25675         this.menu = Roo.menu.MenuMgr.get(this.menu);
25676     }
25677     if(renderTo){
25678         this.render(renderTo);
25679     }
25680     
25681     Roo.util.Observable.call(this);
25682 };
25683
25684 Roo.extend(Roo.Button, Roo.util.Observable, {
25685     /**
25686      * 
25687      */
25688     
25689     /**
25690      * Read-only. True if this button is hidden
25691      * @type Boolean
25692      */
25693     hidden : false,
25694     /**
25695      * Read-only. True if this button is disabled
25696      * @type Boolean
25697      */
25698     disabled : false,
25699     /**
25700      * Read-only. True if this button is pressed (only if enableToggle = true)
25701      * @type Boolean
25702      */
25703     pressed : false,
25704
25705     /**
25706      * @cfg {Number} tabIndex 
25707      * The DOM tabIndex for this button (defaults to undefined)
25708      */
25709     tabIndex : undefined,
25710
25711     /**
25712      * @cfg {Boolean} enableToggle
25713      * True to enable pressed/not pressed toggling (defaults to false)
25714      */
25715     enableToggle: false,
25716     /**
25717      * @cfg {Mixed} menu
25718      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25719      */
25720     menu : undefined,
25721     /**
25722      * @cfg {String} menuAlign
25723      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25724      */
25725     menuAlign : "tl-bl?",
25726
25727     /**
25728      * @cfg {String} iconCls
25729      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25730      */
25731     iconCls : undefined,
25732     /**
25733      * @cfg {String} type
25734      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25735      */
25736     type : 'button',
25737
25738     // private
25739     menuClassTarget: 'tr',
25740
25741     /**
25742      * @cfg {String} clickEvent
25743      * The type of event to map to the button's event handler (defaults to 'click')
25744      */
25745     clickEvent : 'click',
25746
25747     /**
25748      * @cfg {Boolean} handleMouseEvents
25749      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25750      */
25751     handleMouseEvents : true,
25752
25753     /**
25754      * @cfg {String} tooltipType
25755      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25756      */
25757     tooltipType : 'qtip',
25758
25759     /**
25760      * @cfg {String} cls
25761      * A CSS class to apply to the button's main element.
25762      */
25763     
25764     /**
25765      * @cfg {Roo.Template} template (Optional)
25766      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25767      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25768      * require code modifications if required elements (e.g. a button) aren't present.
25769      */
25770
25771     // private
25772     render : function(renderTo){
25773         var btn;
25774         if(this.hideParent){
25775             this.parentEl = Roo.get(renderTo);
25776         }
25777         if(!this.dhconfig){
25778             if(!this.template){
25779                 if(!Roo.Button.buttonTemplate){
25780                     // hideous table template
25781                     Roo.Button.buttonTemplate = new Roo.Template(
25782                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25783                         '<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>',
25784                         "</tr></tbody></table>");
25785                 }
25786                 this.template = Roo.Button.buttonTemplate;
25787             }
25788             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25789             var btnEl = btn.child("button:first");
25790             btnEl.on('focus', this.onFocus, this);
25791             btnEl.on('blur', this.onBlur, this);
25792             if(this.cls){
25793                 btn.addClass(this.cls);
25794             }
25795             if(this.icon){
25796                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25797             }
25798             if(this.iconCls){
25799                 btnEl.addClass(this.iconCls);
25800                 if(!this.cls){
25801                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25802                 }
25803             }
25804             if(this.tabIndex !== undefined){
25805                 btnEl.dom.tabIndex = this.tabIndex;
25806             }
25807             if(this.tooltip){
25808                 if(typeof this.tooltip == 'object'){
25809                     Roo.QuickTips.tips(Roo.apply({
25810                           target: btnEl.id
25811                     }, this.tooltip));
25812                 } else {
25813                     btnEl.dom[this.tooltipType] = this.tooltip;
25814                 }
25815             }
25816         }else{
25817             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25818         }
25819         this.el = btn;
25820         if(this.id){
25821             this.el.dom.id = this.el.id = this.id;
25822         }
25823         if(this.menu){
25824             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25825             this.menu.on("show", this.onMenuShow, this);
25826             this.menu.on("hide", this.onMenuHide, this);
25827         }
25828         btn.addClass("x-btn");
25829         if(Roo.isIE && !Roo.isIE7){
25830             this.autoWidth.defer(1, this);
25831         }else{
25832             this.autoWidth();
25833         }
25834         if(this.handleMouseEvents){
25835             btn.on("mouseover", this.onMouseOver, this);
25836             btn.on("mouseout", this.onMouseOut, this);
25837             btn.on("mousedown", this.onMouseDown, this);
25838         }
25839         btn.on(this.clickEvent, this.onClick, this);
25840         //btn.on("mouseup", this.onMouseUp, this);
25841         if(this.hidden){
25842             this.hide();
25843         }
25844         if(this.disabled){
25845             this.disable();
25846         }
25847         Roo.ButtonToggleMgr.register(this);
25848         if(this.pressed){
25849             this.el.addClass("x-btn-pressed");
25850         }
25851         if(this.repeat){
25852             var repeater = new Roo.util.ClickRepeater(btn,
25853                 typeof this.repeat == "object" ? this.repeat : {}
25854             );
25855             repeater.on("click", this.onClick,  this);
25856         }
25857         this.fireEvent('render', this);
25858         
25859     },
25860     /**
25861      * Returns the button's underlying element
25862      * @return {Roo.Element} The element
25863      */
25864     getEl : function(){
25865         return this.el;  
25866     },
25867     
25868     /**
25869      * Destroys this Button and removes any listeners.
25870      */
25871     destroy : function(){
25872         Roo.ButtonToggleMgr.unregister(this);
25873         this.el.removeAllListeners();
25874         this.purgeListeners();
25875         this.el.remove();
25876     },
25877
25878     // private
25879     autoWidth : function(){
25880         if(this.el){
25881             this.el.setWidth("auto");
25882             if(Roo.isIE7 && Roo.isStrict){
25883                 var ib = this.el.child('button');
25884                 if(ib && ib.getWidth() > 20){
25885                     ib.clip();
25886                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25887                 }
25888             }
25889             if(this.minWidth){
25890                 if(this.hidden){
25891                     this.el.beginMeasure();
25892                 }
25893                 if(this.el.getWidth() < this.minWidth){
25894                     this.el.setWidth(this.minWidth);
25895                 }
25896                 if(this.hidden){
25897                     this.el.endMeasure();
25898                 }
25899             }
25900         }
25901     },
25902
25903     /**
25904      * Assigns this button's click handler
25905      * @param {Function} handler The function to call when the button is clicked
25906      * @param {Object} scope (optional) Scope for the function passed in
25907      */
25908     setHandler : function(handler, scope){
25909         this.handler = handler;
25910         this.scope = scope;  
25911     },
25912     
25913     /**
25914      * Sets this button's text
25915      * @param {String} text The button text
25916      */
25917     setText : function(text){
25918         this.text = text;
25919         if(this.el){
25920             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25921         }
25922         this.autoWidth();
25923     },
25924     
25925     /**
25926      * Gets the text for this button
25927      * @return {String} The button text
25928      */
25929     getText : function(){
25930         return this.text;  
25931     },
25932     
25933     /**
25934      * Show this button
25935      */
25936     show: function(){
25937         this.hidden = false;
25938         if(this.el){
25939             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25940         }
25941     },
25942     
25943     /**
25944      * Hide this button
25945      */
25946     hide: function(){
25947         this.hidden = true;
25948         if(this.el){
25949             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25950         }
25951     },
25952     
25953     /**
25954      * Convenience function for boolean show/hide
25955      * @param {Boolean} visible True to show, false to hide
25956      */
25957     setVisible: function(visible){
25958         if(visible) {
25959             this.show();
25960         }else{
25961             this.hide();
25962         }
25963     },
25964     
25965     /**
25966      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25967      * @param {Boolean} state (optional) Force a particular state
25968      */
25969     toggle : function(state){
25970         state = state === undefined ? !this.pressed : state;
25971         if(state != this.pressed){
25972             if(state){
25973                 this.el.addClass("x-btn-pressed");
25974                 this.pressed = true;
25975                 this.fireEvent("toggle", this, true);
25976             }else{
25977                 this.el.removeClass("x-btn-pressed");
25978                 this.pressed = false;
25979                 this.fireEvent("toggle", this, false);
25980             }
25981             if(this.toggleHandler){
25982                 this.toggleHandler.call(this.scope || this, this, state);
25983             }
25984         }
25985     },
25986     
25987     /**
25988      * Focus the button
25989      */
25990     focus : function(){
25991         this.el.child('button:first').focus();
25992     },
25993     
25994     /**
25995      * Disable this button
25996      */
25997     disable : function(){
25998         if(this.el){
25999             this.el.addClass("x-btn-disabled");
26000         }
26001         this.disabled = true;
26002     },
26003     
26004     /**
26005      * Enable this button
26006      */
26007     enable : function(){
26008         if(this.el){
26009             this.el.removeClass("x-btn-disabled");
26010         }
26011         this.disabled = false;
26012     },
26013
26014     /**
26015      * Convenience function for boolean enable/disable
26016      * @param {Boolean} enabled True to enable, false to disable
26017      */
26018     setDisabled : function(v){
26019         this[v !== true ? "enable" : "disable"]();
26020     },
26021
26022     // private
26023     onClick : function(e){
26024         if(e){
26025             e.preventDefault();
26026         }
26027         if(e.button != 0){
26028             return;
26029         }
26030         if(!this.disabled){
26031             if(this.enableToggle){
26032                 this.toggle();
26033             }
26034             if(this.menu && !this.menu.isVisible()){
26035                 this.menu.show(this.el, this.menuAlign);
26036             }
26037             this.fireEvent("click", this, e);
26038             if(this.handler){
26039                 this.el.removeClass("x-btn-over");
26040                 this.handler.call(this.scope || this, this, e);
26041             }
26042         }
26043     },
26044     // private
26045     onMouseOver : function(e){
26046         if(!this.disabled){
26047             this.el.addClass("x-btn-over");
26048             this.fireEvent('mouseover', this, e);
26049         }
26050     },
26051     // private
26052     onMouseOut : function(e){
26053         if(!e.within(this.el,  true)){
26054             this.el.removeClass("x-btn-over");
26055             this.fireEvent('mouseout', this, e);
26056         }
26057     },
26058     // private
26059     onFocus : function(e){
26060         if(!this.disabled){
26061             this.el.addClass("x-btn-focus");
26062         }
26063     },
26064     // private
26065     onBlur : function(e){
26066         this.el.removeClass("x-btn-focus");
26067     },
26068     // private
26069     onMouseDown : function(e){
26070         if(!this.disabled && e.button == 0){
26071             this.el.addClass("x-btn-click");
26072             Roo.get(document).on('mouseup', this.onMouseUp, this);
26073         }
26074     },
26075     // private
26076     onMouseUp : function(e){
26077         if(e.button == 0){
26078             this.el.removeClass("x-btn-click");
26079             Roo.get(document).un('mouseup', this.onMouseUp, this);
26080         }
26081     },
26082     // private
26083     onMenuShow : function(e){
26084         this.el.addClass("x-btn-menu-active");
26085     },
26086     // private
26087     onMenuHide : function(e){
26088         this.el.removeClass("x-btn-menu-active");
26089     }   
26090 });
26091
26092 // Private utility class used by Button
26093 Roo.ButtonToggleMgr = function(){
26094    var groups = {};
26095    
26096    function toggleGroup(btn, state){
26097        if(state){
26098            var g = groups[btn.toggleGroup];
26099            for(var i = 0, l = g.length; i < l; i++){
26100                if(g[i] != btn){
26101                    g[i].toggle(false);
26102                }
26103            }
26104        }
26105    }
26106    
26107    return {
26108        register : function(btn){
26109            if(!btn.toggleGroup){
26110                return;
26111            }
26112            var g = groups[btn.toggleGroup];
26113            if(!g){
26114                g = groups[btn.toggleGroup] = [];
26115            }
26116            g.push(btn);
26117            btn.on("toggle", toggleGroup);
26118        },
26119        
26120        unregister : function(btn){
26121            if(!btn.toggleGroup){
26122                return;
26123            }
26124            var g = groups[btn.toggleGroup];
26125            if(g){
26126                g.remove(btn);
26127                btn.un("toggle", toggleGroup);
26128            }
26129        }
26130    };
26131 }();/*
26132  * Based on:
26133  * Ext JS Library 1.1.1
26134  * Copyright(c) 2006-2007, Ext JS, LLC.
26135  *
26136  * Originally Released Under LGPL - original licence link has changed is not relivant.
26137  *
26138  * Fork - LGPL
26139  * <script type="text/javascript">
26140  */
26141  
26142 /**
26143  * @class Roo.SplitButton
26144  * @extends Roo.Button
26145  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26146  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26147  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26148  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26149  * @cfg {String} arrowTooltip The title attribute of the arrow
26150  * @constructor
26151  * Create a new menu button
26152  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26153  * @param {Object} config The config object
26154  */
26155 Roo.SplitButton = function(renderTo, config){
26156     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26157     /**
26158      * @event arrowclick
26159      * Fires when this button's arrow is clicked
26160      * @param {SplitButton} this
26161      * @param {EventObject} e The click event
26162      */
26163     this.addEvents({"arrowclick":true});
26164 };
26165
26166 Roo.extend(Roo.SplitButton, Roo.Button, {
26167     render : function(renderTo){
26168         // this is one sweet looking template!
26169         var tpl = new Roo.Template(
26170             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26171             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26172             '<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>',
26173             "</tbody></table></td><td>",
26174             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26175             '<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>',
26176             "</tbody></table></td></tr></table>"
26177         );
26178         var btn = tpl.append(renderTo, [this.text, this.type], true);
26179         var btnEl = btn.child("button");
26180         if(this.cls){
26181             btn.addClass(this.cls);
26182         }
26183         if(this.icon){
26184             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26185         }
26186         if(this.iconCls){
26187             btnEl.addClass(this.iconCls);
26188             if(!this.cls){
26189                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26190             }
26191         }
26192         this.el = btn;
26193         if(this.handleMouseEvents){
26194             btn.on("mouseover", this.onMouseOver, this);
26195             btn.on("mouseout", this.onMouseOut, this);
26196             btn.on("mousedown", this.onMouseDown, this);
26197             btn.on("mouseup", this.onMouseUp, this);
26198         }
26199         btn.on(this.clickEvent, this.onClick, this);
26200         if(this.tooltip){
26201             if(typeof this.tooltip == 'object'){
26202                 Roo.QuickTips.tips(Roo.apply({
26203                       target: btnEl.id
26204                 }, this.tooltip));
26205             } else {
26206                 btnEl.dom[this.tooltipType] = this.tooltip;
26207             }
26208         }
26209         if(this.arrowTooltip){
26210             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26211         }
26212         if(this.hidden){
26213             this.hide();
26214         }
26215         if(this.disabled){
26216             this.disable();
26217         }
26218         if(this.pressed){
26219             this.el.addClass("x-btn-pressed");
26220         }
26221         if(Roo.isIE && !Roo.isIE7){
26222             this.autoWidth.defer(1, this);
26223         }else{
26224             this.autoWidth();
26225         }
26226         if(this.menu){
26227             this.menu.on("show", this.onMenuShow, this);
26228             this.menu.on("hide", this.onMenuHide, this);
26229         }
26230         this.fireEvent('render', this);
26231     },
26232
26233     // private
26234     autoWidth : function(){
26235         if(this.el){
26236             var tbl = this.el.child("table:first");
26237             var tbl2 = this.el.child("table:last");
26238             this.el.setWidth("auto");
26239             tbl.setWidth("auto");
26240             if(Roo.isIE7 && Roo.isStrict){
26241                 var ib = this.el.child('button:first');
26242                 if(ib && ib.getWidth() > 20){
26243                     ib.clip();
26244                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26245                 }
26246             }
26247             if(this.minWidth){
26248                 if(this.hidden){
26249                     this.el.beginMeasure();
26250                 }
26251                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26252                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26253                 }
26254                 if(this.hidden){
26255                     this.el.endMeasure();
26256                 }
26257             }
26258             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26259         } 
26260     },
26261     /**
26262      * Sets this button's click handler
26263      * @param {Function} handler The function to call when the button is clicked
26264      * @param {Object} scope (optional) Scope for the function passed above
26265      */
26266     setHandler : function(handler, scope){
26267         this.handler = handler;
26268         this.scope = scope;  
26269     },
26270     
26271     /**
26272      * Sets this button's arrow click handler
26273      * @param {Function} handler The function to call when the arrow is clicked
26274      * @param {Object} scope (optional) Scope for the function passed above
26275      */
26276     setArrowHandler : function(handler, scope){
26277         this.arrowHandler = handler;
26278         this.scope = scope;  
26279     },
26280     
26281     /**
26282      * Focus the button
26283      */
26284     focus : function(){
26285         if(this.el){
26286             this.el.child("button:first").focus();
26287         }
26288     },
26289
26290     // private
26291     onClick : function(e){
26292         e.preventDefault();
26293         if(!this.disabled){
26294             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26295                 if(this.menu && !this.menu.isVisible()){
26296                     this.menu.show(this.el, this.menuAlign);
26297                 }
26298                 this.fireEvent("arrowclick", this, e);
26299                 if(this.arrowHandler){
26300                     this.arrowHandler.call(this.scope || this, this, e);
26301                 }
26302             }else{
26303                 this.fireEvent("click", this, e);
26304                 if(this.handler){
26305                     this.handler.call(this.scope || this, this, e);
26306                 }
26307             }
26308         }
26309     },
26310     // private
26311     onMouseDown : function(e){
26312         if(!this.disabled){
26313             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26314         }
26315     },
26316     // private
26317     onMouseUp : function(e){
26318         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26319     }   
26320 });
26321
26322
26323 // backwards compat
26324 Roo.MenuButton = Roo.SplitButton;/*
26325  * Based on:
26326  * Ext JS Library 1.1.1
26327  * Copyright(c) 2006-2007, Ext JS, LLC.
26328  *
26329  * Originally Released Under LGPL - original licence link has changed is not relivant.
26330  *
26331  * Fork - LGPL
26332  * <script type="text/javascript">
26333  */
26334
26335 /**
26336  * @class Roo.Toolbar
26337  * Basic Toolbar class.
26338  * @constructor
26339  * Creates a new Toolbar
26340  * @param {Object} config The config object
26341  */ 
26342 Roo.Toolbar = function(container, buttons, config)
26343 {
26344     /// old consturctor format still supported..
26345     if(container instanceof Array){ // omit the container for later rendering
26346         buttons = container;
26347         config = buttons;
26348         container = null;
26349     }
26350     if (typeof(container) == 'object' && container.xtype) {
26351         config = container;
26352         container = config.container;
26353         buttons = config.buttons; // not really - use items!!
26354     }
26355     var xitems = [];
26356     if (config && config.items) {
26357         xitems = config.items;
26358         delete config.items;
26359     }
26360     Roo.apply(this, config);
26361     this.buttons = buttons;
26362     
26363     if(container){
26364         this.render(container);
26365     }
26366     Roo.each(xitems, function(b) {
26367         this.add(b);
26368     }, this);
26369     
26370 };
26371
26372 Roo.Toolbar.prototype = {
26373     /**
26374      * @cfg {Roo.data.Store} items
26375      * array of button configs or elements to add
26376      */
26377     
26378     /**
26379      * @cfg {String/HTMLElement/Element} container
26380      * The id or element that will contain the toolbar
26381      */
26382     // private
26383     render : function(ct){
26384         this.el = Roo.get(ct);
26385         if(this.cls){
26386             this.el.addClass(this.cls);
26387         }
26388         // using a table allows for vertical alignment
26389         // 100% width is needed by Safari...
26390         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26391         this.tr = this.el.child("tr", true);
26392         var autoId = 0;
26393         this.items = new Roo.util.MixedCollection(false, function(o){
26394             return o.id || ("item" + (++autoId));
26395         });
26396         if(this.buttons){
26397             this.add.apply(this, this.buttons);
26398             delete this.buttons;
26399         }
26400     },
26401
26402     /**
26403      * Adds element(s) to the toolbar -- this function takes a variable number of 
26404      * arguments of mixed type and adds them to the toolbar.
26405      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26406      * <ul>
26407      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26408      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26409      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26410      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26411      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26412      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26413      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26414      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26415      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26416      * </ul>
26417      * @param {Mixed} arg2
26418      * @param {Mixed} etc.
26419      */
26420     add : function(){
26421         var a = arguments, l = a.length;
26422         for(var i = 0; i < l; i++){
26423             this._add(a[i]);
26424         }
26425     },
26426     // private..
26427     _add : function(el) {
26428         
26429         if (el.xtype) {
26430             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26431         }
26432         
26433         if (el.applyTo){ // some kind of form field
26434             return this.addField(el);
26435         } 
26436         if (el.render){ // some kind of Toolbar.Item
26437             return this.addItem(el);
26438         }
26439         if (typeof el == "string"){ // string
26440             if(el == "separator" || el == "-"){
26441                 return this.addSeparator();
26442             }
26443             if (el == " "){
26444                 return this.addSpacer();
26445             }
26446             if(el == "->"){
26447                 return this.addFill();
26448             }
26449             return this.addText(el);
26450             
26451         }
26452         if(el.tagName){ // element
26453             return this.addElement(el);
26454         }
26455         if(typeof el == "object"){ // must be button config?
26456             return this.addButton(el);
26457         }
26458         // and now what?!?!
26459         return false;
26460         
26461     },
26462     
26463     /**
26464      * Add an Xtype element
26465      * @param {Object} xtype Xtype Object
26466      * @return {Object} created Object
26467      */
26468     addxtype : function(e){
26469         return this.add(e);  
26470     },
26471     
26472     /**
26473      * Returns the Element for this toolbar.
26474      * @return {Roo.Element}
26475      */
26476     getEl : function(){
26477         return this.el;  
26478     },
26479     
26480     /**
26481      * Adds a separator
26482      * @return {Roo.Toolbar.Item} The separator item
26483      */
26484     addSeparator : function(){
26485         return this.addItem(new Roo.Toolbar.Separator());
26486     },
26487
26488     /**
26489      * Adds a spacer element
26490      * @return {Roo.Toolbar.Spacer} The spacer item
26491      */
26492     addSpacer : function(){
26493         return this.addItem(new Roo.Toolbar.Spacer());
26494     },
26495
26496     /**
26497      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26498      * @return {Roo.Toolbar.Fill} The fill item
26499      */
26500     addFill : function(){
26501         return this.addItem(new Roo.Toolbar.Fill());
26502     },
26503
26504     /**
26505      * Adds any standard HTML element to the toolbar
26506      * @param {String/HTMLElement/Element} el The element or id of the element to add
26507      * @return {Roo.Toolbar.Item} The element's item
26508      */
26509     addElement : function(el){
26510         return this.addItem(new Roo.Toolbar.Item(el));
26511     },
26512     /**
26513      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26514      * @type Roo.util.MixedCollection  
26515      */
26516     items : false,
26517      
26518     /**
26519      * Adds any Toolbar.Item or subclass
26520      * @param {Roo.Toolbar.Item} item
26521      * @return {Roo.Toolbar.Item} The item
26522      */
26523     addItem : function(item){
26524         var td = this.nextBlock();
26525         item.render(td);
26526         this.items.add(item);
26527         return item;
26528     },
26529     
26530     /**
26531      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26532      * @param {Object/Array} config A button config or array of configs
26533      * @return {Roo.Toolbar.Button/Array}
26534      */
26535     addButton : function(config){
26536         if(config instanceof Array){
26537             var buttons = [];
26538             for(var i = 0, len = config.length; i < len; i++) {
26539                 buttons.push(this.addButton(config[i]));
26540             }
26541             return buttons;
26542         }
26543         var b = config;
26544         if(!(config instanceof Roo.Toolbar.Button)){
26545             b = config.split ?
26546                 new Roo.Toolbar.SplitButton(config) :
26547                 new Roo.Toolbar.Button(config);
26548         }
26549         var td = this.nextBlock();
26550         b.render(td);
26551         this.items.add(b);
26552         return b;
26553     },
26554     
26555     /**
26556      * Adds text to the toolbar
26557      * @param {String} text The text to add
26558      * @return {Roo.Toolbar.Item} The element's item
26559      */
26560     addText : function(text){
26561         return this.addItem(new Roo.Toolbar.TextItem(text));
26562     },
26563     
26564     /**
26565      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26566      * @param {Number} index The index where the item is to be inserted
26567      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26568      * @return {Roo.Toolbar.Button/Item}
26569      */
26570     insertButton : function(index, item){
26571         if(item instanceof Array){
26572             var buttons = [];
26573             for(var i = 0, len = item.length; i < len; i++) {
26574                buttons.push(this.insertButton(index + i, item[i]));
26575             }
26576             return buttons;
26577         }
26578         if (!(item instanceof Roo.Toolbar.Button)){
26579            item = new Roo.Toolbar.Button(item);
26580         }
26581         var td = document.createElement("td");
26582         this.tr.insertBefore(td, this.tr.childNodes[index]);
26583         item.render(td);
26584         this.items.insert(index, item);
26585         return item;
26586     },
26587     
26588     /**
26589      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26590      * @param {Object} config
26591      * @return {Roo.Toolbar.Item} The element's item
26592      */
26593     addDom : function(config, returnEl){
26594         var td = this.nextBlock();
26595         Roo.DomHelper.overwrite(td, config);
26596         var ti = new Roo.Toolbar.Item(td.firstChild);
26597         ti.render(td);
26598         this.items.add(ti);
26599         return ti;
26600     },
26601
26602     /**
26603      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26604      * @type Roo.util.MixedCollection  
26605      */
26606     fields : false,
26607     
26608     /**
26609      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26610      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26611      * @param {Roo.form.Field} field
26612      * @return {Roo.ToolbarItem}
26613      */
26614      
26615       
26616     addField : function(field) {
26617         if (!this.fields) {
26618             var autoId = 0;
26619             this.fields = new Roo.util.MixedCollection(false, function(o){
26620                 return o.id || ("item" + (++autoId));
26621             });
26622
26623         }
26624         
26625         var td = this.nextBlock();
26626         field.render(td);
26627         var ti = new Roo.Toolbar.Item(td.firstChild);
26628         ti.render(td);
26629         this.items.add(ti);
26630         this.fields.add(field);
26631         return ti;
26632     },
26633     /**
26634      * Hide the toolbar
26635      * @method hide
26636      */
26637      
26638       
26639     hide : function()
26640     {
26641         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26642         this.el.child('div').hide();
26643     },
26644     /**
26645      * Show the toolbar
26646      * @method show
26647      */
26648     show : function()
26649     {
26650         this.el.child('div').show();
26651     },
26652       
26653     // private
26654     nextBlock : function(){
26655         var td = document.createElement("td");
26656         this.tr.appendChild(td);
26657         return td;
26658     },
26659
26660     // private
26661     destroy : function(){
26662         if(this.items){ // rendered?
26663             Roo.destroy.apply(Roo, this.items.items);
26664         }
26665         if(this.fields){ // rendered?
26666             Roo.destroy.apply(Roo, this.fields.items);
26667         }
26668         Roo.Element.uncache(this.el, this.tr);
26669     }
26670 };
26671
26672 /**
26673  * @class Roo.Toolbar.Item
26674  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26675  * @constructor
26676  * Creates a new Item
26677  * @param {HTMLElement} el 
26678  */
26679 Roo.Toolbar.Item = function(el){
26680     this.el = Roo.getDom(el);
26681     this.id = Roo.id(this.el);
26682     this.hidden = false;
26683 };
26684
26685 Roo.Toolbar.Item.prototype = {
26686     
26687     /**
26688      * Get this item's HTML Element
26689      * @return {HTMLElement}
26690      */
26691     getEl : function(){
26692        return this.el;  
26693     },
26694
26695     // private
26696     render : function(td){
26697         this.td = td;
26698         td.appendChild(this.el);
26699     },
26700     
26701     /**
26702      * Removes and destroys this item.
26703      */
26704     destroy : function(){
26705         this.td.parentNode.removeChild(this.td);
26706     },
26707     
26708     /**
26709      * Shows this item.
26710      */
26711     show: function(){
26712         this.hidden = false;
26713         this.td.style.display = "";
26714     },
26715     
26716     /**
26717      * Hides this item.
26718      */
26719     hide: function(){
26720         this.hidden = true;
26721         this.td.style.display = "none";
26722     },
26723     
26724     /**
26725      * Convenience function for boolean show/hide.
26726      * @param {Boolean} visible true to show/false to hide
26727      */
26728     setVisible: function(visible){
26729         if(visible) {
26730             this.show();
26731         }else{
26732             this.hide();
26733         }
26734     },
26735     
26736     /**
26737      * Try to focus this item.
26738      */
26739     focus : function(){
26740         Roo.fly(this.el).focus();
26741     },
26742     
26743     /**
26744      * Disables this item.
26745      */
26746     disable : function(){
26747         Roo.fly(this.td).addClass("x-item-disabled");
26748         this.disabled = true;
26749         this.el.disabled = true;
26750     },
26751     
26752     /**
26753      * Enables this item.
26754      */
26755     enable : function(){
26756         Roo.fly(this.td).removeClass("x-item-disabled");
26757         this.disabled = false;
26758         this.el.disabled = false;
26759     }
26760 };
26761
26762
26763 /**
26764  * @class Roo.Toolbar.Separator
26765  * @extends Roo.Toolbar.Item
26766  * A simple toolbar separator class
26767  * @constructor
26768  * Creates a new Separator
26769  */
26770 Roo.Toolbar.Separator = function(){
26771     var s = document.createElement("span");
26772     s.className = "ytb-sep";
26773     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26774 };
26775 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26776     enable:Roo.emptyFn,
26777     disable:Roo.emptyFn,
26778     focus:Roo.emptyFn
26779 });
26780
26781 /**
26782  * @class Roo.Toolbar.Spacer
26783  * @extends Roo.Toolbar.Item
26784  * A simple element that adds extra horizontal space to a toolbar.
26785  * @constructor
26786  * Creates a new Spacer
26787  */
26788 Roo.Toolbar.Spacer = function(){
26789     var s = document.createElement("div");
26790     s.className = "ytb-spacer";
26791     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26792 };
26793 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26794     enable:Roo.emptyFn,
26795     disable:Roo.emptyFn,
26796     focus:Roo.emptyFn
26797 });
26798
26799 /**
26800  * @class Roo.Toolbar.Fill
26801  * @extends Roo.Toolbar.Spacer
26802  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26803  * @constructor
26804  * Creates a new Spacer
26805  */
26806 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26807     // private
26808     render : function(td){
26809         td.style.width = '100%';
26810         Roo.Toolbar.Fill.superclass.render.call(this, td);
26811     }
26812 });
26813
26814 /**
26815  * @class Roo.Toolbar.TextItem
26816  * @extends Roo.Toolbar.Item
26817  * A simple class that renders text directly into a toolbar.
26818  * @constructor
26819  * Creates a new TextItem
26820  * @param {String} text
26821  */
26822 Roo.Toolbar.TextItem = function(text){
26823     if (typeof(text) == 'object') {
26824         text = text.text;
26825     }
26826     var s = document.createElement("span");
26827     s.className = "ytb-text";
26828     s.innerHTML = text;
26829     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26830 };
26831 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26832     enable:Roo.emptyFn,
26833     disable:Roo.emptyFn,
26834     focus:Roo.emptyFn
26835 });
26836
26837 /**
26838  * @class Roo.Toolbar.Button
26839  * @extends Roo.Button
26840  * A button that renders into a toolbar.
26841  * @constructor
26842  * Creates a new Button
26843  * @param {Object} config A standard {@link Roo.Button} config object
26844  */
26845 Roo.Toolbar.Button = function(config){
26846     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26847 };
26848 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26849     render : function(td){
26850         this.td = td;
26851         Roo.Toolbar.Button.superclass.render.call(this, td);
26852     },
26853     
26854     /**
26855      * Removes and destroys this button
26856      */
26857     destroy : function(){
26858         Roo.Toolbar.Button.superclass.destroy.call(this);
26859         this.td.parentNode.removeChild(this.td);
26860     },
26861     
26862     /**
26863      * Shows this button
26864      */
26865     show: function(){
26866         this.hidden = false;
26867         this.td.style.display = "";
26868     },
26869     
26870     /**
26871      * Hides this button
26872      */
26873     hide: function(){
26874         this.hidden = true;
26875         this.td.style.display = "none";
26876     },
26877
26878     /**
26879      * Disables this item
26880      */
26881     disable : function(){
26882         Roo.fly(this.td).addClass("x-item-disabled");
26883         this.disabled = true;
26884     },
26885
26886     /**
26887      * Enables this item
26888      */
26889     enable : function(){
26890         Roo.fly(this.td).removeClass("x-item-disabled");
26891         this.disabled = false;
26892     }
26893 });
26894 // backwards compat
26895 Roo.ToolbarButton = Roo.Toolbar.Button;
26896
26897 /**
26898  * @class Roo.Toolbar.SplitButton
26899  * @extends Roo.SplitButton
26900  * A menu button that renders into a toolbar.
26901  * @constructor
26902  * Creates a new SplitButton
26903  * @param {Object} config A standard {@link Roo.SplitButton} config object
26904  */
26905 Roo.Toolbar.SplitButton = function(config){
26906     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26907 };
26908 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26909     render : function(td){
26910         this.td = td;
26911         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26912     },
26913     
26914     /**
26915      * Removes and destroys this button
26916      */
26917     destroy : function(){
26918         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26919         this.td.parentNode.removeChild(this.td);
26920     },
26921     
26922     /**
26923      * Shows this button
26924      */
26925     show: function(){
26926         this.hidden = false;
26927         this.td.style.display = "";
26928     },
26929     
26930     /**
26931      * Hides this button
26932      */
26933     hide: function(){
26934         this.hidden = true;
26935         this.td.style.display = "none";
26936     }
26937 });
26938
26939 // backwards compat
26940 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26941  * Based on:
26942  * Ext JS Library 1.1.1
26943  * Copyright(c) 2006-2007, Ext JS, LLC.
26944  *
26945  * Originally Released Under LGPL - original licence link has changed is not relivant.
26946  *
26947  * Fork - LGPL
26948  * <script type="text/javascript">
26949  */
26950  
26951 /**
26952  * @class Roo.PagingToolbar
26953  * @extends Roo.Toolbar
26954  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26955  * @constructor
26956  * Create a new PagingToolbar
26957  * @param {Object} config The config object
26958  */
26959 Roo.PagingToolbar = function(el, ds, config)
26960 {
26961     // old args format still supported... - xtype is prefered..
26962     if (typeof(el) == 'object' && el.xtype) {
26963         // created from xtype...
26964         config = el;
26965         ds = el.dataSource;
26966         el = config.container;
26967     }
26968     
26969     
26970     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26971     this.ds = ds;
26972     this.cursor = 0;
26973     this.renderButtons(this.el);
26974     this.bind(ds);
26975 };
26976
26977 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26978     /**
26979      * @cfg {Roo.data.Store} dataSource
26980      * The underlying data store providing the paged data
26981      */
26982     /**
26983      * @cfg {String/HTMLElement/Element} container
26984      * container The id or element that will contain the toolbar
26985      */
26986     /**
26987      * @cfg {Boolean} displayInfo
26988      * True to display the displayMsg (defaults to false)
26989      */
26990     /**
26991      * @cfg {Number} pageSize
26992      * The number of records to display per page (defaults to 20)
26993      */
26994     pageSize: 20,
26995     /**
26996      * @cfg {String} displayMsg
26997      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26998      */
26999     displayMsg : 'Displaying {0} - {1} of {2}',
27000     /**
27001      * @cfg {String} emptyMsg
27002      * The message to display when no records are found (defaults to "No data to display")
27003      */
27004     emptyMsg : 'No data to display',
27005     /**
27006      * Customizable piece of the default paging text (defaults to "Page")
27007      * @type String
27008      */
27009     beforePageText : "Page",
27010     /**
27011      * Customizable piece of the default paging text (defaults to "of %0")
27012      * @type String
27013      */
27014     afterPageText : "of {0}",
27015     /**
27016      * Customizable piece of the default paging text (defaults to "First Page")
27017      * @type String
27018      */
27019     firstText : "First Page",
27020     /**
27021      * Customizable piece of the default paging text (defaults to "Previous Page")
27022      * @type String
27023      */
27024     prevText : "Previous Page",
27025     /**
27026      * Customizable piece of the default paging text (defaults to "Next Page")
27027      * @type String
27028      */
27029     nextText : "Next Page",
27030     /**
27031      * Customizable piece of the default paging text (defaults to "Last Page")
27032      * @type String
27033      */
27034     lastText : "Last Page",
27035     /**
27036      * Customizable piece of the default paging text (defaults to "Refresh")
27037      * @type String
27038      */
27039     refreshText : "Refresh",
27040
27041     // private
27042     renderButtons : function(el){
27043         Roo.PagingToolbar.superclass.render.call(this, el);
27044         this.first = this.addButton({
27045             tooltip: this.firstText,
27046             cls: "x-btn-icon x-grid-page-first",
27047             disabled: true,
27048             handler: this.onClick.createDelegate(this, ["first"])
27049         });
27050         this.prev = this.addButton({
27051             tooltip: this.prevText,
27052             cls: "x-btn-icon x-grid-page-prev",
27053             disabled: true,
27054             handler: this.onClick.createDelegate(this, ["prev"])
27055         });
27056         this.addSeparator();
27057         this.add(this.beforePageText);
27058         this.field = Roo.get(this.addDom({
27059            tag: "input",
27060            type: "text",
27061            size: "3",
27062            value: "1",
27063            cls: "x-grid-page-number"
27064         }).el);
27065         this.field.on("keydown", this.onPagingKeydown, this);
27066         this.field.on("focus", function(){this.dom.select();});
27067         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27068         this.field.setHeight(18);
27069         this.addSeparator();
27070         this.next = this.addButton({
27071             tooltip: this.nextText,
27072             cls: "x-btn-icon x-grid-page-next",
27073             disabled: true,
27074             handler: this.onClick.createDelegate(this, ["next"])
27075         });
27076         this.last = this.addButton({
27077             tooltip: this.lastText,
27078             cls: "x-btn-icon x-grid-page-last",
27079             disabled: true,
27080             handler: this.onClick.createDelegate(this, ["last"])
27081         });
27082         this.addSeparator();
27083         this.loading = this.addButton({
27084             tooltip: this.refreshText,
27085             cls: "x-btn-icon x-grid-loading",
27086             handler: this.onClick.createDelegate(this, ["refresh"])
27087         });
27088
27089         if(this.displayInfo){
27090             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27091         }
27092     },
27093
27094     // private
27095     updateInfo : function(){
27096         if(this.displayEl){
27097             var count = this.ds.getCount();
27098             var msg = count == 0 ?
27099                 this.emptyMsg :
27100                 String.format(
27101                     this.displayMsg,
27102                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27103                 );
27104             this.displayEl.update(msg);
27105         }
27106     },
27107
27108     // private
27109     onLoad : function(ds, r, o){
27110        this.cursor = o.params ? o.params.start : 0;
27111        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27112
27113        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27114        this.field.dom.value = ap;
27115        this.first.setDisabled(ap == 1);
27116        this.prev.setDisabled(ap == 1);
27117        this.next.setDisabled(ap == ps);
27118        this.last.setDisabled(ap == ps);
27119        this.loading.enable();
27120        this.updateInfo();
27121     },
27122
27123     // private
27124     getPageData : function(){
27125         var total = this.ds.getTotalCount();
27126         return {
27127             total : total,
27128             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27129             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27130         };
27131     },
27132
27133     // private
27134     onLoadError : function(){
27135         this.loading.enable();
27136     },
27137
27138     // private
27139     onPagingKeydown : function(e){
27140         var k = e.getKey();
27141         var d = this.getPageData();
27142         if(k == e.RETURN){
27143             var v = this.field.dom.value, pageNum;
27144             if(!v || isNaN(pageNum = parseInt(v, 10))){
27145                 this.field.dom.value = d.activePage;
27146                 return;
27147             }
27148             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27149             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27150             e.stopEvent();
27151         }
27152         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))
27153         {
27154           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27155           this.field.dom.value = pageNum;
27156           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27157           e.stopEvent();
27158         }
27159         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27160         {
27161           var v = this.field.dom.value, pageNum; 
27162           var increment = (e.shiftKey) ? 10 : 1;
27163           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27164             increment *= -1;
27165           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27166             this.field.dom.value = d.activePage;
27167             return;
27168           }
27169           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27170           {
27171             this.field.dom.value = parseInt(v, 10) + increment;
27172             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27173             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27174           }
27175           e.stopEvent();
27176         }
27177     },
27178
27179     // private
27180     beforeLoad : function(){
27181         if(this.loading){
27182             this.loading.disable();
27183         }
27184     },
27185
27186     // private
27187     onClick : function(which){
27188         var ds = this.ds;
27189         switch(which){
27190             case "first":
27191                 ds.load({params:{start: 0, limit: this.pageSize}});
27192             break;
27193             case "prev":
27194                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27195             break;
27196             case "next":
27197                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27198             break;
27199             case "last":
27200                 var total = ds.getTotalCount();
27201                 var extra = total % this.pageSize;
27202                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27203                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27204             break;
27205             case "refresh":
27206                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27207             break;
27208         }
27209     },
27210
27211     /**
27212      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27213      * @param {Roo.data.Store} store The data store to unbind
27214      */
27215     unbind : function(ds){
27216         ds.un("beforeload", this.beforeLoad, this);
27217         ds.un("load", this.onLoad, this);
27218         ds.un("loadexception", this.onLoadError, this);
27219         ds.un("remove", this.updateInfo, this);
27220         ds.un("add", this.updateInfo, this);
27221         this.ds = undefined;
27222     },
27223
27224     /**
27225      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27226      * @param {Roo.data.Store} store The data store to bind
27227      */
27228     bind : function(ds){
27229         ds.on("beforeload", this.beforeLoad, this);
27230         ds.on("load", this.onLoad, this);
27231         ds.on("loadexception", this.onLoadError, this);
27232         ds.on("remove", this.updateInfo, this);
27233         ds.on("add", this.updateInfo, this);
27234         this.ds = ds;
27235     }
27236 });/*
27237  * Based on:
27238  * Ext JS Library 1.1.1
27239  * Copyright(c) 2006-2007, Ext JS, LLC.
27240  *
27241  * Originally Released Under LGPL - original licence link has changed is not relivant.
27242  *
27243  * Fork - LGPL
27244  * <script type="text/javascript">
27245  */
27246
27247 /**
27248  * @class Roo.Resizable
27249  * @extends Roo.util.Observable
27250  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27251  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27252  * 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
27253  * the element will be wrapped for you automatically.</p>
27254  * <p>Here is the list of valid resize handles:</p>
27255  * <pre>
27256 Value   Description
27257 ------  -------------------
27258  'n'     north
27259  's'     south
27260  'e'     east
27261  'w'     west
27262  'nw'    northwest
27263  'sw'    southwest
27264  'se'    southeast
27265  'ne'    northeast
27266  'all'   all
27267 </pre>
27268  * <p>Here's an example showing the creation of a typical Resizable:</p>
27269  * <pre><code>
27270 var resizer = new Roo.Resizable("element-id", {
27271     handles: 'all',
27272     minWidth: 200,
27273     minHeight: 100,
27274     maxWidth: 500,
27275     maxHeight: 400,
27276     pinned: true
27277 });
27278 resizer.on("resize", myHandler);
27279 </code></pre>
27280  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27281  * resizer.east.setDisplayed(false);</p>
27282  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27283  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27284  * resize operation's new size (defaults to [0, 0])
27285  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27286  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27287  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27288  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27289  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27290  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27291  * @cfg {Number} width The width of the element in pixels (defaults to null)
27292  * @cfg {Number} height The height of the element in pixels (defaults to null)
27293  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27294  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27295  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27296  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27297  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27298  * in favor of the handles config option (defaults to false)
27299  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27300  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27301  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27302  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27303  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27304  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27305  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27306  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27307  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27308  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27309  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27310  * @constructor
27311  * Create a new resizable component
27312  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27313  * @param {Object} config configuration options
27314   */
27315 Roo.Resizable = function(el, config){
27316     this.el = Roo.get(el);
27317
27318     if(config && config.wrap){
27319         config.resizeChild = this.el;
27320         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27321         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27322         this.el.setStyle("overflow", "hidden");
27323         this.el.setPositioning(config.resizeChild.getPositioning());
27324         config.resizeChild.clearPositioning();
27325         if(!config.width || !config.height){
27326             var csize = config.resizeChild.getSize();
27327             this.el.setSize(csize.width, csize.height);
27328         }
27329         if(config.pinned && !config.adjustments){
27330             config.adjustments = "auto";
27331         }
27332     }
27333
27334     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27335     this.proxy.unselectable();
27336     this.proxy.enableDisplayMode('block');
27337
27338     Roo.apply(this, config);
27339
27340     if(this.pinned){
27341         this.disableTrackOver = true;
27342         this.el.addClass("x-resizable-pinned");
27343     }
27344     // if the element isn't positioned, make it relative
27345     var position = this.el.getStyle("position");
27346     if(position != "absolute" && position != "fixed"){
27347         this.el.setStyle("position", "relative");
27348     }
27349     if(!this.handles){ // no handles passed, must be legacy style
27350         this.handles = 's,e,se';
27351         if(this.multiDirectional){
27352             this.handles += ',n,w';
27353         }
27354     }
27355     if(this.handles == "all"){
27356         this.handles = "n s e w ne nw se sw";
27357     }
27358     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27359     var ps = Roo.Resizable.positions;
27360     for(var i = 0, len = hs.length; i < len; i++){
27361         if(hs[i] && ps[hs[i]]){
27362             var pos = ps[hs[i]];
27363             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27364         }
27365     }
27366     // legacy
27367     this.corner = this.southeast;
27368
27369     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27370         this.updateBox = true;
27371     }
27372
27373     this.activeHandle = null;
27374
27375     if(this.resizeChild){
27376         if(typeof this.resizeChild == "boolean"){
27377             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27378         }else{
27379             this.resizeChild = Roo.get(this.resizeChild, true);
27380         }
27381     }
27382
27383     if(this.adjustments == "auto"){
27384         var rc = this.resizeChild;
27385         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27386         if(rc && (hw || hn)){
27387             rc.position("relative");
27388             rc.setLeft(hw ? hw.el.getWidth() : 0);
27389             rc.setTop(hn ? hn.el.getHeight() : 0);
27390         }
27391         this.adjustments = [
27392             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27393             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27394         ];
27395     }
27396
27397     if(this.draggable){
27398         this.dd = this.dynamic ?
27399             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27400         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27401     }
27402
27403     // public events
27404     this.addEvents({
27405         /**
27406          * @event beforeresize
27407          * Fired before resize is allowed. Set enabled to false to cancel resize.
27408          * @param {Roo.Resizable} this
27409          * @param {Roo.EventObject} e The mousedown event
27410          */
27411         "beforeresize" : true,
27412         /**
27413          * @event resize
27414          * Fired after a resize.
27415          * @param {Roo.Resizable} this
27416          * @param {Number} width The new width
27417          * @param {Number} height The new height
27418          * @param {Roo.EventObject} e The mouseup event
27419          */
27420         "resize" : true
27421     });
27422
27423     if(this.width !== null && this.height !== null){
27424         this.resizeTo(this.width, this.height);
27425     }else{
27426         this.updateChildSize();
27427     }
27428     if(Roo.isIE){
27429         this.el.dom.style.zoom = 1;
27430     }
27431     Roo.Resizable.superclass.constructor.call(this);
27432 };
27433
27434 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27435         resizeChild : false,
27436         adjustments : [0, 0],
27437         minWidth : 5,
27438         minHeight : 5,
27439         maxWidth : 10000,
27440         maxHeight : 10000,
27441         enabled : true,
27442         animate : false,
27443         duration : .35,
27444         dynamic : false,
27445         handles : false,
27446         multiDirectional : false,
27447         disableTrackOver : false,
27448         easing : 'easeOutStrong',
27449         widthIncrement : 0,
27450         heightIncrement : 0,
27451         pinned : false,
27452         width : null,
27453         height : null,
27454         preserveRatio : false,
27455         transparent: false,
27456         minX: 0,
27457         minY: 0,
27458         draggable: false,
27459
27460         /**
27461          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27462          */
27463         constrainTo: undefined,
27464         /**
27465          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27466          */
27467         resizeRegion: undefined,
27468
27469
27470     /**
27471      * Perform a manual resize
27472      * @param {Number} width
27473      * @param {Number} height
27474      */
27475     resizeTo : function(width, height){
27476         this.el.setSize(width, height);
27477         this.updateChildSize();
27478         this.fireEvent("resize", this, width, height, null);
27479     },
27480
27481     // private
27482     startSizing : function(e, handle){
27483         this.fireEvent("beforeresize", this, e);
27484         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27485
27486             if(!this.overlay){
27487                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27488                 this.overlay.unselectable();
27489                 this.overlay.enableDisplayMode("block");
27490                 this.overlay.on("mousemove", this.onMouseMove, this);
27491                 this.overlay.on("mouseup", this.onMouseUp, this);
27492             }
27493             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27494
27495             this.resizing = true;
27496             this.startBox = this.el.getBox();
27497             this.startPoint = e.getXY();
27498             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27499                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27500
27501             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27502             this.overlay.show();
27503
27504             if(this.constrainTo) {
27505                 var ct = Roo.get(this.constrainTo);
27506                 this.resizeRegion = ct.getRegion().adjust(
27507                     ct.getFrameWidth('t'),
27508                     ct.getFrameWidth('l'),
27509                     -ct.getFrameWidth('b'),
27510                     -ct.getFrameWidth('r')
27511                 );
27512             }
27513
27514             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27515             this.proxy.show();
27516             this.proxy.setBox(this.startBox);
27517             if(!this.dynamic){
27518                 this.proxy.setStyle('visibility', 'visible');
27519             }
27520         }
27521     },
27522
27523     // private
27524     onMouseDown : function(handle, e){
27525         if(this.enabled){
27526             e.stopEvent();
27527             this.activeHandle = handle;
27528             this.startSizing(e, handle);
27529         }
27530     },
27531
27532     // private
27533     onMouseUp : function(e){
27534         var size = this.resizeElement();
27535         this.resizing = false;
27536         this.handleOut();
27537         this.overlay.hide();
27538         this.proxy.hide();
27539         this.fireEvent("resize", this, size.width, size.height, e);
27540     },
27541
27542     // private
27543     updateChildSize : function(){
27544         if(this.resizeChild){
27545             var el = this.el;
27546             var child = this.resizeChild;
27547             var adj = this.adjustments;
27548             if(el.dom.offsetWidth){
27549                 var b = el.getSize(true);
27550                 child.setSize(b.width+adj[0], b.height+adj[1]);
27551             }
27552             // Second call here for IE
27553             // The first call enables instant resizing and
27554             // the second call corrects scroll bars if they
27555             // exist
27556             if(Roo.isIE){
27557                 setTimeout(function(){
27558                     if(el.dom.offsetWidth){
27559                         var b = el.getSize(true);
27560                         child.setSize(b.width+adj[0], b.height+adj[1]);
27561                     }
27562                 }, 10);
27563             }
27564         }
27565     },
27566
27567     // private
27568     snap : function(value, inc, min){
27569         if(!inc || !value) return value;
27570         var newValue = value;
27571         var m = value % inc;
27572         if(m > 0){
27573             if(m > (inc/2)){
27574                 newValue = value + (inc-m);
27575             }else{
27576                 newValue = value - m;
27577             }
27578         }
27579         return Math.max(min, newValue);
27580     },
27581
27582     // private
27583     resizeElement : function(){
27584         var box = this.proxy.getBox();
27585         if(this.updateBox){
27586             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27587         }else{
27588             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27589         }
27590         this.updateChildSize();
27591         if(!this.dynamic){
27592             this.proxy.hide();
27593         }
27594         return box;
27595     },
27596
27597     // private
27598     constrain : function(v, diff, m, mx){
27599         if(v - diff < m){
27600             diff = v - m;
27601         }else if(v - diff > mx){
27602             diff = mx - v;
27603         }
27604         return diff;
27605     },
27606
27607     // private
27608     onMouseMove : function(e){
27609         if(this.enabled){
27610             try{// try catch so if something goes wrong the user doesn't get hung
27611
27612             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27613                 return;
27614             }
27615
27616             //var curXY = this.startPoint;
27617             var curSize = this.curSize || this.startBox;
27618             var x = this.startBox.x, y = this.startBox.y;
27619             var ox = x, oy = y;
27620             var w = curSize.width, h = curSize.height;
27621             var ow = w, oh = h;
27622             var mw = this.minWidth, mh = this.minHeight;
27623             var mxw = this.maxWidth, mxh = this.maxHeight;
27624             var wi = this.widthIncrement;
27625             var hi = this.heightIncrement;
27626
27627             var eventXY = e.getXY();
27628             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27629             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27630
27631             var pos = this.activeHandle.position;
27632
27633             switch(pos){
27634                 case "east":
27635                     w += diffX;
27636                     w = Math.min(Math.max(mw, w), mxw);
27637                     break;
27638                 case "south":
27639                     h += diffY;
27640                     h = Math.min(Math.max(mh, h), mxh);
27641                     break;
27642                 case "southeast":
27643                     w += diffX;
27644                     h += diffY;
27645                     w = Math.min(Math.max(mw, w), mxw);
27646                     h = Math.min(Math.max(mh, h), mxh);
27647                     break;
27648                 case "north":
27649                     diffY = this.constrain(h, diffY, mh, mxh);
27650                     y += diffY;
27651                     h -= diffY;
27652                     break;
27653                 case "west":
27654                     diffX = this.constrain(w, diffX, mw, mxw);
27655                     x += diffX;
27656                     w -= diffX;
27657                     break;
27658                 case "northeast":
27659                     w += diffX;
27660                     w = Math.min(Math.max(mw, w), mxw);
27661                     diffY = this.constrain(h, diffY, mh, mxh);
27662                     y += diffY;
27663                     h -= diffY;
27664                     break;
27665                 case "northwest":
27666                     diffX = this.constrain(w, diffX, mw, mxw);
27667                     diffY = this.constrain(h, diffY, mh, mxh);
27668                     y += diffY;
27669                     h -= diffY;
27670                     x += diffX;
27671                     w -= diffX;
27672                     break;
27673                case "southwest":
27674                     diffX = this.constrain(w, diffX, mw, mxw);
27675                     h += diffY;
27676                     h = Math.min(Math.max(mh, h), mxh);
27677                     x += diffX;
27678                     w -= diffX;
27679                     break;
27680             }
27681
27682             var sw = this.snap(w, wi, mw);
27683             var sh = this.snap(h, hi, mh);
27684             if(sw != w || sh != h){
27685                 switch(pos){
27686                     case "northeast":
27687                         y -= sh - h;
27688                     break;
27689                     case "north":
27690                         y -= sh - h;
27691                         break;
27692                     case "southwest":
27693                         x -= sw - w;
27694                     break;
27695                     case "west":
27696                         x -= sw - w;
27697                         break;
27698                     case "northwest":
27699                         x -= sw - w;
27700                         y -= sh - h;
27701                     break;
27702                 }
27703                 w = sw;
27704                 h = sh;
27705             }
27706
27707             if(this.preserveRatio){
27708                 switch(pos){
27709                     case "southeast":
27710                     case "east":
27711                         h = oh * (w/ow);
27712                         h = Math.min(Math.max(mh, h), mxh);
27713                         w = ow * (h/oh);
27714                        break;
27715                     case "south":
27716                         w = ow * (h/oh);
27717                         w = Math.min(Math.max(mw, w), mxw);
27718                         h = oh * (w/ow);
27719                         break;
27720                     case "northeast":
27721                         w = ow * (h/oh);
27722                         w = Math.min(Math.max(mw, w), mxw);
27723                         h = oh * (w/ow);
27724                     break;
27725                     case "north":
27726                         var tw = w;
27727                         w = ow * (h/oh);
27728                         w = Math.min(Math.max(mw, w), mxw);
27729                         h = oh * (w/ow);
27730                         x += (tw - w) / 2;
27731                         break;
27732                     case "southwest":
27733                         h = oh * (w/ow);
27734                         h = Math.min(Math.max(mh, h), mxh);
27735                         var tw = w;
27736                         w = ow * (h/oh);
27737                         x += tw - w;
27738                         break;
27739                     case "west":
27740                         var th = h;
27741                         h = oh * (w/ow);
27742                         h = Math.min(Math.max(mh, h), mxh);
27743                         y += (th - h) / 2;
27744                         var tw = w;
27745                         w = ow * (h/oh);
27746                         x += tw - w;
27747                        break;
27748                     case "northwest":
27749                         var tw = w;
27750                         var th = h;
27751                         h = oh * (w/ow);
27752                         h = Math.min(Math.max(mh, h), mxh);
27753                         w = ow * (h/oh);
27754                         y += th - h;
27755                          x += tw - w;
27756                        break;
27757
27758                 }
27759             }
27760             this.proxy.setBounds(x, y, w, h);
27761             if(this.dynamic){
27762                 this.resizeElement();
27763             }
27764             }catch(e){}
27765         }
27766     },
27767
27768     // private
27769     handleOver : function(){
27770         if(this.enabled){
27771             this.el.addClass("x-resizable-over");
27772         }
27773     },
27774
27775     // private
27776     handleOut : function(){
27777         if(!this.resizing){
27778             this.el.removeClass("x-resizable-over");
27779         }
27780     },
27781
27782     /**
27783      * Returns the element this component is bound to.
27784      * @return {Roo.Element}
27785      */
27786     getEl : function(){
27787         return this.el;
27788     },
27789
27790     /**
27791      * Returns the resizeChild element (or null).
27792      * @return {Roo.Element}
27793      */
27794     getResizeChild : function(){
27795         return this.resizeChild;
27796     },
27797
27798     /**
27799      * Destroys this resizable. If the element was wrapped and
27800      * removeEl is not true then the element remains.
27801      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27802      */
27803     destroy : function(removeEl){
27804         this.proxy.remove();
27805         if(this.overlay){
27806             this.overlay.removeAllListeners();
27807             this.overlay.remove();
27808         }
27809         var ps = Roo.Resizable.positions;
27810         for(var k in ps){
27811             if(typeof ps[k] != "function" && this[ps[k]]){
27812                 var h = this[ps[k]];
27813                 h.el.removeAllListeners();
27814                 h.el.remove();
27815             }
27816         }
27817         if(removeEl){
27818             this.el.update("");
27819             this.el.remove();
27820         }
27821     }
27822 });
27823
27824 // private
27825 // hash to map config positions to true positions
27826 Roo.Resizable.positions = {
27827     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27828 };
27829
27830 // private
27831 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27832     if(!this.tpl){
27833         // only initialize the template if resizable is used
27834         var tpl = Roo.DomHelper.createTemplate(
27835             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27836         );
27837         tpl.compile();
27838         Roo.Resizable.Handle.prototype.tpl = tpl;
27839     }
27840     this.position = pos;
27841     this.rz = rz;
27842     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27843     this.el.unselectable();
27844     if(transparent){
27845         this.el.setOpacity(0);
27846     }
27847     this.el.on("mousedown", this.onMouseDown, this);
27848     if(!disableTrackOver){
27849         this.el.on("mouseover", this.onMouseOver, this);
27850         this.el.on("mouseout", this.onMouseOut, this);
27851     }
27852 };
27853
27854 // private
27855 Roo.Resizable.Handle.prototype = {
27856     afterResize : function(rz){
27857         // do nothing
27858     },
27859     // private
27860     onMouseDown : function(e){
27861         this.rz.onMouseDown(this, e);
27862     },
27863     // private
27864     onMouseOver : function(e){
27865         this.rz.handleOver(this, e);
27866     },
27867     // private
27868     onMouseOut : function(e){
27869         this.rz.handleOut(this, e);
27870     }
27871 };/*
27872  * Based on:
27873  * Ext JS Library 1.1.1
27874  * Copyright(c) 2006-2007, Ext JS, LLC.
27875  *
27876  * Originally Released Under LGPL - original licence link has changed is not relivant.
27877  *
27878  * Fork - LGPL
27879  * <script type="text/javascript">
27880  */
27881
27882 /**
27883  * @class Roo.Editor
27884  * @extends Roo.Component
27885  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27886  * @constructor
27887  * Create a new Editor
27888  * @param {Roo.form.Field} field The Field object (or descendant)
27889  * @param {Object} config The config object
27890  */
27891 Roo.Editor = function(field, config){
27892     Roo.Editor.superclass.constructor.call(this, config);
27893     this.field = field;
27894     this.addEvents({
27895         /**
27896              * @event beforestartedit
27897              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27898              * false from the handler of this event.
27899              * @param {Editor} this
27900              * @param {Roo.Element} boundEl The underlying element bound to this editor
27901              * @param {Mixed} value The field value being set
27902              */
27903         "beforestartedit" : true,
27904         /**
27905              * @event startedit
27906              * Fires when this editor is displayed
27907              * @param {Roo.Element} boundEl The underlying element bound to this editor
27908              * @param {Mixed} value The starting field value
27909              */
27910         "startedit" : true,
27911         /**
27912              * @event beforecomplete
27913              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27914              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27915              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27916              * event will not fire since no edit actually occurred.
27917              * @param {Editor} this
27918              * @param {Mixed} value The current field value
27919              * @param {Mixed} startValue The original field value
27920              */
27921         "beforecomplete" : true,
27922         /**
27923              * @event complete
27924              * Fires after editing is complete and any changed value has been written to the underlying field.
27925              * @param {Editor} this
27926              * @param {Mixed} value The current field value
27927              * @param {Mixed} startValue The original field value
27928              */
27929         "complete" : true,
27930         /**
27931          * @event specialkey
27932          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27933          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27934          * @param {Roo.form.Field} this
27935          * @param {Roo.EventObject} e The event object
27936          */
27937         "specialkey" : true
27938     });
27939 };
27940
27941 Roo.extend(Roo.Editor, Roo.Component, {
27942     /**
27943      * @cfg {Boolean/String} autosize
27944      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27945      * or "height" to adopt the height only (defaults to false)
27946      */
27947     /**
27948      * @cfg {Boolean} revertInvalid
27949      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27950      * validation fails (defaults to true)
27951      */
27952     /**
27953      * @cfg {Boolean} ignoreNoChange
27954      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27955      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27956      * will never be ignored.
27957      */
27958     /**
27959      * @cfg {Boolean} hideEl
27960      * False to keep the bound element visible while the editor is displayed (defaults to true)
27961      */
27962     /**
27963      * @cfg {Mixed} value
27964      * The data value of the underlying field (defaults to "")
27965      */
27966     value : "",
27967     /**
27968      * @cfg {String} alignment
27969      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27970      */
27971     alignment: "c-c?",
27972     /**
27973      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27974      * for bottom-right shadow (defaults to "frame")
27975      */
27976     shadow : "frame",
27977     /**
27978      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27979      */
27980     constrain : false,
27981     /**
27982      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27983      */
27984     completeOnEnter : false,
27985     /**
27986      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27987      */
27988     cancelOnEsc : false,
27989     /**
27990      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27991      */
27992     updateEl : false,
27993
27994     // private
27995     onRender : function(ct, position){
27996         this.el = new Roo.Layer({
27997             shadow: this.shadow,
27998             cls: "x-editor",
27999             parentEl : ct,
28000             shim : this.shim,
28001             shadowOffset:4,
28002             id: this.id,
28003             constrain: this.constrain
28004         });
28005         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28006         if(this.field.msgTarget != 'title'){
28007             this.field.msgTarget = 'qtip';
28008         }
28009         this.field.render(this.el);
28010         if(Roo.isGecko){
28011             this.field.el.dom.setAttribute('autocomplete', 'off');
28012         }
28013         this.field.on("specialkey", this.onSpecialKey, this);
28014         if(this.swallowKeys){
28015             this.field.el.swallowEvent(['keydown','keypress']);
28016         }
28017         this.field.show();
28018         this.field.on("blur", this.onBlur, this);
28019         if(this.field.grow){
28020             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28021         }
28022     },
28023
28024     onSpecialKey : function(field, e){
28025         if(this.completeOnEnter && e.getKey() == e.ENTER){
28026             e.stopEvent();
28027             this.completeEdit();
28028         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28029             this.cancelEdit();
28030         }else{
28031             this.fireEvent('specialkey', field, e);
28032         }
28033     },
28034
28035     /**
28036      * Starts the editing process and shows the editor.
28037      * @param {String/HTMLElement/Element} el The element to edit
28038      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28039       * to the innerHTML of el.
28040      */
28041     startEdit : function(el, value){
28042         if(this.editing){
28043             this.completeEdit();
28044         }
28045         this.boundEl = Roo.get(el);
28046         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28047         if(!this.rendered){
28048             this.render(this.parentEl || document.body);
28049         }
28050         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28051             return;
28052         }
28053         this.startValue = v;
28054         this.field.setValue(v);
28055         if(this.autoSize){
28056             var sz = this.boundEl.getSize();
28057             switch(this.autoSize){
28058                 case "width":
28059                 this.setSize(sz.width,  "");
28060                 break;
28061                 case "height":
28062                 this.setSize("",  sz.height);
28063                 break;
28064                 default:
28065                 this.setSize(sz.width,  sz.height);
28066             }
28067         }
28068         this.el.alignTo(this.boundEl, this.alignment);
28069         this.editing = true;
28070         if(Roo.QuickTips){
28071             Roo.QuickTips.disable();
28072         }
28073         this.show();
28074     },
28075
28076     /**
28077      * Sets the height and width of this editor.
28078      * @param {Number} width The new width
28079      * @param {Number} height The new height
28080      */
28081     setSize : function(w, h){
28082         this.field.setSize(w, h);
28083         if(this.el){
28084             this.el.sync();
28085         }
28086     },
28087
28088     /**
28089      * Realigns the editor to the bound field based on the current alignment config value.
28090      */
28091     realign : function(){
28092         this.el.alignTo(this.boundEl, this.alignment);
28093     },
28094
28095     /**
28096      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28097      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28098      */
28099     completeEdit : function(remainVisible){
28100         if(!this.editing){
28101             return;
28102         }
28103         var v = this.getValue();
28104         if(this.revertInvalid !== false && !this.field.isValid()){
28105             v = this.startValue;
28106             this.cancelEdit(true);
28107         }
28108         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28109             this.editing = false;
28110             this.hide();
28111             return;
28112         }
28113         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28114             this.editing = false;
28115             if(this.updateEl && this.boundEl){
28116                 this.boundEl.update(v);
28117             }
28118             if(remainVisible !== true){
28119                 this.hide();
28120             }
28121             this.fireEvent("complete", this, v, this.startValue);
28122         }
28123     },
28124
28125     // private
28126     onShow : function(){
28127         this.el.show();
28128         if(this.hideEl !== false){
28129             this.boundEl.hide();
28130         }
28131         this.field.show();
28132         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28133             this.fixIEFocus = true;
28134             this.deferredFocus.defer(50, this);
28135         }else{
28136             this.field.focus();
28137         }
28138         this.fireEvent("startedit", this.boundEl, this.startValue);
28139     },
28140
28141     deferredFocus : function(){
28142         if(this.editing){
28143             this.field.focus();
28144         }
28145     },
28146
28147     /**
28148      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28149      * reverted to the original starting value.
28150      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28151      * cancel (defaults to false)
28152      */
28153     cancelEdit : function(remainVisible){
28154         if(this.editing){
28155             this.setValue(this.startValue);
28156             if(remainVisible !== true){
28157                 this.hide();
28158             }
28159         }
28160     },
28161
28162     // private
28163     onBlur : function(){
28164         if(this.allowBlur !== true && this.editing){
28165             this.completeEdit();
28166         }
28167     },
28168
28169     // private
28170     onHide : function(){
28171         if(this.editing){
28172             this.completeEdit();
28173             return;
28174         }
28175         this.field.blur();
28176         if(this.field.collapse){
28177             this.field.collapse();
28178         }
28179         this.el.hide();
28180         if(this.hideEl !== false){
28181             this.boundEl.show();
28182         }
28183         if(Roo.QuickTips){
28184             Roo.QuickTips.enable();
28185         }
28186     },
28187
28188     /**
28189      * Sets the data value of the editor
28190      * @param {Mixed} value Any valid value supported by the underlying field
28191      */
28192     setValue : function(v){
28193         this.field.setValue(v);
28194     },
28195
28196     /**
28197      * Gets the data value of the editor
28198      * @return {Mixed} The data value
28199      */
28200     getValue : function(){
28201         return this.field.getValue();
28202     }
28203 });/*
28204  * Based on:
28205  * Ext JS Library 1.1.1
28206  * Copyright(c) 2006-2007, Ext JS, LLC.
28207  *
28208  * Originally Released Under LGPL - original licence link has changed is not relivant.
28209  *
28210  * Fork - LGPL
28211  * <script type="text/javascript">
28212  */
28213  
28214 /**
28215  * @class Roo.BasicDialog
28216  * @extends Roo.util.Observable
28217  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28218  * <pre><code>
28219 var dlg = new Roo.BasicDialog("my-dlg", {
28220     height: 200,
28221     width: 300,
28222     minHeight: 100,
28223     minWidth: 150,
28224     modal: true,
28225     proxyDrag: true,
28226     shadow: true
28227 });
28228 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28229 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28230 dlg.addButton('Cancel', dlg.hide, dlg);
28231 dlg.show();
28232 </code></pre>
28233   <b>A Dialog should always be a direct child of the body element.</b>
28234  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28235  * @cfg {String} title Default text to display in the title bar (defaults to null)
28236  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28237  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28238  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28239  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28240  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28241  * (defaults to null with no animation)
28242  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28243  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28244  * property for valid values (defaults to 'all')
28245  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28246  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28247  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28248  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28249  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28250  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28251  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28252  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28253  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28254  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28255  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28256  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28257  * draggable = true (defaults to false)
28258  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28259  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28260  * shadow (defaults to false)
28261  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28262  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28263  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28264  * @cfg {Array} buttons Array of buttons
28265  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28266  * @constructor
28267  * Create a new BasicDialog.
28268  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28269  * @param {Object} config Configuration options
28270  */
28271 Roo.BasicDialog = function(el, config){
28272     this.el = Roo.get(el);
28273     var dh = Roo.DomHelper;
28274     if(!this.el && config && config.autoCreate){
28275         if(typeof config.autoCreate == "object"){
28276             if(!config.autoCreate.id){
28277                 config.autoCreate.id = el;
28278             }
28279             this.el = dh.append(document.body,
28280                         config.autoCreate, true);
28281         }else{
28282             this.el = dh.append(document.body,
28283                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28284         }
28285     }
28286     el = this.el;
28287     el.setDisplayed(true);
28288     el.hide = this.hideAction;
28289     this.id = el.id;
28290     el.addClass("x-dlg");
28291
28292     Roo.apply(this, config);
28293
28294     this.proxy = el.createProxy("x-dlg-proxy");
28295     this.proxy.hide = this.hideAction;
28296     this.proxy.setOpacity(.5);
28297     this.proxy.hide();
28298
28299     if(config.width){
28300         el.setWidth(config.width);
28301     }
28302     if(config.height){
28303         el.setHeight(config.height);
28304     }
28305     this.size = el.getSize();
28306     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28307         this.xy = [config.x,config.y];
28308     }else{
28309         this.xy = el.getCenterXY(true);
28310     }
28311     /** The header element @type Roo.Element */
28312     this.header = el.child("> .x-dlg-hd");
28313     /** The body element @type Roo.Element */
28314     this.body = el.child("> .x-dlg-bd");
28315     /** The footer element @type Roo.Element */
28316     this.footer = el.child("> .x-dlg-ft");
28317
28318     if(!this.header){
28319         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28320     }
28321     if(!this.body){
28322         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28323     }
28324
28325     this.header.unselectable();
28326     if(this.title){
28327         this.header.update(this.title);
28328     }
28329     // this element allows the dialog to be focused for keyboard event
28330     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28331     this.focusEl.swallowEvent("click", true);
28332
28333     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28334
28335     // wrap the body and footer for special rendering
28336     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28337     if(this.footer){
28338         this.bwrap.dom.appendChild(this.footer.dom);
28339     }
28340
28341     this.bg = this.el.createChild({
28342         tag: "div", cls:"x-dlg-bg",
28343         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28344     });
28345     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28346
28347
28348     if(this.autoScroll !== false && !this.autoTabs){
28349         this.body.setStyle("overflow", "auto");
28350     }
28351
28352     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28353
28354     if(this.closable !== false){
28355         this.el.addClass("x-dlg-closable");
28356         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28357         this.close.on("click", this.closeClick, this);
28358         this.close.addClassOnOver("x-dlg-close-over");
28359     }
28360     if(this.collapsible !== false){
28361         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28362         this.collapseBtn.on("click", this.collapseClick, this);
28363         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28364         this.header.on("dblclick", this.collapseClick, this);
28365     }
28366     if(this.resizable !== false){
28367         this.el.addClass("x-dlg-resizable");
28368         this.resizer = new Roo.Resizable(el, {
28369             minWidth: this.minWidth || 80,
28370             minHeight:this.minHeight || 80,
28371             handles: this.resizeHandles || "all",
28372             pinned: true
28373         });
28374         this.resizer.on("beforeresize", this.beforeResize, this);
28375         this.resizer.on("resize", this.onResize, this);
28376     }
28377     if(this.draggable !== false){
28378         el.addClass("x-dlg-draggable");
28379         if (!this.proxyDrag) {
28380             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28381         }
28382         else {
28383             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28384         }
28385         dd.setHandleElId(this.header.id);
28386         dd.endDrag = this.endMove.createDelegate(this);
28387         dd.startDrag = this.startMove.createDelegate(this);
28388         dd.onDrag = this.onDrag.createDelegate(this);
28389         dd.scroll = false;
28390         this.dd = dd;
28391     }
28392     if(this.modal){
28393         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28394         this.mask.enableDisplayMode("block");
28395         this.mask.hide();
28396         this.el.addClass("x-dlg-modal");
28397     }
28398     if(this.shadow){
28399         this.shadow = new Roo.Shadow({
28400             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28401             offset : this.shadowOffset
28402         });
28403     }else{
28404         this.shadowOffset = 0;
28405     }
28406     if(Roo.useShims && this.shim !== false){
28407         this.shim = this.el.createShim();
28408         this.shim.hide = this.hideAction;
28409         this.shim.hide();
28410     }else{
28411         this.shim = false;
28412     }
28413     if(this.autoTabs){
28414         this.initTabs();
28415     }
28416     if (this.buttons) { 
28417         var bts= this.buttons;
28418         this.buttons = [];
28419         Roo.each(bts, function(b) {
28420             this.addButton(b);
28421         }, this);
28422     }
28423     
28424     
28425     this.addEvents({
28426         /**
28427          * @event keydown
28428          * Fires when a key is pressed
28429          * @param {Roo.BasicDialog} this
28430          * @param {Roo.EventObject} e
28431          */
28432         "keydown" : true,
28433         /**
28434          * @event move
28435          * Fires when this dialog is moved by the user.
28436          * @param {Roo.BasicDialog} this
28437          * @param {Number} x The new page X
28438          * @param {Number} y The new page Y
28439          */
28440         "move" : true,
28441         /**
28442          * @event resize
28443          * Fires when this dialog is resized by the user.
28444          * @param {Roo.BasicDialog} this
28445          * @param {Number} width The new width
28446          * @param {Number} height The new height
28447          */
28448         "resize" : true,
28449         /**
28450          * @event beforehide
28451          * Fires before this dialog is hidden.
28452          * @param {Roo.BasicDialog} this
28453          */
28454         "beforehide" : true,
28455         /**
28456          * @event hide
28457          * Fires when this dialog is hidden.
28458          * @param {Roo.BasicDialog} this
28459          */
28460         "hide" : true,
28461         /**
28462          * @event beforeshow
28463          * Fires before this dialog is shown.
28464          * @param {Roo.BasicDialog} this
28465          */
28466         "beforeshow" : true,
28467         /**
28468          * @event show
28469          * Fires when this dialog is shown.
28470          * @param {Roo.BasicDialog} this
28471          */
28472         "show" : true
28473     });
28474     el.on("keydown", this.onKeyDown, this);
28475     el.on("mousedown", this.toFront, this);
28476     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28477     this.el.hide();
28478     Roo.DialogManager.register(this);
28479     Roo.BasicDialog.superclass.constructor.call(this);
28480 };
28481
28482 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28483     shadowOffset: Roo.isIE ? 6 : 5,
28484     minHeight: 80,
28485     minWidth: 200,
28486     minButtonWidth: 75,
28487     defaultButton: null,
28488     buttonAlign: "right",
28489     tabTag: 'div',
28490     firstShow: true,
28491
28492     /**
28493      * Sets the dialog title text
28494      * @param {String} text The title text to display
28495      * @return {Roo.BasicDialog} this
28496      */
28497     setTitle : function(text){
28498         this.header.update(text);
28499         return this;
28500     },
28501
28502     // private
28503     closeClick : function(){
28504         this.hide();
28505     },
28506
28507     // private
28508     collapseClick : function(){
28509         this[this.collapsed ? "expand" : "collapse"]();
28510     },
28511
28512     /**
28513      * Collapses the dialog to its minimized state (only the title bar is visible).
28514      * Equivalent to the user clicking the collapse dialog button.
28515      */
28516     collapse : function(){
28517         if(!this.collapsed){
28518             this.collapsed = true;
28519             this.el.addClass("x-dlg-collapsed");
28520             this.restoreHeight = this.el.getHeight();
28521             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28522         }
28523     },
28524
28525     /**
28526      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28527      * clicking the expand dialog button.
28528      */
28529     expand : function(){
28530         if(this.collapsed){
28531             this.collapsed = false;
28532             this.el.removeClass("x-dlg-collapsed");
28533             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28534         }
28535     },
28536
28537     /**
28538      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28539      * @return {Roo.TabPanel} The tabs component
28540      */
28541     initTabs : function(){
28542         var tabs = this.getTabs();
28543         while(tabs.getTab(0)){
28544             tabs.removeTab(0);
28545         }
28546         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28547             var dom = el.dom;
28548             tabs.addTab(Roo.id(dom), dom.title);
28549             dom.title = "";
28550         });
28551         tabs.activate(0);
28552         return tabs;
28553     },
28554
28555     // private
28556     beforeResize : function(){
28557         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28558     },
28559
28560     // private
28561     onResize : function(){
28562         this.refreshSize();
28563         this.syncBodyHeight();
28564         this.adjustAssets();
28565         this.focus();
28566         this.fireEvent("resize", this, this.size.width, this.size.height);
28567     },
28568
28569     // private
28570     onKeyDown : function(e){
28571         if(this.isVisible()){
28572             this.fireEvent("keydown", this, e);
28573         }
28574     },
28575
28576     /**
28577      * Resizes the dialog.
28578      * @param {Number} width
28579      * @param {Number} height
28580      * @return {Roo.BasicDialog} this
28581      */
28582     resizeTo : function(width, height){
28583         this.el.setSize(width, height);
28584         this.size = {width: width, height: height};
28585         this.syncBodyHeight();
28586         if(this.fixedcenter){
28587             this.center();
28588         }
28589         if(this.isVisible()){
28590             this.constrainXY();
28591             this.adjustAssets();
28592         }
28593         this.fireEvent("resize", this, width, height);
28594         return this;
28595     },
28596
28597
28598     /**
28599      * Resizes the dialog to fit the specified content size.
28600      * @param {Number} width
28601      * @param {Number} height
28602      * @return {Roo.BasicDialog} this
28603      */
28604     setContentSize : function(w, h){
28605         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28606         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28607         //if(!this.el.isBorderBox()){
28608             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28609             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28610         //}
28611         if(this.tabs){
28612             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28613             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28614         }
28615         this.resizeTo(w, h);
28616         return this;
28617     },
28618
28619     /**
28620      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28621      * executed in response to a particular key being pressed while the dialog is active.
28622      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28623      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28624      * @param {Function} fn The function to call
28625      * @param {Object} scope (optional) The scope of the function
28626      * @return {Roo.BasicDialog} this
28627      */
28628     addKeyListener : function(key, fn, scope){
28629         var keyCode, shift, ctrl, alt;
28630         if(typeof key == "object" && !(key instanceof Array)){
28631             keyCode = key["key"];
28632             shift = key["shift"];
28633             ctrl = key["ctrl"];
28634             alt = key["alt"];
28635         }else{
28636             keyCode = key;
28637         }
28638         var handler = function(dlg, e){
28639             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28640                 var k = e.getKey();
28641                 if(keyCode instanceof Array){
28642                     for(var i = 0, len = keyCode.length; i < len; i++){
28643                         if(keyCode[i] == k){
28644                           fn.call(scope || window, dlg, k, e);
28645                           return;
28646                         }
28647                     }
28648                 }else{
28649                     if(k == keyCode){
28650                         fn.call(scope || window, dlg, k, e);
28651                     }
28652                 }
28653             }
28654         };
28655         this.on("keydown", handler);
28656         return this;
28657     },
28658
28659     /**
28660      * Returns the TabPanel component (creates it if it doesn't exist).
28661      * Note: If you wish to simply check for the existence of tabs without creating them,
28662      * check for a null 'tabs' property.
28663      * @return {Roo.TabPanel} The tabs component
28664      */
28665     getTabs : function(){
28666         if(!this.tabs){
28667             this.el.addClass("x-dlg-auto-tabs");
28668             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28669             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28670         }
28671         return this.tabs;
28672     },
28673
28674     /**
28675      * Adds a button to the footer section of the dialog.
28676      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28677      * object or a valid Roo.DomHelper element config
28678      * @param {Function} handler The function called when the button is clicked
28679      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28680      * @return {Roo.Button} The new button
28681      */
28682     addButton : function(config, handler, scope){
28683         var dh = Roo.DomHelper;
28684         if(!this.footer){
28685             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28686         }
28687         if(!this.btnContainer){
28688             var tb = this.footer.createChild({
28689
28690                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28691                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28692             }, null, true);
28693             this.btnContainer = tb.firstChild.firstChild.firstChild;
28694         }
28695         var bconfig = {
28696             handler: handler,
28697             scope: scope,
28698             minWidth: this.minButtonWidth,
28699             hideParent:true
28700         };
28701         if(typeof config == "string"){
28702             bconfig.text = config;
28703         }else{
28704             if(config.tag){
28705                 bconfig.dhconfig = config;
28706             }else{
28707                 Roo.apply(bconfig, config);
28708             }
28709         }
28710         var fc = false;
28711         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28712             bconfig.position = Math.max(0, bconfig.position);
28713             fc = this.btnContainer.childNodes[bconfig.position];
28714         }
28715          
28716         var btn = new Roo.Button(
28717             fc ? 
28718                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28719                 : this.btnContainer.appendChild(document.createElement("td")),
28720             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28721             bconfig
28722         );
28723         this.syncBodyHeight();
28724         if(!this.buttons){
28725             /**
28726              * Array of all the buttons that have been added to this dialog via addButton
28727              * @type Array
28728              */
28729             this.buttons = [];
28730         }
28731         this.buttons.push(btn);
28732         return btn;
28733     },
28734
28735     /**
28736      * Sets the default button to be focused when the dialog is displayed.
28737      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28738      * @return {Roo.BasicDialog} this
28739      */
28740     setDefaultButton : function(btn){
28741         this.defaultButton = btn;
28742         return this;
28743     },
28744
28745     // private
28746     getHeaderFooterHeight : function(safe){
28747         var height = 0;
28748         if(this.header){
28749            height += this.header.getHeight();
28750         }
28751         if(this.footer){
28752            var fm = this.footer.getMargins();
28753             height += (this.footer.getHeight()+fm.top+fm.bottom);
28754         }
28755         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28756         height += this.centerBg.getPadding("tb");
28757         return height;
28758     },
28759
28760     // private
28761     syncBodyHeight : function(){
28762         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28763         var height = this.size.height - this.getHeaderFooterHeight(false);
28764         bd.setHeight(height-bd.getMargins("tb"));
28765         var hh = this.header.getHeight();
28766         var h = this.size.height-hh;
28767         cb.setHeight(h);
28768         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28769         bw.setHeight(h-cb.getPadding("tb"));
28770         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28771         bd.setWidth(bw.getWidth(true));
28772         if(this.tabs){
28773             this.tabs.syncHeight();
28774             if(Roo.isIE){
28775                 this.tabs.el.repaint();
28776             }
28777         }
28778     },
28779
28780     /**
28781      * Restores the previous state of the dialog if Roo.state is configured.
28782      * @return {Roo.BasicDialog} this
28783      */
28784     restoreState : function(){
28785         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28786         if(box && box.width){
28787             this.xy = [box.x, box.y];
28788             this.resizeTo(box.width, box.height);
28789         }
28790         return this;
28791     },
28792
28793     // private
28794     beforeShow : function(){
28795         this.expand();
28796         if(this.fixedcenter){
28797             this.xy = this.el.getCenterXY(true);
28798         }
28799         if(this.modal){
28800             Roo.get(document.body).addClass("x-body-masked");
28801             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28802             this.mask.show();
28803         }
28804         this.constrainXY();
28805     },
28806
28807     // private
28808     animShow : function(){
28809         var b = Roo.get(this.animateTarget, true).getBox();
28810         this.proxy.setSize(b.width, b.height);
28811         this.proxy.setLocation(b.x, b.y);
28812         this.proxy.show();
28813         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28814                     true, .35, this.showEl.createDelegate(this));
28815     },
28816
28817     /**
28818      * Shows the dialog.
28819      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28820      * @return {Roo.BasicDialog} this
28821      */
28822     show : function(animateTarget){
28823         if (this.fireEvent("beforeshow", this) === false){
28824             return;
28825         }
28826         if(this.syncHeightBeforeShow){
28827             this.syncBodyHeight();
28828         }else if(this.firstShow){
28829             this.firstShow = false;
28830             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28831         }
28832         this.animateTarget = animateTarget || this.animateTarget;
28833         if(!this.el.isVisible()){
28834             this.beforeShow();
28835             if(this.animateTarget){
28836                 this.animShow();
28837             }else{
28838                 this.showEl();
28839             }
28840         }
28841         return this;
28842     },
28843
28844     // private
28845     showEl : function(){
28846         this.proxy.hide();
28847         this.el.setXY(this.xy);
28848         this.el.show();
28849         this.adjustAssets(true);
28850         this.toFront();
28851         this.focus();
28852         // IE peekaboo bug - fix found by Dave Fenwick
28853         if(Roo.isIE){
28854             this.el.repaint();
28855         }
28856         this.fireEvent("show", this);
28857     },
28858
28859     /**
28860      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28861      * dialog itself will receive focus.
28862      */
28863     focus : function(){
28864         if(this.defaultButton){
28865             this.defaultButton.focus();
28866         }else{
28867             this.focusEl.focus();
28868         }
28869     },
28870
28871     // private
28872     constrainXY : function(){
28873         if(this.constraintoviewport !== false){
28874             if(!this.viewSize){
28875                 if(this.container){
28876                     var s = this.container.getSize();
28877                     this.viewSize = [s.width, s.height];
28878                 }else{
28879                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28880                 }
28881             }
28882             var s = Roo.get(this.container||document).getScroll();
28883
28884             var x = this.xy[0], y = this.xy[1];
28885             var w = this.size.width, h = this.size.height;
28886             var vw = this.viewSize[0], vh = this.viewSize[1];
28887             // only move it if it needs it
28888             var moved = false;
28889             // first validate right/bottom
28890             if(x + w > vw+s.left){
28891                 x = vw - w;
28892                 moved = true;
28893             }
28894             if(y + h > vh+s.top){
28895                 y = vh - h;
28896                 moved = true;
28897             }
28898             // then make sure top/left isn't negative
28899             if(x < s.left){
28900                 x = s.left;
28901                 moved = true;
28902             }
28903             if(y < s.top){
28904                 y = s.top;
28905                 moved = true;
28906             }
28907             if(moved){
28908                 // cache xy
28909                 this.xy = [x, y];
28910                 if(this.isVisible()){
28911                     this.el.setLocation(x, y);
28912                     this.adjustAssets();
28913                 }
28914             }
28915         }
28916     },
28917
28918     // private
28919     onDrag : function(){
28920         if(!this.proxyDrag){
28921             this.xy = this.el.getXY();
28922             this.adjustAssets();
28923         }
28924     },
28925
28926     // private
28927     adjustAssets : function(doShow){
28928         var x = this.xy[0], y = this.xy[1];
28929         var w = this.size.width, h = this.size.height;
28930         if(doShow === true){
28931             if(this.shadow){
28932                 this.shadow.show(this.el);
28933             }
28934             if(this.shim){
28935                 this.shim.show();
28936             }
28937         }
28938         if(this.shadow && this.shadow.isVisible()){
28939             this.shadow.show(this.el);
28940         }
28941         if(this.shim && this.shim.isVisible()){
28942             this.shim.setBounds(x, y, w, h);
28943         }
28944     },
28945
28946     // private
28947     adjustViewport : function(w, h){
28948         if(!w || !h){
28949             w = Roo.lib.Dom.getViewWidth();
28950             h = Roo.lib.Dom.getViewHeight();
28951         }
28952         // cache the size
28953         this.viewSize = [w, h];
28954         if(this.modal && this.mask.isVisible()){
28955             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28956             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28957         }
28958         if(this.isVisible()){
28959             this.constrainXY();
28960         }
28961     },
28962
28963     /**
28964      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28965      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28966      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28967      */
28968     destroy : function(removeEl){
28969         if(this.isVisible()){
28970             this.animateTarget = null;
28971             this.hide();
28972         }
28973         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28974         if(this.tabs){
28975             this.tabs.destroy(removeEl);
28976         }
28977         Roo.destroy(
28978              this.shim,
28979              this.proxy,
28980              this.resizer,
28981              this.close,
28982              this.mask
28983         );
28984         if(this.dd){
28985             this.dd.unreg();
28986         }
28987         if(this.buttons){
28988            for(var i = 0, len = this.buttons.length; i < len; i++){
28989                this.buttons[i].destroy();
28990            }
28991         }
28992         this.el.removeAllListeners();
28993         if(removeEl === true){
28994             this.el.update("");
28995             this.el.remove();
28996         }
28997         Roo.DialogManager.unregister(this);
28998     },
28999
29000     // private
29001     startMove : function(){
29002         if(this.proxyDrag){
29003             this.proxy.show();
29004         }
29005         if(this.constraintoviewport !== false){
29006             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29007         }
29008     },
29009
29010     // private
29011     endMove : function(){
29012         if(!this.proxyDrag){
29013             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29014         }else{
29015             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29016             this.proxy.hide();
29017         }
29018         this.refreshSize();
29019         this.adjustAssets();
29020         this.focus();
29021         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29022     },
29023
29024     /**
29025      * Brings this dialog to the front of any other visible dialogs
29026      * @return {Roo.BasicDialog} this
29027      */
29028     toFront : function(){
29029         Roo.DialogManager.bringToFront(this);
29030         return this;
29031     },
29032
29033     /**
29034      * Sends this dialog to the back (under) of any other visible dialogs
29035      * @return {Roo.BasicDialog} this
29036      */
29037     toBack : function(){
29038         Roo.DialogManager.sendToBack(this);
29039         return this;
29040     },
29041
29042     /**
29043      * Centers this dialog in the viewport
29044      * @return {Roo.BasicDialog} this
29045      */
29046     center : function(){
29047         var xy = this.el.getCenterXY(true);
29048         this.moveTo(xy[0], xy[1]);
29049         return this;
29050     },
29051
29052     /**
29053      * Moves the dialog's top-left corner to the specified point
29054      * @param {Number} x
29055      * @param {Number} y
29056      * @return {Roo.BasicDialog} this
29057      */
29058     moveTo : function(x, y){
29059         this.xy = [x,y];
29060         if(this.isVisible()){
29061             this.el.setXY(this.xy);
29062             this.adjustAssets();
29063         }
29064         return this;
29065     },
29066
29067     /**
29068      * Aligns the dialog to the specified element
29069      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29070      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29071      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29072      * @return {Roo.BasicDialog} this
29073      */
29074     alignTo : function(element, position, offsets){
29075         this.xy = this.el.getAlignToXY(element, position, offsets);
29076         if(this.isVisible()){
29077             this.el.setXY(this.xy);
29078             this.adjustAssets();
29079         }
29080         return this;
29081     },
29082
29083     /**
29084      * Anchors an element to another element and realigns it when the window is resized.
29085      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29086      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29087      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29088      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29089      * is a number, it is used as the buffer delay (defaults to 50ms).
29090      * @return {Roo.BasicDialog} this
29091      */
29092     anchorTo : function(el, alignment, offsets, monitorScroll){
29093         var action = function(){
29094             this.alignTo(el, alignment, offsets);
29095         };
29096         Roo.EventManager.onWindowResize(action, this);
29097         var tm = typeof monitorScroll;
29098         if(tm != 'undefined'){
29099             Roo.EventManager.on(window, 'scroll', action, this,
29100                 {buffer: tm == 'number' ? monitorScroll : 50});
29101         }
29102         action.call(this);
29103         return this;
29104     },
29105
29106     /**
29107      * Returns true if the dialog is visible
29108      * @return {Boolean}
29109      */
29110     isVisible : function(){
29111         return this.el.isVisible();
29112     },
29113
29114     // private
29115     animHide : function(callback){
29116         var b = Roo.get(this.animateTarget).getBox();
29117         this.proxy.show();
29118         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29119         this.el.hide();
29120         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29121                     this.hideEl.createDelegate(this, [callback]));
29122     },
29123
29124     /**
29125      * Hides the dialog.
29126      * @param {Function} callback (optional) Function to call when the dialog is hidden
29127      * @return {Roo.BasicDialog} this
29128      */
29129     hide : function(callback){
29130         if (this.fireEvent("beforehide", this) === false){
29131             return;
29132         }
29133         if(this.shadow){
29134             this.shadow.hide();
29135         }
29136         if(this.shim) {
29137           this.shim.hide();
29138         }
29139         if(this.animateTarget){
29140            this.animHide(callback);
29141         }else{
29142             this.el.hide();
29143             this.hideEl(callback);
29144         }
29145         return this;
29146     },
29147
29148     // private
29149     hideEl : function(callback){
29150         this.proxy.hide();
29151         if(this.modal){
29152             this.mask.hide();
29153             Roo.get(document.body).removeClass("x-body-masked");
29154         }
29155         this.fireEvent("hide", this);
29156         if(typeof callback == "function"){
29157             callback();
29158         }
29159     },
29160
29161     // private
29162     hideAction : function(){
29163         this.setLeft("-10000px");
29164         this.setTop("-10000px");
29165         this.setStyle("visibility", "hidden");
29166     },
29167
29168     // private
29169     refreshSize : function(){
29170         this.size = this.el.getSize();
29171         this.xy = this.el.getXY();
29172         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29173     },
29174
29175     // private
29176     // z-index is managed by the DialogManager and may be overwritten at any time
29177     setZIndex : function(index){
29178         if(this.modal){
29179             this.mask.setStyle("z-index", index);
29180         }
29181         if(this.shim){
29182             this.shim.setStyle("z-index", ++index);
29183         }
29184         if(this.shadow){
29185             this.shadow.setZIndex(++index);
29186         }
29187         this.el.setStyle("z-index", ++index);
29188         if(this.proxy){
29189             this.proxy.setStyle("z-index", ++index);
29190         }
29191         if(this.resizer){
29192             this.resizer.proxy.setStyle("z-index", ++index);
29193         }
29194
29195         this.lastZIndex = index;
29196     },
29197
29198     /**
29199      * Returns the element for this dialog
29200      * @return {Roo.Element} The underlying dialog Element
29201      */
29202     getEl : function(){
29203         return this.el;
29204     }
29205 });
29206
29207 /**
29208  * @class Roo.DialogManager
29209  * Provides global access to BasicDialogs that have been created and
29210  * support for z-indexing (layering) multiple open dialogs.
29211  */
29212 Roo.DialogManager = function(){
29213     var list = {};
29214     var accessList = [];
29215     var front = null;
29216
29217     // private
29218     var sortDialogs = function(d1, d2){
29219         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29220     };
29221
29222     // private
29223     var orderDialogs = function(){
29224         accessList.sort(sortDialogs);
29225         var seed = Roo.DialogManager.zseed;
29226         for(var i = 0, len = accessList.length; i < len; i++){
29227             var dlg = accessList[i];
29228             if(dlg){
29229                 dlg.setZIndex(seed + (i*10));
29230             }
29231         }
29232     };
29233
29234     return {
29235         /**
29236          * The starting z-index for BasicDialogs (defaults to 9000)
29237          * @type Number The z-index value
29238          */
29239         zseed : 9000,
29240
29241         // private
29242         register : function(dlg){
29243             list[dlg.id] = dlg;
29244             accessList.push(dlg);
29245         },
29246
29247         // private
29248         unregister : function(dlg){
29249             delete list[dlg.id];
29250             var i=0;
29251             var len=0;
29252             if(!accessList.indexOf){
29253                 for(  i = 0, len = accessList.length; i < len; i++){
29254                     if(accessList[i] == dlg){
29255                         accessList.splice(i, 1);
29256                         return;
29257                     }
29258                 }
29259             }else{
29260                  i = accessList.indexOf(dlg);
29261                 if(i != -1){
29262                     accessList.splice(i, 1);
29263                 }
29264             }
29265         },
29266
29267         /**
29268          * Gets a registered dialog by id
29269          * @param {String/Object} id The id of the dialog or a dialog
29270          * @return {Roo.BasicDialog} this
29271          */
29272         get : function(id){
29273             return typeof id == "object" ? id : list[id];
29274         },
29275
29276         /**
29277          * Brings the specified dialog to the front
29278          * @param {String/Object} dlg The id of the dialog or a dialog
29279          * @return {Roo.BasicDialog} this
29280          */
29281         bringToFront : function(dlg){
29282             dlg = this.get(dlg);
29283             if(dlg != front){
29284                 front = dlg;
29285                 dlg._lastAccess = new Date().getTime();
29286                 orderDialogs();
29287             }
29288             return dlg;
29289         },
29290
29291         /**
29292          * Sends the specified dialog to the back
29293          * @param {String/Object} dlg The id of the dialog or a dialog
29294          * @return {Roo.BasicDialog} this
29295          */
29296         sendToBack : function(dlg){
29297             dlg = this.get(dlg);
29298             dlg._lastAccess = -(new Date().getTime());
29299             orderDialogs();
29300             return dlg;
29301         },
29302
29303         /**
29304          * Hides all dialogs
29305          */
29306         hideAll : function(){
29307             for(var id in list){
29308                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29309                     list[id].hide();
29310                 }
29311             }
29312         }
29313     };
29314 }();
29315
29316 /**
29317  * @class Roo.LayoutDialog
29318  * @extends Roo.BasicDialog
29319  * Dialog which provides adjustments for working with a layout in a Dialog.
29320  * Add your necessary layout config options to the dialog's config.<br>
29321  * Example usage (including a nested layout):
29322  * <pre><code>
29323 if(!dialog){
29324     dialog = new Roo.LayoutDialog("download-dlg", {
29325         modal: true,
29326         width:600,
29327         height:450,
29328         shadow:true,
29329         minWidth:500,
29330         minHeight:350,
29331         autoTabs:true,
29332         proxyDrag:true,
29333         // layout config merges with the dialog config
29334         center:{
29335             tabPosition: "top",
29336             alwaysShowTabs: true
29337         }
29338     });
29339     dialog.addKeyListener(27, dialog.hide, dialog);
29340     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29341     dialog.addButton("Build It!", this.getDownload, this);
29342
29343     // we can even add nested layouts
29344     var innerLayout = new Roo.BorderLayout("dl-inner", {
29345         east: {
29346             initialSize: 200,
29347             autoScroll:true,
29348             split:true
29349         },
29350         center: {
29351             autoScroll:true
29352         }
29353     });
29354     innerLayout.beginUpdate();
29355     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29356     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29357     innerLayout.endUpdate(true);
29358
29359     var layout = dialog.getLayout();
29360     layout.beginUpdate();
29361     layout.add("center", new Roo.ContentPanel("standard-panel",
29362                         {title: "Download the Source", fitToFrame:true}));
29363     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29364                {title: "Build your own roo.js"}));
29365     layout.getRegion("center").showPanel(sp);
29366     layout.endUpdate();
29367 }
29368 </code></pre>
29369     * @constructor
29370     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29371     * @param {Object} config configuration options
29372   */
29373 Roo.LayoutDialog = function(el, cfg){
29374     
29375     var config=  cfg;
29376     if (typeof(cfg) == 'undefined') {
29377         config = Roo.apply({}, el);
29378         el = Roo.get( document.documentElement || document.body).createChild();
29379         //config.autoCreate = true;
29380     }
29381     
29382     
29383     config.autoTabs = false;
29384     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29385     this.body.setStyle({overflow:"hidden", position:"relative"});
29386     this.layout = new Roo.BorderLayout(this.body.dom, config);
29387     this.layout.monitorWindowResize = false;
29388     this.el.addClass("x-dlg-auto-layout");
29389     // fix case when center region overwrites center function
29390     this.center = Roo.BasicDialog.prototype.center;
29391     this.on("show", this.layout.layout, this.layout, true);
29392     if (config.items) {
29393         var xitems = config.items;
29394         delete config.items;
29395         Roo.each(xitems, this.addxtype, this);
29396     }
29397     
29398     
29399 };
29400 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29401     /**
29402      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29403      * @deprecated
29404      */
29405     endUpdate : function(){
29406         this.layout.endUpdate();
29407     },
29408
29409     /**
29410      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29411      *  @deprecated
29412      */
29413     beginUpdate : function(){
29414         this.layout.beginUpdate();
29415     },
29416
29417     /**
29418      * Get the BorderLayout for this dialog
29419      * @return {Roo.BorderLayout}
29420      */
29421     getLayout : function(){
29422         return this.layout;
29423     },
29424
29425     showEl : function(){
29426         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29427         if(Roo.isIE7){
29428             this.layout.layout();
29429         }
29430     },
29431
29432     // private
29433     // Use the syncHeightBeforeShow config option to control this automatically
29434     syncBodyHeight : function(){
29435         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29436         if(this.layout){this.layout.layout();}
29437     },
29438     
29439       /**
29440      * Add an xtype element (actually adds to the layout.)
29441      * @return {Object} xdata xtype object data.
29442      */
29443     
29444     addxtype : function(c) {
29445         return this.layout.addxtype(c);
29446     }
29447 });/*
29448  * Based on:
29449  * Ext JS Library 1.1.1
29450  * Copyright(c) 2006-2007, Ext JS, LLC.
29451  *
29452  * Originally Released Under LGPL - original licence link has changed is not relivant.
29453  *
29454  * Fork - LGPL
29455  * <script type="text/javascript">
29456  */
29457  
29458 /**
29459  * @class Roo.MessageBox
29460  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29461  * Example usage:
29462  *<pre><code>
29463 // Basic alert:
29464 Roo.Msg.alert('Status', 'Changes saved successfully.');
29465
29466 // Prompt for user data:
29467 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29468     if (btn == 'ok'){
29469         // process text value...
29470     }
29471 });
29472
29473 // Show a dialog using config options:
29474 Roo.Msg.show({
29475    title:'Save Changes?',
29476    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29477    buttons: Roo.Msg.YESNOCANCEL,
29478    fn: processResult,
29479    animEl: 'elId'
29480 });
29481 </code></pre>
29482  * @singleton
29483  */
29484 Roo.MessageBox = function(){
29485     var dlg, opt, mask, waitTimer;
29486     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29487     var buttons, activeTextEl, bwidth;
29488
29489     // private
29490     var handleButton = function(button){
29491         dlg.hide();
29492         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29493     };
29494
29495     // private
29496     var handleHide = function(){
29497         if(opt && opt.cls){
29498             dlg.el.removeClass(opt.cls);
29499         }
29500         if(waitTimer){
29501             Roo.TaskMgr.stop(waitTimer);
29502             waitTimer = null;
29503         }
29504     };
29505
29506     // private
29507     var updateButtons = function(b){
29508         var width = 0;
29509         if(!b){
29510             buttons["ok"].hide();
29511             buttons["cancel"].hide();
29512             buttons["yes"].hide();
29513             buttons["no"].hide();
29514             dlg.footer.dom.style.display = 'none';
29515             return width;
29516         }
29517         dlg.footer.dom.style.display = '';
29518         for(var k in buttons){
29519             if(typeof buttons[k] != "function"){
29520                 if(b[k]){
29521                     buttons[k].show();
29522                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29523                     width += buttons[k].el.getWidth()+15;
29524                 }else{
29525                     buttons[k].hide();
29526                 }
29527             }
29528         }
29529         return width;
29530     };
29531
29532     // private
29533     var handleEsc = function(d, k, e){
29534         if(opt && opt.closable !== false){
29535             dlg.hide();
29536         }
29537         if(e){
29538             e.stopEvent();
29539         }
29540     };
29541
29542     return {
29543         /**
29544          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29545          * @return {Roo.BasicDialog} The BasicDialog element
29546          */
29547         getDialog : function(){
29548            if(!dlg){
29549                 dlg = new Roo.BasicDialog("x-msg-box", {
29550                     autoCreate : true,
29551                     shadow: true,
29552                     draggable: true,
29553                     resizable:false,
29554                     constraintoviewport:false,
29555                     fixedcenter:true,
29556                     collapsible : false,
29557                     shim:true,
29558                     modal: true,
29559                     width:400, height:100,
29560                     buttonAlign:"center",
29561                     closeClick : function(){
29562                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29563                             handleButton("no");
29564                         }else{
29565                             handleButton("cancel");
29566                         }
29567                     }
29568                 });
29569                 dlg.on("hide", handleHide);
29570                 mask = dlg.mask;
29571                 dlg.addKeyListener(27, handleEsc);
29572                 buttons = {};
29573                 var bt = this.buttonText;
29574                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29575                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29576                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29577                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29578                 bodyEl = dlg.body.createChild({
29579
29580                     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>'
29581                 });
29582                 msgEl = bodyEl.dom.firstChild;
29583                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29584                 textboxEl.enableDisplayMode();
29585                 textboxEl.addKeyListener([10,13], function(){
29586                     if(dlg.isVisible() && opt && opt.buttons){
29587                         if(opt.buttons.ok){
29588                             handleButton("ok");
29589                         }else if(opt.buttons.yes){
29590                             handleButton("yes");
29591                         }
29592                     }
29593                 });
29594                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29595                 textareaEl.enableDisplayMode();
29596                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29597                 progressEl.enableDisplayMode();
29598                 var pf = progressEl.dom.firstChild;
29599                 if (pf) {
29600                     pp = Roo.get(pf.firstChild);
29601                     pp.setHeight(pf.offsetHeight);
29602                 }
29603                 
29604             }
29605             return dlg;
29606         },
29607
29608         /**
29609          * Updates the message box body text
29610          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29611          * the XHTML-compliant non-breaking space character '&amp;#160;')
29612          * @return {Roo.MessageBox} This message box
29613          */
29614         updateText : function(text){
29615             if(!dlg.isVisible() && !opt.width){
29616                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29617             }
29618             msgEl.innerHTML = text || '&#160;';
29619             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29620                         Math.max(opt.minWidth || this.minWidth, bwidth));
29621             if(opt.prompt){
29622                 activeTextEl.setWidth(w);
29623             }
29624             if(dlg.isVisible()){
29625                 dlg.fixedcenter = false;
29626             }
29627             dlg.setContentSize(w, bodyEl.getHeight());
29628             if(dlg.isVisible()){
29629                 dlg.fixedcenter = true;
29630             }
29631             return this;
29632         },
29633
29634         /**
29635          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29636          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29637          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29638          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29639          * @return {Roo.MessageBox} This message box
29640          */
29641         updateProgress : function(value, text){
29642             if(text){
29643                 this.updateText(text);
29644             }
29645             if (pp) { // weird bug on my firefox - for some reason this is not defined
29646                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29647             }
29648             return this;
29649         },        
29650
29651         /**
29652          * Returns true if the message box is currently displayed
29653          * @return {Boolean} True if the message box is visible, else false
29654          */
29655         isVisible : function(){
29656             return dlg && dlg.isVisible();  
29657         },
29658
29659         /**
29660          * Hides the message box if it is displayed
29661          */
29662         hide : function(){
29663             if(this.isVisible()){
29664                 dlg.hide();
29665             }  
29666         },
29667
29668         /**
29669          * Displays a new message box, or reinitializes an existing message box, based on the config options
29670          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29671          * The following config object properties are supported:
29672          * <pre>
29673 Property    Type             Description
29674 ----------  ---------------  ------------------------------------------------------------------------------------
29675 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29676                                    closes (defaults to undefined)
29677 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29678                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29679 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29680                                    progress and wait dialogs will ignore this property and always hide the
29681                                    close button as they can only be closed programmatically.
29682 cls               String           A custom CSS class to apply to the message box element
29683 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29684                                    displayed (defaults to 75)
29685 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29686                                    function will be btn (the name of the button that was clicked, if applicable,
29687                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29688                                    Progress and wait dialogs will ignore this option since they do not respond to
29689                                    user actions and can only be closed programmatically, so any required function
29690                                    should be called by the same code after it closes the dialog.
29691 icon              String           A CSS class that provides a background image to be used as an icon for
29692                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29693 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29694 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29695 modal             Boolean          False to allow user interaction with the page while the message box is
29696                                    displayed (defaults to true)
29697 msg               String           A string that will replace the existing message box body text (defaults
29698                                    to the XHTML-compliant non-breaking space character '&#160;')
29699 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29700 progress          Boolean          True to display a progress bar (defaults to false)
29701 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29702 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29703 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29704 title             String           The title text
29705 value             String           The string value to set into the active textbox element if displayed
29706 wait              Boolean          True to display a progress bar (defaults to false)
29707 width             Number           The width of the dialog in pixels
29708 </pre>
29709          *
29710          * Example usage:
29711          * <pre><code>
29712 Roo.Msg.show({
29713    title: 'Address',
29714    msg: 'Please enter your address:',
29715    width: 300,
29716    buttons: Roo.MessageBox.OKCANCEL,
29717    multiline: true,
29718    fn: saveAddress,
29719    animEl: 'addAddressBtn'
29720 });
29721 </code></pre>
29722          * @param {Object} config Configuration options
29723          * @return {Roo.MessageBox} This message box
29724          */
29725         show : function(options){
29726             if(this.isVisible()){
29727                 this.hide();
29728             }
29729             var d = this.getDialog();
29730             opt = options;
29731             d.setTitle(opt.title || "&#160;");
29732             d.close.setDisplayed(opt.closable !== false);
29733             activeTextEl = textboxEl;
29734             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29735             if(opt.prompt){
29736                 if(opt.multiline){
29737                     textboxEl.hide();
29738                     textareaEl.show();
29739                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29740                         opt.multiline : this.defaultTextHeight);
29741                     activeTextEl = textareaEl;
29742                 }else{
29743                     textboxEl.show();
29744                     textareaEl.hide();
29745                 }
29746             }else{
29747                 textboxEl.hide();
29748                 textareaEl.hide();
29749             }
29750             progressEl.setDisplayed(opt.progress === true);
29751             this.updateProgress(0);
29752             activeTextEl.dom.value = opt.value || "";
29753             if(opt.prompt){
29754                 dlg.setDefaultButton(activeTextEl);
29755             }else{
29756                 var bs = opt.buttons;
29757                 var db = null;
29758                 if(bs && bs.ok){
29759                     db = buttons["ok"];
29760                 }else if(bs && bs.yes){
29761                     db = buttons["yes"];
29762                 }
29763                 dlg.setDefaultButton(db);
29764             }
29765             bwidth = updateButtons(opt.buttons);
29766             this.updateText(opt.msg);
29767             if(opt.cls){
29768                 d.el.addClass(opt.cls);
29769             }
29770             d.proxyDrag = opt.proxyDrag === true;
29771             d.modal = opt.modal !== false;
29772             d.mask = opt.modal !== false ? mask : false;
29773             if(!d.isVisible()){
29774                 // force it to the end of the z-index stack so it gets a cursor in FF
29775                 document.body.appendChild(dlg.el.dom);
29776                 d.animateTarget = null;
29777                 d.show(options.animEl);
29778             }
29779             return this;
29780         },
29781
29782         /**
29783          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29784          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29785          * and closing the message box when the process is complete.
29786          * @param {String} title The title bar text
29787          * @param {String} msg The message box body text
29788          * @return {Roo.MessageBox} This message box
29789          */
29790         progress : function(title, msg){
29791             this.show({
29792                 title : title,
29793                 msg : msg,
29794                 buttons: false,
29795                 progress:true,
29796                 closable:false,
29797                 minWidth: this.minProgressWidth,
29798                 modal : true
29799             });
29800             return this;
29801         },
29802
29803         /**
29804          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29805          * If a callback function is passed it will be called after the user clicks the button, and the
29806          * id of the button that was clicked will be passed as the only parameter to the callback
29807          * (could also be the top-right close button).
29808          * @param {String} title The title bar text
29809          * @param {String} msg The message box body text
29810          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29811          * @param {Object} scope (optional) The scope of the callback function
29812          * @return {Roo.MessageBox} This message box
29813          */
29814         alert : function(title, msg, fn, scope){
29815             this.show({
29816                 title : title,
29817                 msg : msg,
29818                 buttons: this.OK,
29819                 fn: fn,
29820                 scope : scope,
29821                 modal : true
29822             });
29823             return this;
29824         },
29825
29826         /**
29827          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29828          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29829          * You are responsible for closing the message box when the process is complete.
29830          * @param {String} msg The message box body text
29831          * @param {String} title (optional) The title bar text
29832          * @return {Roo.MessageBox} This message box
29833          */
29834         wait : function(msg, title){
29835             this.show({
29836                 title : title,
29837                 msg : msg,
29838                 buttons: false,
29839                 closable:false,
29840                 progress:true,
29841                 modal:true,
29842                 width:300,
29843                 wait:true
29844             });
29845             waitTimer = Roo.TaskMgr.start({
29846                 run: function(i){
29847                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29848                 },
29849                 interval: 1000
29850             });
29851             return this;
29852         },
29853
29854         /**
29855          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29856          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29857          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29858          * @param {String} title The title bar text
29859          * @param {String} msg The message box body text
29860          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29861          * @param {Object} scope (optional) The scope of the callback function
29862          * @return {Roo.MessageBox} This message box
29863          */
29864         confirm : function(title, msg, fn, scope){
29865             this.show({
29866                 title : title,
29867                 msg : msg,
29868                 buttons: this.YESNO,
29869                 fn: fn,
29870                 scope : scope,
29871                 modal : true
29872             });
29873             return this;
29874         },
29875
29876         /**
29877          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29878          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29879          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29880          * (could also be the top-right close button) and the text that was entered will be passed as the two
29881          * parameters to the callback.
29882          * @param {String} title The title bar text
29883          * @param {String} msg The message box body text
29884          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29885          * @param {Object} scope (optional) The scope of the callback function
29886          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29887          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29888          * @return {Roo.MessageBox} This message box
29889          */
29890         prompt : function(title, msg, fn, scope, multiline){
29891             this.show({
29892                 title : title,
29893                 msg : msg,
29894                 buttons: this.OKCANCEL,
29895                 fn: fn,
29896                 minWidth:250,
29897                 scope : scope,
29898                 prompt:true,
29899                 multiline: multiline,
29900                 modal : true
29901             });
29902             return this;
29903         },
29904
29905         /**
29906          * Button config that displays a single OK button
29907          * @type Object
29908          */
29909         OK : {ok:true},
29910         /**
29911          * Button config that displays Yes and No buttons
29912          * @type Object
29913          */
29914         YESNO : {yes:true, no:true},
29915         /**
29916          * Button config that displays OK and Cancel buttons
29917          * @type Object
29918          */
29919         OKCANCEL : {ok:true, cancel:true},
29920         /**
29921          * Button config that displays Yes, No and Cancel buttons
29922          * @type Object
29923          */
29924         YESNOCANCEL : {yes:true, no:true, cancel:true},
29925
29926         /**
29927          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29928          * @type Number
29929          */
29930         defaultTextHeight : 75,
29931         /**
29932          * The maximum width in pixels of the message box (defaults to 600)
29933          * @type Number
29934          */
29935         maxWidth : 600,
29936         /**
29937          * The minimum width in pixels of the message box (defaults to 100)
29938          * @type Number
29939          */
29940         minWidth : 100,
29941         /**
29942          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29943          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29944          * @type Number
29945          */
29946         minProgressWidth : 250,
29947         /**
29948          * An object containing the default button text strings that can be overriden for localized language support.
29949          * Supported properties are: ok, cancel, yes and no.
29950          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29951          * @type Object
29952          */
29953         buttonText : {
29954             ok : "OK",
29955             cancel : "Cancel",
29956             yes : "Yes",
29957             no : "No"
29958         }
29959     };
29960 }();
29961
29962 /**
29963  * Shorthand for {@link Roo.MessageBox}
29964  */
29965 Roo.Msg = Roo.MessageBox;/*
29966  * Based on:
29967  * Ext JS Library 1.1.1
29968  * Copyright(c) 2006-2007, Ext JS, LLC.
29969  *
29970  * Originally Released Under LGPL - original licence link has changed is not relivant.
29971  *
29972  * Fork - LGPL
29973  * <script type="text/javascript">
29974  */
29975 /**
29976  * @class Roo.QuickTips
29977  * Provides attractive and customizable tooltips for any element.
29978  * @singleton
29979  */
29980 Roo.QuickTips = function(){
29981     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29982     var ce, bd, xy, dd;
29983     var visible = false, disabled = true, inited = false;
29984     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29985     
29986     var onOver = function(e){
29987         if(disabled){
29988             return;
29989         }
29990         var t = e.getTarget();
29991         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29992             return;
29993         }
29994         if(ce && t == ce.el){
29995             clearTimeout(hideProc);
29996             return;
29997         }
29998         if(t && tagEls[t.id]){
29999             tagEls[t.id].el = t;
30000             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30001             return;
30002         }
30003         var ttp, et = Roo.fly(t);
30004         var ns = cfg.namespace;
30005         if(tm.interceptTitles && t.title){
30006             ttp = t.title;
30007             t.qtip = ttp;
30008             t.removeAttribute("title");
30009             e.preventDefault();
30010         }else{
30011             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30012         }
30013         if(ttp){
30014             showProc = show.defer(tm.showDelay, tm, [{
30015                 el: t, 
30016                 text: ttp, 
30017                 width: et.getAttributeNS(ns, cfg.width),
30018                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30019                 title: et.getAttributeNS(ns, cfg.title),
30020                     cls: et.getAttributeNS(ns, cfg.cls)
30021             }]);
30022         }
30023     };
30024     
30025     var onOut = function(e){
30026         clearTimeout(showProc);
30027         var t = e.getTarget();
30028         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30029             hideProc = setTimeout(hide, tm.hideDelay);
30030         }
30031     };
30032     
30033     var onMove = function(e){
30034         if(disabled){
30035             return;
30036         }
30037         xy = e.getXY();
30038         xy[1] += 18;
30039         if(tm.trackMouse && ce){
30040             el.setXY(xy);
30041         }
30042     };
30043     
30044     var onDown = function(e){
30045         clearTimeout(showProc);
30046         clearTimeout(hideProc);
30047         if(!e.within(el)){
30048             if(tm.hideOnClick){
30049                 hide();
30050                 tm.disable();
30051                 tm.enable.defer(100, tm);
30052             }
30053         }
30054     };
30055     
30056     var getPad = function(){
30057         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30058     };
30059
30060     var show = function(o){
30061         if(disabled){
30062             return;
30063         }
30064         clearTimeout(dismissProc);
30065         ce = o;
30066         if(removeCls){ // in case manually hidden
30067             el.removeClass(removeCls);
30068             removeCls = null;
30069         }
30070         if(ce.cls){
30071             el.addClass(ce.cls);
30072             removeCls = ce.cls;
30073         }
30074         if(ce.title){
30075             tipTitle.update(ce.title);
30076             tipTitle.show();
30077         }else{
30078             tipTitle.update('');
30079             tipTitle.hide();
30080         }
30081         el.dom.style.width  = tm.maxWidth+'px';
30082         //tipBody.dom.style.width = '';
30083         tipBodyText.update(o.text);
30084         var p = getPad(), w = ce.width;
30085         if(!w){
30086             var td = tipBodyText.dom;
30087             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30088             if(aw > tm.maxWidth){
30089                 w = tm.maxWidth;
30090             }else if(aw < tm.minWidth){
30091                 w = tm.minWidth;
30092             }else{
30093                 w = aw;
30094             }
30095         }
30096         //tipBody.setWidth(w);
30097         el.setWidth(parseInt(w, 10) + p);
30098         if(ce.autoHide === false){
30099             close.setDisplayed(true);
30100             if(dd){
30101                 dd.unlock();
30102             }
30103         }else{
30104             close.setDisplayed(false);
30105             if(dd){
30106                 dd.lock();
30107             }
30108         }
30109         if(xy){
30110             el.avoidY = xy[1]-18;
30111             el.setXY(xy);
30112         }
30113         if(tm.animate){
30114             el.setOpacity(.1);
30115             el.setStyle("visibility", "visible");
30116             el.fadeIn({callback: afterShow});
30117         }else{
30118             afterShow();
30119         }
30120     };
30121     
30122     var afterShow = function(){
30123         if(ce){
30124             el.show();
30125             esc.enable();
30126             if(tm.autoDismiss && ce.autoHide !== false){
30127                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30128             }
30129         }
30130     };
30131     
30132     var hide = function(noanim){
30133         clearTimeout(dismissProc);
30134         clearTimeout(hideProc);
30135         ce = null;
30136         if(el.isVisible()){
30137             esc.disable();
30138             if(noanim !== true && tm.animate){
30139                 el.fadeOut({callback: afterHide});
30140             }else{
30141                 afterHide();
30142             } 
30143         }
30144     };
30145     
30146     var afterHide = function(){
30147         el.hide();
30148         if(removeCls){
30149             el.removeClass(removeCls);
30150             removeCls = null;
30151         }
30152     };
30153     
30154     return {
30155         /**
30156         * @cfg {Number} minWidth
30157         * The minimum width of the quick tip (defaults to 40)
30158         */
30159        minWidth : 40,
30160         /**
30161         * @cfg {Number} maxWidth
30162         * The maximum width of the quick tip (defaults to 300)
30163         */
30164        maxWidth : 300,
30165         /**
30166         * @cfg {Boolean} interceptTitles
30167         * True to automatically use the element's DOM title value if available (defaults to false)
30168         */
30169        interceptTitles : false,
30170         /**
30171         * @cfg {Boolean} trackMouse
30172         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30173         */
30174        trackMouse : false,
30175         /**
30176         * @cfg {Boolean} hideOnClick
30177         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30178         */
30179        hideOnClick : true,
30180         /**
30181         * @cfg {Number} showDelay
30182         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30183         */
30184        showDelay : 500,
30185         /**
30186         * @cfg {Number} hideDelay
30187         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30188         */
30189        hideDelay : 200,
30190         /**
30191         * @cfg {Boolean} autoHide
30192         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30193         * Used in conjunction with hideDelay.
30194         */
30195        autoHide : true,
30196         /**
30197         * @cfg {Boolean}
30198         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30199         * (defaults to true).  Used in conjunction with autoDismissDelay.
30200         */
30201        autoDismiss : true,
30202         /**
30203         * @cfg {Number}
30204         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30205         */
30206        autoDismissDelay : 5000,
30207        /**
30208         * @cfg {Boolean} animate
30209         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30210         */
30211        animate : false,
30212
30213        /**
30214         * @cfg {String} title
30215         * Title text to display (defaults to '').  This can be any valid HTML markup.
30216         */
30217         title: '',
30218        /**
30219         * @cfg {String} text
30220         * Body text to display (defaults to '').  This can be any valid HTML markup.
30221         */
30222         text : '',
30223        /**
30224         * @cfg {String} cls
30225         * A CSS class to apply to the base quick tip element (defaults to '').
30226         */
30227         cls : '',
30228        /**
30229         * @cfg {Number} width
30230         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30231         * minWidth or maxWidth.
30232         */
30233         width : null,
30234
30235     /**
30236      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30237      * or display QuickTips in a page.
30238      */
30239        init : function(){
30240           tm = Roo.QuickTips;
30241           cfg = tm.tagConfig;
30242           if(!inited){
30243               if(!Roo.isReady){ // allow calling of init() before onReady
30244                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30245                   return;
30246               }
30247               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30248               el.fxDefaults = {stopFx: true};
30249               // maximum custom styling
30250               //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>');
30251               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>');              
30252               tipTitle = el.child('h3');
30253               tipTitle.enableDisplayMode("block");
30254               tipBody = el.child('div.x-tip-bd');
30255               tipBodyText = el.child('div.x-tip-bd-inner');
30256               //bdLeft = el.child('div.x-tip-bd-left');
30257               //bdRight = el.child('div.x-tip-bd-right');
30258               close = el.child('div.x-tip-close');
30259               close.enableDisplayMode("block");
30260               close.on("click", hide);
30261               var d = Roo.get(document);
30262               d.on("mousedown", onDown);
30263               d.on("mouseover", onOver);
30264               d.on("mouseout", onOut);
30265               d.on("mousemove", onMove);
30266               esc = d.addKeyListener(27, hide);
30267               esc.disable();
30268               if(Roo.dd.DD){
30269                   dd = el.initDD("default", null, {
30270                       onDrag : function(){
30271                           el.sync();  
30272                       }
30273                   });
30274                   dd.setHandleElId(tipTitle.id);
30275                   dd.lock();
30276               }
30277               inited = true;
30278           }
30279           this.enable(); 
30280        },
30281
30282     /**
30283      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30284      * are supported:
30285      * <pre>
30286 Property    Type                   Description
30287 ----------  ---------------------  ------------------------------------------------------------------------
30288 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30289      * </ul>
30290      * @param {Object} config The config object
30291      */
30292        register : function(config){
30293            var cs = config instanceof Array ? config : arguments;
30294            for(var i = 0, len = cs.length; i < len; i++) {
30295                var c = cs[i];
30296                var target = c.target;
30297                if(target){
30298                    if(target instanceof Array){
30299                        for(var j = 0, jlen = target.length; j < jlen; j++){
30300                            tagEls[target[j]] = c;
30301                        }
30302                    }else{
30303                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30304                    }
30305                }
30306            }
30307        },
30308
30309     /**
30310      * Removes this quick tip from its element and destroys it.
30311      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30312      */
30313        unregister : function(el){
30314            delete tagEls[Roo.id(el)];
30315        },
30316
30317     /**
30318      * Enable this quick tip.
30319      */
30320        enable : function(){
30321            if(inited && disabled){
30322                locks.pop();
30323                if(locks.length < 1){
30324                    disabled = false;
30325                }
30326            }
30327        },
30328
30329     /**
30330      * Disable this quick tip.
30331      */
30332        disable : function(){
30333           disabled = true;
30334           clearTimeout(showProc);
30335           clearTimeout(hideProc);
30336           clearTimeout(dismissProc);
30337           if(ce){
30338               hide(true);
30339           }
30340           locks.push(1);
30341        },
30342
30343     /**
30344      * Returns true if the quick tip is enabled, else false.
30345      */
30346        isEnabled : function(){
30347             return !disabled;
30348        },
30349
30350         // private
30351        tagConfig : {
30352            namespace : "ext",
30353            attribute : "qtip",
30354            width : "width",
30355            target : "target",
30356            title : "qtitle",
30357            hide : "hide",
30358            cls : "qclass"
30359        }
30360    };
30361 }();
30362
30363 // backwards compat
30364 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30365  * Based on:
30366  * Ext JS Library 1.1.1
30367  * Copyright(c) 2006-2007, Ext JS, LLC.
30368  *
30369  * Originally Released Under LGPL - original licence link has changed is not relivant.
30370  *
30371  * Fork - LGPL
30372  * <script type="text/javascript">
30373  */
30374  
30375
30376 /**
30377  * @class Roo.tree.TreePanel
30378  * @extends Roo.data.Tree
30379
30380  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30381  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30382  * @cfg {Boolean} enableDD true to enable drag and drop
30383  * @cfg {Boolean} enableDrag true to enable just drag
30384  * @cfg {Boolean} enableDrop true to enable just drop
30385  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30386  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30387  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30388  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30389  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30390  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30391  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30392  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30393  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30394  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30395  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30396  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30397  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30398  * @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>
30399  * @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>
30400  * 
30401  * @constructor
30402  * @param {String/HTMLElement/Element} el The container element
30403  * @param {Object} config
30404  */
30405 Roo.tree.TreePanel = function(el, config){
30406     var root = false;
30407     var loader = false;
30408     if (config.root) {
30409         root = config.root;
30410         delete config.root;
30411     }
30412     if (config.loader) {
30413         loader = config.loader;
30414         delete config.loader;
30415     }
30416     
30417     Roo.apply(this, config);
30418     Roo.tree.TreePanel.superclass.constructor.call(this);
30419     this.el = Roo.get(el);
30420     this.el.addClass('x-tree');
30421     //console.log(root);
30422     if (root) {
30423         this.setRootNode( Roo.factory(root, Roo.tree));
30424     }
30425     if (loader) {
30426         this.loader = Roo.factory(loader, Roo.tree);
30427     }
30428    /**
30429     * Read-only. The id of the container element becomes this TreePanel's id.
30430     */
30431    this.id = this.el.id;
30432    this.addEvents({
30433         /**
30434         * @event beforeload
30435         * Fires before a node is loaded, return false to cancel
30436         * @param {Node} node The node being loaded
30437         */
30438         "beforeload" : true,
30439         /**
30440         * @event load
30441         * Fires when a node is loaded
30442         * @param {Node} node The node that was loaded
30443         */
30444         "load" : true,
30445         /**
30446         * @event textchange
30447         * Fires when the text for a node is changed
30448         * @param {Node} node The node
30449         * @param {String} text The new text
30450         * @param {String} oldText The old text
30451         */
30452         "textchange" : true,
30453         /**
30454         * @event beforeexpand
30455         * Fires before a node is expanded, return false to cancel.
30456         * @param {Node} node The node
30457         * @param {Boolean} deep
30458         * @param {Boolean} anim
30459         */
30460         "beforeexpand" : true,
30461         /**
30462         * @event beforecollapse
30463         * Fires before a node is collapsed, return false to cancel.
30464         * @param {Node} node The node
30465         * @param {Boolean} deep
30466         * @param {Boolean} anim
30467         */
30468         "beforecollapse" : true,
30469         /**
30470         * @event expand
30471         * Fires when a node is expanded
30472         * @param {Node} node The node
30473         */
30474         "expand" : true,
30475         /**
30476         * @event disabledchange
30477         * Fires when the disabled status of a node changes
30478         * @param {Node} node The node
30479         * @param {Boolean} disabled
30480         */
30481         "disabledchange" : true,
30482         /**
30483         * @event collapse
30484         * Fires when a node is collapsed
30485         * @param {Node} node The node
30486         */
30487         "collapse" : true,
30488         /**
30489         * @event beforeclick
30490         * Fires before click processing on a node. Return false to cancel the default action.
30491         * @param {Node} node The node
30492         * @param {Roo.EventObject} e The event object
30493         */
30494         "beforeclick":true,
30495         /**
30496         * @event checkchange
30497         * Fires when a node with a checkbox's checked property changes
30498         * @param {Node} this This node
30499         * @param {Boolean} checked
30500         */
30501         "checkchange":true,
30502         /**
30503         * @event click
30504         * Fires when a node is clicked
30505         * @param {Node} node The node
30506         * @param {Roo.EventObject} e The event object
30507         */
30508         "click":true,
30509         /**
30510         * @event dblclick
30511         * Fires when a node is double clicked
30512         * @param {Node} node The node
30513         * @param {Roo.EventObject} e The event object
30514         */
30515         "dblclick":true,
30516         /**
30517         * @event contextmenu
30518         * Fires when a node is right clicked
30519         * @param {Node} node The node
30520         * @param {Roo.EventObject} e The event object
30521         */
30522         "contextmenu":true,
30523         /**
30524         * @event beforechildrenrendered
30525         * Fires right before the child nodes for a node are rendered
30526         * @param {Node} node The node
30527         */
30528         "beforechildrenrendered":true,
30529        /**
30530              * @event startdrag
30531              * Fires when a node starts being dragged
30532              * @param {Roo.tree.TreePanel} this
30533              * @param {Roo.tree.TreeNode} node
30534              * @param {event} e The raw browser event
30535              */ 
30536             "startdrag" : true,
30537             /**
30538              * @event enddrag
30539              * Fires when a drag operation is complete
30540              * @param {Roo.tree.TreePanel} this
30541              * @param {Roo.tree.TreeNode} node
30542              * @param {event} e The raw browser event
30543              */
30544             "enddrag" : true,
30545             /**
30546              * @event dragdrop
30547              * Fires when a dragged node is dropped on a valid DD target
30548              * @param {Roo.tree.TreePanel} this
30549              * @param {Roo.tree.TreeNode} node
30550              * @param {DD} dd The dd it was dropped on
30551              * @param {event} e The raw browser event
30552              */
30553             "dragdrop" : true,
30554             /**
30555              * @event beforenodedrop
30556              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30557              * passed to handlers has the following properties:<br />
30558              * <ul style="padding:5px;padding-left:16px;">
30559              * <li>tree - The TreePanel</li>
30560              * <li>target - The node being targeted for the drop</li>
30561              * <li>data - The drag data from the drag source</li>
30562              * <li>point - The point of the drop - append, above or below</li>
30563              * <li>source - The drag source</li>
30564              * <li>rawEvent - Raw mouse event</li>
30565              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30566              * to be inserted by setting them on this object.</li>
30567              * <li>cancel - Set this to true to cancel the drop.</li>
30568              * </ul>
30569              * @param {Object} dropEvent
30570              */
30571             "beforenodedrop" : true,
30572             /**
30573              * @event nodedrop
30574              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30575              * passed to handlers has the following properties:<br />
30576              * <ul style="padding:5px;padding-left:16px;">
30577              * <li>tree - The TreePanel</li>
30578              * <li>target - The node being targeted for the drop</li>
30579              * <li>data - The drag data from the drag source</li>
30580              * <li>point - The point of the drop - append, above or below</li>
30581              * <li>source - The drag source</li>
30582              * <li>rawEvent - Raw mouse event</li>
30583              * <li>dropNode - Dropped node(s).</li>
30584              * </ul>
30585              * @param {Object} dropEvent
30586              */
30587             "nodedrop" : true,
30588              /**
30589              * @event nodedragover
30590              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30591              * passed to handlers has the following properties:<br />
30592              * <ul style="padding:5px;padding-left:16px;">
30593              * <li>tree - The TreePanel</li>
30594              * <li>target - The node being targeted for the drop</li>
30595              * <li>data - The drag data from the drag source</li>
30596              * <li>point - The point of the drop - append, above or below</li>
30597              * <li>source - The drag source</li>
30598              * <li>rawEvent - Raw mouse event</li>
30599              * <li>dropNode - Drop node(s) provided by the source.</li>
30600              * <li>cancel - Set this to true to signal drop not allowed.</li>
30601              * </ul>
30602              * @param {Object} dragOverEvent
30603              */
30604             "nodedragover" : true
30605         
30606    });
30607    if(this.singleExpand){
30608        this.on("beforeexpand", this.restrictExpand, this);
30609    }
30610 };
30611 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30612     rootVisible : true,
30613     animate: Roo.enableFx,
30614     lines : true,
30615     enableDD : false,
30616     hlDrop : Roo.enableFx,
30617   
30618     renderer: false,
30619     
30620     rendererTip: false,
30621     // private
30622     restrictExpand : function(node){
30623         var p = node.parentNode;
30624         if(p){
30625             if(p.expandedChild && p.expandedChild.parentNode == p){
30626                 p.expandedChild.collapse();
30627             }
30628             p.expandedChild = node;
30629         }
30630     },
30631
30632     // private override
30633     setRootNode : function(node){
30634         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30635         if(!this.rootVisible){
30636             node.ui = new Roo.tree.RootTreeNodeUI(node);
30637         }
30638         return node;
30639     },
30640
30641     /**
30642      * Returns the container element for this TreePanel
30643      */
30644     getEl : function(){
30645         return this.el;
30646     },
30647
30648     /**
30649      * Returns the default TreeLoader for this TreePanel
30650      */
30651     getLoader : function(){
30652         return this.loader;
30653     },
30654
30655     /**
30656      * Expand all nodes
30657      */
30658     expandAll : function(){
30659         this.root.expand(true);
30660     },
30661
30662     /**
30663      * Collapse all nodes
30664      */
30665     collapseAll : function(){
30666         this.root.collapse(true);
30667     },
30668
30669     /**
30670      * Returns the selection model used by this TreePanel
30671      */
30672     getSelectionModel : function(){
30673         if(!this.selModel){
30674             this.selModel = new Roo.tree.DefaultSelectionModel();
30675         }
30676         return this.selModel;
30677     },
30678
30679     /**
30680      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30681      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30682      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30683      * @return {Array}
30684      */
30685     getChecked : function(a, startNode){
30686         startNode = startNode || this.root;
30687         var r = [];
30688         var f = function(){
30689             if(this.attributes.checked){
30690                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30691             }
30692         }
30693         startNode.cascade(f);
30694         return r;
30695     },
30696
30697     /**
30698      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30699      * @param {String} path
30700      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30701      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30702      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30703      */
30704     expandPath : function(path, attr, callback){
30705         attr = attr || "id";
30706         var keys = path.split(this.pathSeparator);
30707         var curNode = this.root;
30708         if(curNode.attributes[attr] != keys[1]){ // invalid root
30709             if(callback){
30710                 callback(false, null);
30711             }
30712             return;
30713         }
30714         var index = 1;
30715         var f = function(){
30716             if(++index == keys.length){
30717                 if(callback){
30718                     callback(true, curNode);
30719                 }
30720                 return;
30721             }
30722             var c = curNode.findChild(attr, keys[index]);
30723             if(!c){
30724                 if(callback){
30725                     callback(false, curNode);
30726                 }
30727                 return;
30728             }
30729             curNode = c;
30730             c.expand(false, false, f);
30731         };
30732         curNode.expand(false, false, f);
30733     },
30734
30735     /**
30736      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30737      * @param {String} path
30738      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30739      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30740      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30741      */
30742     selectPath : function(path, attr, callback){
30743         attr = attr || "id";
30744         var keys = path.split(this.pathSeparator);
30745         var v = keys.pop();
30746         if(keys.length > 0){
30747             var f = function(success, node){
30748                 if(success && node){
30749                     var n = node.findChild(attr, v);
30750                     if(n){
30751                         n.select();
30752                         if(callback){
30753                             callback(true, n);
30754                         }
30755                     }else if(callback){
30756                         callback(false, n);
30757                     }
30758                 }else{
30759                     if(callback){
30760                         callback(false, n);
30761                     }
30762                 }
30763             };
30764             this.expandPath(keys.join(this.pathSeparator), attr, f);
30765         }else{
30766             this.root.select();
30767             if(callback){
30768                 callback(true, this.root);
30769             }
30770         }
30771     },
30772
30773     getTreeEl : function(){
30774         return this.el;
30775     },
30776
30777     /**
30778      * Trigger rendering of this TreePanel
30779      */
30780     render : function(){
30781         if (this.innerCt) {
30782             return this; // stop it rendering more than once!!
30783         }
30784         
30785         this.innerCt = this.el.createChild({tag:"ul",
30786                cls:"x-tree-root-ct " +
30787                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30788
30789         if(this.containerScroll){
30790             Roo.dd.ScrollManager.register(this.el);
30791         }
30792         if((this.enableDD || this.enableDrop) && !this.dropZone){
30793            /**
30794             * The dropZone used by this tree if drop is enabled
30795             * @type Roo.tree.TreeDropZone
30796             */
30797              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30798                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30799            });
30800         }
30801         if((this.enableDD || this.enableDrag) && !this.dragZone){
30802            /**
30803             * The dragZone used by this tree if drag is enabled
30804             * @type Roo.tree.TreeDragZone
30805             */
30806             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30807                ddGroup: this.ddGroup || "TreeDD",
30808                scroll: this.ddScroll
30809            });
30810         }
30811         this.getSelectionModel().init(this);
30812         if (!this.root) {
30813             console.log("ROOT not set in tree");
30814             return;
30815         }
30816         this.root.render();
30817         if(!this.rootVisible){
30818             this.root.renderChildren();
30819         }
30820         return this;
30821     }
30822 });/*
30823  * Based on:
30824  * Ext JS Library 1.1.1
30825  * Copyright(c) 2006-2007, Ext JS, LLC.
30826  *
30827  * Originally Released Under LGPL - original licence link has changed is not relivant.
30828  *
30829  * Fork - LGPL
30830  * <script type="text/javascript">
30831  */
30832  
30833
30834 /**
30835  * @class Roo.tree.DefaultSelectionModel
30836  * @extends Roo.util.Observable
30837  * The default single selection for a TreePanel.
30838  */
30839 Roo.tree.DefaultSelectionModel = function(){
30840    this.selNode = null;
30841    
30842    this.addEvents({
30843        /**
30844         * @event selectionchange
30845         * Fires when the selected node changes
30846         * @param {DefaultSelectionModel} this
30847         * @param {TreeNode} node the new selection
30848         */
30849        "selectionchange" : true,
30850
30851        /**
30852         * @event beforeselect
30853         * Fires before the selected node changes, return false to cancel the change
30854         * @param {DefaultSelectionModel} this
30855         * @param {TreeNode} node the new selection
30856         * @param {TreeNode} node the old selection
30857         */
30858        "beforeselect" : true
30859    });
30860 };
30861
30862 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30863     init : function(tree){
30864         this.tree = tree;
30865         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30866         tree.on("click", this.onNodeClick, this);
30867     },
30868     
30869     onNodeClick : function(node, e){
30870         if (e.ctrlKey && this.selNode == node)  {
30871             this.unselect(node);
30872             return;
30873         }
30874         this.select(node);
30875     },
30876     
30877     /**
30878      * Select a node.
30879      * @param {TreeNode} node The node to select
30880      * @return {TreeNode} The selected node
30881      */
30882     select : function(node){
30883         var last = this.selNode;
30884         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30885             if(last){
30886                 last.ui.onSelectedChange(false);
30887             }
30888             this.selNode = node;
30889             node.ui.onSelectedChange(true);
30890             this.fireEvent("selectionchange", this, node, last);
30891         }
30892         return node;
30893     },
30894     
30895     /**
30896      * Deselect a node.
30897      * @param {TreeNode} node The node to unselect
30898      */
30899     unselect : function(node){
30900         if(this.selNode == node){
30901             this.clearSelections();
30902         }    
30903     },
30904     
30905     /**
30906      * Clear all selections
30907      */
30908     clearSelections : function(){
30909         var n = this.selNode;
30910         if(n){
30911             n.ui.onSelectedChange(false);
30912             this.selNode = null;
30913             this.fireEvent("selectionchange", this, null);
30914         }
30915         return n;
30916     },
30917     
30918     /**
30919      * Get the selected node
30920      * @return {TreeNode} The selected node
30921      */
30922     getSelectedNode : function(){
30923         return this.selNode;    
30924     },
30925     
30926     /**
30927      * Returns true if the node is selected
30928      * @param {TreeNode} node The node to check
30929      * @return {Boolean}
30930      */
30931     isSelected : function(node){
30932         return this.selNode == node;  
30933     },
30934
30935     /**
30936      * Selects the node above the selected node in the tree, intelligently walking the nodes
30937      * @return TreeNode The new selection
30938      */
30939     selectPrevious : function(){
30940         var s = this.selNode || this.lastSelNode;
30941         if(!s){
30942             return null;
30943         }
30944         var ps = s.previousSibling;
30945         if(ps){
30946             if(!ps.isExpanded() || ps.childNodes.length < 1){
30947                 return this.select(ps);
30948             } else{
30949                 var lc = ps.lastChild;
30950                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30951                     lc = lc.lastChild;
30952                 }
30953                 return this.select(lc);
30954             }
30955         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30956             return this.select(s.parentNode);
30957         }
30958         return null;
30959     },
30960
30961     /**
30962      * Selects the node above the selected node in the tree, intelligently walking the nodes
30963      * @return TreeNode The new selection
30964      */
30965     selectNext : function(){
30966         var s = this.selNode || this.lastSelNode;
30967         if(!s){
30968             return null;
30969         }
30970         if(s.firstChild && s.isExpanded()){
30971              return this.select(s.firstChild);
30972          }else if(s.nextSibling){
30973              return this.select(s.nextSibling);
30974          }else if(s.parentNode){
30975             var newS = null;
30976             s.parentNode.bubble(function(){
30977                 if(this.nextSibling){
30978                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30979                     return false;
30980                 }
30981             });
30982             return newS;
30983          }
30984         return null;
30985     },
30986
30987     onKeyDown : function(e){
30988         var s = this.selNode || this.lastSelNode;
30989         // undesirable, but required
30990         var sm = this;
30991         if(!s){
30992             return;
30993         }
30994         var k = e.getKey();
30995         switch(k){
30996              case e.DOWN:
30997                  e.stopEvent();
30998                  this.selectNext();
30999              break;
31000              case e.UP:
31001                  e.stopEvent();
31002                  this.selectPrevious();
31003              break;
31004              case e.RIGHT:
31005                  e.preventDefault();
31006                  if(s.hasChildNodes()){
31007                      if(!s.isExpanded()){
31008                          s.expand();
31009                      }else if(s.firstChild){
31010                          this.select(s.firstChild, e);
31011                      }
31012                  }
31013              break;
31014              case e.LEFT:
31015                  e.preventDefault();
31016                  if(s.hasChildNodes() && s.isExpanded()){
31017                      s.collapse();
31018                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31019                      this.select(s.parentNode, e);
31020                  }
31021              break;
31022         };
31023     }
31024 });
31025
31026 /**
31027  * @class Roo.tree.MultiSelectionModel
31028  * @extends Roo.util.Observable
31029  * Multi selection for a TreePanel.
31030  */
31031 Roo.tree.MultiSelectionModel = function(){
31032    this.selNodes = [];
31033    this.selMap = {};
31034    this.addEvents({
31035        /**
31036         * @event selectionchange
31037         * Fires when the selected nodes change
31038         * @param {MultiSelectionModel} this
31039         * @param {Array} nodes Array of the selected nodes
31040         */
31041        "selectionchange" : true
31042    });
31043 };
31044
31045 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31046     init : function(tree){
31047         this.tree = tree;
31048         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31049         tree.on("click", this.onNodeClick, this);
31050     },
31051     
31052     onNodeClick : function(node, e){
31053         this.select(node, e, e.ctrlKey);
31054     },
31055     
31056     /**
31057      * Select a node.
31058      * @param {TreeNode} node The node to select
31059      * @param {EventObject} e (optional) An event associated with the selection
31060      * @param {Boolean} keepExisting True to retain existing selections
31061      * @return {TreeNode} The selected node
31062      */
31063     select : function(node, e, keepExisting){
31064         if(keepExisting !== true){
31065             this.clearSelections(true);
31066         }
31067         if(this.isSelected(node)){
31068             this.lastSelNode = node;
31069             return node;
31070         }
31071         this.selNodes.push(node);
31072         this.selMap[node.id] = node;
31073         this.lastSelNode = node;
31074         node.ui.onSelectedChange(true);
31075         this.fireEvent("selectionchange", this, this.selNodes);
31076         return node;
31077     },
31078     
31079     /**
31080      * Deselect a node.
31081      * @param {TreeNode} node The node to unselect
31082      */
31083     unselect : function(node){
31084         if(this.selMap[node.id]){
31085             node.ui.onSelectedChange(false);
31086             var sn = this.selNodes;
31087             var index = -1;
31088             if(sn.indexOf){
31089                 index = sn.indexOf(node);
31090             }else{
31091                 for(var i = 0, len = sn.length; i < len; i++){
31092                     if(sn[i] == node){
31093                         index = i;
31094                         break;
31095                     }
31096                 }
31097             }
31098             if(index != -1){
31099                 this.selNodes.splice(index, 1);
31100             }
31101             delete this.selMap[node.id];
31102             this.fireEvent("selectionchange", this, this.selNodes);
31103         }
31104     },
31105     
31106     /**
31107      * Clear all selections
31108      */
31109     clearSelections : function(suppressEvent){
31110         var sn = this.selNodes;
31111         if(sn.length > 0){
31112             for(var i = 0, len = sn.length; i < len; i++){
31113                 sn[i].ui.onSelectedChange(false);
31114             }
31115             this.selNodes = [];
31116             this.selMap = {};
31117             if(suppressEvent !== true){
31118                 this.fireEvent("selectionchange", this, this.selNodes);
31119             }
31120         }
31121     },
31122     
31123     /**
31124      * Returns true if the node is selected
31125      * @param {TreeNode} node The node to check
31126      * @return {Boolean}
31127      */
31128     isSelected : function(node){
31129         return this.selMap[node.id] ? true : false;  
31130     },
31131     
31132     /**
31133      * Returns an array of the selected nodes
31134      * @return {Array}
31135      */
31136     getSelectedNodes : function(){
31137         return this.selNodes;    
31138     },
31139
31140     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31141
31142     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31143
31144     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31145 });/*
31146  * Based on:
31147  * Ext JS Library 1.1.1
31148  * Copyright(c) 2006-2007, Ext JS, LLC.
31149  *
31150  * Originally Released Under LGPL - original licence link has changed is not relivant.
31151  *
31152  * Fork - LGPL
31153  * <script type="text/javascript">
31154  */
31155  
31156 /**
31157  * @class Roo.tree.TreeNode
31158  * @extends Roo.data.Node
31159  * @cfg {String} text The text for this node
31160  * @cfg {Boolean} expanded true to start the node expanded
31161  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31162  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31163  * @cfg {Boolean} disabled true to start the node disabled
31164  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31165  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31166  * @cfg {String} cls A css class to be added to the node
31167  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31168  * @cfg {String} href URL of the link used for the node (defaults to #)
31169  * @cfg {String} hrefTarget target frame for the link
31170  * @cfg {String} qtip An Ext QuickTip for the node
31171  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31172  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31173  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31174  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31175  * (defaults to undefined with no checkbox rendered)
31176  * @constructor
31177  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31178  */
31179 Roo.tree.TreeNode = function(attributes){
31180     attributes = attributes || {};
31181     if(typeof attributes == "string"){
31182         attributes = {text: attributes};
31183     }
31184     this.childrenRendered = false;
31185     this.rendered = false;
31186     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31187     this.expanded = attributes.expanded === true;
31188     this.isTarget = attributes.isTarget !== false;
31189     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31190     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31191
31192     /**
31193      * Read-only. The text for this node. To change it use setText().
31194      * @type String
31195      */
31196     this.text = attributes.text;
31197     /**
31198      * True if this node is disabled.
31199      * @type Boolean
31200      */
31201     this.disabled = attributes.disabled === true;
31202
31203     this.addEvents({
31204         /**
31205         * @event textchange
31206         * Fires when the text for this node is changed
31207         * @param {Node} this This node
31208         * @param {String} text The new text
31209         * @param {String} oldText The old text
31210         */
31211         "textchange" : true,
31212         /**
31213         * @event beforeexpand
31214         * Fires before this node is expanded, return false to cancel.
31215         * @param {Node} this This node
31216         * @param {Boolean} deep
31217         * @param {Boolean} anim
31218         */
31219         "beforeexpand" : true,
31220         /**
31221         * @event beforecollapse
31222         * Fires before this node is collapsed, return false to cancel.
31223         * @param {Node} this This node
31224         * @param {Boolean} deep
31225         * @param {Boolean} anim
31226         */
31227         "beforecollapse" : true,
31228         /**
31229         * @event expand
31230         * Fires when this node is expanded
31231         * @param {Node} this This node
31232         */
31233         "expand" : true,
31234         /**
31235         * @event disabledchange
31236         * Fires when the disabled status of this node changes
31237         * @param {Node} this This node
31238         * @param {Boolean} disabled
31239         */
31240         "disabledchange" : true,
31241         /**
31242         * @event collapse
31243         * Fires when this node is collapsed
31244         * @param {Node} this This node
31245         */
31246         "collapse" : true,
31247         /**
31248         * @event beforeclick
31249         * Fires before click processing. Return false to cancel the default action.
31250         * @param {Node} this This node
31251         * @param {Roo.EventObject} e The event object
31252         */
31253         "beforeclick":true,
31254         /**
31255         * @event checkchange
31256         * Fires when a node with a checkbox's checked property changes
31257         * @param {Node} this This node
31258         * @param {Boolean} checked
31259         */
31260         "checkchange":true,
31261         /**
31262         * @event click
31263         * Fires when this node is clicked
31264         * @param {Node} this This node
31265         * @param {Roo.EventObject} e The event object
31266         */
31267         "click":true,
31268         /**
31269         * @event dblclick
31270         * Fires when this node is double clicked
31271         * @param {Node} this This node
31272         * @param {Roo.EventObject} e The event object
31273         */
31274         "dblclick":true,
31275         /**
31276         * @event contextmenu
31277         * Fires when this node is right clicked
31278         * @param {Node} this This node
31279         * @param {Roo.EventObject} e The event object
31280         */
31281         "contextmenu":true,
31282         /**
31283         * @event beforechildrenrendered
31284         * Fires right before the child nodes for this node are rendered
31285         * @param {Node} this This node
31286         */
31287         "beforechildrenrendered":true
31288     });
31289
31290     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31291
31292     /**
31293      * Read-only. The UI for this node
31294      * @type TreeNodeUI
31295      */
31296     this.ui = new uiClass(this);
31297 };
31298 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31299     preventHScroll: true,
31300     /**
31301      * Returns true if this node is expanded
31302      * @return {Boolean}
31303      */
31304     isExpanded : function(){
31305         return this.expanded;
31306     },
31307
31308     /**
31309      * Returns the UI object for this node
31310      * @return {TreeNodeUI}
31311      */
31312     getUI : function(){
31313         return this.ui;
31314     },
31315
31316     // private override
31317     setFirstChild : function(node){
31318         var of = this.firstChild;
31319         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31320         if(this.childrenRendered && of && node != of){
31321             of.renderIndent(true, true);
31322         }
31323         if(this.rendered){
31324             this.renderIndent(true, true);
31325         }
31326     },
31327
31328     // private override
31329     setLastChild : function(node){
31330         var ol = this.lastChild;
31331         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31332         if(this.childrenRendered && ol && node != ol){
31333             ol.renderIndent(true, true);
31334         }
31335         if(this.rendered){
31336             this.renderIndent(true, true);
31337         }
31338     },
31339
31340     // these methods are overridden to provide lazy rendering support
31341     // private override
31342     appendChild : function(){
31343         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31344         if(node && this.childrenRendered){
31345             node.render();
31346         }
31347         this.ui.updateExpandIcon();
31348         return node;
31349     },
31350
31351     // private override
31352     removeChild : function(node){
31353         this.ownerTree.getSelectionModel().unselect(node);
31354         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31355         // if it's been rendered remove dom node
31356         if(this.childrenRendered){
31357             node.ui.remove();
31358         }
31359         if(this.childNodes.length < 1){
31360             this.collapse(false, false);
31361         }else{
31362             this.ui.updateExpandIcon();
31363         }
31364         if(!this.firstChild) {
31365             this.childrenRendered = false;
31366         }
31367         return node;
31368     },
31369
31370     // private override
31371     insertBefore : function(node, refNode){
31372         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31373         if(newNode && refNode && this.childrenRendered){
31374             node.render();
31375         }
31376         this.ui.updateExpandIcon();
31377         return newNode;
31378     },
31379
31380     /**
31381      * Sets the text for this node
31382      * @param {String} text
31383      */
31384     setText : function(text){
31385         var oldText = this.text;
31386         this.text = text;
31387         this.attributes.text = text;
31388         if(this.rendered){ // event without subscribing
31389             this.ui.onTextChange(this, text, oldText);
31390         }
31391         this.fireEvent("textchange", this, text, oldText);
31392     },
31393
31394     /**
31395      * Triggers selection of this node
31396      */
31397     select : function(){
31398         this.getOwnerTree().getSelectionModel().select(this);
31399     },
31400
31401     /**
31402      * Triggers deselection of this node
31403      */
31404     unselect : function(){
31405         this.getOwnerTree().getSelectionModel().unselect(this);
31406     },
31407
31408     /**
31409      * Returns true if this node is selected
31410      * @return {Boolean}
31411      */
31412     isSelected : function(){
31413         return this.getOwnerTree().getSelectionModel().isSelected(this);
31414     },
31415
31416     /**
31417      * Expand this node.
31418      * @param {Boolean} deep (optional) True to expand all children as well
31419      * @param {Boolean} anim (optional) false to cancel the default animation
31420      * @param {Function} callback (optional) A callback to be called when
31421      * expanding this node completes (does not wait for deep expand to complete).
31422      * Called with 1 parameter, this node.
31423      */
31424     expand : function(deep, anim, callback){
31425         if(!this.expanded){
31426             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31427                 return;
31428             }
31429             if(!this.childrenRendered){
31430                 this.renderChildren();
31431             }
31432             this.expanded = true;
31433             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31434                 this.ui.animExpand(function(){
31435                     this.fireEvent("expand", this);
31436                     if(typeof callback == "function"){
31437                         callback(this);
31438                     }
31439                     if(deep === true){
31440                         this.expandChildNodes(true);
31441                     }
31442                 }.createDelegate(this));
31443                 return;
31444             }else{
31445                 this.ui.expand();
31446                 this.fireEvent("expand", this);
31447                 if(typeof callback == "function"){
31448                     callback(this);
31449                 }
31450             }
31451         }else{
31452            if(typeof callback == "function"){
31453                callback(this);
31454            }
31455         }
31456         if(deep === true){
31457             this.expandChildNodes(true);
31458         }
31459     },
31460
31461     isHiddenRoot : function(){
31462         return this.isRoot && !this.getOwnerTree().rootVisible;
31463     },
31464
31465     /**
31466      * Collapse this node.
31467      * @param {Boolean} deep (optional) True to collapse all children as well
31468      * @param {Boolean} anim (optional) false to cancel the default animation
31469      */
31470     collapse : function(deep, anim){
31471         if(this.expanded && !this.isHiddenRoot()){
31472             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31473                 return;
31474             }
31475             this.expanded = false;
31476             if((this.getOwnerTree().animate && anim !== false) || anim){
31477                 this.ui.animCollapse(function(){
31478                     this.fireEvent("collapse", this);
31479                     if(deep === true){
31480                         this.collapseChildNodes(true);
31481                     }
31482                 }.createDelegate(this));
31483                 return;
31484             }else{
31485                 this.ui.collapse();
31486                 this.fireEvent("collapse", this);
31487             }
31488         }
31489         if(deep === true){
31490             var cs = this.childNodes;
31491             for(var i = 0, len = cs.length; i < len; i++) {
31492                 cs[i].collapse(true, false);
31493             }
31494         }
31495     },
31496
31497     // private
31498     delayedExpand : function(delay){
31499         if(!this.expandProcId){
31500             this.expandProcId = this.expand.defer(delay, this);
31501         }
31502     },
31503
31504     // private
31505     cancelExpand : function(){
31506         if(this.expandProcId){
31507             clearTimeout(this.expandProcId);
31508         }
31509         this.expandProcId = false;
31510     },
31511
31512     /**
31513      * Toggles expanded/collapsed state of the node
31514      */
31515     toggle : function(){
31516         if(this.expanded){
31517             this.collapse();
31518         }else{
31519             this.expand();
31520         }
31521     },
31522
31523     /**
31524      * Ensures all parent nodes are expanded
31525      */
31526     ensureVisible : function(callback){
31527         var tree = this.getOwnerTree();
31528         tree.expandPath(this.parentNode.getPath(), false, function(){
31529             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31530             Roo.callback(callback);
31531         }.createDelegate(this));
31532     },
31533
31534     /**
31535      * Expand all child nodes
31536      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31537      */
31538     expandChildNodes : function(deep){
31539         var cs = this.childNodes;
31540         for(var i = 0, len = cs.length; i < len; i++) {
31541                 cs[i].expand(deep);
31542         }
31543     },
31544
31545     /**
31546      * Collapse all child nodes
31547      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31548      */
31549     collapseChildNodes : function(deep){
31550         var cs = this.childNodes;
31551         for(var i = 0, len = cs.length; i < len; i++) {
31552                 cs[i].collapse(deep);
31553         }
31554     },
31555
31556     /**
31557      * Disables this node
31558      */
31559     disable : function(){
31560         this.disabled = true;
31561         this.unselect();
31562         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31563             this.ui.onDisableChange(this, true);
31564         }
31565         this.fireEvent("disabledchange", this, true);
31566     },
31567
31568     /**
31569      * Enables this node
31570      */
31571     enable : function(){
31572         this.disabled = false;
31573         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31574             this.ui.onDisableChange(this, false);
31575         }
31576         this.fireEvent("disabledchange", this, false);
31577     },
31578
31579     // private
31580     renderChildren : function(suppressEvent){
31581         if(suppressEvent !== false){
31582             this.fireEvent("beforechildrenrendered", this);
31583         }
31584         var cs = this.childNodes;
31585         for(var i = 0, len = cs.length; i < len; i++){
31586             cs[i].render(true);
31587         }
31588         this.childrenRendered = true;
31589     },
31590
31591     // private
31592     sort : function(fn, scope){
31593         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31594         if(this.childrenRendered){
31595             var cs = this.childNodes;
31596             for(var i = 0, len = cs.length; i < len; i++){
31597                 cs[i].render(true);
31598             }
31599         }
31600     },
31601
31602     // private
31603     render : function(bulkRender){
31604         this.ui.render(bulkRender);
31605         if(!this.rendered){
31606             this.rendered = true;
31607             if(this.expanded){
31608                 this.expanded = false;
31609                 this.expand(false, false);
31610             }
31611         }
31612     },
31613
31614     // private
31615     renderIndent : function(deep, refresh){
31616         if(refresh){
31617             this.ui.childIndent = null;
31618         }
31619         this.ui.renderIndent();
31620         if(deep === true && this.childrenRendered){
31621             var cs = this.childNodes;
31622             for(var i = 0, len = cs.length; i < len; i++){
31623                 cs[i].renderIndent(true, refresh);
31624             }
31625         }
31626     }
31627 });/*
31628  * Based on:
31629  * Ext JS Library 1.1.1
31630  * Copyright(c) 2006-2007, Ext JS, LLC.
31631  *
31632  * Originally Released Under LGPL - original licence link has changed is not relivant.
31633  *
31634  * Fork - LGPL
31635  * <script type="text/javascript">
31636  */
31637  
31638 /**
31639  * @class Roo.tree.AsyncTreeNode
31640  * @extends Roo.tree.TreeNode
31641  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31642  * @constructor
31643  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31644  */
31645  Roo.tree.AsyncTreeNode = function(config){
31646     this.loaded = false;
31647     this.loading = false;
31648     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31649     /**
31650     * @event beforeload
31651     * Fires before this node is loaded, return false to cancel
31652     * @param {Node} this This node
31653     */
31654     this.addEvents({'beforeload':true, 'load': true});
31655     /**
31656     * @event load
31657     * Fires when this node is loaded
31658     * @param {Node} this This node
31659     */
31660     /**
31661      * The loader used by this node (defaults to using the tree's defined loader)
31662      * @type TreeLoader
31663      * @property loader
31664      */
31665 };
31666 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31667     expand : function(deep, anim, callback){
31668         if(this.loading){ // if an async load is already running, waiting til it's done
31669             var timer;
31670             var f = function(){
31671                 if(!this.loading){ // done loading
31672                     clearInterval(timer);
31673                     this.expand(deep, anim, callback);
31674                 }
31675             }.createDelegate(this);
31676             timer = setInterval(f, 200);
31677             return;
31678         }
31679         if(!this.loaded){
31680             if(this.fireEvent("beforeload", this) === false){
31681                 return;
31682             }
31683             this.loading = true;
31684             this.ui.beforeLoad(this);
31685             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31686             if(loader){
31687                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31688                 return;
31689             }
31690         }
31691         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31692     },
31693     
31694     /**
31695      * Returns true if this node is currently loading
31696      * @return {Boolean}
31697      */
31698     isLoading : function(){
31699         return this.loading;  
31700     },
31701     
31702     loadComplete : function(deep, anim, callback){
31703         this.loading = false;
31704         this.loaded = true;
31705         this.ui.afterLoad(this);
31706         this.fireEvent("load", this);
31707         this.expand(deep, anim, callback);
31708     },
31709     
31710     /**
31711      * Returns true if this node has been loaded
31712      * @return {Boolean}
31713      */
31714     isLoaded : function(){
31715         return this.loaded;
31716     },
31717     
31718     hasChildNodes : function(){
31719         if(!this.isLeaf() && !this.loaded){
31720             return true;
31721         }else{
31722             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31723         }
31724     },
31725
31726     /**
31727      * Trigger a reload for this node
31728      * @param {Function} callback
31729      */
31730     reload : function(callback){
31731         this.collapse(false, false);
31732         while(this.firstChild){
31733             this.removeChild(this.firstChild);
31734         }
31735         this.childrenRendered = false;
31736         this.loaded = false;
31737         if(this.isHiddenRoot()){
31738             this.expanded = false;
31739         }
31740         this.expand(false, false, callback);
31741     }
31742 });/*
31743  * Based on:
31744  * Ext JS Library 1.1.1
31745  * Copyright(c) 2006-2007, Ext JS, LLC.
31746  *
31747  * Originally Released Under LGPL - original licence link has changed is not relivant.
31748  *
31749  * Fork - LGPL
31750  * <script type="text/javascript">
31751  */
31752  
31753 /**
31754  * @class Roo.tree.TreeNodeUI
31755  * @constructor
31756  * @param {Object} node The node to render
31757  * The TreeNode UI implementation is separate from the
31758  * tree implementation. Unless you are customizing the tree UI,
31759  * you should never have to use this directly.
31760  */
31761 Roo.tree.TreeNodeUI = function(node){
31762     this.node = node;
31763     this.rendered = false;
31764     this.animating = false;
31765     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31766 };
31767
31768 Roo.tree.TreeNodeUI.prototype = {
31769     removeChild : function(node){
31770         if(this.rendered){
31771             this.ctNode.removeChild(node.ui.getEl());
31772         }
31773     },
31774
31775     beforeLoad : function(){
31776          this.addClass("x-tree-node-loading");
31777     },
31778
31779     afterLoad : function(){
31780          this.removeClass("x-tree-node-loading");
31781     },
31782
31783     onTextChange : function(node, text, oldText){
31784         if(this.rendered){
31785             this.textNode.innerHTML = text;
31786         }
31787     },
31788
31789     onDisableChange : function(node, state){
31790         this.disabled = state;
31791         if(state){
31792             this.addClass("x-tree-node-disabled");
31793         }else{
31794             this.removeClass("x-tree-node-disabled");
31795         }
31796     },
31797
31798     onSelectedChange : function(state){
31799         if(state){
31800             this.focus();
31801             this.addClass("x-tree-selected");
31802         }else{
31803             //this.blur();
31804             this.removeClass("x-tree-selected");
31805         }
31806     },
31807
31808     onMove : function(tree, node, oldParent, newParent, index, refNode){
31809         this.childIndent = null;
31810         if(this.rendered){
31811             var targetNode = newParent.ui.getContainer();
31812             if(!targetNode){//target not rendered
31813                 this.holder = document.createElement("div");
31814                 this.holder.appendChild(this.wrap);
31815                 return;
31816             }
31817             var insertBefore = refNode ? refNode.ui.getEl() : null;
31818             if(insertBefore){
31819                 targetNode.insertBefore(this.wrap, insertBefore);
31820             }else{
31821                 targetNode.appendChild(this.wrap);
31822             }
31823             this.node.renderIndent(true);
31824         }
31825     },
31826
31827     addClass : function(cls){
31828         if(this.elNode){
31829             Roo.fly(this.elNode).addClass(cls);
31830         }
31831     },
31832
31833     removeClass : function(cls){
31834         if(this.elNode){
31835             Roo.fly(this.elNode).removeClass(cls);
31836         }
31837     },
31838
31839     remove : function(){
31840         if(this.rendered){
31841             this.holder = document.createElement("div");
31842             this.holder.appendChild(this.wrap);
31843         }
31844     },
31845
31846     fireEvent : function(){
31847         return this.node.fireEvent.apply(this.node, arguments);
31848     },
31849
31850     initEvents : function(){
31851         this.node.on("move", this.onMove, this);
31852         var E = Roo.EventManager;
31853         var a = this.anchor;
31854
31855         var el = Roo.fly(a, '_treeui');
31856
31857         if(Roo.isOpera){ // opera render bug ignores the CSS
31858             el.setStyle("text-decoration", "none");
31859         }
31860
31861         el.on("click", this.onClick, this);
31862         el.on("dblclick", this.onDblClick, this);
31863
31864         if(this.checkbox){
31865             Roo.EventManager.on(this.checkbox,
31866                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31867         }
31868
31869         el.on("contextmenu", this.onContextMenu, this);
31870
31871         var icon = Roo.fly(this.iconNode);
31872         icon.on("click", this.onClick, this);
31873         icon.on("dblclick", this.onDblClick, this);
31874         icon.on("contextmenu", this.onContextMenu, this);
31875         E.on(this.ecNode, "click", this.ecClick, this, true);
31876
31877         if(this.node.disabled){
31878             this.addClass("x-tree-node-disabled");
31879         }
31880         if(this.node.hidden){
31881             this.addClass("x-tree-node-disabled");
31882         }
31883         var ot = this.node.getOwnerTree();
31884         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31885         if(dd && (!this.node.isRoot || ot.rootVisible)){
31886             Roo.dd.Registry.register(this.elNode, {
31887                 node: this.node,
31888                 handles: this.getDDHandles(),
31889                 isHandle: false
31890             });
31891         }
31892     },
31893
31894     getDDHandles : function(){
31895         return [this.iconNode, this.textNode];
31896     },
31897
31898     hide : function(){
31899         if(this.rendered){
31900             this.wrap.style.display = "none";
31901         }
31902     },
31903
31904     show : function(){
31905         if(this.rendered){
31906             this.wrap.style.display = "";
31907         }
31908     },
31909
31910     onContextMenu : function(e){
31911         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31912             e.preventDefault();
31913             this.focus();
31914             this.fireEvent("contextmenu", this.node, e);
31915         }
31916     },
31917
31918     onClick : function(e){
31919         if(this.dropping){
31920             e.stopEvent();
31921             return;
31922         }
31923         if(this.fireEvent("beforeclick", this.node, e) !== false){
31924             if(!this.disabled && this.node.attributes.href){
31925                 this.fireEvent("click", this.node, e);
31926                 return;
31927             }
31928             e.preventDefault();
31929             if(this.disabled){
31930                 return;
31931             }
31932
31933             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31934                 this.node.toggle();
31935             }
31936
31937             this.fireEvent("click", this.node, e);
31938         }else{
31939             e.stopEvent();
31940         }
31941     },
31942
31943     onDblClick : function(e){
31944         e.preventDefault();
31945         if(this.disabled){
31946             return;
31947         }
31948         if(this.checkbox){
31949             this.toggleCheck();
31950         }
31951         if(!this.animating && this.node.hasChildNodes()){
31952             this.node.toggle();
31953         }
31954         this.fireEvent("dblclick", this.node, e);
31955     },
31956
31957     onCheckChange : function(){
31958         var checked = this.checkbox.checked;
31959         this.node.attributes.checked = checked;
31960         this.fireEvent('checkchange', this.node, checked);
31961     },
31962
31963     ecClick : function(e){
31964         if(!this.animating && this.node.hasChildNodes()){
31965             this.node.toggle();
31966         }
31967     },
31968
31969     startDrop : function(){
31970         this.dropping = true;
31971     },
31972
31973     // delayed drop so the click event doesn't get fired on a drop
31974     endDrop : function(){
31975        setTimeout(function(){
31976            this.dropping = false;
31977        }.createDelegate(this), 50);
31978     },
31979
31980     expand : function(){
31981         this.updateExpandIcon();
31982         this.ctNode.style.display = "";
31983     },
31984
31985     focus : function(){
31986         if(!this.node.preventHScroll){
31987             try{this.anchor.focus();
31988             }catch(e){}
31989         }else if(!Roo.isIE){
31990             try{
31991                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31992                 var l = noscroll.scrollLeft;
31993                 this.anchor.focus();
31994                 noscroll.scrollLeft = l;
31995             }catch(e){}
31996         }
31997     },
31998
31999     toggleCheck : function(value){
32000         var cb = this.checkbox;
32001         if(cb){
32002             cb.checked = (value === undefined ? !cb.checked : value);
32003         }
32004     },
32005
32006     blur : function(){
32007         try{
32008             this.anchor.blur();
32009         }catch(e){}
32010     },
32011
32012     animExpand : function(callback){
32013         var ct = Roo.get(this.ctNode);
32014         ct.stopFx();
32015         if(!this.node.hasChildNodes()){
32016             this.updateExpandIcon();
32017             this.ctNode.style.display = "";
32018             Roo.callback(callback);
32019             return;
32020         }
32021         this.animating = true;
32022         this.updateExpandIcon();
32023
32024         ct.slideIn('t', {
32025            callback : function(){
32026                this.animating = false;
32027                Roo.callback(callback);
32028             },
32029             scope: this,
32030             duration: this.node.ownerTree.duration || .25
32031         });
32032     },
32033
32034     highlight : function(){
32035         var tree = this.node.getOwnerTree();
32036         Roo.fly(this.wrap).highlight(
32037             tree.hlColor || "C3DAF9",
32038             {endColor: tree.hlBaseColor}
32039         );
32040     },
32041
32042     collapse : function(){
32043         this.updateExpandIcon();
32044         this.ctNode.style.display = "none";
32045     },
32046
32047     animCollapse : function(callback){
32048         var ct = Roo.get(this.ctNode);
32049         ct.enableDisplayMode('block');
32050         ct.stopFx();
32051
32052         this.animating = true;
32053         this.updateExpandIcon();
32054
32055         ct.slideOut('t', {
32056             callback : function(){
32057                this.animating = false;
32058                Roo.callback(callback);
32059             },
32060             scope: this,
32061             duration: this.node.ownerTree.duration || .25
32062         });
32063     },
32064
32065     getContainer : function(){
32066         return this.ctNode;
32067     },
32068
32069     getEl : function(){
32070         return this.wrap;
32071     },
32072
32073     appendDDGhost : function(ghostNode){
32074         ghostNode.appendChild(this.elNode.cloneNode(true));
32075     },
32076
32077     getDDRepairXY : function(){
32078         return Roo.lib.Dom.getXY(this.iconNode);
32079     },
32080
32081     onRender : function(){
32082         this.render();
32083     },
32084
32085     render : function(bulkRender){
32086         var n = this.node, a = n.attributes;
32087         var targetNode = n.parentNode ?
32088               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32089
32090         if(!this.rendered){
32091             this.rendered = true;
32092
32093             this.renderElements(n, a, targetNode, bulkRender);
32094
32095             if(a.qtip){
32096                if(this.textNode.setAttributeNS){
32097                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32098                    if(a.qtipTitle){
32099                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32100                    }
32101                }else{
32102                    this.textNode.setAttribute("ext:qtip", a.qtip);
32103                    if(a.qtipTitle){
32104                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32105                    }
32106                }
32107             }else if(a.qtipCfg){
32108                 a.qtipCfg.target = Roo.id(this.textNode);
32109                 Roo.QuickTips.register(a.qtipCfg);
32110             }
32111             this.initEvents();
32112             if(!this.node.expanded){
32113                 this.updateExpandIcon();
32114             }
32115         }else{
32116             if(bulkRender === true) {
32117                 targetNode.appendChild(this.wrap);
32118             }
32119         }
32120     },
32121
32122     renderElements : function(n, a, targetNode, bulkRender){
32123         // add some indent caching, this helps performance when rendering a large tree
32124         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32125         var t = n.getOwnerTree();
32126         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32127         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32128         var cb = typeof a.checked == 'boolean';
32129         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32130         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32131             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32132             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32133             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32134             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32135             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32136              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32137                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32138             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32139             "</li>"];
32140
32141         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32142             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32143                                 n.nextSibling.ui.getEl(), buf.join(""));
32144         }else{
32145             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32146         }
32147
32148         this.elNode = this.wrap.childNodes[0];
32149         this.ctNode = this.wrap.childNodes[1];
32150         var cs = this.elNode.childNodes;
32151         this.indentNode = cs[0];
32152         this.ecNode = cs[1];
32153         this.iconNode = cs[2];
32154         var index = 3;
32155         if(cb){
32156             this.checkbox = cs[3];
32157             index++;
32158         }
32159         this.anchor = cs[index];
32160         this.textNode = cs[index].firstChild;
32161     },
32162
32163     getAnchor : function(){
32164         return this.anchor;
32165     },
32166
32167     getTextEl : function(){
32168         return this.textNode;
32169     },
32170
32171     getIconEl : function(){
32172         return this.iconNode;
32173     },
32174
32175     isChecked : function(){
32176         return this.checkbox ? this.checkbox.checked : false;
32177     },
32178
32179     updateExpandIcon : function(){
32180         if(this.rendered){
32181             var n = this.node, c1, c2;
32182             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32183             var hasChild = n.hasChildNodes();
32184             if(hasChild){
32185                 if(n.expanded){
32186                     cls += "-minus";
32187                     c1 = "x-tree-node-collapsed";
32188                     c2 = "x-tree-node-expanded";
32189                 }else{
32190                     cls += "-plus";
32191                     c1 = "x-tree-node-expanded";
32192                     c2 = "x-tree-node-collapsed";
32193                 }
32194                 if(this.wasLeaf){
32195                     this.removeClass("x-tree-node-leaf");
32196                     this.wasLeaf = false;
32197                 }
32198                 if(this.c1 != c1 || this.c2 != c2){
32199                     Roo.fly(this.elNode).replaceClass(c1, c2);
32200                     this.c1 = c1; this.c2 = c2;
32201                 }
32202             }else{
32203                 if(!this.wasLeaf){
32204                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32205                     delete this.c1;
32206                     delete this.c2;
32207                     this.wasLeaf = true;
32208                 }
32209             }
32210             var ecc = "x-tree-ec-icon "+cls;
32211             if(this.ecc != ecc){
32212                 this.ecNode.className = ecc;
32213                 this.ecc = ecc;
32214             }
32215         }
32216     },
32217
32218     getChildIndent : function(){
32219         if(!this.childIndent){
32220             var buf = [];
32221             var p = this.node;
32222             while(p){
32223                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32224                     if(!p.isLast()) {
32225                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32226                     } else {
32227                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32228                     }
32229                 }
32230                 p = p.parentNode;
32231             }
32232             this.childIndent = buf.join("");
32233         }
32234         return this.childIndent;
32235     },
32236
32237     renderIndent : function(){
32238         if(this.rendered){
32239             var indent = "";
32240             var p = this.node.parentNode;
32241             if(p){
32242                 indent = p.ui.getChildIndent();
32243             }
32244             if(this.indentMarkup != indent){ // don't rerender if not required
32245                 this.indentNode.innerHTML = indent;
32246                 this.indentMarkup = indent;
32247             }
32248             this.updateExpandIcon();
32249         }
32250     }
32251 };
32252
32253 Roo.tree.RootTreeNodeUI = function(){
32254     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32255 };
32256 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32257     render : function(){
32258         if(!this.rendered){
32259             var targetNode = this.node.ownerTree.innerCt.dom;
32260             this.node.expanded = true;
32261             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32262             this.wrap = this.ctNode = targetNode.firstChild;
32263         }
32264     },
32265     collapse : function(){
32266     },
32267     expand : function(){
32268     }
32269 });/*
32270  * Based on:
32271  * Ext JS Library 1.1.1
32272  * Copyright(c) 2006-2007, Ext JS, LLC.
32273  *
32274  * Originally Released Under LGPL - original licence link has changed is not relivant.
32275  *
32276  * Fork - LGPL
32277  * <script type="text/javascript">
32278  */
32279 /**
32280  * @class Roo.tree.TreeLoader
32281  * @extends Roo.util.Observable
32282  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32283  * nodes from a specified URL. The response must be a javascript Array definition
32284  * who's elements are node definition objects. eg:
32285  * <pre><code>
32286    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32287     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32288 </code></pre>
32289  * <br><br>
32290  * A server request is sent, and child nodes are loaded only when a node is expanded.
32291  * The loading node's id is passed to the server under the parameter name "node" to
32292  * enable the server to produce the correct child nodes.
32293  * <br><br>
32294  * To pass extra parameters, an event handler may be attached to the "beforeload"
32295  * event, and the parameters specified in the TreeLoader's baseParams property:
32296  * <pre><code>
32297     myTreeLoader.on("beforeload", function(treeLoader, node) {
32298         this.baseParams.category = node.attributes.category;
32299     }, this);
32300 </code></pre><
32301  * This would pass an HTTP parameter called "category" to the server containing
32302  * the value of the Node's "category" attribute.
32303  * @constructor
32304  * Creates a new Treeloader.
32305  * @param {Object} config A config object containing config properties.
32306  */
32307 Roo.tree.TreeLoader = function(config){
32308     this.baseParams = {};
32309     this.requestMethod = "POST";
32310     Roo.apply(this, config);
32311
32312     this.addEvents({
32313     
32314         /**
32315          * @event beforeload
32316          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32317          * @param {Object} This TreeLoader object.
32318          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32319          * @param {Object} callback The callback function specified in the {@link #load} call.
32320          */
32321         beforeload : true,
32322         /**
32323          * @event load
32324          * Fires when the node has been successfuly loaded.
32325          * @param {Object} This TreeLoader object.
32326          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32327          * @param {Object} response The response object containing the data from the server.
32328          */
32329         load : true,
32330         /**
32331          * @event loadexception
32332          * Fires if the network request failed.
32333          * @param {Object} This TreeLoader object.
32334          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32335          * @param {Object} response The response object containing the data from the server.
32336          */
32337         loadexception : true,
32338         /**
32339          * @event create
32340          * Fires before a node is created, enabling you to return custom Node types 
32341          * @param {Object} This TreeLoader object.
32342          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32343          */
32344         create : true
32345     });
32346
32347     Roo.tree.TreeLoader.superclass.constructor.call(this);
32348 };
32349
32350 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32351     /**
32352     * @cfg {String} dataUrl The URL from which to request a Json string which
32353     * specifies an array of node definition object representing the child nodes
32354     * to be loaded.
32355     */
32356     /**
32357     * @cfg {Object} baseParams (optional) An object containing properties which
32358     * specify HTTP parameters to be passed to each request for child nodes.
32359     */
32360     /**
32361     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32362     * created by this loader. If the attributes sent by the server have an attribute in this object,
32363     * they take priority.
32364     */
32365     /**
32366     * @cfg {Object} uiProviders (optional) An object containing properties which
32367     * 
32368     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32369     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32370     * <i>uiProvider</i> attribute of a returned child node is a string rather
32371     * than a reference to a TreeNodeUI implementation, this that string value
32372     * is used as a property name in the uiProviders object. You can define the provider named
32373     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32374     */
32375     uiProviders : {},
32376
32377     /**
32378     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32379     * child nodes before loading.
32380     */
32381     clearOnLoad : true,
32382
32383     /**
32384     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32385     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32386     * Grid query { data : [ .....] }
32387     */
32388     
32389     root : false,
32390      /**
32391     * @cfg {String} queryParam (optional) 
32392     * Name of the query as it will be passed on the querystring (defaults to 'node')
32393     * eg. the request will be ?node=[id]
32394     */
32395     
32396     
32397     queryParam: false,
32398     
32399     /**
32400      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32401      * This is called automatically when a node is expanded, but may be used to reload
32402      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32403      * @param {Roo.tree.TreeNode} node
32404      * @param {Function} callback
32405      */
32406     load : function(node, callback){
32407         if(this.clearOnLoad){
32408             while(node.firstChild){
32409                 node.removeChild(node.firstChild);
32410             }
32411         }
32412         if(node.attributes.children){ // preloaded json children
32413             var cs = node.attributes.children;
32414             for(var i = 0, len = cs.length; i < len; i++){
32415                 node.appendChild(this.createNode(cs[i]));
32416             }
32417             if(typeof callback == "function"){
32418                 callback();
32419             }
32420         }else if(this.dataUrl){
32421             this.requestData(node, callback);
32422         }
32423     },
32424
32425     getParams: function(node){
32426         var buf = [], bp = this.baseParams;
32427         for(var key in bp){
32428             if(typeof bp[key] != "function"){
32429                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32430             }
32431         }
32432         var n = this.queryParam === false ? 'node' : this.queryParam;
32433         buf.push(n + "=", encodeURIComponent(node.id));
32434         return buf.join("");
32435     },
32436
32437     requestData : function(node, callback){
32438         if(this.fireEvent("beforeload", this, node, callback) !== false){
32439             this.transId = Roo.Ajax.request({
32440                 method:this.requestMethod,
32441                 url: this.dataUrl||this.url,
32442                 success: this.handleResponse,
32443                 failure: this.handleFailure,
32444                 scope: this,
32445                 argument: {callback: callback, node: node},
32446                 params: this.getParams(node)
32447             });
32448         }else{
32449             // if the load is cancelled, make sure we notify
32450             // the node that we are done
32451             if(typeof callback == "function"){
32452                 callback();
32453             }
32454         }
32455     },
32456
32457     isLoading : function(){
32458         return this.transId ? true : false;
32459     },
32460
32461     abort : function(){
32462         if(this.isLoading()){
32463             Roo.Ajax.abort(this.transId);
32464         }
32465     },
32466
32467     // private
32468     createNode : function(attr){
32469         // apply baseAttrs, nice idea Corey!
32470         if(this.baseAttrs){
32471             Roo.applyIf(attr, this.baseAttrs);
32472         }
32473         if(this.applyLoader !== false){
32474             attr.loader = this;
32475         }
32476         // uiProvider = depreciated..
32477         
32478         if(typeof(attr.uiProvider) == 'string'){
32479            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32480                 /**  eval:var:attr */ eval(attr.uiProvider);
32481         }
32482         if(typeof(this.uiProviders['default']) != 'undefined') {
32483             attr.uiProvider = this.uiProviders['default'];
32484         }
32485         
32486         this.fireEvent('create', this, attr);
32487         
32488         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32489         return(attr.leaf ?
32490                         new Roo.tree.TreeNode(attr) :
32491                         new Roo.tree.AsyncTreeNode(attr));
32492     },
32493
32494     processResponse : function(response, node, callback){
32495         var json = response.responseText;
32496         try {
32497             
32498             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32499             if (this.root !== false) {
32500                 o = o[this.root];
32501             }
32502             
32503             for(var i = 0, len = o.length; i < len; i++){
32504                 var n = this.createNode(o[i]);
32505                 if(n){
32506                     node.appendChild(n);
32507                 }
32508             }
32509             if(typeof callback == "function"){
32510                 callback(this, node);
32511             }
32512         }catch(e){
32513             this.handleFailure(response);
32514         }
32515     },
32516
32517     handleResponse : function(response){
32518         this.transId = false;
32519         var a = response.argument;
32520         this.processResponse(response, a.node, a.callback);
32521         this.fireEvent("load", this, a.node, response);
32522     },
32523
32524     handleFailure : function(response){
32525         this.transId = false;
32526         var a = response.argument;
32527         this.fireEvent("loadexception", this, a.node, response);
32528         if(typeof a.callback == "function"){
32529             a.callback(this, a.node);
32530         }
32531     }
32532 });/*
32533  * Based on:
32534  * Ext JS Library 1.1.1
32535  * Copyright(c) 2006-2007, Ext JS, LLC.
32536  *
32537  * Originally Released Under LGPL - original licence link has changed is not relivant.
32538  *
32539  * Fork - LGPL
32540  * <script type="text/javascript">
32541  */
32542
32543 /**
32544 * @class Roo.tree.TreeFilter
32545 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32546 * @param {TreePanel} tree
32547 * @param {Object} config (optional)
32548  */
32549 Roo.tree.TreeFilter = function(tree, config){
32550     this.tree = tree;
32551     this.filtered = {};
32552     Roo.apply(this, config);
32553 };
32554
32555 Roo.tree.TreeFilter.prototype = {
32556     clearBlank:false,
32557     reverse:false,
32558     autoClear:false,
32559     remove:false,
32560
32561      /**
32562      * Filter the data by a specific attribute.
32563      * @param {String/RegExp} value Either string that the attribute value
32564      * should start with or a RegExp to test against the attribute
32565      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32566      * @param {TreeNode} startNode (optional) The node to start the filter at.
32567      */
32568     filter : function(value, attr, startNode){
32569         attr = attr || "text";
32570         var f;
32571         if(typeof value == "string"){
32572             var vlen = value.length;
32573             // auto clear empty filter
32574             if(vlen == 0 && this.clearBlank){
32575                 this.clear();
32576                 return;
32577             }
32578             value = value.toLowerCase();
32579             f = function(n){
32580                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32581             };
32582         }else if(value.exec){ // regex?
32583             f = function(n){
32584                 return value.test(n.attributes[attr]);
32585             };
32586         }else{
32587             throw 'Illegal filter type, must be string or regex';
32588         }
32589         this.filterBy(f, null, startNode);
32590         },
32591
32592     /**
32593      * Filter by a function. The passed function will be called with each
32594      * node in the tree (or from the startNode). If the function returns true, the node is kept
32595      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32596      * @param {Function} fn The filter function
32597      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32598      */
32599     filterBy : function(fn, scope, startNode){
32600         startNode = startNode || this.tree.root;
32601         if(this.autoClear){
32602             this.clear();
32603         }
32604         var af = this.filtered, rv = this.reverse;
32605         var f = function(n){
32606             if(n == startNode){
32607                 return true;
32608             }
32609             if(af[n.id]){
32610                 return false;
32611             }
32612             var m = fn.call(scope || n, n);
32613             if(!m || rv){
32614                 af[n.id] = n;
32615                 n.ui.hide();
32616                 return false;
32617             }
32618             return true;
32619         };
32620         startNode.cascade(f);
32621         if(this.remove){
32622            for(var id in af){
32623                if(typeof id != "function"){
32624                    var n = af[id];
32625                    if(n && n.parentNode){
32626                        n.parentNode.removeChild(n);
32627                    }
32628                }
32629            }
32630         }
32631     },
32632
32633     /**
32634      * Clears the current filter. Note: with the "remove" option
32635      * set a filter cannot be cleared.
32636      */
32637     clear : function(){
32638         var t = this.tree;
32639         var af = this.filtered;
32640         for(var id in af){
32641             if(typeof id != "function"){
32642                 var n = af[id];
32643                 if(n){
32644                     n.ui.show();
32645                 }
32646             }
32647         }
32648         this.filtered = {};
32649     }
32650 };
32651 /*
32652  * Based on:
32653  * Ext JS Library 1.1.1
32654  * Copyright(c) 2006-2007, Ext JS, LLC.
32655  *
32656  * Originally Released Under LGPL - original licence link has changed is not relivant.
32657  *
32658  * Fork - LGPL
32659  * <script type="text/javascript">
32660  */
32661  
32662
32663 /**
32664  * @class Roo.tree.TreeSorter
32665  * Provides sorting of nodes in a TreePanel
32666  * 
32667  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32668  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32669  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32670  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32671  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32672  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32673  * @constructor
32674  * @param {TreePanel} tree
32675  * @param {Object} config
32676  */
32677 Roo.tree.TreeSorter = function(tree, config){
32678     Roo.apply(this, config);
32679     tree.on("beforechildrenrendered", this.doSort, this);
32680     tree.on("append", this.updateSort, this);
32681     tree.on("insert", this.updateSort, this);
32682     
32683     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32684     var p = this.property || "text";
32685     var sortType = this.sortType;
32686     var fs = this.folderSort;
32687     var cs = this.caseSensitive === true;
32688     var leafAttr = this.leafAttr || 'leaf';
32689
32690     this.sortFn = function(n1, n2){
32691         if(fs){
32692             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32693                 return 1;
32694             }
32695             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32696                 return -1;
32697             }
32698         }
32699         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32700         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32701         if(v1 < v2){
32702                         return dsc ? +1 : -1;
32703                 }else if(v1 > v2){
32704                         return dsc ? -1 : +1;
32705         }else{
32706                 return 0;
32707         }
32708     };
32709 };
32710
32711 Roo.tree.TreeSorter.prototype = {
32712     doSort : function(node){
32713         node.sort(this.sortFn);
32714     },
32715     
32716     compareNodes : function(n1, n2){
32717         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32718     },
32719     
32720     updateSort : function(tree, node){
32721         if(node.childrenRendered){
32722             this.doSort.defer(1, this, [node]);
32723         }
32724     }
32725 };/*
32726  * Based on:
32727  * Ext JS Library 1.1.1
32728  * Copyright(c) 2006-2007, Ext JS, LLC.
32729  *
32730  * Originally Released Under LGPL - original licence link has changed is not relivant.
32731  *
32732  * Fork - LGPL
32733  * <script type="text/javascript">
32734  */
32735
32736 if(Roo.dd.DropZone){
32737     
32738 Roo.tree.TreeDropZone = function(tree, config){
32739     this.allowParentInsert = false;
32740     this.allowContainerDrop = false;
32741     this.appendOnly = false;
32742     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32743     this.tree = tree;
32744     this.lastInsertClass = "x-tree-no-status";
32745     this.dragOverData = {};
32746 };
32747
32748 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32749     ddGroup : "TreeDD",
32750     
32751     expandDelay : 1000,
32752     
32753     expandNode : function(node){
32754         if(node.hasChildNodes() && !node.isExpanded()){
32755             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32756         }
32757     },
32758     
32759     queueExpand : function(node){
32760         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32761     },
32762     
32763     cancelExpand : function(){
32764         if(this.expandProcId){
32765             clearTimeout(this.expandProcId);
32766             this.expandProcId = false;
32767         }
32768     },
32769     
32770     isValidDropPoint : function(n, pt, dd, e, data){
32771         if(!n || !data){ return false; }
32772         var targetNode = n.node;
32773         var dropNode = data.node;
32774         // default drop rules
32775         if(!(targetNode && targetNode.isTarget && pt)){
32776             return false;
32777         }
32778         if(pt == "append" && targetNode.allowChildren === false){
32779             return false;
32780         }
32781         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32782             return false;
32783         }
32784         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32785             return false;
32786         }
32787         // reuse the object
32788         var overEvent = this.dragOverData;
32789         overEvent.tree = this.tree;
32790         overEvent.target = targetNode;
32791         overEvent.data = data;
32792         overEvent.point = pt;
32793         overEvent.source = dd;
32794         overEvent.rawEvent = e;
32795         overEvent.dropNode = dropNode;
32796         overEvent.cancel = false;  
32797         var result = this.tree.fireEvent("nodedragover", overEvent);
32798         return overEvent.cancel === false && result !== false;
32799     },
32800     
32801     getDropPoint : function(e, n, dd){
32802         var tn = n.node;
32803         if(tn.isRoot){
32804             return tn.allowChildren !== false ? "append" : false; // always append for root
32805         }
32806         var dragEl = n.ddel;
32807         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32808         var y = Roo.lib.Event.getPageY(e);
32809         var noAppend = tn.allowChildren === false || tn.isLeaf();
32810         if(this.appendOnly || tn.parentNode.allowChildren === false){
32811             return noAppend ? false : "append";
32812         }
32813         var noBelow = false;
32814         if(!this.allowParentInsert){
32815             noBelow = tn.hasChildNodes() && tn.isExpanded();
32816         }
32817         var q = (b - t) / (noAppend ? 2 : 3);
32818         if(y >= t && y < (t + q)){
32819             return "above";
32820         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32821             return "below";
32822         }else{
32823             return "append";
32824         }
32825     },
32826     
32827     onNodeEnter : function(n, dd, e, data){
32828         this.cancelExpand();
32829     },
32830     
32831     onNodeOver : function(n, dd, e, data){
32832         var pt = this.getDropPoint(e, n, dd);
32833         var node = n.node;
32834         
32835         // auto node expand check
32836         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32837             this.queueExpand(node);
32838         }else if(pt != "append"){
32839             this.cancelExpand();
32840         }
32841         
32842         // set the insert point style on the target node
32843         var returnCls = this.dropNotAllowed;
32844         if(this.isValidDropPoint(n, pt, dd, e, data)){
32845            if(pt){
32846                var el = n.ddel;
32847                var cls;
32848                if(pt == "above"){
32849                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32850                    cls = "x-tree-drag-insert-above";
32851                }else if(pt == "below"){
32852                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32853                    cls = "x-tree-drag-insert-below";
32854                }else{
32855                    returnCls = "x-tree-drop-ok-append";
32856                    cls = "x-tree-drag-append";
32857                }
32858                if(this.lastInsertClass != cls){
32859                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32860                    this.lastInsertClass = cls;
32861                }
32862            }
32863        }
32864        return returnCls;
32865     },
32866     
32867     onNodeOut : function(n, dd, e, data){
32868         this.cancelExpand();
32869         this.removeDropIndicators(n);
32870     },
32871     
32872     onNodeDrop : function(n, dd, e, data){
32873         var point = this.getDropPoint(e, n, dd);
32874         var targetNode = n.node;
32875         targetNode.ui.startDrop();
32876         if(!this.isValidDropPoint(n, point, dd, e, data)){
32877             targetNode.ui.endDrop();
32878             return false;
32879         }
32880         // first try to find the drop node
32881         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32882         var dropEvent = {
32883             tree : this.tree,
32884             target: targetNode,
32885             data: data,
32886             point: point,
32887             source: dd,
32888             rawEvent: e,
32889             dropNode: dropNode,
32890             cancel: !dropNode   
32891         };
32892         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32893         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32894             targetNode.ui.endDrop();
32895             return false;
32896         }
32897         // allow target changing
32898         targetNode = dropEvent.target;
32899         if(point == "append" && !targetNode.isExpanded()){
32900             targetNode.expand(false, null, function(){
32901                 this.completeDrop(dropEvent);
32902             }.createDelegate(this));
32903         }else{
32904             this.completeDrop(dropEvent);
32905         }
32906         return true;
32907     },
32908     
32909     completeDrop : function(de){
32910         var ns = de.dropNode, p = de.point, t = de.target;
32911         if(!(ns instanceof Array)){
32912             ns = [ns];
32913         }
32914         var n;
32915         for(var i = 0, len = ns.length; i < len; i++){
32916             n = ns[i];
32917             if(p == "above"){
32918                 t.parentNode.insertBefore(n, t);
32919             }else if(p == "below"){
32920                 t.parentNode.insertBefore(n, t.nextSibling);
32921             }else{
32922                 t.appendChild(n);
32923             }
32924         }
32925         n.ui.focus();
32926         if(this.tree.hlDrop){
32927             n.ui.highlight();
32928         }
32929         t.ui.endDrop();
32930         this.tree.fireEvent("nodedrop", de);
32931     },
32932     
32933     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32934         if(this.tree.hlDrop){
32935             dropNode.ui.focus();
32936             dropNode.ui.highlight();
32937         }
32938         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32939     },
32940     
32941     getTree : function(){
32942         return this.tree;
32943     },
32944     
32945     removeDropIndicators : function(n){
32946         if(n && n.ddel){
32947             var el = n.ddel;
32948             Roo.fly(el).removeClass([
32949                     "x-tree-drag-insert-above",
32950                     "x-tree-drag-insert-below",
32951                     "x-tree-drag-append"]);
32952             this.lastInsertClass = "_noclass";
32953         }
32954     },
32955     
32956     beforeDragDrop : function(target, e, id){
32957         this.cancelExpand();
32958         return true;
32959     },
32960     
32961     afterRepair : function(data){
32962         if(data && Roo.enableFx){
32963             data.node.ui.highlight();
32964         }
32965         this.hideProxy();
32966     }    
32967 });
32968
32969 }/*
32970  * Based on:
32971  * Ext JS Library 1.1.1
32972  * Copyright(c) 2006-2007, Ext JS, LLC.
32973  *
32974  * Originally Released Under LGPL - original licence link has changed is not relivant.
32975  *
32976  * Fork - LGPL
32977  * <script type="text/javascript">
32978  */
32979  
32980
32981 if(Roo.dd.DragZone){
32982 Roo.tree.TreeDragZone = function(tree, config){
32983     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32984     this.tree = tree;
32985 };
32986
32987 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32988     ddGroup : "TreeDD",
32989     
32990     onBeforeDrag : function(data, e){
32991         var n = data.node;
32992         return n && n.draggable && !n.disabled;
32993     },
32994     
32995     onInitDrag : function(e){
32996         var data = this.dragData;
32997         this.tree.getSelectionModel().select(data.node);
32998         this.proxy.update("");
32999         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33000         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33001     },
33002     
33003     getRepairXY : function(e, data){
33004         return data.node.ui.getDDRepairXY();
33005     },
33006     
33007     onEndDrag : function(data, e){
33008         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33009     },
33010     
33011     onValidDrop : function(dd, e, id){
33012         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33013         this.hideProxy();
33014     },
33015     
33016     beforeInvalidDrop : function(e, id){
33017         // this scrolls the original position back into view
33018         var sm = this.tree.getSelectionModel();
33019         sm.clearSelections();
33020         sm.select(this.dragData.node);
33021     }
33022 });
33023 }/*
33024  * Based on:
33025  * Ext JS Library 1.1.1
33026  * Copyright(c) 2006-2007, Ext JS, LLC.
33027  *
33028  * Originally Released Under LGPL - original licence link has changed is not relivant.
33029  *
33030  * Fork - LGPL
33031  * <script type="text/javascript">
33032  */
33033 /**
33034  * @class Roo.tree.TreeEditor
33035  * @extends Roo.Editor
33036  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33037  * as the editor field.
33038  * @constructor
33039  * @param {TreePanel} tree
33040  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33041  */
33042 Roo.tree.TreeEditor = function(tree, config){
33043     config = config || {};
33044     var field = config.events ? config : new Roo.form.TextField(config);
33045     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33046
33047     this.tree = tree;
33048
33049     tree.on('beforeclick', this.beforeNodeClick, this);
33050     tree.getTreeEl().on('mousedown', this.hide, this);
33051     this.on('complete', this.updateNode, this);
33052     this.on('beforestartedit', this.fitToTree, this);
33053     this.on('startedit', this.bindScroll, this, {delay:10});
33054     this.on('specialkey', this.onSpecialKey, this);
33055 };
33056
33057 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33058     /**
33059      * @cfg {String} alignment
33060      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33061      */
33062     alignment: "l-l",
33063     // inherit
33064     autoSize: false,
33065     /**
33066      * @cfg {Boolean} hideEl
33067      * True to hide the bound element while the editor is displayed (defaults to false)
33068      */
33069     hideEl : false,
33070     /**
33071      * @cfg {String} cls
33072      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33073      */
33074     cls: "x-small-editor x-tree-editor",
33075     /**
33076      * @cfg {Boolean} shim
33077      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33078      */
33079     shim:false,
33080     // inherit
33081     shadow:"frame",
33082     /**
33083      * @cfg {Number} maxWidth
33084      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33085      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33086      * scroll and client offsets into account prior to each edit.
33087      */
33088     maxWidth: 250,
33089
33090     editDelay : 350,
33091
33092     // private
33093     fitToTree : function(ed, el){
33094         var td = this.tree.getTreeEl().dom, nd = el.dom;
33095         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33096             td.scrollLeft = nd.offsetLeft;
33097         }
33098         var w = Math.min(
33099                 this.maxWidth,
33100                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33101         this.setSize(w, '');
33102     },
33103
33104     // private
33105     triggerEdit : function(node){
33106         this.completeEdit();
33107         this.editNode = node;
33108         this.startEdit(node.ui.textNode, node.text);
33109     },
33110
33111     // private
33112     bindScroll : function(){
33113         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33114     },
33115
33116     // private
33117     beforeNodeClick : function(node, e){
33118         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33119         this.lastClick = new Date();
33120         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33121             e.stopEvent();
33122             this.triggerEdit(node);
33123             return false;
33124         }
33125     },
33126
33127     // private
33128     updateNode : function(ed, value){
33129         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33130         this.editNode.setText(value);
33131     },
33132
33133     // private
33134     onHide : function(){
33135         Roo.tree.TreeEditor.superclass.onHide.call(this);
33136         if(this.editNode){
33137             this.editNode.ui.focus();
33138         }
33139     },
33140
33141     // private
33142     onSpecialKey : function(field, e){
33143         var k = e.getKey();
33144         if(k == e.ESC){
33145             e.stopEvent();
33146             this.cancelEdit();
33147         }else if(k == e.ENTER && !e.hasModifier()){
33148             e.stopEvent();
33149             this.completeEdit();
33150         }
33151     }
33152 });//<Script type="text/javascript">
33153 /*
33154  * Based on:
33155  * Ext JS Library 1.1.1
33156  * Copyright(c) 2006-2007, Ext JS, LLC.
33157  *
33158  * Originally Released Under LGPL - original licence link has changed is not relivant.
33159  *
33160  * Fork - LGPL
33161  * <script type="text/javascript">
33162  */
33163  
33164 /**
33165  * Not documented??? - probably should be...
33166  */
33167
33168 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33169     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33170     
33171     renderElements : function(n, a, targetNode, bulkRender){
33172         //consel.log("renderElements?");
33173         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33174
33175         var t = n.getOwnerTree();
33176         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33177         
33178         var cols = t.columns;
33179         var bw = t.borderWidth;
33180         var c = cols[0];
33181         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33182          var cb = typeof a.checked == "boolean";
33183         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33184         var colcls = 'x-t-' + tid + '-c0';
33185         var buf = [
33186             '<li class="x-tree-node">',
33187             
33188                 
33189                 '<div class="x-tree-node-el ', a.cls,'">',
33190                     // extran...
33191                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33192                 
33193                 
33194                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33195                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33196                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33197                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33198                            (a.iconCls ? ' '+a.iconCls : ''),
33199                            '" unselectable="on" />',
33200                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33201                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33202                              
33203                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33204                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33205                             '<span unselectable="on" qtip="' + tx + '">',
33206                              tx,
33207                              '</span></a>' ,
33208                     '</div>',
33209                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33210                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33211                  ];
33212         
33213         for(var i = 1, len = cols.length; i < len; i++){
33214             c = cols[i];
33215             colcls = 'x-t-' + tid + '-c' +i;
33216             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33217             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33218                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33219                       "</div>");
33220          }
33221          
33222          buf.push(
33223             '</a>',
33224             '<div class="x-clear"></div></div>',
33225             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33226             "</li>");
33227         
33228         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33229             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33230                                 n.nextSibling.ui.getEl(), buf.join(""));
33231         }else{
33232             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33233         }
33234         var el = this.wrap.firstChild;
33235         this.elRow = el;
33236         this.elNode = el.firstChild;
33237         this.ranchor = el.childNodes[1];
33238         this.ctNode = this.wrap.childNodes[1];
33239         var cs = el.firstChild.childNodes;
33240         this.indentNode = cs[0];
33241         this.ecNode = cs[1];
33242         this.iconNode = cs[2];
33243         var index = 3;
33244         if(cb){
33245             this.checkbox = cs[3];
33246             index++;
33247         }
33248         this.anchor = cs[index];
33249         
33250         this.textNode = cs[index].firstChild;
33251         
33252         //el.on("click", this.onClick, this);
33253         //el.on("dblclick", this.onDblClick, this);
33254         
33255         
33256        // console.log(this);
33257     },
33258     initEvents : function(){
33259         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33260         
33261             
33262         var a = this.ranchor;
33263
33264         var el = Roo.get(a);
33265
33266         if(Roo.isOpera){ // opera render bug ignores the CSS
33267             el.setStyle("text-decoration", "none");
33268         }
33269
33270         el.on("click", this.onClick, this);
33271         el.on("dblclick", this.onDblClick, this);
33272         el.on("contextmenu", this.onContextMenu, this);
33273         
33274     },
33275     
33276     /*onSelectedChange : function(state){
33277         if(state){
33278             this.focus();
33279             this.addClass("x-tree-selected");
33280         }else{
33281             //this.blur();
33282             this.removeClass("x-tree-selected");
33283         }
33284     },*/
33285     addClass : function(cls){
33286         if(this.elRow){
33287             Roo.fly(this.elRow).addClass(cls);
33288         }
33289         
33290     },
33291     
33292     
33293     removeClass : function(cls){
33294         if(this.elRow){
33295             Roo.fly(this.elRow).removeClass(cls);
33296         }
33297     }
33298
33299     
33300     
33301 });//<Script type="text/javascript">
33302
33303 /*
33304  * Based on:
33305  * Ext JS Library 1.1.1
33306  * Copyright(c) 2006-2007, Ext JS, LLC.
33307  *
33308  * Originally Released Under LGPL - original licence link has changed is not relivant.
33309  *
33310  * Fork - LGPL
33311  * <script type="text/javascript">
33312  */
33313  
33314
33315 /**
33316  * @class Roo.tree.ColumnTree
33317  * @extends Roo.data.TreePanel
33318  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33319  * @cfg {int} borderWidth  compined right/left border allowance
33320  * @constructor
33321  * @param {String/HTMLElement/Element} el The container element
33322  * @param {Object} config
33323  */
33324 Roo.tree.ColumnTree =  function(el, config)
33325 {
33326    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33327    this.addEvents({
33328         /**
33329         * @event resize
33330         * Fire this event on a container when it resizes
33331         * @param {int} w Width
33332         * @param {int} h Height
33333         */
33334        "resize" : true
33335     });
33336     this.on('resize', this.onResize, this);
33337 };
33338
33339 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33340     //lines:false,
33341     
33342     
33343     borderWidth: Roo.isBorderBox ? 0 : 2, 
33344     headEls : false,
33345     
33346     render : function(){
33347         // add the header.....
33348        
33349         Roo.tree.ColumnTree.superclass.render.apply(this);
33350         
33351         this.el.addClass('x-column-tree');
33352         
33353         this.headers = this.el.createChild(
33354             {cls:'x-tree-headers'},this.innerCt.dom);
33355    
33356         var cols = this.columns, c;
33357         var totalWidth = 0;
33358         this.headEls = [];
33359         var  len = cols.length;
33360         for(var i = 0; i < len; i++){
33361              c = cols[i];
33362              totalWidth += c.width;
33363             this.headEls.push(this.headers.createChild({
33364                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33365                  cn: {
33366                      cls:'x-tree-hd-text',
33367                      html: c.header
33368                  },
33369                  style:'width:'+(c.width-this.borderWidth)+'px;'
33370              }));
33371         }
33372         this.headers.createChild({cls:'x-clear'});
33373         // prevent floats from wrapping when clipped
33374         this.headers.setWidth(totalWidth);
33375         //this.innerCt.setWidth(totalWidth);
33376         this.innerCt.setStyle({ overflow: 'auto' });
33377         this.onResize(this.width, this.height);
33378              
33379         
33380     },
33381     onResize : function(w,h)
33382     {
33383         this.height = h;
33384         this.width = w;
33385         // resize cols..
33386         this.innerCt.setWidth(this.width);
33387         this.innerCt.setHeight(this.height-20);
33388         
33389         // headers...
33390         var cols = this.columns, c;
33391         var totalWidth = 0;
33392         var expEl = false;
33393         var len = cols.length;
33394         for(var i = 0; i < len; i++){
33395             c = cols[i];
33396             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33397                 // it's the expander..
33398                 expEl  = this.headEls[i];
33399                 continue;
33400             }
33401             totalWidth += c.width;
33402             
33403         }
33404         if (expEl) {
33405             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33406         }
33407         this.headers.setWidth(w-20);
33408
33409         
33410         
33411         
33412     }
33413 });
33414 /*
33415  * Based on:
33416  * Ext JS Library 1.1.1
33417  * Copyright(c) 2006-2007, Ext JS, LLC.
33418  *
33419  * Originally Released Under LGPL - original licence link has changed is not relivant.
33420  *
33421  * Fork - LGPL
33422  * <script type="text/javascript">
33423  */
33424  
33425 /**
33426  * @class Roo.menu.Menu
33427  * @extends Roo.util.Observable
33428  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33429  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33430  * @constructor
33431  * Creates a new Menu
33432  * @param {Object} config Configuration options
33433  */
33434 Roo.menu.Menu = function(config){
33435     Roo.apply(this, config);
33436     this.id = this.id || Roo.id();
33437     this.addEvents({
33438         /**
33439          * @event beforeshow
33440          * Fires before this menu is displayed
33441          * @param {Roo.menu.Menu} this
33442          */
33443         beforeshow : true,
33444         /**
33445          * @event beforehide
33446          * Fires before this menu is hidden
33447          * @param {Roo.menu.Menu} this
33448          */
33449         beforehide : true,
33450         /**
33451          * @event show
33452          * Fires after this menu is displayed
33453          * @param {Roo.menu.Menu} this
33454          */
33455         show : true,
33456         /**
33457          * @event hide
33458          * Fires after this menu is hidden
33459          * @param {Roo.menu.Menu} this
33460          */
33461         hide : true,
33462         /**
33463          * @event click
33464          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33465          * @param {Roo.menu.Menu} this
33466          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33467          * @param {Roo.EventObject} e
33468          */
33469         click : true,
33470         /**
33471          * @event mouseover
33472          * Fires when the mouse is hovering over this menu
33473          * @param {Roo.menu.Menu} this
33474          * @param {Roo.EventObject} e
33475          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33476          */
33477         mouseover : true,
33478         /**
33479          * @event mouseout
33480          * Fires when the mouse exits this menu
33481          * @param {Roo.menu.Menu} this
33482          * @param {Roo.EventObject} e
33483          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33484          */
33485         mouseout : true,
33486         /**
33487          * @event itemclick
33488          * Fires when a menu item contained in this menu is clicked
33489          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33490          * @param {Roo.EventObject} e
33491          */
33492         itemclick: true
33493     });
33494     if (this.registerMenu) {
33495         Roo.menu.MenuMgr.register(this);
33496     }
33497     
33498     var mis = this.items;
33499     this.items = new Roo.util.MixedCollection();
33500     if(mis){
33501         this.add.apply(this, mis);
33502     }
33503 };
33504
33505 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33506     /**
33507      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33508      */
33509     minWidth : 120,
33510     /**
33511      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33512      * for bottom-right shadow (defaults to "sides")
33513      */
33514     shadow : "sides",
33515     /**
33516      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33517      * this menu (defaults to "tl-tr?")
33518      */
33519     subMenuAlign : "tl-tr?",
33520     /**
33521      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33522      * relative to its element of origin (defaults to "tl-bl?")
33523      */
33524     defaultAlign : "tl-bl?",
33525     /**
33526      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33527      */
33528     allowOtherMenus : false,
33529     /**
33530      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33531      */
33532     registerMenu : true,
33533
33534     hidden:true,
33535
33536     // private
33537     render : function(){
33538         if(this.el){
33539             return;
33540         }
33541         var el = this.el = new Roo.Layer({
33542             cls: "x-menu",
33543             shadow:this.shadow,
33544             constrain: false,
33545             parentEl: this.parentEl || document.body,
33546             zindex:15000
33547         });
33548
33549         this.keyNav = new Roo.menu.MenuNav(this);
33550
33551         if(this.plain){
33552             el.addClass("x-menu-plain");
33553         }
33554         if(this.cls){
33555             el.addClass(this.cls);
33556         }
33557         // generic focus element
33558         this.focusEl = el.createChild({
33559             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33560         });
33561         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33562         ul.on("click", this.onClick, this);
33563         ul.on("mouseover", this.onMouseOver, this);
33564         ul.on("mouseout", this.onMouseOut, this);
33565         this.items.each(function(item){
33566             var li = document.createElement("li");
33567             li.className = "x-menu-list-item";
33568             ul.dom.appendChild(li);
33569             item.render(li, this);
33570         }, this);
33571         this.ul = ul;
33572         this.autoWidth();
33573     },
33574
33575     // private
33576     autoWidth : function(){
33577         var el = this.el, ul = this.ul;
33578         if(!el){
33579             return;
33580         }
33581         var w = this.width;
33582         if(w){
33583             el.setWidth(w);
33584         }else if(Roo.isIE){
33585             el.setWidth(this.minWidth);
33586             var t = el.dom.offsetWidth; // force recalc
33587             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33588         }
33589     },
33590
33591     // private
33592     delayAutoWidth : function(){
33593         if(this.rendered){
33594             if(!this.awTask){
33595                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33596             }
33597             this.awTask.delay(20);
33598         }
33599     },
33600
33601     // private
33602     findTargetItem : function(e){
33603         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33604         if(t && t.menuItemId){
33605             return this.items.get(t.menuItemId);
33606         }
33607     },
33608
33609     // private
33610     onClick : function(e){
33611         var t;
33612         if(t = this.findTargetItem(e)){
33613             t.onClick(e);
33614             this.fireEvent("click", this, t, e);
33615         }
33616     },
33617
33618     // private
33619     setActiveItem : function(item, autoExpand){
33620         if(item != this.activeItem){
33621             if(this.activeItem){
33622                 this.activeItem.deactivate();
33623             }
33624             this.activeItem = item;
33625             item.activate(autoExpand);
33626         }else if(autoExpand){
33627             item.expandMenu();
33628         }
33629     },
33630
33631     // private
33632     tryActivate : function(start, step){
33633         var items = this.items;
33634         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33635             var item = items.get(i);
33636             if(!item.disabled && item.canActivate){
33637                 this.setActiveItem(item, false);
33638                 return item;
33639             }
33640         }
33641         return false;
33642     },
33643
33644     // private
33645     onMouseOver : function(e){
33646         var t;
33647         if(t = this.findTargetItem(e)){
33648             if(t.canActivate && !t.disabled){
33649                 this.setActiveItem(t, true);
33650             }
33651         }
33652         this.fireEvent("mouseover", this, e, t);
33653     },
33654
33655     // private
33656     onMouseOut : function(e){
33657         var t;
33658         if(t = this.findTargetItem(e)){
33659             if(t == this.activeItem && t.shouldDeactivate(e)){
33660                 this.activeItem.deactivate();
33661                 delete this.activeItem;
33662             }
33663         }
33664         this.fireEvent("mouseout", this, e, t);
33665     },
33666
33667     /**
33668      * Read-only.  Returns true if the menu is currently displayed, else false.
33669      * @type Boolean
33670      */
33671     isVisible : function(){
33672         return this.el && !this.hidden;
33673     },
33674
33675     /**
33676      * Displays this menu relative to another element
33677      * @param {String/HTMLElement/Roo.Element} element The element to align to
33678      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33679      * the element (defaults to this.defaultAlign)
33680      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33681      */
33682     show : function(el, pos, parentMenu){
33683         this.parentMenu = parentMenu;
33684         if(!this.el){
33685             this.render();
33686         }
33687         this.fireEvent("beforeshow", this);
33688         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33689     },
33690
33691     /**
33692      * Displays this menu at a specific xy position
33693      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33695      */
33696     showAt : function(xy, parentMenu, /* private: */_e){
33697         this.parentMenu = parentMenu;
33698         if(!this.el){
33699             this.render();
33700         }
33701         if(_e !== false){
33702             this.fireEvent("beforeshow", this);
33703             xy = this.el.adjustForConstraints(xy);
33704         }
33705         this.el.setXY(xy);
33706         this.el.show();
33707         this.hidden = false;
33708         this.focus();
33709         this.fireEvent("show", this);
33710     },
33711
33712     focus : function(){
33713         if(!this.hidden){
33714             this.doFocus.defer(50, this);
33715         }
33716     },
33717
33718     doFocus : function(){
33719         if(!this.hidden){
33720             this.focusEl.focus();
33721         }
33722     },
33723
33724     /**
33725      * Hides this menu and optionally all parent menus
33726      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33727      */
33728     hide : function(deep){
33729         if(this.el && this.isVisible()){
33730             this.fireEvent("beforehide", this);
33731             if(this.activeItem){
33732                 this.activeItem.deactivate();
33733                 this.activeItem = null;
33734             }
33735             this.el.hide();
33736             this.hidden = true;
33737             this.fireEvent("hide", this);
33738         }
33739         if(deep === true && this.parentMenu){
33740             this.parentMenu.hide(true);
33741         }
33742     },
33743
33744     /**
33745      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33746      * Any of the following are valid:
33747      * <ul>
33748      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33749      * <li>An HTMLElement object which will be converted to a menu item</li>
33750      * <li>A menu item config object that will be created as a new menu item</li>
33751      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33752      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33753      * </ul>
33754      * Usage:
33755      * <pre><code>
33756 // Create the menu
33757 var menu = new Roo.menu.Menu();
33758
33759 // Create a menu item to add by reference
33760 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33761
33762 // Add a bunch of items at once using different methods.
33763 // Only the last item added will be returned.
33764 var item = menu.add(
33765     menuItem,                // add existing item by ref
33766     'Dynamic Item',          // new TextItem
33767     '-',                     // new separator
33768     { text: 'Config Item' }  // new item by config
33769 );
33770 </code></pre>
33771      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33772      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33773      */
33774     add : function(){
33775         var a = arguments, l = a.length, item;
33776         for(var i = 0; i < l; i++){
33777             var el = a[i];
33778             if(el.render){ // some kind of Item
33779                 item = this.addItem(el);
33780             }else if(typeof el == "string"){ // string
33781                 if(el == "separator" || el == "-"){
33782                     item = this.addSeparator();
33783                 }else{
33784                     item = this.addText(el);
33785                 }
33786             }else if(el.tagName || el.el){ // element
33787                 item = this.addElement(el);
33788             }else if(typeof el == "object"){ // must be menu item config?
33789                 item = this.addMenuItem(el);
33790             }
33791         }
33792         return item;
33793     },
33794
33795     /**
33796      * Returns this menu's underlying {@link Roo.Element} object
33797      * @return {Roo.Element} The element
33798      */
33799     getEl : function(){
33800         if(!this.el){
33801             this.render();
33802         }
33803         return this.el;
33804     },
33805
33806     /**
33807      * Adds a separator bar to the menu
33808      * @return {Roo.menu.Item} The menu item that was added
33809      */
33810     addSeparator : function(){
33811         return this.addItem(new Roo.menu.Separator());
33812     },
33813
33814     /**
33815      * Adds an {@link Roo.Element} object to the menu
33816      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33817      * @return {Roo.menu.Item} The menu item that was added
33818      */
33819     addElement : function(el){
33820         return this.addItem(new Roo.menu.BaseItem(el));
33821     },
33822
33823     /**
33824      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33825      * @param {Roo.menu.Item} item The menu item to add
33826      * @return {Roo.menu.Item} The menu item that was added
33827      */
33828     addItem : function(item){
33829         this.items.add(item);
33830         if(this.ul){
33831             var li = document.createElement("li");
33832             li.className = "x-menu-list-item";
33833             this.ul.dom.appendChild(li);
33834             item.render(li, this);
33835             this.delayAutoWidth();
33836         }
33837         return item;
33838     },
33839
33840     /**
33841      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33842      * @param {Object} config A MenuItem config object
33843      * @return {Roo.menu.Item} The menu item that was added
33844      */
33845     addMenuItem : function(config){
33846         if(!(config instanceof Roo.menu.Item)){
33847             if(typeof config.checked == "boolean"){ // must be check menu item config?
33848                 config = new Roo.menu.CheckItem(config);
33849             }else{
33850                 config = new Roo.menu.Item(config);
33851             }
33852         }
33853         return this.addItem(config);
33854     },
33855
33856     /**
33857      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33858      * @param {String} text The text to display in the menu item
33859      * @return {Roo.menu.Item} The menu item that was added
33860      */
33861     addText : function(text){
33862         return this.addItem(new Roo.menu.TextItem(text));
33863     },
33864
33865     /**
33866      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33867      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33868      * @param {Roo.menu.Item} item The menu item to add
33869      * @return {Roo.menu.Item} The menu item that was added
33870      */
33871     insert : function(index, item){
33872         this.items.insert(index, item);
33873         if(this.ul){
33874             var li = document.createElement("li");
33875             li.className = "x-menu-list-item";
33876             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33877             item.render(li, this);
33878             this.delayAutoWidth();
33879         }
33880         return item;
33881     },
33882
33883     /**
33884      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33885      * @param {Roo.menu.Item} item The menu item to remove
33886      */
33887     remove : function(item){
33888         this.items.removeKey(item.id);
33889         item.destroy();
33890     },
33891
33892     /**
33893      * Removes and destroys all items in the menu
33894      */
33895     removeAll : function(){
33896         var f;
33897         while(f = this.items.first()){
33898             this.remove(f);
33899         }
33900     }
33901 });
33902
33903 // MenuNav is a private utility class used internally by the Menu
33904 Roo.menu.MenuNav = function(menu){
33905     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33906     this.scope = this.menu = menu;
33907 };
33908
33909 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33910     doRelay : function(e, h){
33911         var k = e.getKey();
33912         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33913             this.menu.tryActivate(0, 1);
33914             return false;
33915         }
33916         return h.call(this.scope || this, e, this.menu);
33917     },
33918
33919     up : function(e, m){
33920         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33921             m.tryActivate(m.items.length-1, -1);
33922         }
33923     },
33924
33925     down : function(e, m){
33926         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33927             m.tryActivate(0, 1);
33928         }
33929     },
33930
33931     right : function(e, m){
33932         if(m.activeItem){
33933             m.activeItem.expandMenu(true);
33934         }
33935     },
33936
33937     left : function(e, m){
33938         m.hide();
33939         if(m.parentMenu && m.parentMenu.activeItem){
33940             m.parentMenu.activeItem.activate();
33941         }
33942     },
33943
33944     enter : function(e, m){
33945         if(m.activeItem){
33946             e.stopPropagation();
33947             m.activeItem.onClick(e);
33948             m.fireEvent("click", this, m.activeItem);
33949             return true;
33950         }
33951     }
33952 });/*
33953  * Based on:
33954  * Ext JS Library 1.1.1
33955  * Copyright(c) 2006-2007, Ext JS, LLC.
33956  *
33957  * Originally Released Under LGPL - original licence link has changed is not relivant.
33958  *
33959  * Fork - LGPL
33960  * <script type="text/javascript">
33961  */
33962  
33963 /**
33964  * @class Roo.menu.MenuMgr
33965  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33966  * @singleton
33967  */
33968 Roo.menu.MenuMgr = function(){
33969    var menus, active, groups = {}, attached = false, lastShow = new Date();
33970
33971    // private - called when first menu is created
33972    function init(){
33973        menus = {};
33974        active = new Roo.util.MixedCollection();
33975        Roo.get(document).addKeyListener(27, function(){
33976            if(active.length > 0){
33977                hideAll();
33978            }
33979        });
33980    }
33981
33982    // private
33983    function hideAll(){
33984        if(active && active.length > 0){
33985            var c = active.clone();
33986            c.each(function(m){
33987                m.hide();
33988            });
33989        }
33990    }
33991
33992    // private
33993    function onHide(m){
33994        active.remove(m);
33995        if(active.length < 1){
33996            Roo.get(document).un("mousedown", onMouseDown);
33997            attached = false;
33998        }
33999    }
34000
34001    // private
34002    function onShow(m){
34003        var last = active.last();
34004        lastShow = new Date();
34005        active.add(m);
34006        if(!attached){
34007            Roo.get(document).on("mousedown", onMouseDown);
34008            attached = true;
34009        }
34010        if(m.parentMenu){
34011           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34012           m.parentMenu.activeChild = m;
34013        }else if(last && last.isVisible()){
34014           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34015        }
34016    }
34017
34018    // private
34019    function onBeforeHide(m){
34020        if(m.activeChild){
34021            m.activeChild.hide();
34022        }
34023        if(m.autoHideTimer){
34024            clearTimeout(m.autoHideTimer);
34025            delete m.autoHideTimer;
34026        }
34027    }
34028
34029    // private
34030    function onBeforeShow(m){
34031        var pm = m.parentMenu;
34032        if(!pm && !m.allowOtherMenus){
34033            hideAll();
34034        }else if(pm && pm.activeChild && active != m){
34035            pm.activeChild.hide();
34036        }
34037    }
34038
34039    // private
34040    function onMouseDown(e){
34041        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34042            hideAll();
34043        }
34044    }
34045
34046    // private
34047    function onBeforeCheck(mi, state){
34048        if(state){
34049            var g = groups[mi.group];
34050            for(var i = 0, l = g.length; i < l; i++){
34051                if(g[i] != mi){
34052                    g[i].setChecked(false);
34053                }
34054            }
34055        }
34056    }
34057
34058    return {
34059
34060        /**
34061         * Hides all menus that are currently visible
34062         */
34063        hideAll : function(){
34064             hideAll();  
34065        },
34066
34067        // private
34068        register : function(menu){
34069            if(!menus){
34070                init();
34071            }
34072            menus[menu.id] = menu;
34073            menu.on("beforehide", onBeforeHide);
34074            menu.on("hide", onHide);
34075            menu.on("beforeshow", onBeforeShow);
34076            menu.on("show", onShow);
34077            var g = menu.group;
34078            if(g && menu.events["checkchange"]){
34079                if(!groups[g]){
34080                    groups[g] = [];
34081                }
34082                groups[g].push(menu);
34083                menu.on("checkchange", onCheck);
34084            }
34085        },
34086
34087         /**
34088          * Returns a {@link Roo.menu.Menu} object
34089          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34090          * be used to generate and return a new Menu instance.
34091          */
34092        get : function(menu){
34093            if(typeof menu == "string"){ // menu id
34094                return menus[menu];
34095            }else if(menu.events){  // menu instance
34096                return menu;
34097            }else if(typeof menu.length == 'number'){ // array of menu items?
34098                return new Roo.menu.Menu({items:menu});
34099            }else{ // otherwise, must be a config
34100                return new Roo.menu.Menu(menu);
34101            }
34102        },
34103
34104        // private
34105        unregister : function(menu){
34106            delete menus[menu.id];
34107            menu.un("beforehide", onBeforeHide);
34108            menu.un("hide", onHide);
34109            menu.un("beforeshow", onBeforeShow);
34110            menu.un("show", onShow);
34111            var g = menu.group;
34112            if(g && menu.events["checkchange"]){
34113                groups[g].remove(menu);
34114                menu.un("checkchange", onCheck);
34115            }
34116        },
34117
34118        // private
34119        registerCheckable : function(menuItem){
34120            var g = menuItem.group;
34121            if(g){
34122                if(!groups[g]){
34123                    groups[g] = [];
34124                }
34125                groups[g].push(menuItem);
34126                menuItem.on("beforecheckchange", onBeforeCheck);
34127            }
34128        },
34129
34130        // private
34131        unregisterCheckable : function(menuItem){
34132            var g = menuItem.group;
34133            if(g){
34134                groups[g].remove(menuItem);
34135                menuItem.un("beforecheckchange", onBeforeCheck);
34136            }
34137        }
34138    };
34139 }();/*
34140  * Based on:
34141  * Ext JS Library 1.1.1
34142  * Copyright(c) 2006-2007, Ext JS, LLC.
34143  *
34144  * Originally Released Under LGPL - original licence link has changed is not relivant.
34145  *
34146  * Fork - LGPL
34147  * <script type="text/javascript">
34148  */
34149  
34150
34151 /**
34152  * @class Roo.menu.BaseItem
34153  * @extends Roo.Component
34154  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34155  * management and base configuration options shared by all menu components.
34156  * @constructor
34157  * Creates a new BaseItem
34158  * @param {Object} config Configuration options
34159  */
34160 Roo.menu.BaseItem = function(config){
34161     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34162
34163     this.addEvents({
34164         /**
34165          * @event click
34166          * Fires when this item is clicked
34167          * @param {Roo.menu.BaseItem} this
34168          * @param {Roo.EventObject} e
34169          */
34170         click: true,
34171         /**
34172          * @event activate
34173          * Fires when this item is activated
34174          * @param {Roo.menu.BaseItem} this
34175          */
34176         activate : true,
34177         /**
34178          * @event deactivate
34179          * Fires when this item is deactivated
34180          * @param {Roo.menu.BaseItem} this
34181          */
34182         deactivate : true
34183     });
34184
34185     if(this.handler){
34186         this.on("click", this.handler, this.scope, true);
34187     }
34188 };
34189
34190 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34191     /**
34192      * @cfg {Function} handler
34193      * A function that will handle the click event of this menu item (defaults to undefined)
34194      */
34195     /**
34196      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34197      */
34198     canActivate : false,
34199     /**
34200      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34201      */
34202     activeClass : "x-menu-item-active",
34203     /**
34204      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34205      */
34206     hideOnClick : true,
34207     /**
34208      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34209      */
34210     hideDelay : 100,
34211
34212     // private
34213     ctype: "Roo.menu.BaseItem",
34214
34215     // private
34216     actionMode : "container",
34217
34218     // private
34219     render : function(container, parentMenu){
34220         this.parentMenu = parentMenu;
34221         Roo.menu.BaseItem.superclass.render.call(this, container);
34222         this.container.menuItemId = this.id;
34223     },
34224
34225     // private
34226     onRender : function(container, position){
34227         this.el = Roo.get(this.el);
34228         container.dom.appendChild(this.el.dom);
34229     },
34230
34231     // private
34232     onClick : function(e){
34233         if(!this.disabled && this.fireEvent("click", this, e) !== false
34234                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34235             this.handleClick(e);
34236         }else{
34237             e.stopEvent();
34238         }
34239     },
34240
34241     // private
34242     activate : function(){
34243         if(this.disabled){
34244             return false;
34245         }
34246         var li = this.container;
34247         li.addClass(this.activeClass);
34248         this.region = li.getRegion().adjust(2, 2, -2, -2);
34249         this.fireEvent("activate", this);
34250         return true;
34251     },
34252
34253     // private
34254     deactivate : function(){
34255         this.container.removeClass(this.activeClass);
34256         this.fireEvent("deactivate", this);
34257     },
34258
34259     // private
34260     shouldDeactivate : function(e){
34261         return !this.region || !this.region.contains(e.getPoint());
34262     },
34263
34264     // private
34265     handleClick : function(e){
34266         if(this.hideOnClick){
34267             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34268         }
34269     },
34270
34271     // private
34272     expandMenu : function(autoActivate){
34273         // do nothing
34274     },
34275
34276     // private
34277     hideMenu : function(){
34278         // do nothing
34279     }
34280 });/*
34281  * Based on:
34282  * Ext JS Library 1.1.1
34283  * Copyright(c) 2006-2007, Ext JS, LLC.
34284  *
34285  * Originally Released Under LGPL - original licence link has changed is not relivant.
34286  *
34287  * Fork - LGPL
34288  * <script type="text/javascript">
34289  */
34290  
34291 /**
34292  * @class Roo.menu.Adapter
34293  * @extends Roo.menu.BaseItem
34294  * 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.
34295  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34296  * @constructor
34297  * Creates a new Adapter
34298  * @param {Object} config Configuration options
34299  */
34300 Roo.menu.Adapter = function(component, config){
34301     Roo.menu.Adapter.superclass.constructor.call(this, config);
34302     this.component = component;
34303 };
34304 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34305     // private
34306     canActivate : true,
34307
34308     // private
34309     onRender : function(container, position){
34310         this.component.render(container);
34311         this.el = this.component.getEl();
34312     },
34313
34314     // private
34315     activate : function(){
34316         if(this.disabled){
34317             return false;
34318         }
34319         this.component.focus();
34320         this.fireEvent("activate", this);
34321         return true;
34322     },
34323
34324     // private
34325     deactivate : function(){
34326         this.fireEvent("deactivate", this);
34327     },
34328
34329     // private
34330     disable : function(){
34331         this.component.disable();
34332         Roo.menu.Adapter.superclass.disable.call(this);
34333     },
34334
34335     // private
34336     enable : function(){
34337         this.component.enable();
34338         Roo.menu.Adapter.superclass.enable.call(this);
34339     }
34340 });/*
34341  * Based on:
34342  * Ext JS Library 1.1.1
34343  * Copyright(c) 2006-2007, Ext JS, LLC.
34344  *
34345  * Originally Released Under LGPL - original licence link has changed is not relivant.
34346  *
34347  * Fork - LGPL
34348  * <script type="text/javascript">
34349  */
34350
34351 /**
34352  * @class Roo.menu.TextItem
34353  * @extends Roo.menu.BaseItem
34354  * Adds a static text string to a menu, usually used as either a heading or group separator.
34355  * @constructor
34356  * Creates a new TextItem
34357  * @param {String} text The text to display
34358  */
34359 Roo.menu.TextItem = function(text){
34360     this.text = text;
34361     Roo.menu.TextItem.superclass.constructor.call(this);
34362 };
34363
34364 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34365     /**
34366      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34367      */
34368     hideOnClick : false,
34369     /**
34370      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34371      */
34372     itemCls : "x-menu-text",
34373
34374     // private
34375     onRender : function(){
34376         var s = document.createElement("span");
34377         s.className = this.itemCls;
34378         s.innerHTML = this.text;
34379         this.el = s;
34380         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34381     }
34382 });/*
34383  * Based on:
34384  * Ext JS Library 1.1.1
34385  * Copyright(c) 2006-2007, Ext JS, LLC.
34386  *
34387  * Originally Released Under LGPL - original licence link has changed is not relivant.
34388  *
34389  * Fork - LGPL
34390  * <script type="text/javascript">
34391  */
34392
34393 /**
34394  * @class Roo.menu.Separator
34395  * @extends Roo.menu.BaseItem
34396  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34397  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34398  * @constructor
34399  * @param {Object} config Configuration options
34400  */
34401 Roo.menu.Separator = function(config){
34402     Roo.menu.Separator.superclass.constructor.call(this, config);
34403 };
34404
34405 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34406     /**
34407      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34408      */
34409     itemCls : "x-menu-sep",
34410     /**
34411      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34412      */
34413     hideOnClick : false,
34414
34415     // private
34416     onRender : function(li){
34417         var s = document.createElement("span");
34418         s.className = this.itemCls;
34419         s.innerHTML = "&#160;";
34420         this.el = s;
34421         li.addClass("x-menu-sep-li");
34422         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34423     }
34424 });/*
34425  * Based on:
34426  * Ext JS Library 1.1.1
34427  * Copyright(c) 2006-2007, Ext JS, LLC.
34428  *
34429  * Originally Released Under LGPL - original licence link has changed is not relivant.
34430  *
34431  * Fork - LGPL
34432  * <script type="text/javascript">
34433  */
34434 /**
34435  * @class Roo.menu.Item
34436  * @extends Roo.menu.BaseItem
34437  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34438  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34439  * activation and click handling.
34440  * @constructor
34441  * Creates a new Item
34442  * @param {Object} config Configuration options
34443  */
34444 Roo.menu.Item = function(config){
34445     Roo.menu.Item.superclass.constructor.call(this, config);
34446     if(this.menu){
34447         this.menu = Roo.menu.MenuMgr.get(this.menu);
34448     }
34449 };
34450 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34451     /**
34452      * @cfg {String} icon
34453      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34454      */
34455     /**
34456      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34457      */
34458     itemCls : "x-menu-item",
34459     /**
34460      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34461      */
34462     canActivate : true,
34463     /**
34464      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34465      */
34466     showDelay: 200,
34467     // doc'd in BaseItem
34468     hideDelay: 200,
34469
34470     // private
34471     ctype: "Roo.menu.Item",
34472     
34473     // private
34474     onRender : function(container, position){
34475         var el = document.createElement("a");
34476         el.hideFocus = true;
34477         el.unselectable = "on";
34478         el.href = this.href || "#";
34479         if(this.hrefTarget){
34480             el.target = this.hrefTarget;
34481         }
34482         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34483         el.innerHTML = String.format(
34484                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34485                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34486         this.el = el;
34487         Roo.menu.Item.superclass.onRender.call(this, container, position);
34488     },
34489
34490     /**
34491      * Sets the text to display in this menu item
34492      * @param {String} text The text to display
34493      */
34494     setText : function(text){
34495         this.text = text;
34496         if(this.rendered){
34497             this.el.update(String.format(
34498                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34499                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34500             this.parentMenu.autoWidth();
34501         }
34502     },
34503
34504     // private
34505     handleClick : function(e){
34506         if(!this.href){ // if no link defined, stop the event automatically
34507             e.stopEvent();
34508         }
34509         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34510     },
34511
34512     // private
34513     activate : function(autoExpand){
34514         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34515             this.focus();
34516             if(autoExpand){
34517                 this.expandMenu();
34518             }
34519         }
34520         return true;
34521     },
34522
34523     // private
34524     shouldDeactivate : function(e){
34525         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34526             if(this.menu && this.menu.isVisible()){
34527                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34528             }
34529             return true;
34530         }
34531         return false;
34532     },
34533
34534     // private
34535     deactivate : function(){
34536         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34537         this.hideMenu();
34538     },
34539
34540     // private
34541     expandMenu : function(autoActivate){
34542         if(!this.disabled && this.menu){
34543             clearTimeout(this.hideTimer);
34544             delete this.hideTimer;
34545             if(!this.menu.isVisible() && !this.showTimer){
34546                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34547             }else if (this.menu.isVisible() && autoActivate){
34548                 this.menu.tryActivate(0, 1);
34549             }
34550         }
34551     },
34552
34553     // private
34554     deferExpand : function(autoActivate){
34555         delete this.showTimer;
34556         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34557         if(autoActivate){
34558             this.menu.tryActivate(0, 1);
34559         }
34560     },
34561
34562     // private
34563     hideMenu : function(){
34564         clearTimeout(this.showTimer);
34565         delete this.showTimer;
34566         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34567             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34568         }
34569     },
34570
34571     // private
34572     deferHide : function(){
34573         delete this.hideTimer;
34574         this.menu.hide();
34575     }
34576 });/*
34577  * Based on:
34578  * Ext JS Library 1.1.1
34579  * Copyright(c) 2006-2007, Ext JS, LLC.
34580  *
34581  * Originally Released Under LGPL - original licence link has changed is not relivant.
34582  *
34583  * Fork - LGPL
34584  * <script type="text/javascript">
34585  */
34586  
34587 /**
34588  * @class Roo.menu.CheckItem
34589  * @extends Roo.menu.Item
34590  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34591  * @constructor
34592  * Creates a new CheckItem
34593  * @param {Object} config Configuration options
34594  */
34595 Roo.menu.CheckItem = function(config){
34596     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34597     this.addEvents({
34598         /**
34599          * @event beforecheckchange
34600          * Fires before the checked value is set, providing an opportunity to cancel if needed
34601          * @param {Roo.menu.CheckItem} this
34602          * @param {Boolean} checked The new checked value that will be set
34603          */
34604         "beforecheckchange" : true,
34605         /**
34606          * @event checkchange
34607          * Fires after the checked value has been set
34608          * @param {Roo.menu.CheckItem} this
34609          * @param {Boolean} checked The checked value that was set
34610          */
34611         "checkchange" : true
34612     });
34613     if(this.checkHandler){
34614         this.on('checkchange', this.checkHandler, this.scope);
34615     }
34616 };
34617 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34618     /**
34619      * @cfg {String} group
34620      * All check items with the same group name will automatically be grouped into a single-select
34621      * radio button group (defaults to '')
34622      */
34623     /**
34624      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34625      */
34626     itemCls : "x-menu-item x-menu-check-item",
34627     /**
34628      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34629      */
34630     groupClass : "x-menu-group-item",
34631
34632     /**
34633      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34634      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34635      * initialized with checked = true will be rendered as checked.
34636      */
34637     checked: false,
34638
34639     // private
34640     ctype: "Roo.menu.CheckItem",
34641
34642     // private
34643     onRender : function(c){
34644         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34645         if(this.group){
34646             this.el.addClass(this.groupClass);
34647         }
34648         Roo.menu.MenuMgr.registerCheckable(this);
34649         if(this.checked){
34650             this.checked = false;
34651             this.setChecked(true, true);
34652         }
34653     },
34654
34655     // private
34656     destroy : function(){
34657         if(this.rendered){
34658             Roo.menu.MenuMgr.unregisterCheckable(this);
34659         }
34660         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34661     },
34662
34663     /**
34664      * Set the checked state of this item
34665      * @param {Boolean} checked The new checked value
34666      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34667      */
34668     setChecked : function(state, suppressEvent){
34669         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34670             if(this.container){
34671                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34672             }
34673             this.checked = state;
34674             if(suppressEvent !== true){
34675                 this.fireEvent("checkchange", this, state);
34676             }
34677         }
34678     },
34679
34680     // private
34681     handleClick : function(e){
34682        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34683            this.setChecked(!this.checked);
34684        }
34685        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34686     }
34687 });/*
34688  * Based on:
34689  * Ext JS Library 1.1.1
34690  * Copyright(c) 2006-2007, Ext JS, LLC.
34691  *
34692  * Originally Released Under LGPL - original licence link has changed is not relivant.
34693  *
34694  * Fork - LGPL
34695  * <script type="text/javascript">
34696  */
34697  
34698 /**
34699  * @class Roo.menu.DateItem
34700  * @extends Roo.menu.Adapter
34701  * A menu item that wraps the {@link Roo.DatPicker} component.
34702  * @constructor
34703  * Creates a new DateItem
34704  * @param {Object} config Configuration options
34705  */
34706 Roo.menu.DateItem = function(config){
34707     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34708     /** The Roo.DatePicker object @type Roo.DatePicker */
34709     this.picker = this.component;
34710     this.addEvents({select: true});
34711     
34712     this.picker.on("render", function(picker){
34713         picker.getEl().swallowEvent("click");
34714         picker.container.addClass("x-menu-date-item");
34715     });
34716
34717     this.picker.on("select", this.onSelect, this);
34718 };
34719
34720 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34721     // private
34722     onSelect : function(picker, date){
34723         this.fireEvent("select", this, date, picker);
34724         Roo.menu.DateItem.superclass.handleClick.call(this);
34725     }
34726 });/*
34727  * Based on:
34728  * Ext JS Library 1.1.1
34729  * Copyright(c) 2006-2007, Ext JS, LLC.
34730  *
34731  * Originally Released Under LGPL - original licence link has changed is not relivant.
34732  *
34733  * Fork - LGPL
34734  * <script type="text/javascript">
34735  */
34736  
34737 /**
34738  * @class Roo.menu.ColorItem
34739  * @extends Roo.menu.Adapter
34740  * A menu item that wraps the {@link Roo.ColorPalette} component.
34741  * @constructor
34742  * Creates a new ColorItem
34743  * @param {Object} config Configuration options
34744  */
34745 Roo.menu.ColorItem = function(config){
34746     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34747     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34748     this.palette = this.component;
34749     this.relayEvents(this.palette, ["select"]);
34750     if(this.selectHandler){
34751         this.on('select', this.selectHandler, this.scope);
34752     }
34753 };
34754 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34755  * Based on:
34756  * Ext JS Library 1.1.1
34757  * Copyright(c) 2006-2007, Ext JS, LLC.
34758  *
34759  * Originally Released Under LGPL - original licence link has changed is not relivant.
34760  *
34761  * Fork - LGPL
34762  * <script type="text/javascript">
34763  */
34764  
34765
34766 /**
34767  * @class Roo.menu.DateMenu
34768  * @extends Roo.menu.Menu
34769  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34770  * @constructor
34771  * Creates a new DateMenu
34772  * @param {Object} config Configuration options
34773  */
34774 Roo.menu.DateMenu = function(config){
34775     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34776     this.plain = true;
34777     var di = new Roo.menu.DateItem(config);
34778     this.add(di);
34779     /**
34780      * The {@link Roo.DatePicker} instance for this DateMenu
34781      * @type DatePicker
34782      */
34783     this.picker = di.picker;
34784     /**
34785      * @event select
34786      * @param {DatePicker} picker
34787      * @param {Date} date
34788      */
34789     this.relayEvents(di, ["select"]);
34790
34791     this.on('beforeshow', function(){
34792         if(this.picker){
34793             this.picker.hideMonthPicker(true);
34794         }
34795     }, this);
34796 };
34797 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34798     cls:'x-date-menu'
34799 });/*
34800  * Based on:
34801  * Ext JS Library 1.1.1
34802  * Copyright(c) 2006-2007, Ext JS, LLC.
34803  *
34804  * Originally Released Under LGPL - original licence link has changed is not relivant.
34805  *
34806  * Fork - LGPL
34807  * <script type="text/javascript">
34808  */
34809  
34810
34811 /**
34812  * @class Roo.menu.ColorMenu
34813  * @extends Roo.menu.Menu
34814  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34815  * @constructor
34816  * Creates a new ColorMenu
34817  * @param {Object} config Configuration options
34818  */
34819 Roo.menu.ColorMenu = function(config){
34820     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34821     this.plain = true;
34822     var ci = new Roo.menu.ColorItem(config);
34823     this.add(ci);
34824     /**
34825      * The {@link Roo.ColorPalette} instance for this ColorMenu
34826      * @type ColorPalette
34827      */
34828     this.palette = ci.palette;
34829     /**
34830      * @event select
34831      * @param {ColorPalette} palette
34832      * @param {String} color
34833      */
34834     this.relayEvents(ci, ["select"]);
34835 };
34836 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34837  * Based on:
34838  * Ext JS Library 1.1.1
34839  * Copyright(c) 2006-2007, Ext JS, LLC.
34840  *
34841  * Originally Released Under LGPL - original licence link has changed is not relivant.
34842  *
34843  * Fork - LGPL
34844  * <script type="text/javascript">
34845  */
34846  
34847 /**
34848  * @class Roo.form.Field
34849  * @extends Roo.BoxComponent
34850  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34851  * @constructor
34852  * Creates a new Field
34853  * @param {Object} config Configuration options
34854  */
34855 Roo.form.Field = function(config){
34856     Roo.form.Field.superclass.constructor.call(this, config);
34857 };
34858
34859 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34860     /**
34861      * @cfg {String} fieldLabel Label to use when rendering a form.
34862      */
34863        /**
34864      * @cfg {String} qtip Mouse over tip
34865      */
34866      
34867     /**
34868      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34869      */
34870     invalidClass : "x-form-invalid",
34871     /**
34872      * @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")
34873      */
34874     invalidText : "The value in this field is invalid",
34875     /**
34876      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34877      */
34878     focusClass : "x-form-focus",
34879     /**
34880      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34881       automatic validation (defaults to "keyup").
34882      */
34883     validationEvent : "keyup",
34884     /**
34885      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34886      */
34887     validateOnBlur : true,
34888     /**
34889      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34890      */
34891     validationDelay : 250,
34892     /**
34893      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34894      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34895      */
34896     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34897     /**
34898      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34899      */
34900     fieldClass : "x-form-field",
34901     /**
34902      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34903      *<pre>
34904 Value         Description
34905 -----------   ----------------------------------------------------------------------
34906 qtip          Display a quick tip when the user hovers over the field
34907 title         Display a default browser title attribute popup
34908 under         Add a block div beneath the field containing the error text
34909 side          Add an error icon to the right of the field with a popup on hover
34910 [element id]  Add the error text directly to the innerHTML of the specified element
34911 </pre>
34912      */
34913     msgTarget : 'qtip',
34914     /**
34915      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34916      */
34917     msgFx : 'normal',
34918
34919     /**
34920      * @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.
34921      */
34922     readOnly : false,
34923
34924     /**
34925      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34926      */
34927     disabled : false,
34928
34929     /**
34930      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34931      */
34932     inputType : undefined,
34933     
34934     /**
34935      * @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).
34936          */
34937         tabIndex : undefined,
34938         
34939     // private
34940     isFormField : true,
34941
34942     // private
34943     hasFocus : false,
34944     /**
34945      * @property {Roo.Element} fieldEl
34946      * Element Containing the rendered Field (with label etc.)
34947      */
34948     /**
34949      * @cfg {Mixed} value A value to initialize this field with.
34950      */
34951     value : undefined,
34952
34953     /**
34954      * @cfg {String} name The field's HTML name attribute.
34955      */
34956     /**
34957      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34958      */
34959
34960         // private ??
34961         initComponent : function(){
34962         Roo.form.Field.superclass.initComponent.call(this);
34963         this.addEvents({
34964             /**
34965              * @event focus
34966              * Fires when this field receives input focus.
34967              * @param {Roo.form.Field} this
34968              */
34969             focus : true,
34970             /**
34971              * @event blur
34972              * Fires when this field loses input focus.
34973              * @param {Roo.form.Field} this
34974              */
34975             blur : true,
34976             /**
34977              * @event specialkey
34978              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34979              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34980              * @param {Roo.form.Field} this
34981              * @param {Roo.EventObject} e The event object
34982              */
34983             specialkey : true,
34984             /**
34985              * @event change
34986              * Fires just before the field blurs if the field value has changed.
34987              * @param {Roo.form.Field} this
34988              * @param {Mixed} newValue The new value
34989              * @param {Mixed} oldValue The original value
34990              */
34991             change : true,
34992             /**
34993              * @event invalid
34994              * Fires after the field has been marked as invalid.
34995              * @param {Roo.form.Field} this
34996              * @param {String} msg The validation message
34997              */
34998             invalid : true,
34999             /**
35000              * @event valid
35001              * Fires after the field has been validated with no errors.
35002              * @param {Roo.form.Field} this
35003              */
35004             valid : true
35005         });
35006     },
35007
35008     /**
35009      * Returns the name attribute of the field if available
35010      * @return {String} name The field name
35011      */
35012     getName: function(){
35013          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35014     },
35015
35016     // private
35017     onRender : function(ct, position){
35018         Roo.form.Field.superclass.onRender.call(this, ct, position);
35019         if(!this.el){
35020             var cfg = this.getAutoCreate();
35021             if(!cfg.name){
35022                 cfg.name = this.name || this.id;
35023             }
35024             if(this.inputType){
35025                 cfg.type = this.inputType;
35026             }
35027             this.el = ct.createChild(cfg, position);
35028         }
35029         var type = this.el.dom.type;
35030         if(type){
35031             if(type == 'password'){
35032                 type = 'text';
35033             }
35034             this.el.addClass('x-form-'+type);
35035         }
35036         if(this.readOnly){
35037             this.el.dom.readOnly = true;
35038         }
35039         if(this.tabIndex !== undefined){
35040             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35041         }
35042
35043         this.el.addClass([this.fieldClass, this.cls]);
35044         this.initValue();
35045     },
35046
35047     /**
35048      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35049      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35050      * @return {Roo.form.Field} this
35051      */
35052     applyTo : function(target){
35053         this.allowDomMove = false;
35054         this.el = Roo.get(target);
35055         this.render(this.el.dom.parentNode);
35056         return this;
35057     },
35058
35059     // private
35060     initValue : function(){
35061         if(this.value !== undefined){
35062             this.setValue(this.value);
35063         }else if(this.el.dom.value.length > 0){
35064             this.setValue(this.el.dom.value);
35065         }
35066     },
35067
35068     /**
35069      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35070      */
35071     isDirty : function() {
35072         if(this.disabled) {
35073             return false;
35074         }
35075         return String(this.getValue()) !== String(this.originalValue);
35076     },
35077
35078     // private
35079     afterRender : function(){
35080         Roo.form.Field.superclass.afterRender.call(this);
35081         this.initEvents();
35082     },
35083
35084     // private
35085     fireKey : function(e){
35086         if(e.isNavKeyPress()){
35087             this.fireEvent("specialkey", this, e);
35088         }
35089     },
35090
35091     /**
35092      * Resets the current field value to the originally loaded value and clears any validation messages
35093      */
35094     reset : function(){
35095         this.setValue(this.originalValue);
35096         this.clearInvalid();
35097     },
35098
35099     // private
35100     initEvents : function(){
35101         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35102         this.el.on("focus", this.onFocus,  this);
35103         this.el.on("blur", this.onBlur,  this);
35104
35105         // reference to original value for reset
35106         this.originalValue = this.getValue();
35107     },
35108
35109     // private
35110     onFocus : function(){
35111         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35112             this.el.addClass(this.focusClass);
35113         }
35114         if(!this.hasFocus){
35115             this.hasFocus = true;
35116             this.startValue = this.getValue();
35117             this.fireEvent("focus", this);
35118         }
35119     },
35120
35121     beforeBlur : Roo.emptyFn,
35122
35123     // private
35124     onBlur : function(){
35125         this.beforeBlur();
35126         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35127             this.el.removeClass(this.focusClass);
35128         }
35129         this.hasFocus = false;
35130         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35131             this.validate();
35132         }
35133         var v = this.getValue();
35134         if(String(v) !== String(this.startValue)){
35135             this.fireEvent('change', this, v, this.startValue);
35136         }
35137         this.fireEvent("blur", this);
35138     },
35139
35140     /**
35141      * Returns whether or not the field value is currently valid
35142      * @param {Boolean} preventMark True to disable marking the field invalid
35143      * @return {Boolean} True if the value is valid, else false
35144      */
35145     isValid : function(preventMark){
35146         if(this.disabled){
35147             return true;
35148         }
35149         var restore = this.preventMark;
35150         this.preventMark = preventMark === true;
35151         var v = this.validateValue(this.processValue(this.getRawValue()));
35152         this.preventMark = restore;
35153         return v;
35154     },
35155
35156     /**
35157      * Validates the field value
35158      * @return {Boolean} True if the value is valid, else false
35159      */
35160     validate : function(){
35161         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35162             this.clearInvalid();
35163             return true;
35164         }
35165         return false;
35166     },
35167
35168     processValue : function(value){
35169         return value;
35170     },
35171
35172     // private
35173     // Subclasses should provide the validation implementation by overriding this
35174     validateValue : function(value){
35175         return true;
35176     },
35177
35178     /**
35179      * Mark this field as invalid
35180      * @param {String} msg The validation message
35181      */
35182     markInvalid : function(msg){
35183         if(!this.rendered || this.preventMark){ // not rendered
35184             return;
35185         }
35186         this.el.addClass(this.invalidClass);
35187         msg = msg || this.invalidText;
35188         switch(this.msgTarget){
35189             case 'qtip':
35190                 this.el.dom.qtip = msg;
35191                 this.el.dom.qclass = 'x-form-invalid-tip';
35192                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35193                     Roo.QuickTips.enable();
35194                 }
35195                 break;
35196             case 'title':
35197                 this.el.dom.title = msg;
35198                 break;
35199             case 'under':
35200                 if(!this.errorEl){
35201                     var elp = this.el.findParent('.x-form-element', 5, true);
35202                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35203                     this.errorEl.setWidth(elp.getWidth(true)-20);
35204                 }
35205                 this.errorEl.update(msg);
35206                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35207                 break;
35208             case 'side':
35209                 if(!this.errorIcon){
35210                     var elp = this.el.findParent('.x-form-element', 5, true);
35211                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35212                 }
35213                 this.alignErrorIcon();
35214                 this.errorIcon.dom.qtip = msg;
35215                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35216                 this.errorIcon.show();
35217                 this.on('resize', this.alignErrorIcon, this);
35218                 break;
35219             default:
35220                 var t = Roo.getDom(this.msgTarget);
35221                 t.innerHTML = msg;
35222                 t.style.display = this.msgDisplay;
35223                 break;
35224         }
35225         this.fireEvent('invalid', this, msg);
35226     },
35227
35228     // private
35229     alignErrorIcon : function(){
35230         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35231     },
35232
35233     /**
35234      * Clear any invalid styles/messages for this field
35235      */
35236     clearInvalid : function(){
35237         if(!this.rendered || this.preventMark){ // not rendered
35238             return;
35239         }
35240         this.el.removeClass(this.invalidClass);
35241         switch(this.msgTarget){
35242             case 'qtip':
35243                 this.el.dom.qtip = '';
35244                 break;
35245             case 'title':
35246                 this.el.dom.title = '';
35247                 break;
35248             case 'under':
35249                 if(this.errorEl){
35250                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35251                 }
35252                 break;
35253             case 'side':
35254                 if(this.errorIcon){
35255                     this.errorIcon.dom.qtip = '';
35256                     this.errorIcon.hide();
35257                     this.un('resize', this.alignErrorIcon, this);
35258                 }
35259                 break;
35260             default:
35261                 var t = Roo.getDom(this.msgTarget);
35262                 t.innerHTML = '';
35263                 t.style.display = 'none';
35264                 break;
35265         }
35266         this.fireEvent('valid', this);
35267     },
35268
35269     /**
35270      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35271      * @return {Mixed} value The field value
35272      */
35273     getRawValue : function(){
35274         var v = this.el.getValue();
35275         if(v === this.emptyText){
35276             v = '';
35277         }
35278         return v;
35279     },
35280
35281     /**
35282      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35283      * @return {Mixed} value The field value
35284      */
35285     getValue : function(){
35286         var v = this.el.getValue();
35287         if(v === this.emptyText || v === undefined){
35288             v = '';
35289         }
35290         return v;
35291     },
35292
35293     /**
35294      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35295      * @param {Mixed} value The value to set
35296      */
35297     setRawValue : function(v){
35298         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35299     },
35300
35301     /**
35302      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35303      * @param {Mixed} value The value to set
35304      */
35305     setValue : function(v){
35306         this.value = v;
35307         if(this.rendered){
35308             this.el.dom.value = (v === null || v === undefined ? '' : v);
35309             this.validate();
35310         }
35311     },
35312
35313     adjustSize : function(w, h){
35314         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35315         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35316         return s;
35317     },
35318
35319     adjustWidth : function(tag, w){
35320         tag = tag.toLowerCase();
35321         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35322             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35323                 if(tag == 'input'){
35324                     return w + 2;
35325                 }
35326                 if(tag = 'textarea'){
35327                     return w-2;
35328                 }
35329             }else if(Roo.isOpera){
35330                 if(tag == 'input'){
35331                     return w + 2;
35332                 }
35333                 if(tag = 'textarea'){
35334                     return w-2;
35335                 }
35336             }
35337         }
35338         return w;
35339     }
35340 });
35341
35342
35343 // anything other than normal should be considered experimental
35344 Roo.form.Field.msgFx = {
35345     normal : {
35346         show: function(msgEl, f){
35347             msgEl.setDisplayed('block');
35348         },
35349
35350         hide : function(msgEl, f){
35351             msgEl.setDisplayed(false).update('');
35352         }
35353     },
35354
35355     slide : {
35356         show: function(msgEl, f){
35357             msgEl.slideIn('t', {stopFx:true});
35358         },
35359
35360         hide : function(msgEl, f){
35361             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35362         }
35363     },
35364
35365     slideRight : {
35366         show: function(msgEl, f){
35367             msgEl.fixDisplay();
35368             msgEl.alignTo(f.el, 'tl-tr');
35369             msgEl.slideIn('l', {stopFx:true});
35370         },
35371
35372         hide : function(msgEl, f){
35373             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35374         }
35375     }
35376 };/*
35377  * Based on:
35378  * Ext JS Library 1.1.1
35379  * Copyright(c) 2006-2007, Ext JS, LLC.
35380  *
35381  * Originally Released Under LGPL - original licence link has changed is not relivant.
35382  *
35383  * Fork - LGPL
35384  * <script type="text/javascript">
35385  */
35386  
35387
35388 /**
35389  * @class Roo.form.TextField
35390  * @extends Roo.form.Field
35391  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35392  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35393  * @constructor
35394  * Creates a new TextField
35395  * @param {Object} config Configuration options
35396  */
35397 Roo.form.TextField = function(config){
35398     Roo.form.TextField.superclass.constructor.call(this, config);
35399     this.addEvents({
35400         /**
35401          * @event autosize
35402          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35403          * according to the default logic, but this event provides a hook for the developer to apply additional
35404          * logic at runtime to resize the field if needed.
35405              * @param {Roo.form.Field} this This text field
35406              * @param {Number} width The new field width
35407              */
35408         autosize : true
35409     });
35410 };
35411
35412 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35413     /**
35414      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35415      */
35416     grow : false,
35417     /**
35418      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35419      */
35420     growMin : 30,
35421     /**
35422      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35423      */
35424     growMax : 800,
35425     /**
35426      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35427      */
35428     vtype : null,
35429     /**
35430      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35431      */
35432     maskRe : null,
35433     /**
35434      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35435      */
35436     disableKeyFilter : false,
35437     /**
35438      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35439      */
35440     allowBlank : true,
35441     /**
35442      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35443      */
35444     minLength : 0,
35445     /**
35446      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35447      */
35448     maxLength : Number.MAX_VALUE,
35449     /**
35450      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35451      */
35452     minLengthText : "The minimum length for this field is {0}",
35453     /**
35454      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35455      */
35456     maxLengthText : "The maximum length for this field is {0}",
35457     /**
35458      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35459      */
35460     selectOnFocus : false,
35461     /**
35462      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35463      */
35464     blankText : "This field is required",
35465     /**
35466      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35467      * If available, this function will be called only after the basic validators all return true, and will be passed the
35468      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35469      */
35470     validator : null,
35471     /**
35472      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35473      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35474      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35475      */
35476     regex : null,
35477     /**
35478      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35479      */
35480     regexText : "",
35481     /**
35482      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35483      */
35484     emptyText : null,
35485     /**
35486      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35487      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35488      */
35489     emptyClass : 'x-form-empty-field',
35490
35491     // private
35492     initEvents : function(){
35493         Roo.form.TextField.superclass.initEvents.call(this);
35494         if(this.validationEvent == 'keyup'){
35495             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35496             this.el.on('keyup', this.filterValidation, this);
35497         }
35498         else if(this.validationEvent !== false){
35499             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35500         }
35501         if(this.selectOnFocus || this.emptyText){
35502             this.on("focus", this.preFocus, this);
35503             if(this.emptyText){
35504                 this.on('blur', this.postBlur, this);
35505                 this.applyEmptyText();
35506             }
35507         }
35508         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35509             this.el.on("keypress", this.filterKeys, this);
35510         }
35511         if(this.grow){
35512             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35513             this.el.on("click", this.autoSize,  this);
35514         }
35515     },
35516
35517     processValue : function(value){
35518         if(this.stripCharsRe){
35519             var newValue = value.replace(this.stripCharsRe, '');
35520             if(newValue !== value){
35521                 this.setRawValue(newValue);
35522                 return newValue;
35523             }
35524         }
35525         return value;
35526     },
35527
35528     filterValidation : function(e){
35529         if(!e.isNavKeyPress()){
35530             this.validationTask.delay(this.validationDelay);
35531         }
35532     },
35533
35534     // private
35535     onKeyUp : function(e){
35536         if(!e.isNavKeyPress()){
35537             this.autoSize();
35538         }
35539     },
35540
35541     /**
35542      * Resets the current field value to the originally-loaded value and clears any validation messages.
35543      * Also adds emptyText and emptyClass if the original value was blank.
35544      */
35545     reset : function(){
35546         Roo.form.TextField.superclass.reset.call(this);
35547         this.applyEmptyText();
35548     },
35549
35550     applyEmptyText : function(){
35551         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35552             this.setRawValue(this.emptyText);
35553             this.el.addClass(this.emptyClass);
35554         }
35555     },
35556
35557     // private
35558     preFocus : function(){
35559         if(this.emptyText){
35560             if(this.el.dom.value == this.emptyText){
35561                 this.setRawValue('');
35562             }
35563             this.el.removeClass(this.emptyClass);
35564         }
35565         if(this.selectOnFocus){
35566             this.el.dom.select();
35567         }
35568     },
35569
35570     // private
35571     postBlur : function(){
35572         this.applyEmptyText();
35573     },
35574
35575     // private
35576     filterKeys : function(e){
35577         var k = e.getKey();
35578         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35579             return;
35580         }
35581         var c = e.getCharCode(), cc = String.fromCharCode(c);
35582         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35583             return;
35584         }
35585         if(!this.maskRe.test(cc)){
35586             e.stopEvent();
35587         }
35588     },
35589
35590     setValue : function(v){
35591         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35592             this.el.removeClass(this.emptyClass);
35593         }
35594         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35595         this.applyEmptyText();
35596         this.autoSize();
35597     },
35598
35599     /**
35600      * Validates a value according to the field's validation rules and marks the field as invalid
35601      * if the validation fails
35602      * @param {Mixed} value The value to validate
35603      * @return {Boolean} True if the value is valid, else false
35604      */
35605     validateValue : function(value){
35606         if(value.length < 1 || value === this.emptyText){ // if it's blank
35607              if(this.allowBlank){
35608                 this.clearInvalid();
35609                 return true;
35610              }else{
35611                 this.markInvalid(this.blankText);
35612                 return false;
35613              }
35614         }
35615         if(value.length < this.minLength){
35616             this.markInvalid(String.format(this.minLengthText, this.minLength));
35617             return false;
35618         }
35619         if(value.length > this.maxLength){
35620             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35621             return false;
35622         }
35623         if(this.vtype){
35624             var vt = Roo.form.VTypes;
35625             if(!vt[this.vtype](value, this)){
35626                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35627                 return false;
35628             }
35629         }
35630         if(typeof this.validator == "function"){
35631             var msg = this.validator(value);
35632             if(msg !== true){
35633                 this.markInvalid(msg);
35634                 return false;
35635             }
35636         }
35637         if(this.regex && !this.regex.test(value)){
35638             this.markInvalid(this.regexText);
35639             return false;
35640         }
35641         return true;
35642     },
35643
35644     /**
35645      * Selects text in this field
35646      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35647      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35648      */
35649     selectText : function(start, end){
35650         var v = this.getRawValue();
35651         if(v.length > 0){
35652             start = start === undefined ? 0 : start;
35653             end = end === undefined ? v.length : end;
35654             var d = this.el.dom;
35655             if(d.setSelectionRange){
35656                 d.setSelectionRange(start, end);
35657             }else if(d.createTextRange){
35658                 var range = d.createTextRange();
35659                 range.moveStart("character", start);
35660                 range.moveEnd("character", v.length-end);
35661                 range.select();
35662             }
35663         }
35664     },
35665
35666     /**
35667      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35668      * This only takes effect if grow = true, and fires the autosize event.
35669      */
35670     autoSize : function(){
35671         if(!this.grow || !this.rendered){
35672             return;
35673         }
35674         if(!this.metrics){
35675             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35676         }
35677         var el = this.el;
35678         var v = el.dom.value;
35679         var d = document.createElement('div');
35680         d.appendChild(document.createTextNode(v));
35681         v = d.innerHTML;
35682         d = null;
35683         v += "&#160;";
35684         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35685         this.el.setWidth(w);
35686         this.fireEvent("autosize", this, w);
35687     }
35688 });/*
35689  * Based on:
35690  * Ext JS Library 1.1.1
35691  * Copyright(c) 2006-2007, Ext JS, LLC.
35692  *
35693  * Originally Released Under LGPL - original licence link has changed is not relivant.
35694  *
35695  * Fork - LGPL
35696  * <script type="text/javascript">
35697  */
35698  
35699 /**
35700  * @class Roo.form.Hidden
35701  * @extends Roo.form.TextField
35702  * Simple Hidden element used on forms 
35703  * 
35704  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35705  * 
35706  * @constructor
35707  * Creates a new Hidden form element.
35708  * @param {Object} config Configuration options
35709  */
35710
35711
35712
35713 // easy hidden field...
35714 Roo.form.Hidden = function(config){
35715     Roo.form.Hidden.superclass.constructor.call(this, config);
35716 };
35717   
35718 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35719     fieldLabel:      '',
35720     inputType:      'hidden',
35721     width:          50,
35722     allowBlank:     true,
35723     labelSeparator: '',
35724     hidden:         true,
35725     itemCls :       'x-form-item-display-none'
35726
35727
35728 });
35729
35730
35731 /*
35732  * Based on:
35733  * Ext JS Library 1.1.1
35734  * Copyright(c) 2006-2007, Ext JS, LLC.
35735  *
35736  * Originally Released Under LGPL - original licence link has changed is not relivant.
35737  *
35738  * Fork - LGPL
35739  * <script type="text/javascript">
35740  */
35741  
35742 /**
35743  * @class Roo.form.TriggerField
35744  * @extends Roo.form.TextField
35745  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35746  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35747  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35748  * for which you can provide a custom implementation.  For example:
35749  * <pre><code>
35750 var trigger = new Roo.form.TriggerField();
35751 trigger.onTriggerClick = myTriggerFn;
35752 trigger.applyTo('my-field');
35753 </code></pre>
35754  *
35755  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35756  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35757  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35758  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35759  * @constructor
35760  * Create a new TriggerField.
35761  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35762  * to the base TextField)
35763  */
35764 Roo.form.TriggerField = function(config){
35765     this.mimicing = false;
35766     Roo.form.TriggerField.superclass.constructor.call(this, config);
35767 };
35768
35769 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35770     /**
35771      * @cfg {String} triggerClass A CSS class to apply to the trigger
35772      */
35773     /**
35774      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35775      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35776      */
35777     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35778     /**
35779      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35780      */
35781     hideTrigger:false,
35782
35783     /** @cfg {Boolean} grow @hide */
35784     /** @cfg {Number} growMin @hide */
35785     /** @cfg {Number} growMax @hide */
35786
35787     /**
35788      * @hide 
35789      * @method
35790      */
35791     autoSize: Roo.emptyFn,
35792     // private
35793     monitorTab : true,
35794     // private
35795     deferHeight : true,
35796
35797     
35798     actionMode : 'wrap',
35799     // private
35800     onResize : function(w, h){
35801         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35802         if(typeof w == 'number'){
35803             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35804         }
35805     },
35806
35807     // private
35808     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35809
35810     // private
35811     getResizeEl : function(){
35812         return this.wrap;
35813     },
35814
35815     // private
35816     getPositionEl : function(){
35817         return this.wrap;
35818     },
35819
35820     // private
35821     alignErrorIcon : function(){
35822         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35823     },
35824
35825     // private
35826     onRender : function(ct, position){
35827         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35828         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35829         this.trigger = this.wrap.createChild(this.triggerConfig ||
35830                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35831         if(this.hideTrigger){
35832             this.trigger.setDisplayed(false);
35833         }
35834         this.initTrigger();
35835         if(!this.width){
35836             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35837         }
35838     },
35839
35840     // private
35841     initTrigger : function(){
35842         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35843         this.trigger.addClassOnOver('x-form-trigger-over');
35844         this.trigger.addClassOnClick('x-form-trigger-click');
35845     },
35846
35847     // private
35848     onDestroy : function(){
35849         if(this.trigger){
35850             this.trigger.removeAllListeners();
35851             this.trigger.remove();
35852         }
35853         if(this.wrap){
35854             this.wrap.remove();
35855         }
35856         Roo.form.TriggerField.superclass.onDestroy.call(this);
35857     },
35858
35859     // private
35860     onFocus : function(){
35861         Roo.form.TriggerField.superclass.onFocus.call(this);
35862         if(!this.mimicing){
35863             this.wrap.addClass('x-trigger-wrap-focus');
35864             this.mimicing = true;
35865             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35866             if(this.monitorTab){
35867                 this.el.on("keydown", this.checkTab, this);
35868             }
35869         }
35870     },
35871
35872     // private
35873     checkTab : function(e){
35874         if(e.getKey() == e.TAB){
35875             this.triggerBlur();
35876         }
35877     },
35878
35879     // private
35880     onBlur : function(){
35881         // do nothing
35882     },
35883
35884     // private
35885     mimicBlur : function(e, t){
35886         if(!this.wrap.contains(t) && this.validateBlur()){
35887             this.triggerBlur();
35888         }
35889     },
35890
35891     // private
35892     triggerBlur : function(){
35893         this.mimicing = false;
35894         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35895         if(this.monitorTab){
35896             this.el.un("keydown", this.checkTab, this);
35897         }
35898         this.wrap.removeClass('x-trigger-wrap-focus');
35899         Roo.form.TriggerField.superclass.onBlur.call(this);
35900     },
35901
35902     // private
35903     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35904     validateBlur : function(e, t){
35905         return true;
35906     },
35907
35908     // private
35909     onDisable : function(){
35910         Roo.form.TriggerField.superclass.onDisable.call(this);
35911         if(this.wrap){
35912             this.wrap.addClass('x-item-disabled');
35913         }
35914     },
35915
35916     // private
35917     onEnable : function(){
35918         Roo.form.TriggerField.superclass.onEnable.call(this);
35919         if(this.wrap){
35920             this.wrap.removeClass('x-item-disabled');
35921         }
35922     },
35923
35924     // private
35925     onShow : function(){
35926         var ae = this.getActionEl();
35927         
35928         if(ae){
35929             ae.dom.style.display = '';
35930             ae.dom.style.visibility = 'visible';
35931         }
35932     },
35933
35934     // private
35935     
35936     onHide : function(){
35937         var ae = this.getActionEl();
35938         ae.dom.style.display = 'none';
35939     },
35940
35941     /**
35942      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35943      * by an implementing function.
35944      * @method
35945      * @param {EventObject} e
35946      */
35947     onTriggerClick : Roo.emptyFn
35948 });
35949
35950 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35951 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35952 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35953 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35954     initComponent : function(){
35955         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35956
35957         this.triggerConfig = {
35958             tag:'span', cls:'x-form-twin-triggers', cn:[
35959             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35960             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35961         ]};
35962     },
35963
35964     getTrigger : function(index){
35965         return this.triggers[index];
35966     },
35967
35968     initTrigger : function(){
35969         var ts = this.trigger.select('.x-form-trigger', true);
35970         this.wrap.setStyle('overflow', 'hidden');
35971         var triggerField = this;
35972         ts.each(function(t, all, index){
35973             t.hide = function(){
35974                 var w = triggerField.wrap.getWidth();
35975                 this.dom.style.display = 'none';
35976                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35977             };
35978             t.show = function(){
35979                 var w = triggerField.wrap.getWidth();
35980                 this.dom.style.display = '';
35981                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35982             };
35983             var triggerIndex = 'Trigger'+(index+1);
35984
35985             if(this['hide'+triggerIndex]){
35986                 t.dom.style.display = 'none';
35987             }
35988             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35989             t.addClassOnOver('x-form-trigger-over');
35990             t.addClassOnClick('x-form-trigger-click');
35991         }, this);
35992         this.triggers = ts.elements;
35993     },
35994
35995     onTrigger1Click : Roo.emptyFn,
35996     onTrigger2Click : Roo.emptyFn
35997 });/*
35998  * Based on:
35999  * Ext JS Library 1.1.1
36000  * Copyright(c) 2006-2007, Ext JS, LLC.
36001  *
36002  * Originally Released Under LGPL - original licence link has changed is not relivant.
36003  *
36004  * Fork - LGPL
36005  * <script type="text/javascript">
36006  */
36007  
36008 /**
36009  * @class Roo.form.TextArea
36010  * @extends Roo.form.TextField
36011  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36012  * support for auto-sizing.
36013  * @constructor
36014  * Creates a new TextArea
36015  * @param {Object} config Configuration options
36016  */
36017 Roo.form.TextArea = function(config){
36018     Roo.form.TextArea.superclass.constructor.call(this, config);
36019     // these are provided exchanges for backwards compat
36020     // minHeight/maxHeight were replaced by growMin/growMax to be
36021     // compatible with TextField growing config values
36022     if(this.minHeight !== undefined){
36023         this.growMin = this.minHeight;
36024     }
36025     if(this.maxHeight !== undefined){
36026         this.growMax = this.maxHeight;
36027     }
36028 };
36029
36030 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36031     /**
36032      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36033      */
36034     growMin : 60,
36035     /**
36036      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36037      */
36038     growMax: 1000,
36039     /**
36040      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36041      * in the field (equivalent to setting overflow: hidden, defaults to false)
36042      */
36043     preventScrollbars: false,
36044     /**
36045      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36046      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36047      */
36048
36049     // private
36050     onRender : function(ct, position){
36051         if(!this.el){
36052             this.defaultAutoCreate = {
36053                 tag: "textarea",
36054                 style:"width:300px;height:60px;",
36055                 autocomplete: "off"
36056             };
36057         }
36058         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36059         if(this.grow){
36060             this.textSizeEl = Roo.DomHelper.append(document.body, {
36061                 tag: "pre", cls: "x-form-grow-sizer"
36062             });
36063             if(this.preventScrollbars){
36064                 this.el.setStyle("overflow", "hidden");
36065             }
36066             this.el.setHeight(this.growMin);
36067         }
36068     },
36069
36070     onDestroy : function(){
36071         if(this.textSizeEl){
36072             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36073         }
36074         Roo.form.TextArea.superclass.onDestroy.call(this);
36075     },
36076
36077     // private
36078     onKeyUp : function(e){
36079         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36080             this.autoSize();
36081         }
36082     },
36083
36084     /**
36085      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36086      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36087      */
36088     autoSize : function(){
36089         if(!this.grow || !this.textSizeEl){
36090             return;
36091         }
36092         var el = this.el;
36093         var v = el.dom.value;
36094         var ts = this.textSizeEl;
36095
36096         ts.innerHTML = '';
36097         ts.appendChild(document.createTextNode(v));
36098         v = ts.innerHTML;
36099
36100         Roo.fly(ts).setWidth(this.el.getWidth());
36101         if(v.length < 1){
36102             v = "&#160;&#160;";
36103         }else{
36104             if(Roo.isIE){
36105                 v = v.replace(/\n/g, '<p>&#160;</p>');
36106             }
36107             v += "&#160;\n&#160;";
36108         }
36109         ts.innerHTML = v;
36110         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36111         if(h != this.lastHeight){
36112             this.lastHeight = h;
36113             this.el.setHeight(h);
36114             this.fireEvent("autosize", this, h);
36115         }
36116     }
36117 });/*
36118  * Based on:
36119  * Ext JS Library 1.1.1
36120  * Copyright(c) 2006-2007, Ext JS, LLC.
36121  *
36122  * Originally Released Under LGPL - original licence link has changed is not relivant.
36123  *
36124  * Fork - LGPL
36125  * <script type="text/javascript">
36126  */
36127  
36128
36129 /**
36130  * @class Roo.form.NumberField
36131  * @extends Roo.form.TextField
36132  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36133  * @constructor
36134  * Creates a new NumberField
36135  * @param {Object} config Configuration options
36136  */
36137 Roo.form.NumberField = function(config){
36138     Roo.form.NumberField.superclass.constructor.call(this, config);
36139 };
36140
36141 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36142     /**
36143      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36144      */
36145     fieldClass: "x-form-field x-form-num-field",
36146     /**
36147      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36148      */
36149     allowDecimals : true,
36150     /**
36151      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36152      */
36153     decimalSeparator : ".",
36154     /**
36155      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36156      */
36157     decimalPrecision : 2,
36158     /**
36159      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36160      */
36161     allowNegative : true,
36162     /**
36163      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36164      */
36165     minValue : Number.NEGATIVE_INFINITY,
36166     /**
36167      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36168      */
36169     maxValue : Number.MAX_VALUE,
36170     /**
36171      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36172      */
36173     minText : "The minimum value for this field is {0}",
36174     /**
36175      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36176      */
36177     maxText : "The maximum value for this field is {0}",
36178     /**
36179      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36180      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36181      */
36182     nanText : "{0} is not a valid number",
36183
36184     // private
36185     initEvents : function(){
36186         Roo.form.NumberField.superclass.initEvents.call(this);
36187         var allowed = "0123456789";
36188         if(this.allowDecimals){
36189             allowed += this.decimalSeparator;
36190         }
36191         if(this.allowNegative){
36192             allowed += "-";
36193         }
36194         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36195         var keyPress = function(e){
36196             var k = e.getKey();
36197             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36198                 return;
36199             }
36200             var c = e.getCharCode();
36201             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36202                 e.stopEvent();
36203             }
36204         };
36205         this.el.on("keypress", keyPress, this);
36206     },
36207
36208     // private
36209     validateValue : function(value){
36210         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36211             return false;
36212         }
36213         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36214              return true;
36215         }
36216         var num = this.parseValue(value);
36217         if(isNaN(num)){
36218             this.markInvalid(String.format(this.nanText, value));
36219             return false;
36220         }
36221         if(num < this.minValue){
36222             this.markInvalid(String.format(this.minText, this.minValue));
36223             return false;
36224         }
36225         if(num > this.maxValue){
36226             this.markInvalid(String.format(this.maxText, this.maxValue));
36227             return false;
36228         }
36229         return true;
36230     },
36231
36232     getValue : function(){
36233         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36234     },
36235
36236     // private
36237     parseValue : function(value){
36238         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36239         return isNaN(value) ? '' : value;
36240     },
36241
36242     // private
36243     fixPrecision : function(value){
36244         var nan = isNaN(value);
36245         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36246             return nan ? '' : value;
36247         }
36248         return parseFloat(value).toFixed(this.decimalPrecision);
36249     },
36250
36251     setValue : function(v){
36252         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36253     },
36254
36255     // private
36256     decimalPrecisionFcn : function(v){
36257         return Math.floor(v);
36258     },
36259
36260     beforeBlur : function(){
36261         var v = this.parseValue(this.getRawValue());
36262         if(v){
36263             this.setValue(this.fixPrecision(v));
36264         }
36265     }
36266 });/*
36267  * Based on:
36268  * Ext JS Library 1.1.1
36269  * Copyright(c) 2006-2007, Ext JS, LLC.
36270  *
36271  * Originally Released Under LGPL - original licence link has changed is not relivant.
36272  *
36273  * Fork - LGPL
36274  * <script type="text/javascript">
36275  */
36276  
36277 /**
36278  * @class Roo.form.DateField
36279  * @extends Roo.form.TriggerField
36280  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36281 * @constructor
36282 * Create a new DateField
36283 * @param {Object} config
36284  */
36285 Roo.form.DateField = function(config){
36286     Roo.form.DateField.superclass.constructor.call(this, config);
36287     
36288       this.addEvents({
36289          
36290         /**
36291          * @event select
36292          * Fires when a date is selected
36293              * @param {Roo.form.DateField} combo This combo box
36294              * @param {Date} date The date selected
36295              */
36296         'select' : true
36297          
36298     });
36299     
36300     
36301     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36302     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36303     this.ddMatch = null;
36304     if(this.disabledDates){
36305         var dd = this.disabledDates;
36306         var re = "(?:";
36307         for(var i = 0; i < dd.length; i++){
36308             re += dd[i];
36309             if(i != dd.length-1) re += "|";
36310         }
36311         this.ddMatch = new RegExp(re + ")");
36312     }
36313 };
36314
36315 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36316     /**
36317      * @cfg {String} format
36318      * The default date format string which can be overriden for localization support.  The format must be
36319      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36320      */
36321     format : "m/d/y",
36322     /**
36323      * @cfg {String} altFormats
36324      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36325      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36326      */
36327     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36328     /**
36329      * @cfg {Array} disabledDays
36330      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36331      */
36332     disabledDays : null,
36333     /**
36334      * @cfg {String} disabledDaysText
36335      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36336      */
36337     disabledDaysText : "Disabled",
36338     /**
36339      * @cfg {Array} disabledDates
36340      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36341      * expression so they are very powerful. Some examples:
36342      * <ul>
36343      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36344      * <li>["03/08", "09/16"] would disable those days for every year</li>
36345      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36346      * <li>["03/../2006"] would disable every day in March 2006</li>
36347      * <li>["^03"] would disable every day in every March</li>
36348      * </ul>
36349      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36350      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36351      */
36352     disabledDates : null,
36353     /**
36354      * @cfg {String} disabledDatesText
36355      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36356      */
36357     disabledDatesText : "Disabled",
36358     /**
36359      * @cfg {Date/String} minValue
36360      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36361      * valid format (defaults to null).
36362      */
36363     minValue : null,
36364     /**
36365      * @cfg {Date/String} maxValue
36366      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36367      * valid format (defaults to null).
36368      */
36369     maxValue : null,
36370     /**
36371      * @cfg {String} minText
36372      * The error text to display when the date in the cell is before minValue (defaults to
36373      * 'The date in this field must be after {minValue}').
36374      */
36375     minText : "The date in this field must be equal to or after {0}",
36376     /**
36377      * @cfg {String} maxText
36378      * The error text to display when the date in the cell is after maxValue (defaults to
36379      * 'The date in this field must be before {maxValue}').
36380      */
36381     maxText : "The date in this field must be equal to or before {0}",
36382     /**
36383      * @cfg {String} invalidText
36384      * The error text to display when the date in the field is invalid (defaults to
36385      * '{value} is not a valid date - it must be in the format {format}').
36386      */
36387     invalidText : "{0} is not a valid date - it must be in the format {1}",
36388     /**
36389      * @cfg {String} triggerClass
36390      * An additional CSS class used to style the trigger button.  The trigger will always get the
36391      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36392      * which displays a calendar icon).
36393      */
36394     triggerClass : 'x-form-date-trigger',
36395     
36396
36397     /**
36398      * @cfg {bool} useIso
36399      * if enabled, then the date field will use a hidden field to store the 
36400      * real value as iso formated date. default (false)
36401      */ 
36402     useIso : false,
36403     /**
36404      * @cfg {String/Object} autoCreate
36405      * A DomHelper element spec, or true for a default element spec (defaults to
36406      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36407      */ 
36408     // private
36409     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36410     
36411     // private
36412     hiddenField: false,
36413     
36414     onRender : function(ct, position)
36415     {
36416         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36417         if (this.useIso) {
36418             this.el.dom.removeAttribute('name'); 
36419             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36420                     'before', true);
36421             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36422             // prevent input submission
36423             this.hiddenName = this.name;
36424         }
36425             
36426             
36427     },
36428     
36429     // private
36430     validateValue : function(value)
36431     {
36432         value = this.formatDate(value);
36433         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36434             return false;
36435         }
36436         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36437              return true;
36438         }
36439         var svalue = value;
36440         value = this.parseDate(value);
36441         if(!value){
36442             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36443             return false;
36444         }
36445         var time = value.getTime();
36446         if(this.minValue && time < this.minValue.getTime()){
36447             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36448             return false;
36449         }
36450         if(this.maxValue && time > this.maxValue.getTime()){
36451             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36452             return false;
36453         }
36454         if(this.disabledDays){
36455             var day = value.getDay();
36456             for(var i = 0; i < this.disabledDays.length; i++) {
36457                 if(day === this.disabledDays[i]){
36458                     this.markInvalid(this.disabledDaysText);
36459                     return false;
36460                 }
36461             }
36462         }
36463         var fvalue = this.formatDate(value);
36464         if(this.ddMatch && this.ddMatch.test(fvalue)){
36465             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36466             return false;
36467         }
36468         return true;
36469     },
36470
36471     // private
36472     // Provides logic to override the default TriggerField.validateBlur which just returns true
36473     validateBlur : function(){
36474         return !this.menu || !this.menu.isVisible();
36475     },
36476
36477     /**
36478      * Returns the current date value of the date field.
36479      * @return {Date} The date value
36480      */
36481     getValue : function(){
36482         
36483         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36484     },
36485
36486     /**
36487      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36488      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36489      * (the default format used is "m/d/y").
36490      * <br />Usage:
36491      * <pre><code>
36492 //All of these calls set the same date value (May 4, 2006)
36493
36494 //Pass a date object:
36495 var dt = new Date('5/4/06');
36496 dateField.setValue(dt);
36497
36498 //Pass a date string (default format):
36499 dateField.setValue('5/4/06');
36500
36501 //Pass a date string (custom format):
36502 dateField.format = 'Y-m-d';
36503 dateField.setValue('2006-5-4');
36504 </code></pre>
36505      * @param {String/Date} date The date or valid date string
36506      */
36507     setValue : function(date){
36508         if (this.hiddenField) {
36509             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36510         }
36511         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36512     },
36513
36514     // private
36515     parseDate : function(value){
36516         if(!value || value instanceof Date){
36517             return value;
36518         }
36519         var v = Date.parseDate(value, this.format);
36520         if(!v && this.altFormats){
36521             if(!this.altFormatsArray){
36522                 this.altFormatsArray = this.altFormats.split("|");
36523             }
36524             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36525                 v = Date.parseDate(value, this.altFormatsArray[i]);
36526             }
36527         }
36528         return v;
36529     },
36530
36531     // private
36532     formatDate : function(date, fmt){
36533         return (!date || !(date instanceof Date)) ?
36534                date : date.dateFormat(fmt || this.format);
36535     },
36536
36537     // private
36538     menuListeners : {
36539         select: function(m, d){
36540             this.setValue(d);
36541             this.fireEvent('select', this, d);
36542         },
36543         show : function(){ // retain focus styling
36544             this.onFocus();
36545         },
36546         hide : function(){
36547             this.focus.defer(10, this);
36548             var ml = this.menuListeners;
36549             this.menu.un("select", ml.select,  this);
36550             this.menu.un("show", ml.show,  this);
36551             this.menu.un("hide", ml.hide,  this);
36552         }
36553     },
36554
36555     // private
36556     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36557     onTriggerClick : function(){
36558         if(this.disabled){
36559             return;
36560         }
36561         if(this.menu == null){
36562             this.menu = new Roo.menu.DateMenu();
36563         }
36564         Roo.apply(this.menu.picker,  {
36565             showClear: this.allowBlank,
36566             minDate : this.minValue,
36567             maxDate : this.maxValue,
36568             disabledDatesRE : this.ddMatch,
36569             disabledDatesText : this.disabledDatesText,
36570             disabledDays : this.disabledDays,
36571             disabledDaysText : this.disabledDaysText,
36572             format : this.format,
36573             minText : String.format(this.minText, this.formatDate(this.minValue)),
36574             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36575         });
36576         this.menu.on(Roo.apply({}, this.menuListeners, {
36577             scope:this
36578         }));
36579         this.menu.picker.setValue(this.getValue() || new Date());
36580         this.menu.show(this.el, "tl-bl?");
36581     },
36582
36583     beforeBlur : function(){
36584         var v = this.parseDate(this.getRawValue());
36585         if(v){
36586             this.setValue(v);
36587         }
36588     }
36589
36590     /** @cfg {Boolean} grow @hide */
36591     /** @cfg {Number} growMin @hide */
36592     /** @cfg {Number} growMax @hide */
36593     /**
36594      * @hide
36595      * @method autoSize
36596      */
36597 });/*
36598  * Based on:
36599  * Ext JS Library 1.1.1
36600  * Copyright(c) 2006-2007, Ext JS, LLC.
36601  *
36602  * Originally Released Under LGPL - original licence link has changed is not relivant.
36603  *
36604  * Fork - LGPL
36605  * <script type="text/javascript">
36606  */
36607  
36608
36609 /**
36610  * @class Roo.form.ComboBox
36611  * @extends Roo.form.TriggerField
36612  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36613  * @constructor
36614  * Create a new ComboBox.
36615  * @param {Object} config Configuration options
36616  */
36617 Roo.form.ComboBox = function(config){
36618     Roo.form.ComboBox.superclass.constructor.call(this, config);
36619     this.addEvents({
36620         /**
36621          * @event expand
36622          * Fires when the dropdown list is expanded
36623              * @param {Roo.form.ComboBox} combo This combo box
36624              */
36625         'expand' : true,
36626         /**
36627          * @event collapse
36628          * Fires when the dropdown list is collapsed
36629              * @param {Roo.form.ComboBox} combo This combo box
36630              */
36631         'collapse' : true,
36632         /**
36633          * @event beforeselect
36634          * Fires before a list item is selected. Return false to cancel the selection.
36635              * @param {Roo.form.ComboBox} combo This combo box
36636              * @param {Roo.data.Record} record The data record returned from the underlying store
36637              * @param {Number} index The index of the selected item in the dropdown list
36638              */
36639         'beforeselect' : true,
36640         /**
36641          * @event select
36642          * Fires when a list item is selected
36643              * @param {Roo.form.ComboBox} combo This combo box
36644              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36645              * @param {Number} index The index of the selected item in the dropdown list
36646              */
36647         'select' : true,
36648         /**
36649          * @event beforequery
36650          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36651          * The event object passed has these properties:
36652              * @param {Roo.form.ComboBox} combo This combo box
36653              * @param {String} query The query
36654              * @param {Boolean} forceAll true to force "all" query
36655              * @param {Boolean} cancel true to cancel the query
36656              * @param {Object} e The query event object
36657              */
36658         'beforequery': true
36659     });
36660     if(this.transform){
36661         this.allowDomMove = false;
36662         var s = Roo.getDom(this.transform);
36663         if(!this.hiddenName){
36664             this.hiddenName = s.name;
36665         }
36666         if(!this.store){
36667             this.mode = 'local';
36668             var d = [], opts = s.options;
36669             for(var i = 0, len = opts.length;i < len; i++){
36670                 var o = opts[i];
36671                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36672                 if(o.selected) {
36673                     this.value = value;
36674                 }
36675                 d.push([value, o.text]);
36676             }
36677             this.store = new Roo.data.SimpleStore({
36678                 'id': 0,
36679                 fields: ['value', 'text'],
36680                 data : d
36681             });
36682             this.valueField = 'value';
36683             this.displayField = 'text';
36684         }
36685         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36686         if(!this.lazyRender){
36687             this.target = true;
36688             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36689             s.parentNode.removeChild(s); // remove it
36690             this.render(this.el.parentNode);
36691         }else{
36692             s.parentNode.removeChild(s); // remove it
36693         }
36694
36695     }
36696     if (this.store) {
36697         this.store = Roo.factory(this.store, Roo.data);
36698     }
36699     
36700     this.selectedIndex = -1;
36701     if(this.mode == 'local'){
36702         if(config.queryDelay === undefined){
36703             this.queryDelay = 10;
36704         }
36705         if(config.minChars === undefined){
36706             this.minChars = 0;
36707         }
36708     }
36709 };
36710
36711 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36712     /**
36713      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36714      */
36715     /**
36716      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36717      * rendering into an Roo.Editor, defaults to false)
36718      */
36719     /**
36720      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36721      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36722      */
36723     /**
36724      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36725      */
36726     /**
36727      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36728      * the dropdown list (defaults to undefined, with no header element)
36729      */
36730
36731      /**
36732      * @cfg {String/Roo.Template} tpl The template to use to render the output
36733      */
36734      
36735     // private
36736     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36737     /**
36738      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36739      */
36740     listWidth: undefined,
36741     /**
36742      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36743      * mode = 'remote' or 'text' if mode = 'local')
36744      */
36745     displayField: undefined,
36746     /**
36747      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36748      * mode = 'remote' or 'value' if mode = 'local'). 
36749      * Note: use of a valueField requires the user make a selection
36750      * in order for a value to be mapped.
36751      */
36752     valueField: undefined,
36753     /**
36754      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36755      * field's data value (defaults to the underlying DOM element's name)
36756      */
36757     hiddenName: undefined,
36758     /**
36759      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36760      */
36761     listClass: '',
36762     /**
36763      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36764      */
36765     selectedClass: 'x-combo-selected',
36766     /**
36767      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36768      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36769      * which displays a downward arrow icon).
36770      */
36771     triggerClass : 'x-form-arrow-trigger',
36772     /**
36773      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36774      */
36775     shadow:'sides',
36776     /**
36777      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36778      * anchor positions (defaults to 'tl-bl')
36779      */
36780     listAlign: 'tl-bl?',
36781     /**
36782      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36783      */
36784     maxHeight: 300,
36785     /**
36786      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36787      * query specified by the allQuery config option (defaults to 'query')
36788      */
36789     triggerAction: 'query',
36790     /**
36791      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36792      * (defaults to 4, does not apply if editable = false)
36793      */
36794     minChars : 4,
36795     /**
36796      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36797      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36798      */
36799     typeAhead: false,
36800     /**
36801      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36802      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36803      */
36804     queryDelay: 500,
36805     /**
36806      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36807      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36808      */
36809     pageSize: 0,
36810     /**
36811      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36812      * when editable = true (defaults to false)
36813      */
36814     selectOnFocus:false,
36815     /**
36816      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36817      */
36818     queryParam: 'query',
36819     /**
36820      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36821      * when mode = 'remote' (defaults to 'Loading...')
36822      */
36823     loadingText: 'Loading...',
36824     /**
36825      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36826      */
36827     resizable: false,
36828     /**
36829      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36830      */
36831     handleHeight : 8,
36832     /**
36833      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36834      * traditional select (defaults to true)
36835      */
36836     editable: true,
36837     /**
36838      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36839      */
36840     allQuery: '',
36841     /**
36842      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36843      */
36844     mode: 'remote',
36845     /**
36846      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36847      * listWidth has a higher value)
36848      */
36849     minListWidth : 70,
36850     /**
36851      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36852      * allow the user to set arbitrary text into the field (defaults to false)
36853      */
36854     forceSelection:false,
36855     /**
36856      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36857      * if typeAhead = true (defaults to 250)
36858      */
36859     typeAheadDelay : 250,
36860     /**
36861      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36862      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36863      */
36864     valueNotFoundText : undefined,
36865     /**
36866      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36867      */
36868     blockFocus : false,
36869     
36870     /**
36871      * @cfg {bool} disableClear Disable showing of clear button.
36872      */
36873     disableClear : false,
36874     
36875     // private
36876     onRender : function(ct, position){
36877         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36878         if(this.hiddenName){
36879             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36880                     'before', true);
36881             this.hiddenField.value =
36882                 this.hiddenValue !== undefined ? this.hiddenValue :
36883                 this.value !== undefined ? this.value : '';
36884
36885             // prevent input submission
36886             this.el.dom.removeAttribute('name');
36887         }
36888         if(Roo.isGecko){
36889             this.el.dom.setAttribute('autocomplete', 'off');
36890         }
36891
36892         var cls = 'x-combo-list';
36893
36894         this.list = new Roo.Layer({
36895             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36896         });
36897
36898         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36899         this.list.setWidth(lw);
36900         this.list.swallowEvent('mousewheel');
36901         this.assetHeight = 0;
36902
36903         if(this.title){
36904             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36905             this.assetHeight += this.header.getHeight();
36906         }
36907
36908         this.innerList = this.list.createChild({cls:cls+'-inner'});
36909         this.innerList.on('mouseover', this.onViewOver, this);
36910         this.innerList.on('mousemove', this.onViewMove, this);
36911         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36912         
36913         if(this.allowBlank && !this.pageSize && !this.disableClear){
36914             this.footer = this.list.createChild({cls:cls+'-ft'});
36915             this.pageTb = new Roo.Toolbar(this.footer);
36916            
36917         }
36918         if(this.pageSize){
36919             this.footer = this.list.createChild({cls:cls+'-ft'});
36920             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36921                     {pageSize: this.pageSize});
36922             
36923         }
36924         
36925         if (this.pageTb && this.allowBlank && !this.disableClear) {
36926             var _this = this;
36927             this.pageTb.add(new Roo.Toolbar.Fill(), {
36928                 cls: 'x-btn-icon x-btn-clear',
36929                 text: '&#160;',
36930                 handler: function()
36931                 {
36932                     _this.collapse();
36933                     _this.clearValue();
36934                     _this.onSelect(false, -1);
36935                 }
36936             });
36937         }
36938         if (this.footer) {
36939             this.assetHeight += this.footer.getHeight();
36940         }
36941         
36942
36943         if(!this.tpl){
36944             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36945         }
36946
36947         this.view = new Roo.View(this.innerList, this.tpl, {
36948             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36949         });
36950
36951         this.view.on('click', this.onViewClick, this);
36952
36953         this.store.on('beforeload', this.onBeforeLoad, this);
36954         this.store.on('load', this.onLoad, this);
36955         this.store.on('loadexception', this.collapse, this);
36956
36957         if(this.resizable){
36958             this.resizer = new Roo.Resizable(this.list,  {
36959                pinned:true, handles:'se'
36960             });
36961             this.resizer.on('resize', function(r, w, h){
36962                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36963                 this.listWidth = w;
36964                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36965                 this.restrictHeight();
36966             }, this);
36967             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36968         }
36969         if(!this.editable){
36970             this.editable = true;
36971             this.setEditable(false);
36972         }
36973     },
36974
36975     // private
36976     initEvents : function(){
36977         Roo.form.ComboBox.superclass.initEvents.call(this);
36978
36979         this.keyNav = new Roo.KeyNav(this.el, {
36980             "up" : function(e){
36981                 this.inKeyMode = true;
36982                 this.selectPrev();
36983             },
36984
36985             "down" : function(e){
36986                 if(!this.isExpanded()){
36987                     this.onTriggerClick();
36988                 }else{
36989                     this.inKeyMode = true;
36990                     this.selectNext();
36991                 }
36992             },
36993
36994             "enter" : function(e){
36995                 this.onViewClick();
36996                 //return true;
36997             },
36998
36999             "esc" : function(e){
37000                 this.collapse();
37001             },
37002
37003             "tab" : function(e){
37004                 this.onViewClick(false);
37005                 return true;
37006             },
37007
37008             scope : this,
37009
37010             doRelay : function(foo, bar, hname){
37011                 if(hname == 'down' || this.scope.isExpanded()){
37012                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37013                 }
37014                 return true;
37015             },
37016
37017             forceKeyDown: true
37018         });
37019         this.queryDelay = Math.max(this.queryDelay || 10,
37020                 this.mode == 'local' ? 10 : 250);
37021         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37022         if(this.typeAhead){
37023             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37024         }
37025         if(this.editable !== false){
37026             this.el.on("keyup", this.onKeyUp, this);
37027         }
37028         if(this.forceSelection){
37029             this.on('blur', this.doForce, this);
37030         }
37031     },
37032
37033     onDestroy : function(){
37034         if(this.view){
37035             this.view.setStore(null);
37036             this.view.el.removeAllListeners();
37037             this.view.el.remove();
37038             this.view.purgeListeners();
37039         }
37040         if(this.list){
37041             this.list.destroy();
37042         }
37043         if(this.store){
37044             this.store.un('beforeload', this.onBeforeLoad, this);
37045             this.store.un('load', this.onLoad, this);
37046             this.store.un('loadexception', this.collapse, this);
37047         }
37048         Roo.form.ComboBox.superclass.onDestroy.call(this);
37049     },
37050
37051     // private
37052     fireKey : function(e){
37053         if(e.isNavKeyPress() && !this.list.isVisible()){
37054             this.fireEvent("specialkey", this, e);
37055         }
37056     },
37057
37058     // private
37059     onResize: function(w, h){
37060         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37061         if(this.list && this.listWidth === undefined){
37062             var lw = Math.max(w, this.minListWidth);
37063             this.list.setWidth(lw);
37064             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37065         }
37066     },
37067
37068     /**
37069      * Allow or prevent the user from directly editing the field text.  If false is passed,
37070      * the user will only be able to select from the items defined in the dropdown list.  This method
37071      * is the runtime equivalent of setting the 'editable' config option at config time.
37072      * @param {Boolean} value True to allow the user to directly edit the field text
37073      */
37074     setEditable : function(value){
37075         if(value == this.editable){
37076             return;
37077         }
37078         this.editable = value;
37079         if(!value){
37080             this.el.dom.setAttribute('readOnly', true);
37081             this.el.on('mousedown', this.onTriggerClick,  this);
37082             this.el.addClass('x-combo-noedit');
37083         }else{
37084             this.el.dom.setAttribute('readOnly', false);
37085             this.el.un('mousedown', this.onTriggerClick,  this);
37086             this.el.removeClass('x-combo-noedit');
37087         }
37088     },
37089
37090     // private
37091     onBeforeLoad : function(){
37092         if(!this.hasFocus){
37093             return;
37094         }
37095         this.innerList.update(this.loadingText ?
37096                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37097         this.restrictHeight();
37098         this.selectedIndex = -1;
37099     },
37100
37101     // private
37102     onLoad : function(){
37103         if(!this.hasFocus){
37104             return;
37105         }
37106         if(this.store.getCount() > 0){
37107             this.expand();
37108             this.restrictHeight();
37109             if(this.lastQuery == this.allQuery){
37110                 if(this.editable){
37111                     this.el.dom.select();
37112                 }
37113                 if(!this.selectByValue(this.value, true)){
37114                     this.select(0, true);
37115                 }
37116             }else{
37117                 this.selectNext();
37118                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37119                     this.taTask.delay(this.typeAheadDelay);
37120                 }
37121             }
37122         }else{
37123             this.onEmptyResults();
37124         }
37125         //this.el.focus();
37126     },
37127
37128     // private
37129     onTypeAhead : function(){
37130         if(this.store.getCount() > 0){
37131             var r = this.store.getAt(0);
37132             var newValue = r.data[this.displayField];
37133             var len = newValue.length;
37134             var selStart = this.getRawValue().length;
37135             if(selStart != len){
37136                 this.setRawValue(newValue);
37137                 this.selectText(selStart, newValue.length);
37138             }
37139         }
37140     },
37141
37142     // private
37143     onSelect : function(record, index){
37144         if(this.fireEvent('beforeselect', this, record, index) !== false){
37145             this.setFromData(index > -1 ? record.data : false);
37146             this.collapse();
37147             this.fireEvent('select', this, record, index);
37148         }
37149     },
37150
37151     /**
37152      * Returns the currently selected field value or empty string if no value is set.
37153      * @return {String} value The selected value
37154      */
37155     getValue : function(){
37156         if(this.valueField){
37157             return typeof this.value != 'undefined' ? this.value : '';
37158         }else{
37159             return Roo.form.ComboBox.superclass.getValue.call(this);
37160         }
37161     },
37162
37163     /**
37164      * Clears any text/value currently set in the field
37165      */
37166     clearValue : function(){
37167         if(this.hiddenField){
37168             this.hiddenField.value = '';
37169         }
37170         this.value = '';
37171         this.setRawValue('');
37172         this.lastSelectionText = '';
37173         this.applyEmptyText();
37174     },
37175
37176     /**
37177      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37178      * will be displayed in the field.  If the value does not match the data value of an existing item,
37179      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37180      * Otherwise the field will be blank (although the value will still be set).
37181      * @param {String} value The value to match
37182      */
37183     setValue : function(v){
37184         var text = v;
37185         if(this.valueField){
37186             var r = this.findRecord(this.valueField, v);
37187             if(r){
37188                 text = r.data[this.displayField];
37189             }else if(this.valueNotFoundText !== undefined){
37190                 text = this.valueNotFoundText;
37191             }
37192         }
37193         this.lastSelectionText = text;
37194         if(this.hiddenField){
37195             this.hiddenField.value = v;
37196         }
37197         Roo.form.ComboBox.superclass.setValue.call(this, text);
37198         this.value = v;
37199     },
37200     /**
37201      * @property {Object} the last set data for the element
37202      */
37203     
37204     lastData : false,
37205     /**
37206      * Sets the value of the field based on a object which is related to the record format for the store.
37207      * @param {Object} value the value to set as. or false on reset?
37208      */
37209     setFromData : function(o){
37210         var dv = ''; // display value
37211         var vv = ''; // value value..
37212         this.lastData = o;
37213         if (this.displayField) {
37214             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37215         } else {
37216             // this is an error condition!!!
37217             console.log('no value field set for '+ this.name);
37218         }
37219         
37220         if(this.valueField){
37221             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37222         }
37223         if(this.hiddenField){
37224             this.hiddenField.value = vv;
37225             
37226             this.lastSelectionText = dv;
37227             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37228             this.value = vv;
37229             return;
37230         }
37231         // no hidden field.. - we store the value in 'value', but still display
37232         // display field!!!!
37233         this.lastSelectionText = dv;
37234         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37235         this.value = vv;
37236         
37237         
37238     },
37239     // private
37240     reset : function(){
37241         // overridden so that last data is reset..
37242         this.setValue(this.originalValue);
37243         this.clearInvalid();
37244         this.lastData = false;
37245     },
37246     // private
37247     findRecord : function(prop, value){
37248         var record;
37249         if(this.store.getCount() > 0){
37250             this.store.each(function(r){
37251                 if(r.data[prop] == value){
37252                     record = r;
37253                     return false;
37254                 }
37255             });
37256         }
37257         return record;
37258     },
37259
37260     // private
37261     onViewMove : function(e, t){
37262         this.inKeyMode = false;
37263     },
37264
37265     // private
37266     onViewOver : function(e, t){
37267         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37268             return;
37269         }
37270         var item = this.view.findItemFromChild(t);
37271         if(item){
37272             var index = this.view.indexOf(item);
37273             this.select(index, false);
37274         }
37275     },
37276
37277     // private
37278     onViewClick : function(doFocus){
37279         var index = this.view.getSelectedIndexes()[0];
37280         var r = this.store.getAt(index);
37281         if(r){
37282             this.onSelect(r, index);
37283         }
37284         if(doFocus !== false && !this.blockFocus){
37285             this.el.focus();
37286         }
37287     },
37288
37289     // private
37290     restrictHeight : function(){
37291         this.innerList.dom.style.height = '';
37292         var inner = this.innerList.dom;
37293         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37294         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37295         this.list.beginUpdate();
37296         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37297         this.list.alignTo(this.el, this.listAlign);
37298         this.list.endUpdate();
37299     },
37300
37301     // private
37302     onEmptyResults : function(){
37303         this.collapse();
37304     },
37305
37306     /**
37307      * Returns true if the dropdown list is expanded, else false.
37308      */
37309     isExpanded : function(){
37310         return this.list.isVisible();
37311     },
37312
37313     /**
37314      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37315      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37316      * @param {String} value The data value of the item to select
37317      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37318      * selected item if it is not currently in view (defaults to true)
37319      * @return {Boolean} True if the value matched an item in the list, else false
37320      */
37321     selectByValue : function(v, scrollIntoView){
37322         if(v !== undefined && v !== null){
37323             var r = this.findRecord(this.valueField || this.displayField, v);
37324             if(r){
37325                 this.select(this.store.indexOf(r), scrollIntoView);
37326                 return true;
37327             }
37328         }
37329         return false;
37330     },
37331
37332     /**
37333      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37335      * @param {Number} index The zero-based index of the list item to select
37336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37337      * selected item if it is not currently in view (defaults to true)
37338      */
37339     select : function(index, scrollIntoView){
37340         this.selectedIndex = index;
37341         this.view.select(index);
37342         if(scrollIntoView !== false){
37343             var el = this.view.getNode(index);
37344             if(el){
37345                 this.innerList.scrollChildIntoView(el, false);
37346             }
37347         }
37348     },
37349
37350     // private
37351     selectNext : function(){
37352         var ct = this.store.getCount();
37353         if(ct > 0){
37354             if(this.selectedIndex == -1){
37355                 this.select(0);
37356             }else if(this.selectedIndex < ct-1){
37357                 this.select(this.selectedIndex+1);
37358             }
37359         }
37360     },
37361
37362     // private
37363     selectPrev : function(){
37364         var ct = this.store.getCount();
37365         if(ct > 0){
37366             if(this.selectedIndex == -1){
37367                 this.select(0);
37368             }else if(this.selectedIndex != 0){
37369                 this.select(this.selectedIndex-1);
37370             }
37371         }
37372     },
37373
37374     // private
37375     onKeyUp : function(e){
37376         if(this.editable !== false && !e.isSpecialKey()){
37377             this.lastKey = e.getKey();
37378             this.dqTask.delay(this.queryDelay);
37379         }
37380     },
37381
37382     // private
37383     validateBlur : function(){
37384         return !this.list || !this.list.isVisible();   
37385     },
37386
37387     // private
37388     initQuery : function(){
37389         this.doQuery(this.getRawValue());
37390     },
37391
37392     // private
37393     doForce : function(){
37394         if(this.el.dom.value.length > 0){
37395             this.el.dom.value =
37396                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37397             this.applyEmptyText();
37398         }
37399     },
37400
37401     /**
37402      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37403      * query allowing the query action to be canceled if needed.
37404      * @param {String} query The SQL query to execute
37405      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37406      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37407      * saved in the current store (defaults to false)
37408      */
37409     doQuery : function(q, forceAll){
37410         if(q === undefined || q === null){
37411             q = '';
37412         }
37413         var qe = {
37414             query: q,
37415             forceAll: forceAll,
37416             combo: this,
37417             cancel:false
37418         };
37419         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37420             return false;
37421         }
37422         q = qe.query;
37423         forceAll = qe.forceAll;
37424         if(forceAll === true || (q.length >= this.minChars)){
37425             if(this.lastQuery != q){
37426                 this.lastQuery = q;
37427                 if(this.mode == 'local'){
37428                     this.selectedIndex = -1;
37429                     if(forceAll){
37430                         this.store.clearFilter();
37431                     }else{
37432                         this.store.filter(this.displayField, q);
37433                     }
37434                     this.onLoad();
37435                 }else{
37436                     this.store.baseParams[this.queryParam] = q;
37437                     this.store.load({
37438                         params: this.getParams(q)
37439                     });
37440                     this.expand();
37441                 }
37442             }else{
37443                 this.selectedIndex = -1;
37444                 this.onLoad();   
37445             }
37446         }
37447     },
37448
37449     // private
37450     getParams : function(q){
37451         var p = {};
37452         //p[this.queryParam] = q;
37453         if(this.pageSize){
37454             p.start = 0;
37455             p.limit = this.pageSize;
37456         }
37457         return p;
37458     },
37459
37460     /**
37461      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37462      */
37463     collapse : function(){
37464         if(!this.isExpanded()){
37465             return;
37466         }
37467         this.list.hide();
37468         Roo.get(document).un('mousedown', this.collapseIf, this);
37469         Roo.get(document).un('mousewheel', this.collapseIf, this);
37470         this.fireEvent('collapse', this);
37471     },
37472
37473     // private
37474     collapseIf : function(e){
37475         if(!e.within(this.wrap) && !e.within(this.list)){
37476             this.collapse();
37477         }
37478     },
37479
37480     /**
37481      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37482      */
37483     expand : function(){
37484         if(this.isExpanded() || !this.hasFocus){
37485             return;
37486         }
37487         this.list.alignTo(this.el, this.listAlign);
37488         this.list.show();
37489         Roo.get(document).on('mousedown', this.collapseIf, this);
37490         Roo.get(document).on('mousewheel', this.collapseIf, this);
37491         this.fireEvent('expand', this);
37492     },
37493
37494     // private
37495     // Implements the default empty TriggerField.onTriggerClick function
37496     onTriggerClick : function(){
37497         if(this.disabled){
37498             return;
37499         }
37500         if(this.isExpanded()){
37501             this.collapse();
37502             if (!this.blockFocus) {
37503                 this.el.focus();
37504             }
37505             
37506         }else {
37507             this.hasFocus = true;
37508             if(this.triggerAction == 'all') {
37509                 this.doQuery(this.allQuery, true);
37510             } else {
37511                 this.doQuery(this.getRawValue());
37512             }
37513             if (!this.blockFocus) {
37514                 this.el.focus();
37515             }
37516         }
37517     }
37518
37519     /** 
37520     * @cfg {Boolean} grow 
37521     * @hide 
37522     */
37523     /** 
37524     * @cfg {Number} growMin 
37525     * @hide 
37526     */
37527     /** 
37528     * @cfg {Number} growMax 
37529     * @hide 
37530     */
37531     /**
37532      * @hide
37533      * @method autoSize
37534      */
37535 });/*
37536  * Based on:
37537  * Ext JS Library 1.1.1
37538  * Copyright(c) 2006-2007, Ext JS, LLC.
37539  *
37540  * Originally Released Under LGPL - original licence link has changed is not relivant.
37541  *
37542  * Fork - LGPL
37543  * <script type="text/javascript">
37544  */
37545 /**
37546  * @class Roo.form.Checkbox
37547  * @extends Roo.form.Field
37548  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37549  * @constructor
37550  * Creates a new Checkbox
37551  * @param {Object} config Configuration options
37552  */
37553 Roo.form.Checkbox = function(config){
37554     Roo.form.Checkbox.superclass.constructor.call(this, config);
37555     this.addEvents({
37556         /**
37557          * @event check
37558          * Fires when the checkbox is checked or unchecked.
37559              * @param {Roo.form.Checkbox} this This checkbox
37560              * @param {Boolean} checked The new checked value
37561              */
37562         check : true
37563     });
37564 };
37565
37566 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37567     /**
37568      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37569      */
37570     focusClass : undefined,
37571     /**
37572      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37573      */
37574     fieldClass: "x-form-field",
37575     /**
37576      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37577      */
37578     checked: false,
37579     /**
37580      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37581      * {tag: "input", type: "checkbox", autocomplete: "off"})
37582      */
37583     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37584     /**
37585      * @cfg {String} boxLabel The text that appears beside the checkbox
37586      */
37587     boxLabel : "",
37588     /**
37589      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37590      */  
37591     inputValue : '1',
37592     /**
37593      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37594      */
37595      valueOff: '0', // value when not checked..
37596
37597     actionMode : 'viewEl', 
37598     //
37599     // private
37600     itemCls : 'x-menu-check-item x-form-item',
37601     groupClass : 'x-menu-group-item',
37602     inputType : 'hidden',
37603     
37604     
37605     inSetChecked: false, // check that we are not calling self...
37606     
37607     inputElement: false, // real input element?
37608     basedOn: false, // ????
37609     
37610     isFormField: true, // not sure where this is needed!!!!
37611
37612     onResize : function(){
37613         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37614         if(!this.boxLabel){
37615             this.el.alignTo(this.wrap, 'c-c');
37616         }
37617     },
37618
37619     initEvents : function(){
37620         Roo.form.Checkbox.superclass.initEvents.call(this);
37621         this.el.on("click", this.onClick,  this);
37622         this.el.on("change", this.onClick,  this);
37623     },
37624
37625
37626     getResizeEl : function(){
37627         return this.wrap;
37628     },
37629
37630     getPositionEl : function(){
37631         return this.wrap;
37632     },
37633
37634     // private
37635     onRender : function(ct, position){
37636         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37637         /*
37638         if(this.inputValue !== undefined){
37639             this.el.dom.value = this.inputValue;
37640         }
37641         */
37642         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37643         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37644         var viewEl = this.wrap.createChild({ 
37645             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37646         this.viewEl = viewEl;   
37647         this.wrap.on('click', this.onClick,  this); 
37648         
37649         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37650         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37651         
37652         
37653         
37654         if(this.boxLabel){
37655             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37656         //    viewEl.on('click', this.onClick,  this); 
37657         }
37658         //if(this.checked){
37659             this.setChecked(this.checked);
37660         //}else{
37661             //this.checked = this.el.dom;
37662         //}
37663
37664     },
37665
37666     // private
37667     initValue : Roo.emptyFn,
37668
37669     /**
37670      * Returns the checked state of the checkbox.
37671      * @return {Boolean} True if checked, else false
37672      */
37673     getValue : function(){
37674         if(this.el){
37675             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37676         }
37677         return this.valueOff;
37678         
37679     },
37680
37681         // private
37682     onClick : function(){ 
37683         this.setChecked(!this.checked);
37684
37685         //if(this.el.dom.checked != this.checked){
37686         //    this.setValue(this.el.dom.checked);
37687        // }
37688     },
37689
37690     /**
37691      * Sets the checked state of the checkbox.
37692      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37693      */
37694     setValue : function(v,suppressEvent){
37695         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37696         //if(this.el && this.el.dom){
37697         //    this.el.dom.checked = this.checked;
37698         //    this.el.dom.defaultChecked = this.checked;
37699         //}
37700         this.setChecked(v === this.inputValue);
37701         //this.fireEvent("check", this, this.checked);
37702     },
37703     // private..
37704     setChecked : function(state,suppressEvent)
37705     {
37706         if (this.inSetChecked) {
37707             this.checked = state;
37708             return;
37709         }
37710         
37711     
37712         if(this.wrap){
37713             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37714         }
37715         this.checked = state;
37716         if(suppressEvent !== true){
37717             this.fireEvent('checkchange', this, state);
37718         }
37719         this.inSetChecked = true;
37720         this.el.dom.value = state ? this.inputValue : this.valueOff;
37721         this.inSetChecked = false;
37722         
37723     },
37724     // handle setting of hidden value by some other method!!?!?
37725     setFromHidden: function()
37726     {
37727         if(!this.el){
37728             return;
37729         }
37730         //console.log("SET FROM HIDDEN");
37731         //alert('setFrom hidden');
37732         this.setValue(this.el.dom.value);
37733     },
37734     
37735     onDestroy : function()
37736     {
37737         if(this.viewEl){
37738             Roo.get(this.viewEl).remove();
37739         }
37740          
37741         Roo.form.Checkbox.superclass.onDestroy.call(this);
37742     }
37743
37744 });/*
37745  * Based on:
37746  * Ext JS Library 1.1.1
37747  * Copyright(c) 2006-2007, Ext JS, LLC.
37748  *
37749  * Originally Released Under LGPL - original licence link has changed is not relivant.
37750  *
37751  * Fork - LGPL
37752  * <script type="text/javascript">
37753  */
37754  
37755 /**
37756  * @class Roo.form.Radio
37757  * @extends Roo.form.Checkbox
37758  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37759  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37760  * @constructor
37761  * Creates a new Radio
37762  * @param {Object} config Configuration options
37763  */
37764 Roo.form.Radio = function(){
37765     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37766 };
37767 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37768     inputType: 'radio',
37769
37770     /**
37771      * If this radio is part of a group, it will return the selected value
37772      * @return {String}
37773      */
37774     getGroupValue : function(){
37775         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37776     }
37777 });//<script type="text/javascript">
37778
37779 /*
37780  * Ext JS Library 1.1.1
37781  * Copyright(c) 2006-2007, Ext JS, LLC.
37782  * licensing@extjs.com
37783  * 
37784  * http://www.extjs.com/license
37785  */
37786  
37787  /*
37788   * 
37789   * Known bugs:
37790   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37791   * - IE ? - no idea how much works there.
37792   * 
37793   * 
37794   * 
37795   */
37796  
37797
37798 /**
37799  * @class Ext.form.HtmlEditor
37800  * @extends Ext.form.Field
37801  * Provides a lightweight HTML Editor component.
37802  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37803  * 
37804  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37805  * supported by this editor.</b><br/><br/>
37806  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37807  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37808  */
37809 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37810       /**
37811      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37812      */
37813     toolbars : false,
37814     /**
37815      * @cfg {String} createLinkText The default text for the create link prompt
37816      */
37817     createLinkText : 'Please enter the URL for the link:',
37818     /**
37819      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37820      */
37821     defaultLinkValue : 'http:/'+'/',
37822    
37823     
37824     // id of frame..
37825     frameId: false,
37826     
37827     // private properties
37828     validationEvent : false,
37829     deferHeight: true,
37830     initialized : false,
37831     activated : false,
37832     sourceEditMode : false,
37833     onFocus : Roo.emptyFn,
37834     iframePad:3,
37835     hideMode:'offsets',
37836     defaultAutoCreate : {
37837         tag: "textarea",
37838         style:"width:500px;height:300px;",
37839         autocomplete: "off"
37840     },
37841
37842     // private
37843     initComponent : function(){
37844         this.addEvents({
37845             /**
37846              * @event initialize
37847              * Fires when the editor is fully initialized (including the iframe)
37848              * @param {HtmlEditor} this
37849              */
37850             initialize: true,
37851             /**
37852              * @event activate
37853              * Fires when the editor is first receives the focus. Any insertion must wait
37854              * until after this event.
37855              * @param {HtmlEditor} this
37856              */
37857             activate: true,
37858              /**
37859              * @event beforesync
37860              * Fires before the textarea is updated with content from the editor iframe. Return false
37861              * to cancel the sync.
37862              * @param {HtmlEditor} this
37863              * @param {String} html
37864              */
37865             beforesync: true,
37866              /**
37867              * @event beforepush
37868              * Fires before the iframe editor is updated with content from the textarea. Return false
37869              * to cancel the push.
37870              * @param {HtmlEditor} this
37871              * @param {String} html
37872              */
37873             beforepush: true,
37874              /**
37875              * @event sync
37876              * Fires when the textarea is updated with content from the editor iframe.
37877              * @param {HtmlEditor} this
37878              * @param {String} html
37879              */
37880             sync: true,
37881              /**
37882              * @event push
37883              * Fires when the iframe editor is updated with content from the textarea.
37884              * @param {HtmlEditor} this
37885              * @param {String} html
37886              */
37887             push: true,
37888              /**
37889              * @event editmodechange
37890              * Fires when the editor switches edit modes
37891              * @param {HtmlEditor} this
37892              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37893              */
37894             editmodechange: true,
37895             /**
37896              * @event editorevent
37897              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37898              * @param {HtmlEditor} this
37899              */
37900             editorevent: true
37901         })
37902     },
37903
37904     /**
37905      * Protected method that will not generally be called directly. It
37906      * is called when the editor creates its toolbar. Override this method if you need to
37907      * add custom toolbar buttons.
37908      * @param {HtmlEditor} editor
37909      */
37910     createToolbar : function(editor){
37911         if (!editor.toolbars || !editor.toolbars.length) {
37912             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37913         }
37914         
37915         for (var i =0 ; i < editor.toolbars.length;i++) {
37916             editor.toolbars[i].init(editor);
37917         }
37918          
37919         
37920     },
37921
37922     /**
37923      * Protected method that will not generally be called directly. It
37924      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37925      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37926      */
37927     getDocMarkup : function(){
37928         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37929     },
37930
37931     // private
37932     onRender : function(ct, position){
37933         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37934         this.el.dom.style.border = '0 none';
37935         this.el.dom.setAttribute('tabIndex', -1);
37936         this.el.addClass('x-hidden');
37937         if(Roo.isIE){ // fix IE 1px bogus margin
37938             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37939         }
37940         this.wrap = this.el.wrap({
37941             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37942         });
37943
37944         this.frameId = Roo.id();
37945         this.createToolbar(this);
37946         
37947         
37948         
37949         
37950       
37951         
37952         var iframe = this.wrap.createChild({
37953             tag: 'iframe',
37954             id: this.frameId,
37955             name: this.frameId,
37956             frameBorder : 'no',
37957             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37958         });
37959         
37960        // console.log(iframe);
37961         //this.wrap.dom.appendChild(iframe);
37962
37963         this.iframe = iframe.dom;
37964
37965          this.assignDocWin();
37966         
37967         this.doc.designMode = 'on';
37968        
37969         this.doc.open();
37970         this.doc.write(this.getDocMarkup());
37971         this.doc.close();
37972
37973         
37974         var task = { // must defer to wait for browser to be ready
37975             run : function(){
37976                 //console.log("run task?" + this.doc.readyState);
37977                 this.assignDocWin();
37978                 if(this.doc.body || this.doc.readyState == 'complete'){
37979                     try {
37980                         
37981                        
37982                         this.doc.designMode="on";
37983                     } catch (e) {
37984                         return;
37985                     }
37986                     Roo.TaskMgr.stop(task);
37987                     this.initEditor.defer(10, this);
37988                 }
37989             },
37990             interval : 10,
37991             duration:10000,
37992             scope: this
37993         };
37994         Roo.TaskMgr.start(task);
37995
37996         if(!this.width){
37997             this.setSize(this.el.getSize());
37998         }
37999     },
38000
38001     // private
38002     onResize : function(w, h){
38003         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38004         if(this.el && this.iframe){
38005             if(typeof w == 'number'){
38006                 var aw = w - this.wrap.getFrameWidth('lr');
38007                 this.el.setWidth(this.adjustWidth('textarea', aw));
38008                 this.iframe.style.width = aw + 'px';
38009             }
38010             if(typeof h == 'number'){
38011                 var tbh = 0;
38012                 for (var i =0; i < this.toolbars.length;i++) {
38013                     // fixme - ask toolbars for heights?
38014                     tbh += this.toolbars[i].tb.el.getHeight();
38015                 }
38016                 
38017                 
38018                 
38019                 
38020                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38021                 this.el.setHeight(this.adjustWidth('textarea', ah));
38022                 this.iframe.style.height = ah + 'px';
38023                 if(this.doc){
38024                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38025                 }
38026             }
38027         }
38028     },
38029
38030     /**
38031      * Toggles the editor between standard and source edit mode.
38032      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38033      */
38034     toggleSourceEdit : function(sourceEditMode){
38035         
38036         this.sourceEditMode = sourceEditMode === true;
38037         
38038         if(this.sourceEditMode){
38039           
38040             this.syncValue();
38041             this.iframe.className = 'x-hidden';
38042             this.el.removeClass('x-hidden');
38043             this.el.dom.removeAttribute('tabIndex');
38044             this.el.focus();
38045         }else{
38046              
38047             this.pushValue();
38048             this.iframe.className = '';
38049             this.el.addClass('x-hidden');
38050             this.el.dom.setAttribute('tabIndex', -1);
38051             this.deferFocus();
38052         }
38053         this.setSize(this.wrap.getSize());
38054         this.fireEvent('editmodechange', this, this.sourceEditMode);
38055     },
38056
38057     // private used internally
38058     createLink : function(){
38059         var url = prompt(this.createLinkText, this.defaultLinkValue);
38060         if(url && url != 'http:/'+'/'){
38061             this.relayCmd('createlink', url);
38062         }
38063     },
38064
38065     // private (for BoxComponent)
38066     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38067
38068     // private (for BoxComponent)
38069     getResizeEl : function(){
38070         return this.wrap;
38071     },
38072
38073     // private (for BoxComponent)
38074     getPositionEl : function(){
38075         return this.wrap;
38076     },
38077
38078     // private
38079     initEvents : function(){
38080         this.originalValue = this.getValue();
38081     },
38082
38083     /**
38084      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38085      * @method
38086      */
38087     markInvalid : Roo.emptyFn,
38088     /**
38089      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38090      * @method
38091      */
38092     clearInvalid : Roo.emptyFn,
38093
38094     setValue : function(v){
38095         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38096         this.pushValue();
38097     },
38098
38099     /**
38100      * Protected method that will not generally be called directly. If you need/want
38101      * custom HTML cleanup, this is the method you should override.
38102      * @param {String} html The HTML to be cleaned
38103      * return {String} The cleaned HTML
38104      */
38105     cleanHtml : function(html){
38106         html = String(html);
38107         if(html.length > 5){
38108             if(Roo.isSafari){ // strip safari nonsense
38109                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38110             }
38111         }
38112         if(html == '&nbsp;'){
38113             html = '';
38114         }
38115         return html;
38116     },
38117
38118     /**
38119      * Protected method that will not generally be called directly. Syncs the contents
38120      * of the editor iframe with the textarea.
38121      */
38122     syncValue : function(){
38123         if(this.initialized){
38124             var bd = (this.doc.body || this.doc.documentElement);
38125             var html = bd.innerHTML;
38126             if(Roo.isSafari){
38127                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38128                 var m = bs.match(/text-align:(.*?);/i);
38129                 if(m && m[1]){
38130                     html = '<div style="'+m[0]+'">' + html + '</div>';
38131                 }
38132             }
38133             html = this.cleanHtml(html);
38134             if(this.fireEvent('beforesync', this, html) !== false){
38135                 this.el.dom.value = html;
38136                 this.fireEvent('sync', this, html);
38137             }
38138         }
38139     },
38140
38141     /**
38142      * Protected method that will not generally be called directly. Pushes the value of the textarea
38143      * into the iframe editor.
38144      */
38145     pushValue : function(){
38146         if(this.initialized){
38147             var v = this.el.dom.value;
38148             if(v.length < 1){
38149                 v = '&#160;';
38150             }
38151             if(this.fireEvent('beforepush', this, v) !== false){
38152                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38153                 this.fireEvent('push', this, v);
38154             }
38155         }
38156     },
38157
38158     // private
38159     deferFocus : function(){
38160         this.focus.defer(10, this);
38161     },
38162
38163     // doc'ed in Field
38164     focus : function(){
38165         if(this.win && !this.sourceEditMode){
38166             this.win.focus();
38167         }else{
38168             this.el.focus();
38169         }
38170     },
38171     
38172     assignDocWin: function()
38173     {
38174         var iframe = this.iframe;
38175         
38176          if(Roo.isIE){
38177             this.doc = iframe.contentWindow.document;
38178             this.win = iframe.contentWindow;
38179         } else {
38180             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38181             this.win = Roo.get(this.frameId).dom.contentWindow;
38182         }
38183     },
38184     
38185     // private
38186     initEditor : function(){
38187         //console.log("INIT EDITOR");
38188         this.assignDocWin();
38189         
38190         
38191         
38192         this.doc.designMode="on";
38193         this.doc.open();
38194         this.doc.write(this.getDocMarkup());
38195         this.doc.close();
38196         
38197         var dbody = (this.doc.body || this.doc.documentElement);
38198         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38199         // this copies styles from the containing element into thsi one..
38200         // not sure why we need all of this..
38201         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38202         ss['background-attachment'] = 'fixed'; // w3c
38203         dbody.bgProperties = 'fixed'; // ie
38204         Roo.DomHelper.applyStyles(dbody, ss);
38205         Roo.EventManager.on(this.doc, {
38206             'mousedown': this.onEditorEvent,
38207             'dblclick': this.onEditorEvent,
38208             'click': this.onEditorEvent,
38209             'keyup': this.onEditorEvent,
38210             buffer:100,
38211             scope: this
38212         });
38213         if(Roo.isGecko){
38214             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38215         }
38216         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38217             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38218         }
38219         this.initialized = true;
38220
38221         this.fireEvent('initialize', this);
38222         this.pushValue();
38223     },
38224
38225     // private
38226     onDestroy : function(){
38227         
38228         
38229         
38230         if(this.rendered){
38231             
38232             for (var i =0; i < this.toolbars.length;i++) {
38233                 // fixme - ask toolbars for heights?
38234                 this.toolbars[i].onDestroy();
38235             }
38236             
38237             this.wrap.dom.innerHTML = '';
38238             this.wrap.remove();
38239         }
38240     },
38241
38242     // private
38243     onFirstFocus : function(){
38244         
38245         this.assignDocWin();
38246         
38247         
38248         this.activated = true;
38249         for (var i =0; i < this.toolbars.length;i++) {
38250             this.toolbars[i].onFirstFocus();
38251         }
38252        
38253         if(Roo.isGecko){ // prevent silly gecko errors
38254             this.win.focus();
38255             var s = this.win.getSelection();
38256             if(!s.focusNode || s.focusNode.nodeType != 3){
38257                 var r = s.getRangeAt(0);
38258                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38259                 r.collapse(true);
38260                 this.deferFocus();
38261             }
38262             try{
38263                 this.execCmd('useCSS', true);
38264                 this.execCmd('styleWithCSS', false);
38265             }catch(e){}
38266         }
38267         this.fireEvent('activate', this);
38268     },
38269
38270     // private
38271     adjustFont: function(btn){
38272         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38273         //if(Roo.isSafari){ // safari
38274         //    adjust *= 2;
38275        // }
38276         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38277         if(Roo.isSafari){ // safari
38278             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38279             v =  (v < 10) ? 10 : v;
38280             v =  (v > 48) ? 48 : v;
38281             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38282             
38283         }
38284         
38285         
38286         v = Math.max(1, v+adjust);
38287         
38288         this.execCmd('FontSize', v  );
38289     },
38290
38291     onEditorEvent : function(e){
38292         this.fireEvent('editorevent', this, e);
38293       //  this.updateToolbar();
38294         this.syncValue();
38295     },
38296
38297     insertTag : function(tg)
38298     {
38299         // could be a bit smarter... -> wrap the current selected tRoo..
38300         
38301         this.execCmd("formatblock",   tg);
38302         
38303     },
38304     
38305     insertText : function(txt)
38306     {
38307         
38308         
38309         range = this.createRange();
38310         range.deleteContents();
38311                //alert(Sender.getAttribute('label'));
38312                
38313         range.insertNode(this.doc.createTextNode(txt));
38314     } ,
38315     
38316     // private
38317     relayBtnCmd : function(btn){
38318         this.relayCmd(btn.cmd);
38319     },
38320
38321     /**
38322      * Executes a Midas editor command on the editor document and performs necessary focus and
38323      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38324      * @param {String} cmd The Midas command
38325      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38326      */
38327     relayCmd : function(cmd, value){
38328         this.win.focus();
38329         this.execCmd(cmd, value);
38330         this.fireEvent('editorevent', this);
38331         //this.updateToolbar();
38332         this.deferFocus();
38333     },
38334
38335     /**
38336      * Executes a Midas editor command directly on the editor document.
38337      * For visual commands, you should use {@link #relayCmd} instead.
38338      * <b>This should only be called after the editor is initialized.</b>
38339      * @param {String} cmd The Midas command
38340      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38341      */
38342     execCmd : function(cmd, value){
38343         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38344         this.syncValue();
38345     },
38346
38347     // private
38348     applyCommand : function(e){
38349         if(e.ctrlKey){
38350             var c = e.getCharCode(), cmd;
38351             if(c > 0){
38352                 c = String.fromCharCode(c);
38353                 switch(c){
38354                     case 'b':
38355                         cmd = 'bold';
38356                     break;
38357                     case 'i':
38358                         cmd = 'italic';
38359                     break;
38360                     case 'u':
38361                         cmd = 'underline';
38362                     break;
38363                 }
38364                 if(cmd){
38365                     this.win.focus();
38366                     this.execCmd(cmd);
38367                     this.deferFocus();
38368                     e.preventDefault();
38369                 }
38370             }
38371         }
38372     },
38373
38374     /**
38375      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38376      * to insert tRoo.
38377      * @param {String} text
38378      */
38379     insertAtCursor : function(text){
38380         if(!this.activated){
38381             return;
38382         }
38383         if(Roo.isIE){
38384             this.win.focus();
38385             var r = this.doc.selection.createRange();
38386             if(r){
38387                 r.collapse(true);
38388                 r.pasteHTML(text);
38389                 this.syncValue();
38390                 this.deferFocus();
38391             }
38392         }else if(Roo.isGecko || Roo.isOpera){
38393             this.win.focus();
38394             this.execCmd('InsertHTML', text);
38395             this.deferFocus();
38396         }else if(Roo.isSafari){
38397             this.execCmd('InsertText', text);
38398             this.deferFocus();
38399         }
38400     },
38401
38402     // private
38403     fixKeys : function(){ // load time branching for fastest keydown performance
38404         if(Roo.isIE){
38405             return function(e){
38406                 var k = e.getKey(), r;
38407                 if(k == e.TAB){
38408                     e.stopEvent();
38409                     r = this.doc.selection.createRange();
38410                     if(r){
38411                         r.collapse(true);
38412                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38413                         this.deferFocus();
38414                     }
38415                 }else if(k == e.ENTER){
38416                     r = this.doc.selection.createRange();
38417                     if(r){
38418                         var target = r.parentElement();
38419                         if(!target || target.tagName.toLowerCase() != 'li'){
38420                             e.stopEvent();
38421                             r.pasteHTML('<br />');
38422                             r.collapse(false);
38423                             r.select();
38424                         }
38425                     }
38426                 }
38427             };
38428         }else if(Roo.isOpera){
38429             return function(e){
38430                 var k = e.getKey();
38431                 if(k == e.TAB){
38432                     e.stopEvent();
38433                     this.win.focus();
38434                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38435                     this.deferFocus();
38436                 }
38437             };
38438         }else if(Roo.isSafari){
38439             return function(e){
38440                 var k = e.getKey();
38441                 if(k == e.TAB){
38442                     e.stopEvent();
38443                     this.execCmd('InsertText','\t');
38444                     this.deferFocus();
38445                 }
38446              };
38447         }
38448     }(),
38449     
38450     getAllAncestors: function()
38451     {
38452         var p = this.getSelectedNode();
38453         var a = [];
38454         if (!p) {
38455             a.push(p); // push blank onto stack..
38456             p = this.getParentElement();
38457         }
38458         
38459         
38460         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38461             a.push(p);
38462             p = p.parentNode;
38463         }
38464         a.push(this.doc.body);
38465         return a;
38466     },
38467     lastSel : false,
38468     lastSelNode : false,
38469     
38470     
38471     getSelection : function() 
38472     {
38473         this.assignDocWin();
38474         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38475     },
38476     
38477     getSelectedNode: function() 
38478     {
38479         // this may only work on Gecko!!!
38480         
38481         // should we cache this!!!!
38482         
38483         
38484         
38485          
38486         var range = this.createRange(this.getSelection());
38487         
38488         if (Roo.isIE) {
38489             var parent = range.parentElement();
38490             while (true) {
38491                 var testRange = range.duplicate();
38492                 testRange.moveToElementText(parent);
38493                 if (testRange.inRange(range)) {
38494                     break;
38495                 }
38496                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38497                     break;
38498                 }
38499                 parent = parent.parentElement;
38500             }
38501             return parent;
38502         }
38503         
38504         
38505         var ar = range.endContainer.childNodes;
38506         if (!ar.length) {
38507             ar = range.commonAncestorContainer.childNodes;
38508             //alert(ar.length);
38509         }
38510         var nodes = [];
38511         var other_nodes = [];
38512         var has_other_nodes = false;
38513         for (var i=0;i<ar.length;i++) {
38514             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38515                 continue;
38516             }
38517             // fullly contained node.
38518             
38519             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38520                 nodes.push(ar[i]);
38521                 continue;
38522             }
38523             
38524             // probably selected..
38525             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38526                 other_nodes.push(ar[i]);
38527                 continue;
38528             }
38529             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38530                 continue;
38531             }
38532             
38533             
38534             has_other_nodes = true;
38535         }
38536         if (!nodes.length && other_nodes.length) {
38537             nodes= other_nodes;
38538         }
38539         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38540             return false;
38541         }
38542         
38543         return nodes[0];
38544     },
38545     createRange: function(sel)
38546     {
38547         // this has strange effects when using with 
38548         // top toolbar - not sure if it's a great idea.
38549         //this.editor.contentWindow.focus();
38550         if (typeof sel != "undefined") {
38551             try {
38552                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38553             } catch(e) {
38554                 return this.doc.createRange();
38555             }
38556         } else {
38557             return this.doc.createRange();
38558         }
38559     },
38560     getParentElement: function()
38561     {
38562         
38563         this.assignDocWin();
38564         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38565         
38566         var range = this.createRange(sel);
38567          
38568         try {
38569             var p = range.commonAncestorContainer;
38570             while (p.nodeType == 3) { // text node
38571                 p = p.parentNode;
38572             }
38573             return p;
38574         } catch (e) {
38575             return null;
38576         }
38577     
38578     },
38579     
38580     
38581     
38582     // BC Hacks - cause I cant work out what i was trying to do..
38583     rangeIntersectsNode : function(range, node)
38584     {
38585         var nodeRange = node.ownerDocument.createRange();
38586         try {
38587             nodeRange.selectNode(node);
38588         }
38589         catch (e) {
38590             nodeRange.selectNodeContents(node);
38591         }
38592
38593         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38594                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38595     },
38596     rangeCompareNode : function(range, node) {
38597         var nodeRange = node.ownerDocument.createRange();
38598         try {
38599             nodeRange.selectNode(node);
38600         } catch (e) {
38601             nodeRange.selectNodeContents(node);
38602         }
38603         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38604         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38605
38606         if (nodeIsBefore && !nodeIsAfter)
38607             return 0;
38608         if (!nodeIsBefore && nodeIsAfter)
38609             return 1;
38610         if (nodeIsBefore && nodeIsAfter)
38611             return 2;
38612
38613         return 3;
38614     }
38615
38616     
38617     
38618     // hide stuff that is not compatible
38619     /**
38620      * @event blur
38621      * @hide
38622      */
38623     /**
38624      * @event change
38625      * @hide
38626      */
38627     /**
38628      * @event focus
38629      * @hide
38630      */
38631     /**
38632      * @event specialkey
38633      * @hide
38634      */
38635     /**
38636      * @cfg {String} fieldClass @hide
38637      */
38638     /**
38639      * @cfg {String} focusClass @hide
38640      */
38641     /**
38642      * @cfg {String} autoCreate @hide
38643      */
38644     /**
38645      * @cfg {String} inputType @hide
38646      */
38647     /**
38648      * @cfg {String} invalidClass @hide
38649      */
38650     /**
38651      * @cfg {String} invalidText @hide
38652      */
38653     /**
38654      * @cfg {String} msgFx @hide
38655      */
38656     /**
38657      * @cfg {String} validateOnBlur @hide
38658      */
38659 });// <script type="text/javascript">
38660 /*
38661  * Based on
38662  * Ext JS Library 1.1.1
38663  * Copyright(c) 2006-2007, Ext JS, LLC.
38664  *  
38665  
38666  */
38667
38668 /**
38669  * @class Roo.form.HtmlEditorToolbar1
38670  * Basic Toolbar
38671  * 
38672  * Usage:
38673  *
38674  new Roo.form.HtmlEditor({
38675     ....
38676     toolbars : [
38677         new Roo.form.HtmlEditorToolbar1({
38678             disable : { fonts: 1 , format: 1, ..., ... , ...],
38679             btns : [ .... ]
38680         })
38681     }
38682      
38683  * 
38684  * @cfg {Object} disable List of elements to disable..
38685  * @cfg {Array} btns List of additional buttons.
38686  * 
38687  * 
38688  * NEEDS Extra CSS? 
38689  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38690  */
38691  
38692 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38693 {
38694     
38695     Roo.apply(this, config);
38696     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38697     // dont call parent... till later.
38698 }
38699
38700 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38701     
38702     tb: false,
38703     
38704     rendered: false,
38705     
38706     editor : false,
38707     /**
38708      * @cfg {Object} disable  List of toolbar elements to disable
38709          
38710      */
38711     disable : false,
38712       /**
38713      * @cfg {Array} fontFamilies An array of available font families
38714      */
38715     fontFamilies : [
38716         'Arial',
38717         'Courier New',
38718         'Tahoma',
38719         'Times New Roman',
38720         'Verdana'
38721     ],
38722     
38723     specialChars : [
38724            "&#169;",
38725           "&#174;",     
38726           "&#8482;",    
38727           "&#163;" ,    
38728          // "&#8212;",    
38729           "&#8230;",    
38730           "&#247;" ,    
38731         //  "&#225;" ,     ?? a acute?
38732            "&#8364;"    , //Euro
38733        //   "&#8220;"    ,
38734         //  "&#8221;"    ,
38735         //  "&#8226;"    ,
38736           "&#176;"  //   , // degrees
38737
38738          // "&#233;"     , // e ecute
38739          // "&#250;"     , // u ecute?
38740     ],
38741     inputElements : [ 
38742             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38743             "input:submit", "input:button", "select", "textarea", "label" ],
38744     formats : [
38745         ["p"] ,  
38746         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38747         ["pre"],[ "code"], 
38748         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38749     ],
38750      /**
38751      * @cfg {String} defaultFont default font to use.
38752      */
38753     defaultFont: 'tahoma',
38754    
38755     fontSelect : false,
38756     
38757     
38758     formatCombo : false,
38759     
38760     init : function(editor)
38761     {
38762         this.editor = editor;
38763         
38764         
38765         var fid = editor.frameId;
38766         var etb = this;
38767         function btn(id, toggle, handler){
38768             var xid = fid + '-'+ id ;
38769             return {
38770                 id : xid,
38771                 cmd : id,
38772                 cls : 'x-btn-icon x-edit-'+id,
38773                 enableToggle:toggle !== false,
38774                 scope: editor, // was editor...
38775                 handler:handler||editor.relayBtnCmd,
38776                 clickEvent:'mousedown',
38777                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38778                 tabIndex:-1
38779             };
38780         }
38781         
38782         
38783         
38784         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38785         this.tb = tb;
38786          // stop form submits
38787         tb.el.on('click', function(e){
38788             e.preventDefault(); // what does this do?
38789         });
38790
38791         if(!this.disable.font && !Roo.isSafari){
38792             /* why no safari for fonts
38793             editor.fontSelect = tb.el.createChild({
38794                 tag:'select',
38795                 tabIndex: -1,
38796                 cls:'x-font-select',
38797                 html: editor.createFontOptions()
38798             });
38799             editor.fontSelect.on('change', function(){
38800                 var font = editor.fontSelect.dom.value;
38801                 editor.relayCmd('fontname', font);
38802                 editor.deferFocus();
38803             }, editor);
38804             tb.add(
38805                 editor.fontSelect.dom,
38806                 '-'
38807             );
38808             */
38809         };
38810         if(!this.disable.formats){
38811             this.formatCombo = new Roo.form.ComboBox({
38812                 store: new Roo.data.SimpleStore({
38813                     id : 'tag',
38814                     fields: ['tag'],
38815                     data : this.formats // from states.js
38816                 }),
38817                 blockFocus : true,
38818                 //autoCreate : {tag: "div",  size: "20"},
38819                 displayField:'tag',
38820                 typeAhead: false,
38821                 mode: 'local',
38822                 editable : false,
38823                 triggerAction: 'all',
38824                 emptyText:'Add tag',
38825                 selectOnFocus:true,
38826                 width:135,
38827                 listeners : {
38828                     'select': function(c, r, i) {
38829                         editor.insertTag(r.get('tag'));
38830                         editor.focus();
38831                     }
38832                 }
38833
38834             });
38835             tb.addField(this.formatCombo);
38836             
38837         }
38838         
38839         if(!this.disable.format){
38840             tb.add(
38841                 btn('bold'),
38842                 btn('italic'),
38843                 btn('underline')
38844             );
38845         };
38846         if(!this.disable.fontSize){
38847             tb.add(
38848                 '-',
38849                 
38850                 
38851                 btn('increasefontsize', false, editor.adjustFont),
38852                 btn('decreasefontsize', false, editor.adjustFont)
38853             );
38854         };
38855         
38856         
38857         if(this.disable.colors){
38858             tb.add(
38859                 '-', {
38860                     id:editor.frameId +'-forecolor',
38861                     cls:'x-btn-icon x-edit-forecolor',
38862                     clickEvent:'mousedown',
38863                     tooltip: this.buttonTips['forecolor'] || undefined,
38864                     tabIndex:-1,
38865                     menu : new Roo.menu.ColorMenu({
38866                         allowReselect: true,
38867                         focus: Roo.emptyFn,
38868                         value:'000000',
38869                         plain:true,
38870                         selectHandler: function(cp, color){
38871                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38872                             editor.deferFocus();
38873                         },
38874                         scope: editor,
38875                         clickEvent:'mousedown'
38876                     })
38877                 }, {
38878                     id:editor.frameId +'backcolor',
38879                     cls:'x-btn-icon x-edit-backcolor',
38880                     clickEvent:'mousedown',
38881                     tooltip: this.buttonTips['backcolor'] || undefined,
38882                     tabIndex:-1,
38883                     menu : new Roo.menu.ColorMenu({
38884                         focus: Roo.emptyFn,
38885                         value:'FFFFFF',
38886                         plain:true,
38887                         allowReselect: true,
38888                         selectHandler: function(cp, color){
38889                             if(Roo.isGecko){
38890                                 editor.execCmd('useCSS', false);
38891                                 editor.execCmd('hilitecolor', color);
38892                                 editor.execCmd('useCSS', true);
38893                                 editor.deferFocus();
38894                             }else{
38895                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38896                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38897                                 editor.deferFocus();
38898                             }
38899                         },
38900                         scope:editor,
38901                         clickEvent:'mousedown'
38902                     })
38903                 }
38904             );
38905         };
38906         // now add all the items...
38907         
38908
38909         if(!this.disable.alignments){
38910             tb.add(
38911                 '-',
38912                 btn('justifyleft'),
38913                 btn('justifycenter'),
38914                 btn('justifyright')
38915             );
38916         };
38917
38918         //if(!Roo.isSafari){
38919             if(!this.disable.links){
38920                 tb.add(
38921                     '-',
38922                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38923                 );
38924             };
38925
38926             if(!this.disable.lists){
38927                 tb.add(
38928                     '-',
38929                     btn('insertorderedlist'),
38930                     btn('insertunorderedlist')
38931                 );
38932             }
38933             if(!this.disable.sourceEdit){
38934                 tb.add(
38935                     '-',
38936                     btn('sourceedit', true, function(btn){
38937                         this.toggleSourceEdit(btn.pressed);
38938                     })
38939                 );
38940             }
38941         //}
38942         
38943         var smenu = { };
38944         // special menu.. - needs to be tidied up..
38945         if (!this.disable.special) {
38946             smenu = {
38947                 text: "&#169;",
38948                 cls: 'x-edit-none',
38949                 menu : {
38950                     items : []
38951                    }
38952             };
38953             for (var i =0; i < this.specialChars.length; i++) {
38954                 smenu.menu.items.push({
38955                     
38956                     text: this.specialChars[i],
38957                     handler: function(a,b) {
38958                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38959                     },
38960                     tabIndex:-1
38961                 });
38962             }
38963             
38964             
38965             tb.add(smenu);
38966             
38967             
38968         }
38969         if (this.btns) {
38970             for(var i =0; i< this.btns.length;i++) {
38971                 var b = this.btns[i];
38972                 b.cls =  'x-edit-none';
38973                 b.scope = editor;
38974                 tb.add(b);
38975             }
38976         
38977         }
38978         
38979         
38980         
38981         // disable everything...
38982         
38983         this.tb.items.each(function(item){
38984            if(item.id != editor.frameId+ '-sourceedit'){
38985                 item.disable();
38986             }
38987         });
38988         this.rendered = true;
38989         
38990         // the all the btns;
38991         editor.on('editorevent', this.updateToolbar, this);
38992         // other toolbars need to implement this..
38993         //editor.on('editmodechange', this.updateToolbar, this);
38994     },
38995     
38996     
38997     
38998     /**
38999      * Protected method that will not generally be called directly. It triggers
39000      * a toolbar update by reading the markup state of the current selection in the editor.
39001      */
39002     updateToolbar: function(){
39003
39004         if(!this.editor.activated){
39005             this.editor.onFirstFocus();
39006             return;
39007         }
39008
39009         var btns = this.tb.items.map, 
39010             doc = this.editor.doc,
39011             frameId = this.editor.frameId;
39012
39013         if(!this.disable.font && !Roo.isSafari){
39014             /*
39015             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39016             if(name != this.fontSelect.dom.value){
39017                 this.fontSelect.dom.value = name;
39018             }
39019             */
39020         }
39021         if(!this.disable.format){
39022             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39023             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39024             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39025         }
39026         if(!this.disable.alignments){
39027             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39028             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39029             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39030         }
39031         if(!Roo.isSafari && !this.disable.lists){
39032             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39033             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39034         }
39035         
39036         var ans = this.editor.getAllAncestors();
39037         if (this.formatCombo) {
39038             
39039             
39040             var store = this.formatCombo.store;
39041             this.formatCombo.setValue("");
39042             for (var i =0; i < ans.length;i++) {
39043                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39044                     // select it..
39045                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39046                     break;
39047                 }
39048             }
39049         }
39050         
39051         
39052         
39053         // hides menus... - so this cant be on a menu...
39054         Roo.menu.MenuMgr.hideAll();
39055
39056         //this.editorsyncValue();
39057     },
39058    
39059     
39060     createFontOptions : function(){
39061         var buf = [], fs = this.fontFamilies, ff, lc;
39062         for(var i = 0, len = fs.length; i< len; i++){
39063             ff = fs[i];
39064             lc = ff.toLowerCase();
39065             buf.push(
39066                 '<option value="',lc,'" style="font-family:',ff,';"',
39067                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39068                     ff,
39069                 '</option>'
39070             );
39071         }
39072         return buf.join('');
39073     },
39074     
39075     toggleSourceEdit : function(sourceEditMode){
39076         if(sourceEditMode === undefined){
39077             sourceEditMode = !this.sourceEditMode;
39078         }
39079         this.sourceEditMode = sourceEditMode === true;
39080         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39081         // just toggle the button?
39082         if(btn.pressed !== this.editor.sourceEditMode){
39083             btn.toggle(this.editor.sourceEditMode);
39084             return;
39085         }
39086         
39087         if(this.sourceEditMode){
39088             this.tb.items.each(function(item){
39089                 if(item.cmd != 'sourceedit'){
39090                     item.disable();
39091                 }
39092             });
39093           
39094         }else{
39095             if(this.initialized){
39096                 this.tb.items.each(function(item){
39097                     item.enable();
39098                 });
39099             }
39100             
39101         }
39102         // tell the editor that it's been pressed..
39103         this.editor.toggleSourceEdit(sourceEditMode);
39104        
39105     },
39106      /**
39107      * Object collection of toolbar tooltips for the buttons in the editor. The key
39108      * is the command id associated with that button and the value is a valid QuickTips object.
39109      * For example:
39110 <pre><code>
39111 {
39112     bold : {
39113         title: 'Bold (Ctrl+B)',
39114         text: 'Make the selected text bold.',
39115         cls: 'x-html-editor-tip'
39116     },
39117     italic : {
39118         title: 'Italic (Ctrl+I)',
39119         text: 'Make the selected text italic.',
39120         cls: 'x-html-editor-tip'
39121     },
39122     ...
39123 </code></pre>
39124     * @type Object
39125      */
39126     buttonTips : {
39127         bold : {
39128             title: 'Bold (Ctrl+B)',
39129             text: 'Make the selected text bold.',
39130             cls: 'x-html-editor-tip'
39131         },
39132         italic : {
39133             title: 'Italic (Ctrl+I)',
39134             text: 'Make the selected text italic.',
39135             cls: 'x-html-editor-tip'
39136         },
39137         underline : {
39138             title: 'Underline (Ctrl+U)',
39139             text: 'Underline the selected text.',
39140             cls: 'x-html-editor-tip'
39141         },
39142         increasefontsize : {
39143             title: 'Grow Text',
39144             text: 'Increase the font size.',
39145             cls: 'x-html-editor-tip'
39146         },
39147         decreasefontsize : {
39148             title: 'Shrink Text',
39149             text: 'Decrease the font size.',
39150             cls: 'x-html-editor-tip'
39151         },
39152         backcolor : {
39153             title: 'Text Highlight Color',
39154             text: 'Change the background color of the selected text.',
39155             cls: 'x-html-editor-tip'
39156         },
39157         forecolor : {
39158             title: 'Font Color',
39159             text: 'Change the color of the selected text.',
39160             cls: 'x-html-editor-tip'
39161         },
39162         justifyleft : {
39163             title: 'Align Text Left',
39164             text: 'Align text to the left.',
39165             cls: 'x-html-editor-tip'
39166         },
39167         justifycenter : {
39168             title: 'Center Text',
39169             text: 'Center text in the editor.',
39170             cls: 'x-html-editor-tip'
39171         },
39172         justifyright : {
39173             title: 'Align Text Right',
39174             text: 'Align text to the right.',
39175             cls: 'x-html-editor-tip'
39176         },
39177         insertunorderedlist : {
39178             title: 'Bullet List',
39179             text: 'Start a bulleted list.',
39180             cls: 'x-html-editor-tip'
39181         },
39182         insertorderedlist : {
39183             title: 'Numbered List',
39184             text: 'Start a numbered list.',
39185             cls: 'x-html-editor-tip'
39186         },
39187         createlink : {
39188             title: 'Hyperlink',
39189             text: 'Make the selected text a hyperlink.',
39190             cls: 'x-html-editor-tip'
39191         },
39192         sourceedit : {
39193             title: 'Source Edit',
39194             text: 'Switch to source editing mode.',
39195             cls: 'x-html-editor-tip'
39196         }
39197     },
39198     // private
39199     onDestroy : function(){
39200         if(this.rendered){
39201             
39202             this.tb.items.each(function(item){
39203                 if(item.menu){
39204                     item.menu.removeAll();
39205                     if(item.menu.el){
39206                         item.menu.el.destroy();
39207                     }
39208                 }
39209                 item.destroy();
39210             });
39211              
39212         }
39213     },
39214     onFirstFocus: function() {
39215         this.tb.items.each(function(item){
39216            item.enable();
39217         });
39218     }
39219 });
39220
39221
39222
39223
39224 // <script type="text/javascript">
39225 /*
39226  * Based on
39227  * Ext JS Library 1.1.1
39228  * Copyright(c) 2006-2007, Ext JS, LLC.
39229  *  
39230  
39231  */
39232
39233  
39234 /**
39235  * @class Roo.form.HtmlEditor.ToolbarContext
39236  * Context Toolbar
39237  * 
39238  * Usage:
39239  *
39240  new Roo.form.HtmlEditor({
39241     ....
39242     toolbars : [
39243         new Roo.form.HtmlEditor.ToolbarStandard(),
39244         new Roo.form.HtmlEditor.ToolbarContext()
39245         })
39246     }
39247      
39248  * 
39249  * @config : {Object} disable List of elements to disable.. (not done yet.)
39250  * 
39251  * 
39252  */
39253
39254 Roo.form.HtmlEditor.ToolbarContext = function(config)
39255 {
39256     
39257     Roo.apply(this, config);
39258     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39259     // dont call parent... till later.
39260 }
39261 Roo.form.HtmlEditor.ToolbarContext.types = {
39262     'IMG' : {
39263         width : {
39264             title: "Width",
39265             width: 40
39266         },
39267         height:  {
39268             title: "Height",
39269             width: 40
39270         },
39271         align: {
39272             title: "Align",
39273             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39274             width : 80
39275             
39276         },
39277         border: {
39278             title: "Border",
39279             width: 40
39280         },
39281         alt: {
39282             title: "Alt",
39283             width: 120
39284         },
39285         src : {
39286             title: "Src",
39287             width: 220
39288         }
39289         
39290     },
39291     'A' : {
39292         name : {
39293             title: "Name",
39294             width: 50
39295         },
39296         href:  {
39297             title: "Href",
39298             width: 220
39299         } // border?
39300         
39301     },
39302     'TABLE' : {
39303         rows : {
39304             title: "Rows",
39305             width: 20
39306         },
39307         cols : {
39308             title: "Cols",
39309             width: 20
39310         },
39311         width : {
39312             title: "Width",
39313             width: 40
39314         },
39315         height : {
39316             title: "Height",
39317             width: 40
39318         },
39319         border : {
39320             title: "Border",
39321             width: 20
39322         }
39323     },
39324     'TD' : {
39325         width : {
39326             title: "Width",
39327             width: 40
39328         },
39329         height : {
39330             title: "Height",
39331             width: 40
39332         },   
39333         align: {
39334             title: "Align",
39335             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39336             width: 40
39337         },
39338         valign: {
39339             title: "Valign",
39340             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39341             width: 40
39342         },
39343         colspan: {
39344             title: "Colspan",
39345             width: 20
39346             
39347         }
39348     },
39349     'INPUT' : {
39350         name : {
39351             title: "name",
39352             width: 120
39353         },
39354         value : {
39355             title: "Value",
39356             width: 120
39357         },
39358         width : {
39359             title: "Width",
39360             width: 40
39361         }
39362     },
39363     'LABEL' : {
39364         'for' : {
39365             title: "For",
39366             width: 120
39367         }
39368     },
39369     'TEXTAREA' : {
39370           name : {
39371             title: "name",
39372             width: 120
39373         },
39374         rows : {
39375             title: "Rows",
39376             width: 20
39377         },
39378         cols : {
39379             title: "Cols",
39380             width: 20
39381         }
39382     },
39383     'SELECT' : {
39384         name : {
39385             title: "name",
39386             width: 120
39387         },
39388         selectoptions : {
39389             title: "Options",
39390             width: 200
39391         }
39392     },
39393     'BODY' : {
39394         title : {
39395             title: "title",
39396             width: 120,
39397             disabled : true
39398         }
39399     }
39400 };
39401
39402
39403
39404 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39405     
39406     tb: false,
39407     
39408     rendered: false,
39409     
39410     editor : false,
39411     /**
39412      * @cfg {Object} disable  List of toolbar elements to disable
39413          
39414      */
39415     disable : false,
39416     
39417     
39418     
39419     toolbars : false,
39420     
39421     init : function(editor)
39422     {
39423         this.editor = editor;
39424         
39425         
39426         var fid = editor.frameId;
39427         var etb = this;
39428         function btn(id, toggle, handler){
39429             var xid = fid + '-'+ id ;
39430             return {
39431                 id : xid,
39432                 cmd : id,
39433                 cls : 'x-btn-icon x-edit-'+id,
39434                 enableToggle:toggle !== false,
39435                 scope: editor, // was editor...
39436                 handler:handler||editor.relayBtnCmd,
39437                 clickEvent:'mousedown',
39438                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39439                 tabIndex:-1
39440             };
39441         }
39442         // create a new element.
39443         var wdiv = editor.wrap.createChild({
39444                 tag: 'div'
39445             }, editor.wrap.dom.firstChild.nextSibling, true);
39446         
39447         // can we do this more than once??
39448         
39449          // stop form submits
39450       
39451  
39452         // disable everything...
39453         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39454         this.toolbars = {};
39455            
39456         for (var i in  ty) {
39457             this.toolbars[i] = this.buildToolbar(ty[i],i);
39458         }
39459         this.tb = this.toolbars.BODY;
39460         this.tb.el.show();
39461         
39462          
39463         this.rendered = true;
39464         
39465         // the all the btns;
39466         editor.on('editorevent', this.updateToolbar, this);
39467         // other toolbars need to implement this..
39468         //editor.on('editmodechange', this.updateToolbar, this);
39469     },
39470     
39471     
39472     
39473     /**
39474      * Protected method that will not generally be called directly. It triggers
39475      * a toolbar update by reading the markup state of the current selection in the editor.
39476      */
39477     updateToolbar: function(){
39478
39479         if(!this.editor.activated){
39480             this.editor.onFirstFocus();
39481             return;
39482         }
39483
39484         
39485         var ans = this.editor.getAllAncestors();
39486         
39487         // pick
39488         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39489         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39490         sel = sel ? sel : this.editor.doc.body;
39491         sel = sel.tagName.length ? sel : this.editor.doc.body;
39492         var tn = sel.tagName.toUpperCase();
39493         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39494         tn = sel.tagName.toUpperCase();
39495         if (this.tb.name  == tn) {
39496             return; // no change
39497         }
39498         this.tb.el.hide();
39499         ///console.log("show: " + tn);
39500         this.tb =  this.toolbars[tn];
39501         this.tb.el.show();
39502         this.tb.fields.each(function(e) {
39503             e.setValue(sel.getAttribute(e.name));
39504         });
39505         this.tb.selectedNode = sel;
39506         
39507         
39508         Roo.menu.MenuMgr.hideAll();
39509
39510         //this.editorsyncValue();
39511     },
39512    
39513        
39514     // private
39515     onDestroy : function(){
39516         if(this.rendered){
39517             
39518             this.tb.items.each(function(item){
39519                 if(item.menu){
39520                     item.menu.removeAll();
39521                     if(item.menu.el){
39522                         item.menu.el.destroy();
39523                     }
39524                 }
39525                 item.destroy();
39526             });
39527              
39528         }
39529     },
39530     onFirstFocus: function() {
39531         // need to do this for all the toolbars..
39532         this.tb.items.each(function(item){
39533            item.enable();
39534         });
39535     },
39536     buildToolbar: function(tlist, nm)
39537     {
39538         var editor = this.editor;
39539          // create a new element.
39540         var wdiv = editor.wrap.createChild({
39541                 tag: 'div'
39542             }, editor.wrap.dom.firstChild.nextSibling, true);
39543         
39544        
39545         var tb = new Roo.Toolbar(wdiv);
39546         tb.add(nm+ ":&nbsp;");
39547         for (var i in tlist) {
39548             var item = tlist[i];
39549             tb.add(item.title + ":&nbsp;");
39550             if (item.opts) {
39551                 // fixme
39552                 
39553               
39554                 tb.addField( new Roo.form.ComboBox({
39555                     store: new Roo.data.SimpleStore({
39556                         id : 'val',
39557                         fields: ['val'],
39558                         data : item.opts // from states.js
39559                     }),
39560                     name : i,
39561                     displayField:'val',
39562                     typeAhead: false,
39563                     mode: 'local',
39564                     editable : false,
39565                     triggerAction: 'all',
39566                     emptyText:'Select',
39567                     selectOnFocus:true,
39568                     width: item.width ? item.width  : 130,
39569                     listeners : {
39570                         'select': function(c, r, i) {
39571                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39572                         }
39573                     }
39574
39575                 }));
39576                 continue;
39577                     
39578                 
39579                 
39580                 
39581                 
39582                 tb.addField( new Roo.form.TextField({
39583                     name: i,
39584                     width: 100,
39585                     //allowBlank:false,
39586                     value: ''
39587                 }));
39588                 continue;
39589             }
39590             tb.addField( new Roo.form.TextField({
39591                 name: i,
39592                 width: item.width,
39593                 //allowBlank:true,
39594                 value: '',
39595                 listeners: {
39596                     'change' : function(f, nv, ov) {
39597                         tb.selectedNode.setAttribute(f.name, nv);
39598                     }
39599                 }
39600             }));
39601              
39602         }
39603         tb.el.on('click', function(e){
39604             e.preventDefault(); // what does this do?
39605         });
39606         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39607         tb.el.hide();
39608         tb.name = nm;
39609         // dont need to disable them... as they will get hidden
39610         return tb;
39611          
39612         
39613     }
39614     
39615     
39616     
39617     
39618 });
39619
39620
39621
39622
39623
39624 /*
39625  * Based on:
39626  * Ext JS Library 1.1.1
39627  * Copyright(c) 2006-2007, Ext JS, LLC.
39628  *
39629  * Originally Released Under LGPL - original licence link has changed is not relivant.
39630  *
39631  * Fork - LGPL
39632  * <script type="text/javascript">
39633  */
39634  
39635 /**
39636  * @class Roo.form.BasicForm
39637  * @extends Roo.util.Observable
39638  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39639  * @constructor
39640  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39641  * @param {Object} config Configuration options
39642  */
39643 Roo.form.BasicForm = function(el, config){
39644     this.allItems = [];
39645     this.childForms = [];
39646     Roo.apply(this, config);
39647     /*
39648      * The Roo.form.Field items in this form.
39649      * @type MixedCollection
39650      */
39651      
39652      
39653     this.items = new Roo.util.MixedCollection(false, function(o){
39654         return o.id || (o.id = Roo.id());
39655     });
39656     this.addEvents({
39657         /**
39658          * @event beforeaction
39659          * Fires before any action is performed. Return false to cancel the action.
39660          * @param {Form} this
39661          * @param {Action} action The action to be performed
39662          */
39663         beforeaction: true,
39664         /**
39665          * @event actionfailed
39666          * Fires when an action fails.
39667          * @param {Form} this
39668          * @param {Action} action The action that failed
39669          */
39670         actionfailed : true,
39671         /**
39672          * @event actioncomplete
39673          * Fires when an action is completed.
39674          * @param {Form} this
39675          * @param {Action} action The action that completed
39676          */
39677         actioncomplete : true
39678     });
39679     if(el){
39680         this.initEl(el);
39681     }
39682     Roo.form.BasicForm.superclass.constructor.call(this);
39683 };
39684
39685 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39686     /**
39687      * @cfg {String} method
39688      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39689      */
39690     /**
39691      * @cfg {DataReader} reader
39692      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39693      * This is optional as there is built-in support for processing JSON.
39694      */
39695     /**
39696      * @cfg {DataReader} errorReader
39697      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39698      * This is completely optional as there is built-in support for processing JSON.
39699      */
39700     /**
39701      * @cfg {String} url
39702      * The URL to use for form actions if one isn't supplied in the action options.
39703      */
39704     /**
39705      * @cfg {Boolean} fileUpload
39706      * Set to true if this form is a file upload.
39707      */
39708     /**
39709      * @cfg {Object} baseParams
39710      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39711      */
39712     /**
39713      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39714      */
39715     timeout: 30,
39716
39717     // private
39718     activeAction : null,
39719
39720     /**
39721      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39722      * or setValues() data instead of when the form was first created.
39723      */
39724     trackResetOnLoad : false,
39725     
39726     
39727     /**
39728      * childForms - used for multi-tab forms
39729      * @type {Array}
39730      */
39731     childForms : false,
39732     
39733     /**
39734      * allItems - full list of fields.
39735      * @type {Array}
39736      */
39737     allItems : false,
39738     
39739     /**
39740      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39741      * element by passing it or its id or mask the form itself by passing in true.
39742      * @type Mixed
39743      */
39744     waitMsgTarget : undefined,
39745
39746     // private
39747     initEl : function(el){
39748         this.el = Roo.get(el);
39749         this.id = this.el.id || Roo.id();
39750         this.el.on('submit', this.onSubmit, this);
39751         this.el.addClass('x-form');
39752     },
39753
39754     // private
39755     onSubmit : function(e){
39756         e.stopEvent();
39757     },
39758
39759     /**
39760      * Returns true if client-side validation on the form is successful.
39761      * @return Boolean
39762      */
39763     isValid : function(){
39764         var valid = true;
39765         this.items.each(function(f){
39766            if(!f.validate()){
39767                valid = false;
39768            }
39769         });
39770         return valid;
39771     },
39772
39773     /**
39774      * Returns true if any fields in this form have changed since their original load.
39775      * @return Boolean
39776      */
39777     isDirty : function(){
39778         var dirty = false;
39779         this.items.each(function(f){
39780            if(f.isDirty()){
39781                dirty = true;
39782                return false;
39783            }
39784         });
39785         return dirty;
39786     },
39787
39788     /**
39789      * Performs a predefined action (submit or load) or custom actions you define on this form.
39790      * @param {String} actionName The name of the action type
39791      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39792      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39793      * accept other config options):
39794      * <pre>
39795 Property          Type             Description
39796 ----------------  ---------------  ----------------------------------------------------------------------------------
39797 url               String           The url for the action (defaults to the form's url)
39798 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39799 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39800 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39801                                    validate the form on the client (defaults to false)
39802      * </pre>
39803      * @return {BasicForm} this
39804      */
39805     doAction : function(action, options){
39806         if(typeof action == 'string'){
39807             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39808         }
39809         if(this.fireEvent('beforeaction', this, action) !== false){
39810             this.beforeAction(action);
39811             action.run.defer(100, action);
39812         }
39813         return this;
39814     },
39815
39816     /**
39817      * Shortcut to do a submit action.
39818      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39819      * @return {BasicForm} this
39820      */
39821     submit : function(options){
39822         this.doAction('submit', options);
39823         return this;
39824     },
39825
39826     /**
39827      * Shortcut to do a load action.
39828      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39829      * @return {BasicForm} this
39830      */
39831     load : function(options){
39832         this.doAction('load', options);
39833         return this;
39834     },
39835
39836     /**
39837      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39838      * @param {Record} record The record to edit
39839      * @return {BasicForm} this
39840      */
39841     updateRecord : function(record){
39842         record.beginEdit();
39843         var fs = record.fields;
39844         fs.each(function(f){
39845             var field = this.findField(f.name);
39846             if(field){
39847                 record.set(f.name, field.getValue());
39848             }
39849         }, this);
39850         record.endEdit();
39851         return this;
39852     },
39853
39854     /**
39855      * Loads an Roo.data.Record into this form.
39856      * @param {Record} record The record to load
39857      * @return {BasicForm} this
39858      */
39859     loadRecord : function(record){
39860         this.setValues(record.data);
39861         return this;
39862     },
39863
39864     // private
39865     beforeAction : function(action){
39866         var o = action.options;
39867         if(o.waitMsg){
39868             if(this.waitMsgTarget === true){
39869                 this.el.mask(o.waitMsg, 'x-mask-loading');
39870             }else if(this.waitMsgTarget){
39871                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39872                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39873             }else{
39874                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39875             }
39876         }
39877     },
39878
39879     // private
39880     afterAction : function(action, success){
39881         this.activeAction = null;
39882         var o = action.options;
39883         if(o.waitMsg){
39884             if(this.waitMsgTarget === true){
39885                 this.el.unmask();
39886             }else if(this.waitMsgTarget){
39887                 this.waitMsgTarget.unmask();
39888             }else{
39889                 Roo.MessageBox.updateProgress(1);
39890                 Roo.MessageBox.hide();
39891             }
39892         }
39893         if(success){
39894             if(o.reset){
39895                 this.reset();
39896             }
39897             Roo.callback(o.success, o.scope, [this, action]);
39898             this.fireEvent('actioncomplete', this, action);
39899         }else{
39900             Roo.callback(o.failure, o.scope, [this, action]);
39901             this.fireEvent('actionfailed', this, action);
39902         }
39903     },
39904
39905     /**
39906      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39907      * @param {String} id The value to search for
39908      * @return Field
39909      */
39910     findField : function(id){
39911         var field = this.items.get(id);
39912         if(!field){
39913             this.items.each(function(f){
39914                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39915                     field = f;
39916                     return false;
39917                 }
39918             });
39919         }
39920         return field || null;
39921     },
39922
39923     /**
39924      * Add a secondary form to this one, 
39925      * Used to provide tabbed forms. One form is primary, with hidden values 
39926      * which mirror the elements from the other forms.
39927      * 
39928      * @param {Roo.form.Form} form to add.
39929      * 
39930      */
39931     addForm : function(form){
39932        
39933         this.childForms.push(form);
39934         Roo.each(form.allItems, function (fe) {
39935             
39936             if (this.findField(fe.name)) { // already added..
39937                 return;
39938             }
39939             this.add( new Roo.form.Hidden({
39940                 name : fe.name
39941             }));
39942         }, this);
39943         
39944     },
39945     /**
39946      * Mark fields in this form invalid in bulk.
39947      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39948      * @return {BasicForm} this
39949      */
39950     markInvalid : function(errors){
39951         if(errors instanceof Array){
39952             for(var i = 0, len = errors.length; i < len; i++){
39953                 var fieldError = errors[i];
39954                 var f = this.findField(fieldError.id);
39955                 if(f){
39956                     f.markInvalid(fieldError.msg);
39957                 }
39958             }
39959         }else{
39960             var field, id;
39961             for(id in errors){
39962                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39963                     field.markInvalid(errors[id]);
39964                 }
39965             }
39966         }
39967         Roo.each(this.childForms || [], function (f) {
39968             f.markInvalid(errors);
39969         });
39970         
39971         return this;
39972     },
39973
39974     /**
39975      * Set values for fields in this form in bulk.
39976      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
39977      * @return {BasicForm} this
39978      */
39979     setValues : function(values){
39980         if(values instanceof Array){ // array of objects
39981             for(var i = 0, len = values.length; i < len; i++){
39982                 var v = values[i];
39983                 var f = this.findField(v.id);
39984                 if(f){
39985                     f.setValue(v.value);
39986                     if(this.trackResetOnLoad){
39987                         f.originalValue = f.getValue();
39988                     }
39989                 }
39990             }
39991         }else{ // object hash
39992             var field, id;
39993             for(id in values){
39994                 if(typeof values[id] != 'function' && (field = this.findField(id))){
39995                     
39996                     if (field.setFromData && 
39997                         field.valueField && 
39998                         field.displayField &&
39999                         // combos' with local stores can 
40000                         // be queried via setValue()
40001                         // to set their value..
40002                         (field.store && !field.store.isLocal)
40003                         ) {
40004                         // it's a combo
40005                         var sd = { };
40006                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40007                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40008                         field.setFromData(sd);
40009                         
40010                     } else {
40011                         field.setValue(values[id]);
40012                     }
40013                     
40014                     
40015                     if(this.trackResetOnLoad){
40016                         field.originalValue = field.getValue();
40017                     }
40018                 }
40019             }
40020         }
40021          
40022         Roo.each(this.childForms || [], function (f) {
40023             f.setValues(values);
40024         });
40025                 
40026         return this;
40027     },
40028
40029     /**
40030      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40031      * they are returned as an array.
40032      * @param {Boolean} asString
40033      * @return {Object}
40034      */
40035     getValues : function(asString){
40036         if (this.childForms) {
40037             // copy values from the child forms
40038             Roo.each(this.childForms, function (f) {
40039                 if (f.allFields) {
40040                     Roo.each(f.allFields, function (e) {
40041                         if (e.name && e.getValue && this.findField(e.name)) {
40042                             this.findField(e.name).setValue(e.getValue());
40043                         }
40044                     });
40045                 }
40046             }, this);
40047         }
40048         
40049         
40050         
40051         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40052         if(asString === true){
40053             return fs;
40054         }
40055         return Roo.urlDecode(fs);
40056     },
40057
40058     /**
40059      * Clears all invalid messages in this form.
40060      * @return {BasicForm} this
40061      */
40062     clearInvalid : function(){
40063         this.items.each(function(f){
40064            f.clearInvalid();
40065         });
40066         
40067         Roo.each(this.childForms || [], function (f) {
40068             f.clearInvalid();
40069         });
40070         
40071         
40072         return this;
40073     },
40074
40075     /**
40076      * Resets this form.
40077      * @return {BasicForm} this
40078      */
40079     reset : function(){
40080         this.items.each(function(f){
40081             f.reset();
40082         });
40083         
40084         Roo.each(this.childForms || [], function (f) {
40085             f.reset();
40086         });
40087        
40088         
40089         return this;
40090     },
40091
40092     /**
40093      * Add Roo.form components to this form.
40094      * @param {Field} field1
40095      * @param {Field} field2 (optional)
40096      * @param {Field} etc (optional)
40097      * @return {BasicForm} this
40098      */
40099     add : function(){
40100         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40101         return this;
40102     },
40103
40104
40105     /**
40106      * Removes a field from the items collection (does NOT remove its markup).
40107      * @param {Field} field
40108      * @return {BasicForm} this
40109      */
40110     remove : function(field){
40111         this.items.remove(field);
40112         return this;
40113     },
40114
40115     /**
40116      * Looks at the fields in this form, checks them for an id attribute,
40117      * and calls applyTo on the existing dom element with that id.
40118      * @return {BasicForm} this
40119      */
40120     render : function(){
40121         this.items.each(function(f){
40122             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40123                 f.applyTo(f.id);
40124             }
40125         });
40126         return this;
40127     },
40128
40129     /**
40130      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40131      * @param {Object} values
40132      * @return {BasicForm} this
40133      */
40134     applyToFields : function(o){
40135         this.items.each(function(f){
40136            Roo.apply(f, o);
40137         });
40138         return this;
40139     },
40140
40141     /**
40142      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40143      * @param {Object} values
40144      * @return {BasicForm} this
40145      */
40146     applyIfToFields : function(o){
40147         this.items.each(function(f){
40148            Roo.applyIf(f, o);
40149         });
40150         return this;
40151     }
40152 });
40153
40154 // back compat
40155 Roo.BasicForm = Roo.form.BasicForm;/*
40156  * Based on:
40157  * Ext JS Library 1.1.1
40158  * Copyright(c) 2006-2007, Ext JS, LLC.
40159  *
40160  * Originally Released Under LGPL - original licence link has changed is not relivant.
40161  *
40162  * Fork - LGPL
40163  * <script type="text/javascript">
40164  */
40165
40166 /**
40167  * @class Roo.form.Form
40168  * @extends Roo.form.BasicForm
40169  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40170  * @constructor
40171  * @param {Object} config Configuration options
40172  */
40173 Roo.form.Form = function(config){
40174     var xitems =  [];
40175     if (config.items) {
40176         xitems = config.items;
40177         delete config.items;
40178     }
40179    
40180     
40181     Roo.form.Form.superclass.constructor.call(this, null, config);
40182     this.url = this.url || this.action;
40183     if(!this.root){
40184         this.root = new Roo.form.Layout(Roo.applyIf({
40185             id: Roo.id()
40186         }, config));
40187     }
40188     this.active = this.root;
40189     /**
40190      * Array of all the buttons that have been added to this form via {@link addButton}
40191      * @type Array
40192      */
40193     this.buttons = [];
40194     this.allItems = [];
40195     this.addEvents({
40196         /**
40197          * @event clientvalidation
40198          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40199          * @param {Form} this
40200          * @param {Boolean} valid true if the form has passed client-side validation
40201          */
40202         clientvalidation: true,
40203         /**
40204          * @event rendered
40205          * Fires when the form is rendered
40206          * @param {Roo.form.Form} form
40207          */
40208         rendered : true
40209     });
40210     
40211     Roo.each(xitems, this.addxtype, this);
40212     
40213     
40214     
40215 };
40216
40217 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40218     /**
40219      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40220      */
40221     /**
40222      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40223      */
40224     /**
40225      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40226      */
40227     buttonAlign:'center',
40228
40229     /**
40230      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40231      */
40232     minButtonWidth:75,
40233
40234     /**
40235      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40236      * This property cascades to child containers if not set.
40237      */
40238     labelAlign:'left',
40239
40240     /**
40241      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40242      * fires a looping event with that state. This is required to bind buttons to the valid
40243      * state using the config value formBind:true on the button.
40244      */
40245     monitorValid : false,
40246
40247     /**
40248      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40249      */
40250     monitorPoll : 200,
40251
40252   
40253     /**
40254      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40255      * fields are added and the column is closed. If no fields are passed the column remains open
40256      * until end() is called.
40257      * @param {Object} config The config to pass to the column
40258      * @param {Field} field1 (optional)
40259      * @param {Field} field2 (optional)
40260      * @param {Field} etc (optional)
40261      * @return Column The column container object
40262      */
40263     column : function(c){
40264         var col = new Roo.form.Column(c);
40265         this.start(col);
40266         if(arguments.length > 1){ // duplicate code required because of Opera
40267             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40268             this.end();
40269         }
40270         return col;
40271     },
40272
40273     /**
40274      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40275      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40276      * until end() is called.
40277      * @param {Object} config The config to pass to the fieldset
40278      * @param {Field} field1 (optional)
40279      * @param {Field} field2 (optional)
40280      * @param {Field} etc (optional)
40281      * @return FieldSet The fieldset container object
40282      */
40283     fieldset : function(c){
40284         var fs = new Roo.form.FieldSet(c);
40285         this.start(fs);
40286         if(arguments.length > 1){ // duplicate code required because of Opera
40287             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40288             this.end();
40289         }
40290         return fs;
40291     },
40292
40293     /**
40294      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40295      * fields are added and the container is closed. If no fields are passed the container remains open
40296      * until end() is called.
40297      * @param {Object} config The config to pass to the Layout
40298      * @param {Field} field1 (optional)
40299      * @param {Field} field2 (optional)
40300      * @param {Field} etc (optional)
40301      * @return Layout The container object
40302      */
40303     container : function(c){
40304         var l = new Roo.form.Layout(c);
40305         this.start(l);
40306         if(arguments.length > 1){ // duplicate code required because of Opera
40307             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40308             this.end();
40309         }
40310         return l;
40311     },
40312
40313     /**
40314      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40315      * @param {Object} container A Roo.form.Layout or subclass of Layout
40316      * @return {Form} this
40317      */
40318     start : function(c){
40319         // cascade label info
40320         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40321         this.active.stack.push(c);
40322         c.ownerCt = this.active;
40323         this.active = c;
40324         return this;
40325     },
40326
40327     /**
40328      * Closes the current open container
40329      * @return {Form} this
40330      */
40331     end : function(){
40332         if(this.active == this.root){
40333             return this;
40334         }
40335         this.active = this.active.ownerCt;
40336         return this;
40337     },
40338
40339     /**
40340      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40341      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40342      * as the label of the field.
40343      * @param {Field} field1
40344      * @param {Field} field2 (optional)
40345      * @param {Field} etc. (optional)
40346      * @return {Form} this
40347      */
40348     add : function(){
40349         this.active.stack.push.apply(this.active.stack, arguments);
40350         this.allItems.push.apply(this.allItems,arguments);
40351         var r = [];
40352         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40353             if(a[i].isFormField){
40354                 r.push(a[i]);
40355             }
40356         }
40357         if(r.length > 0){
40358             Roo.form.Form.superclass.add.apply(this, r);
40359         }
40360         return this;
40361     },
40362     
40363
40364     
40365     
40366     
40367      /**
40368      * Find any element that has been added to a form, using it's ID or name
40369      * This can include framesets, columns etc. along with regular fields..
40370      * @param {String} id - id or name to find.
40371      
40372      * @return {Element} e - or false if nothing found.
40373      */
40374     findbyId : function(id)
40375     {
40376         var ret = false;
40377         if (!id) {
40378             return ret;
40379         }
40380         Ext.each(this.allItems, function(f){
40381             if (f.id == id || f.name == id ){
40382                 ret = f;
40383                 return false;
40384             }
40385         });
40386         return ret;
40387     },
40388
40389     
40390     
40391     /**
40392      * Render this form into the passed container. This should only be called once!
40393      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40394      * @return {Form} this
40395      */
40396     render : function(ct){
40397         ct = Roo.get(ct);
40398         var o = this.autoCreate || {
40399             tag: 'form',
40400             method : this.method || 'POST',
40401             id : this.id || Roo.id()
40402         };
40403         this.initEl(ct.createChild(o));
40404
40405         this.root.render(this.el);
40406
40407         this.items.each(function(f){
40408             f.render('x-form-el-'+f.id);
40409         });
40410
40411         if(this.buttons.length > 0){
40412             // tables are required to maintain order and for correct IE layout
40413             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40414                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40415                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40416             }}, null, true);
40417             var tr = tb.getElementsByTagName('tr')[0];
40418             for(var i = 0, len = this.buttons.length; i < len; i++) {
40419                 var b = this.buttons[i];
40420                 var td = document.createElement('td');
40421                 td.className = 'x-form-btn-td';
40422                 b.render(tr.appendChild(td));
40423             }
40424         }
40425         if(this.monitorValid){ // initialize after render
40426             this.startMonitoring();
40427         }
40428         this.fireEvent('rendered', this);
40429         return this;
40430     },
40431
40432     /**
40433      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40434      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40435      * object or a valid Roo.DomHelper element config
40436      * @param {Function} handler The function called when the button is clicked
40437      * @param {Object} scope (optional) The scope of the handler function
40438      * @return {Roo.Button}
40439      */
40440     addButton : function(config, handler, scope){
40441         var bc = {
40442             handler: handler,
40443             scope: scope,
40444             minWidth: this.minButtonWidth,
40445             hideParent:true
40446         };
40447         if(typeof config == "string"){
40448             bc.text = config;
40449         }else{
40450             Roo.apply(bc, config);
40451         }
40452         var btn = new Roo.Button(null, bc);
40453         this.buttons.push(btn);
40454         return btn;
40455     },
40456
40457      /**
40458      * Adds a series of form elements (using the xtype property as the factory method.
40459      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40460      * @param {Object} config 
40461      */
40462     
40463     addxtype : function()
40464     {
40465         var ar = Array.prototype.slice.call(arguments, 0);
40466         var ret = false;
40467         for(var i = 0; i < ar.length; i++) {
40468             if (!ar[i]) {
40469                 continue; // skip -- if this happends something invalid got sent, we 
40470                 // should ignore it, as basically that interface element will not show up
40471                 // and that should be pretty obvious!!
40472             }
40473             
40474             if (Roo.form[ar[i].xtype]) {
40475                 ar[i].form = this;
40476                 var fe = Roo.factory(ar[i], Roo.form);
40477                 if (!ret) {
40478                     ret = fe;
40479                 }
40480                 fe.form = this;
40481                 if (fe.store) {
40482                     fe.store.form = this;
40483                 }
40484                 if (fe.isLayout) {  
40485                          
40486                     this.start(fe);
40487                     this.allItems.push(fe);
40488                     if (fe.items && fe.addxtype) {
40489                         fe.addxtype.apply(fe, fe.items);
40490                         delete fe.items;
40491                     }
40492                      this.end();
40493                     continue;
40494                 }
40495                 
40496                 
40497                  
40498                 this.add(fe);
40499               //  console.log('adding ' + ar[i].xtype);
40500             }
40501             if (ar[i].xtype == 'Button') {  
40502                 //console.log('adding button');
40503                 //console.log(ar[i]);
40504                 this.addButton(ar[i]);
40505                 this.allItems.push(fe);
40506                 continue;
40507             }
40508             
40509             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40510                 alert('end is not supported on xtype any more, use items');
40511             //    this.end();
40512             //    //console.log('adding end');
40513             }
40514             
40515         }
40516         return ret;
40517     },
40518     
40519     /**
40520      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40521      * option "monitorValid"
40522      */
40523     startMonitoring : function(){
40524         if(!this.bound){
40525             this.bound = true;
40526             Roo.TaskMgr.start({
40527                 run : this.bindHandler,
40528                 interval : this.monitorPoll || 200,
40529                 scope: this
40530             });
40531         }
40532     },
40533
40534     /**
40535      * Stops monitoring of the valid state of this form
40536      */
40537     stopMonitoring : function(){
40538         this.bound = false;
40539     },
40540
40541     // private
40542     bindHandler : function(){
40543         if(!this.bound){
40544             return false; // stops binding
40545         }
40546         var valid = true;
40547         this.items.each(function(f){
40548             if(!f.isValid(true)){
40549                 valid = false;
40550                 return false;
40551             }
40552         });
40553         for(var i = 0, len = this.buttons.length; i < len; i++){
40554             var btn = this.buttons[i];
40555             if(btn.formBind === true && btn.disabled === valid){
40556                 btn.setDisabled(!valid);
40557             }
40558         }
40559         this.fireEvent('clientvalidation', this, valid);
40560     }
40561     
40562     
40563     
40564     
40565     
40566     
40567     
40568     
40569 });
40570
40571
40572 // back compat
40573 Roo.Form = Roo.form.Form;
40574 /*
40575  * Based on:
40576  * Ext JS Library 1.1.1
40577  * Copyright(c) 2006-2007, Ext JS, LLC.
40578  *
40579  * Originally Released Under LGPL - original licence link has changed is not relivant.
40580  *
40581  * Fork - LGPL
40582  * <script type="text/javascript">
40583  */
40584  
40585  /**
40586  * @class Roo.form.Action
40587  * Internal Class used to handle form actions
40588  * @constructor
40589  * @param {Roo.form.BasicForm} el The form element or its id
40590  * @param {Object} config Configuration options
40591  */
40592  
40593  
40594 // define the action interface
40595 Roo.form.Action = function(form, options){
40596     this.form = form;
40597     this.options = options || {};
40598 };
40599 /**
40600  * Client Validation Failed
40601  * @const 
40602  */
40603 Roo.form.Action.CLIENT_INVALID = 'client';
40604 /**
40605  * Server Validation Failed
40606  * @const 
40607  */
40608  Roo.form.Action.SERVER_INVALID = 'server';
40609  /**
40610  * Connect to Server Failed
40611  * @const 
40612  */
40613 Roo.form.Action.CONNECT_FAILURE = 'connect';
40614 /**
40615  * Reading Data from Server Failed
40616  * @const 
40617  */
40618 Roo.form.Action.LOAD_FAILURE = 'load';
40619
40620 Roo.form.Action.prototype = {
40621     type : 'default',
40622     failureType : undefined,
40623     response : undefined,
40624     result : undefined,
40625
40626     // interface method
40627     run : function(options){
40628
40629     },
40630
40631     // interface method
40632     success : function(response){
40633
40634     },
40635
40636     // interface method
40637     handleResponse : function(response){
40638
40639     },
40640
40641     // default connection failure
40642     failure : function(response){
40643         this.response = response;
40644         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40645         this.form.afterAction(this, false);
40646     },
40647
40648     processResponse : function(response){
40649         this.response = response;
40650         if(!response.responseText){
40651             return true;
40652         }
40653         this.result = this.handleResponse(response);
40654         return this.result;
40655     },
40656
40657     // utility functions used internally
40658     getUrl : function(appendParams){
40659         var url = this.options.url || this.form.url || this.form.el.dom.action;
40660         if(appendParams){
40661             var p = this.getParams();
40662             if(p){
40663                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40664             }
40665         }
40666         return url;
40667     },
40668
40669     getMethod : function(){
40670         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40671     },
40672
40673     getParams : function(){
40674         var bp = this.form.baseParams;
40675         var p = this.options.params;
40676         if(p){
40677             if(typeof p == "object"){
40678                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40679             }else if(typeof p == 'string' && bp){
40680                 p += '&' + Roo.urlEncode(bp);
40681             }
40682         }else if(bp){
40683             p = Roo.urlEncode(bp);
40684         }
40685         return p;
40686     },
40687
40688     createCallback : function(){
40689         return {
40690             success: this.success,
40691             failure: this.failure,
40692             scope: this,
40693             timeout: (this.form.timeout*1000),
40694             upload: this.form.fileUpload ? this.success : undefined
40695         };
40696     }
40697 };
40698
40699 Roo.form.Action.Submit = function(form, options){
40700     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40701 };
40702
40703 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40704     type : 'submit',
40705
40706     run : function(){
40707         var o = this.options;
40708         var method = this.getMethod();
40709         var isPost = method == 'POST';
40710         if(o.clientValidation === false || this.form.isValid()){
40711             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40712                 form:this.form.el.dom,
40713                 url:this.getUrl(!isPost),
40714                 method: method,
40715                 params:isPost ? this.getParams() : null,
40716                 isUpload: this.form.fileUpload
40717             }));
40718
40719         }else if (o.clientValidation !== false){ // client validation failed
40720             this.failureType = Roo.form.Action.CLIENT_INVALID;
40721             this.form.afterAction(this, false);
40722         }
40723     },
40724
40725     success : function(response){
40726         var result = this.processResponse(response);
40727         if(result === true || result.success){
40728             this.form.afterAction(this, true);
40729             return;
40730         }
40731         if(result.errors){
40732             this.form.markInvalid(result.errors);
40733             this.failureType = Roo.form.Action.SERVER_INVALID;
40734         }
40735         this.form.afterAction(this, false);
40736     },
40737
40738     handleResponse : function(response){
40739         if(this.form.errorReader){
40740             var rs = this.form.errorReader.read(response);
40741             var errors = [];
40742             if(rs.records){
40743                 for(var i = 0, len = rs.records.length; i < len; i++) {
40744                     var r = rs.records[i];
40745                     errors[i] = r.data;
40746                 }
40747             }
40748             if(errors.length < 1){
40749                 errors = null;
40750             }
40751             return {
40752                 success : rs.success,
40753                 errors : errors
40754             };
40755         }
40756         var ret = false;
40757         try {
40758             ret = Roo.decode(response.responseText);
40759         } catch (e) {
40760             ret = {
40761                 success: false,
40762                 errorMsg: "Failed to read server message: " + response.responseText,
40763                 errors : []
40764             };
40765         }
40766         return ret;
40767         
40768     }
40769 });
40770
40771
40772 Roo.form.Action.Load = function(form, options){
40773     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40774     this.reader = this.form.reader;
40775 };
40776
40777 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40778     type : 'load',
40779
40780     run : function(){
40781         Roo.Ajax.request(Roo.apply(
40782                 this.createCallback(), {
40783                     method:this.getMethod(),
40784                     url:this.getUrl(false),
40785                     params:this.getParams()
40786         }));
40787     },
40788
40789     success : function(response){
40790         var result = this.processResponse(response);
40791         if(result === true || !result.success || !result.data){
40792             this.failureType = Roo.form.Action.LOAD_FAILURE;
40793             this.form.afterAction(this, false);
40794             return;
40795         }
40796         this.form.clearInvalid();
40797         this.form.setValues(result.data);
40798         this.form.afterAction(this, true);
40799     },
40800
40801     handleResponse : function(response){
40802         if(this.form.reader){
40803             var rs = this.form.reader.read(response);
40804             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40805             return {
40806                 success : rs.success,
40807                 data : data
40808             };
40809         }
40810         return Roo.decode(response.responseText);
40811     }
40812 });
40813
40814 Roo.form.Action.ACTION_TYPES = {
40815     'load' : Roo.form.Action.Load,
40816     'submit' : Roo.form.Action.Submit
40817 };/*
40818  * Based on:
40819  * Ext JS Library 1.1.1
40820  * Copyright(c) 2006-2007, Ext JS, LLC.
40821  *
40822  * Originally Released Under LGPL - original licence link has changed is not relivant.
40823  *
40824  * Fork - LGPL
40825  * <script type="text/javascript">
40826  */
40827  
40828 /**
40829  * @class Roo.form.Layout
40830  * @extends Roo.Component
40831  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40832  * @constructor
40833  * @param {Object} config Configuration options
40834  */
40835 Roo.form.Layout = function(config){
40836     var xitems = [];
40837     if (config.items) {
40838         xitems = config.items;
40839         delete config.items;
40840     }
40841     Roo.form.Layout.superclass.constructor.call(this, config);
40842     this.stack = [];
40843     Roo.each(xitems, this.addxtype, this);
40844      
40845 };
40846
40847 Roo.extend(Roo.form.Layout, Roo.Component, {
40848     /**
40849      * @cfg {String/Object} autoCreate
40850      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40851      */
40852     /**
40853      * @cfg {String/Object/Function} style
40854      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40855      * a function which returns such a specification.
40856      */
40857     /**
40858      * @cfg {String} labelAlign
40859      * Valid values are "left," "top" and "right" (defaults to "left")
40860      */
40861     /**
40862      * @cfg {Number} labelWidth
40863      * Fixed width in pixels of all field labels (defaults to undefined)
40864      */
40865     /**
40866      * @cfg {Boolean} clear
40867      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40868      */
40869     clear : true,
40870     /**
40871      * @cfg {String} labelSeparator
40872      * The separator to use after field labels (defaults to ':')
40873      */
40874     labelSeparator : ':',
40875     /**
40876      * @cfg {Boolean} hideLabels
40877      * True to suppress the display of field labels in this layout (defaults to false)
40878      */
40879     hideLabels : false,
40880
40881     // private
40882     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40883     
40884     isLayout : true,
40885     
40886     // private
40887     onRender : function(ct, position){
40888         if(this.el){ // from markup
40889             this.el = Roo.get(this.el);
40890         }else {  // generate
40891             var cfg = this.getAutoCreate();
40892             this.el = ct.createChild(cfg, position);
40893         }
40894         if(this.style){
40895             this.el.applyStyles(this.style);
40896         }
40897         if(this.labelAlign){
40898             this.el.addClass('x-form-label-'+this.labelAlign);
40899         }
40900         if(this.hideLabels){
40901             this.labelStyle = "display:none";
40902             this.elementStyle = "padding-left:0;";
40903         }else{
40904             if(typeof this.labelWidth == 'number'){
40905                 this.labelStyle = "width:"+this.labelWidth+"px;";
40906                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40907             }
40908             if(this.labelAlign == 'top'){
40909                 this.labelStyle = "width:auto;";
40910                 this.elementStyle = "padding-left:0;";
40911             }
40912         }
40913         var stack = this.stack;
40914         var slen = stack.length;
40915         if(slen > 0){
40916             if(!this.fieldTpl){
40917                 var t = new Roo.Template(
40918                     '<div class="x-form-item {5}">',
40919                         '<label for="{0}" style="{2}">{1}{4}</label>',
40920                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40921                         '</div>',
40922                     '</div><div class="x-form-clear-left"></div>'
40923                 );
40924                 t.disableFormats = true;
40925                 t.compile();
40926                 Roo.form.Layout.prototype.fieldTpl = t;
40927             }
40928             for(var i = 0; i < slen; i++) {
40929                 if(stack[i].isFormField){
40930                     this.renderField(stack[i]);
40931                 }else{
40932                     this.renderComponent(stack[i]);
40933                 }
40934             }
40935         }
40936         if(this.clear){
40937             this.el.createChild({cls:'x-form-clear'});
40938         }
40939     },
40940
40941     // private
40942     renderField : function(f){
40943         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40944                f.id, //0
40945                f.fieldLabel, //1
40946                f.labelStyle||this.labelStyle||'', //2
40947                this.elementStyle||'', //3
40948                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40949                f.itemCls||this.itemCls||''  //5
40950        ], true).getPrevSibling());
40951     },
40952
40953     // private
40954     renderComponent : function(c){
40955         c.render(c.isLayout ? this.el : this.el.createChild());    
40956     },
40957     /**
40958      * Adds a object form elements (using the xtype property as the factory method.)
40959      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40960      * @param {Object} config 
40961      */
40962     addxtype : function(o)
40963     {
40964         // create the lement.
40965         o.form = this.form;
40966         var fe = Roo.factory(o, Roo.form);
40967         this.form.allItems.push(fe);
40968         this.stack.push(fe);
40969         
40970         if (fe.isFormField) {
40971             this.form.items.add(fe);
40972         }
40973          
40974         return fe;
40975     }
40976 });
40977
40978 /**
40979  * @class Roo.form.Column
40980  * @extends Roo.form.Layout
40981  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
40982  * @constructor
40983  * @param {Object} config Configuration options
40984  */
40985 Roo.form.Column = function(config){
40986     Roo.form.Column.superclass.constructor.call(this, config);
40987 };
40988
40989 Roo.extend(Roo.form.Column, Roo.form.Layout, {
40990     /**
40991      * @cfg {Number/String} width
40992      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40993      */
40994     /**
40995      * @cfg {String/Object} autoCreate
40996      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
40997      */
40998
40999     // private
41000     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41001
41002     // private
41003     onRender : function(ct, position){
41004         Roo.form.Column.superclass.onRender.call(this, ct, position);
41005         if(this.width){
41006             this.el.setWidth(this.width);
41007         }
41008     }
41009 });
41010
41011
41012 /**
41013  * @class Roo.form.Row
41014  * @extends Roo.form.Layout
41015  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41016  * @constructor
41017  * @param {Object} config Configuration options
41018  */
41019
41020  
41021 Roo.form.Row = function(config){
41022     Roo.form.Row.superclass.constructor.call(this, config);
41023 };
41024  
41025 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41026       /**
41027      * @cfg {Number/String} width
41028      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41029      */
41030     /**
41031      * @cfg {Number/String} height
41032      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41033      */
41034     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41035     
41036     padWidth : 20,
41037     // private
41038     onRender : function(ct, position){
41039         //console.log('row render');
41040         if(!this.rowTpl){
41041             var t = new Roo.Template(
41042                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41043                     '<label for="{0}" style="{2}">{1}{4}</label>',
41044                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41045                     '</div>',
41046                 '</div>'
41047             );
41048             t.disableFormats = true;
41049             t.compile();
41050             Roo.form.Layout.prototype.rowTpl = t;
41051         }
41052         this.fieldTpl = this.rowTpl;
41053         
41054         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41055         var labelWidth = 100;
41056         
41057         if ((this.labelAlign != 'top')) {
41058             if (typeof this.labelWidth == 'number') {
41059                 labelWidth = this.labelWidth
41060             }
41061             this.padWidth =  20 + labelWidth;
41062             
41063         }
41064         
41065         Roo.form.Column.superclass.onRender.call(this, ct, position);
41066         if(this.width){
41067             this.el.setWidth(this.width);
41068         }
41069         if(this.height){
41070             this.el.setHeight(this.height);
41071         }
41072     },
41073     
41074     // private
41075     renderField : function(f){
41076         f.fieldEl = this.fieldTpl.append(this.el, [
41077                f.id, f.fieldLabel,
41078                f.labelStyle||this.labelStyle||'',
41079                this.elementStyle||'',
41080                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41081                f.itemCls||this.itemCls||'',
41082                f.width ? f.width + this.padWidth : 160 + this.padWidth
41083        ],true);
41084     }
41085 });
41086  
41087
41088 /**
41089  * @class Roo.form.FieldSet
41090  * @extends Roo.form.Layout
41091  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41092  * @constructor
41093  * @param {Object} config Configuration options
41094  */
41095 Roo.form.FieldSet = function(config){
41096     Roo.form.FieldSet.superclass.constructor.call(this, config);
41097 };
41098
41099 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41100     /**
41101      * @cfg {String} legend
41102      * The text to display as the legend for the FieldSet (defaults to '')
41103      */
41104     /**
41105      * @cfg {String/Object} autoCreate
41106      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41107      */
41108
41109     // private
41110     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41111
41112     // private
41113     onRender : function(ct, position){
41114         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41115         if(this.legend){
41116             this.setLegend(this.legend);
41117         }
41118     },
41119
41120     // private
41121     setLegend : function(text){
41122         if(this.rendered){
41123             this.el.child('legend').update(text);
41124         }
41125     }
41126 });/*
41127  * Based on:
41128  * Ext JS Library 1.1.1
41129  * Copyright(c) 2006-2007, Ext JS, LLC.
41130  *
41131  * Originally Released Under LGPL - original licence link has changed is not relivant.
41132  *
41133  * Fork - LGPL
41134  * <script type="text/javascript">
41135  */
41136 /**
41137  * @class Roo.form.VTypes
41138  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41139  * @singleton
41140  */
41141 Roo.form.VTypes = function(){
41142     // closure these in so they are only created once.
41143     var alpha = /^[a-zA-Z_]+$/;
41144     var alphanum = /^[a-zA-Z0-9_]+$/;
41145     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41146     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41147
41148     // All these messages and functions are configurable
41149     return {
41150         /**
41151          * The function used to validate email addresses
41152          * @param {String} value The email address
41153          */
41154         'email' : function(v){
41155             return email.test(v);
41156         },
41157         /**
41158          * The error text to display when the email validation function returns false
41159          * @type String
41160          */
41161         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41162         /**
41163          * The keystroke filter mask to be applied on email input
41164          * @type RegExp
41165          */
41166         'emailMask' : /[a-z0-9_\.\-@]/i,
41167
41168         /**
41169          * The function used to validate URLs
41170          * @param {String} value The URL
41171          */
41172         'url' : function(v){
41173             return url.test(v);
41174         },
41175         /**
41176          * The error text to display when the url validation function returns false
41177          * @type String
41178          */
41179         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41180         
41181         /**
41182          * The function used to validate alpha values
41183          * @param {String} value The value
41184          */
41185         'alpha' : function(v){
41186             return alpha.test(v);
41187         },
41188         /**
41189          * The error text to display when the alpha validation function returns false
41190          * @type String
41191          */
41192         'alphaText' : 'This field should only contain letters and _',
41193         /**
41194          * The keystroke filter mask to be applied on alpha input
41195          * @type RegExp
41196          */
41197         'alphaMask' : /[a-z_]/i,
41198
41199         /**
41200          * The function used to validate alphanumeric values
41201          * @param {String} value The value
41202          */
41203         'alphanum' : function(v){
41204             return alphanum.test(v);
41205         },
41206         /**
41207          * The error text to display when the alphanumeric validation function returns false
41208          * @type String
41209          */
41210         'alphanumText' : 'This field should only contain letters, numbers and _',
41211         /**
41212          * The keystroke filter mask to be applied on alphanumeric input
41213          * @type RegExp
41214          */
41215         'alphanumMask' : /[a-z0-9_]/i
41216     };
41217 }();//<script type="text/javascript">
41218
41219 /**
41220  * @class Roo.form.FCKeditor
41221  * @extends Roo.form.TextArea
41222  * Wrapper around the FCKEditor http://www.fckeditor.net
41223  * @constructor
41224  * Creates a new FCKeditor
41225  * @param {Object} config Configuration options
41226  */
41227 Roo.form.FCKeditor = function(config){
41228     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41229     this.addEvents({
41230          /**
41231          * @event editorinit
41232          * Fired when the editor is initialized - you can add extra handlers here..
41233          * @param {FCKeditor} this
41234          * @param {Object} the FCK object.
41235          */
41236         editorinit : true
41237     });
41238     
41239     
41240 };
41241 Roo.form.FCKeditor.editors = { };
41242 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41243 {
41244     //defaultAutoCreate : {
41245     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41246     //},
41247     // private
41248     /**
41249      * @cfg {Object} fck options - see fck manual for details.
41250      */
41251     fckconfig : false,
41252     
41253     /**
41254      * @cfg {Object} fck toolbar set (Basic or Default)
41255      */
41256     toolbarSet : 'Basic',
41257     /**
41258      * @cfg {Object} fck BasePath
41259      */ 
41260     basePath : '/fckeditor/',
41261     
41262     
41263     frame : false,
41264     
41265     value : '',
41266     
41267    
41268     onRender : function(ct, position)
41269     {
41270         if(!this.el){
41271             this.defaultAutoCreate = {
41272                 tag: "textarea",
41273                 style:"width:300px;height:60px;",
41274                 autocomplete: "off"
41275             };
41276         }
41277         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41278         /*
41279         if(this.grow){
41280             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41281             if(this.preventScrollbars){
41282                 this.el.setStyle("overflow", "hidden");
41283             }
41284             this.el.setHeight(this.growMin);
41285         }
41286         */
41287         //console.log('onrender' + this.getId() );
41288         Roo.form.FCKeditor.editors[this.getId()] = this;
41289          
41290
41291         this.replaceTextarea() ;
41292         
41293     },
41294     
41295     getEditor : function() {
41296         return this.fckEditor;
41297     },
41298     /**
41299      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41300      * @param {Mixed} value The value to set
41301      */
41302     
41303     
41304     setValue : function(value)
41305     {
41306         //console.log('setValue: ' + value);
41307         
41308         if(typeof(value) == 'undefined') { // not sure why this is happending...
41309             return;
41310         }
41311         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41312         
41313         //if(!this.el || !this.getEditor()) {
41314         //    this.value = value;
41315             //this.setValue.defer(100,this,[value]);    
41316         //    return;
41317         //} 
41318         
41319         if(!this.getEditor()) {
41320             return;
41321         }
41322         
41323         this.getEditor().SetData(value);
41324         
41325         //
41326
41327     },
41328
41329     /**
41330      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41331      * @return {Mixed} value The field value
41332      */
41333     getValue : function()
41334     {
41335         
41336         if (this.frame && this.frame.dom.style.display == 'none') {
41337             return Roo.form.FCKeditor.superclass.getValue.call(this);
41338         }
41339         
41340         if(!this.el || !this.getEditor()) {
41341            
41342            // this.getValue.defer(100,this); 
41343             return this.value;
41344         }
41345        
41346         
41347         var value=this.getEditor().GetData();
41348         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41349         return Roo.form.FCKeditor.superclass.getValue.call(this);
41350         
41351
41352     },
41353
41354     /**
41355      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41356      * @return {Mixed} value The field value
41357      */
41358     getRawValue : function()
41359     {
41360         if (this.frame && this.frame.dom.style.display == 'none') {
41361             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41362         }
41363         
41364         if(!this.el || !this.getEditor()) {
41365             //this.getRawValue.defer(100,this); 
41366             return this.value;
41367             return;
41368         }
41369         
41370         
41371         
41372         var value=this.getEditor().GetData();
41373         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41374         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41375          
41376     },
41377     
41378     setSize : function(w,h) {
41379         
41380         
41381         
41382         //if (this.frame && this.frame.dom.style.display == 'none') {
41383         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41384         //    return;
41385         //}
41386         //if(!this.el || !this.getEditor()) {
41387         //    this.setSize.defer(100,this, [w,h]); 
41388         //    return;
41389         //}
41390         
41391         
41392         
41393         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41394         
41395         this.frame.dom.setAttribute('width', w);
41396         this.frame.dom.setAttribute('height', h);
41397         this.frame.setSize(w,h);
41398         
41399     },
41400     
41401     toggleSourceEdit : function(value) {
41402         
41403       
41404          
41405         this.el.dom.style.display = value ? '' : 'none';
41406         this.frame.dom.style.display = value ?  'none' : '';
41407         
41408     },
41409     
41410     
41411     focus: function(tag)
41412     {
41413         if (this.frame.dom.style.display == 'none') {
41414             return Roo.form.FCKeditor.superclass.focus.call(this);
41415         }
41416         if(!this.el || !this.getEditor()) {
41417             this.focus.defer(100,this, [tag]); 
41418             return;
41419         }
41420         
41421         
41422         
41423         
41424         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41425         this.getEditor().Focus();
41426         if (tgs.length) {
41427             if (!this.getEditor().Selection.GetSelection()) {
41428                 this.focus.defer(100,this, [tag]); 
41429                 return;
41430             }
41431             
41432             
41433             var r = this.getEditor().EditorDocument.createRange();
41434             r.setStart(tgs[0],0);
41435             r.setEnd(tgs[0],0);
41436             this.getEditor().Selection.GetSelection().removeAllRanges();
41437             this.getEditor().Selection.GetSelection().addRange(r);
41438             this.getEditor().Focus();
41439         }
41440         
41441     },
41442     
41443     
41444     
41445     replaceTextarea : function()
41446     {
41447         if ( document.getElementById( this.getId() + '___Frame' ) )
41448             return ;
41449         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41450         //{
41451             // We must check the elements firstly using the Id and then the name.
41452         var oTextarea = document.getElementById( this.getId() );
41453         
41454         var colElementsByName = document.getElementsByName( this.getId() ) ;
41455          
41456         oTextarea.style.display = 'none' ;
41457
41458         if ( oTextarea.tabIndex ) {            
41459             this.TabIndex = oTextarea.tabIndex ;
41460         }
41461         
41462         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41463         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41464         this.frame = Roo.get(this.getId() + '___Frame')
41465     },
41466     
41467     _getConfigHtml : function()
41468     {
41469         var sConfig = '' ;
41470
41471         for ( var o in this.fckconfig ) {
41472             sConfig += sConfig.length > 0  ? '&amp;' : '';
41473             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41474         }
41475
41476         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41477     },
41478     
41479     
41480     _getIFrameHtml : function()
41481     {
41482         var sFile = 'fckeditor.html' ;
41483         /* no idea what this is about..
41484         try
41485         {
41486             if ( (/fcksource=true/i).test( window.top.location.search ) )
41487                 sFile = 'fckeditor.original.html' ;
41488         }
41489         catch (e) { 
41490         */
41491
41492         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41493         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41494         
41495         
41496         var html = '<iframe id="' + this.getId() +
41497             '___Frame" src="' + sLink +
41498             '" width="' + this.width +
41499             '" height="' + this.height + '"' +
41500             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41501             ' frameborder="0" scrolling="no"></iframe>' ;
41502
41503         return html ;
41504     },
41505     
41506     _insertHtmlBefore : function( html, element )
41507     {
41508         if ( element.insertAdjacentHTML )       {
41509             // IE
41510             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41511         } else { // Gecko
41512             var oRange = document.createRange() ;
41513             oRange.setStartBefore( element ) ;
41514             var oFragment = oRange.createContextualFragment( html );
41515             element.parentNode.insertBefore( oFragment, element ) ;
41516         }
41517     }
41518     
41519     
41520   
41521     
41522     
41523     
41524     
41525
41526 });
41527
41528 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41529
41530 function FCKeditor_OnComplete(editorInstance){
41531     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41532     f.fckEditor = editorInstance;
41533     //console.log("loaded");
41534     f.fireEvent('editorinit', f, editorInstance);
41535
41536   
41537
41538  
41539
41540
41541
41542
41543
41544
41545
41546
41547
41548
41549
41550
41551
41552
41553
41554 //<script type="text/javascript">
41555 /**
41556  * @class Roo.form.GridField
41557  * @extends Roo.form.Field
41558  * Embed a grid (or editable grid into a form)
41559  * STATUS ALPHA
41560  * @constructor
41561  * Creates a new GridField
41562  * @param {Object} config Configuration options
41563  */
41564 Roo.form.GridField = function(config){
41565     Roo.form.GridField.superclass.constructor.call(this, config);
41566      
41567 };
41568
41569 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41570     /**
41571      * @cfg {Number} width  - used to restrict width of grid..
41572      */
41573     width : 100,
41574     /**
41575      * @cfg {Number} height - used to restrict height of grid..
41576      */
41577     height : 50,
41578      /**
41579      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41580      */
41581     xgrid : false, 
41582     /**
41583      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41584      * {tag: "input", type: "checkbox", autocomplete: "off"})
41585      */
41586    // defaultAutoCreate : { tag: 'div' },
41587     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41588     /**
41589      * @cfg {String} addTitle Text to include for adding a title.
41590      */
41591     addTitle : false,
41592     //
41593     onResize : function(){
41594         Roo.form.Field.superclass.onResize.apply(this, arguments);
41595     },
41596
41597     initEvents : function(){
41598         // Roo.form.Checkbox.superclass.initEvents.call(this);
41599         // has no events...
41600        
41601     },
41602
41603
41604     getResizeEl : function(){
41605         return this.wrap;
41606     },
41607
41608     getPositionEl : function(){
41609         return this.wrap;
41610     },
41611
41612     // private
41613     onRender : function(ct, position){
41614         
41615         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41616         var style = this.style;
41617         delete this.style;
41618         
41619         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41620         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41621         this.viewEl = this.wrap.createChild({ tag: 'div' });
41622         if (style) {
41623             this.viewEl.applyStyles(style);
41624         }
41625         if (this.width) {
41626             this.viewEl.setWidth(this.width);
41627         }
41628         if (this.height) {
41629             this.viewEl.setHeight(this.height);
41630         }
41631         //if(this.inputValue !== undefined){
41632         //this.setValue(this.value);
41633         
41634         
41635         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41636         
41637         
41638         this.grid.render();
41639         this.grid.getDataSource().on('remove', this.refreshValue, this);
41640         this.grid.getDataSource().on('update', this.refreshValue, this);
41641         this.grid.on('afteredit', this.refreshValue, this);
41642  
41643     },
41644      
41645     
41646     /**
41647      * Sets the value of the item. 
41648      * @param {String} either an object  or a string..
41649      */
41650     setValue : function(v){
41651         //this.value = v;
41652         v = v || []; // empty set..
41653         // this does not seem smart - it really only affects memoryproxy grids..
41654         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41655             var ds = this.grid.getDataSource();
41656             // assumes a json reader..
41657             var data = {}
41658             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41659             ds.loadData( data);
41660         }
41661         Roo.form.GridField.superclass.setValue.call(this, v);
41662         this.refreshValue();
41663         // should load data in the grid really....
41664     },
41665     
41666     // private
41667     refreshValue: function() {
41668          var val = [];
41669         this.grid.getDataSource().each(function(r) {
41670             val.push(r.data);
41671         });
41672         this.el.dom.value = Roo.encode(val);
41673     }
41674     
41675      
41676     
41677     
41678 });//<script type="text/javasscript">
41679  
41680
41681 /**
41682  * @class Roo.DDView
41683  * A DnD enabled version of Roo.View.
41684  * @param {Element/String} container The Element in which to create the View.
41685  * @param {String} tpl The template string used to create the markup for each element of the View
41686  * @param {Object} config The configuration properties. These include all the config options of
41687  * {@link Roo.View} plus some specific to this class.<br>
41688  * <p>
41689  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41690  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41691  * <p>
41692  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41693 .x-view-drag-insert-above {
41694         border-top:1px dotted #3366cc;
41695 }
41696 .x-view-drag-insert-below {
41697         border-bottom:1px dotted #3366cc;
41698 }
41699 </code></pre>
41700  * 
41701  */
41702  
41703 Roo.DDView = function(container, tpl, config) {
41704     Roo.DDView.superclass.constructor.apply(this, arguments);
41705     this.getEl().setStyle("outline", "0px none");
41706     this.getEl().unselectable();
41707     if (this.dragGroup) {
41708                 this.setDraggable(this.dragGroup.split(","));
41709     }
41710     if (this.dropGroup) {
41711                 this.setDroppable(this.dropGroup.split(","));
41712     }
41713     if (this.deletable) {
41714         this.setDeletable();
41715     }
41716     this.isDirtyFlag = false;
41717         this.addEvents({
41718                 "drop" : true
41719         });
41720 };
41721
41722 Roo.extend(Roo.DDView, Roo.View, {
41723 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41724 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41725 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41726 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41727
41728         isFormField: true,
41729
41730         reset: Roo.emptyFn,
41731         
41732         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41733
41734         validate: function() {
41735                 return true;
41736         },
41737         
41738         destroy: function() {
41739                 this.purgeListeners();
41740                 this.getEl.removeAllListeners();
41741                 this.getEl().remove();
41742                 if (this.dragZone) {
41743                         if (this.dragZone.destroy) {
41744                                 this.dragZone.destroy();
41745                         }
41746                 }
41747                 if (this.dropZone) {
41748                         if (this.dropZone.destroy) {
41749                                 this.dropZone.destroy();
41750                         }
41751                 }
41752         },
41753
41754 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41755         getName: function() {
41756                 return this.name;
41757         },
41758
41759 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41760         setValue: function(v) {
41761                 if (!this.store) {
41762                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41763                 }
41764                 var data = {};
41765                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41766                 this.store.proxy = new Roo.data.MemoryProxy(data);
41767                 this.store.load();
41768         },
41769
41770 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41771         getValue: function() {
41772                 var result = '(';
41773                 this.store.each(function(rec) {
41774                         result += rec.id + ',';
41775                 });
41776                 return result.substr(0, result.length - 1) + ')';
41777         },
41778         
41779         getIds: function() {
41780                 var i = 0, result = new Array(this.store.getCount());
41781                 this.store.each(function(rec) {
41782                         result[i++] = rec.id;
41783                 });
41784                 return result;
41785         },
41786         
41787         isDirty: function() {
41788                 return this.isDirtyFlag;
41789         },
41790
41791 /**
41792  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41793  *      whole Element becomes the target, and this causes the drop gesture to append.
41794  */
41795     getTargetFromEvent : function(e) {
41796                 var target = e.getTarget();
41797                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41798                 target = target.parentNode;
41799                 }
41800                 if (!target) {
41801                         target = this.el.dom.lastChild || this.el.dom;
41802                 }
41803                 return target;
41804     },
41805
41806 /**
41807  *      Create the drag data which consists of an object which has the property "ddel" as
41808  *      the drag proxy element. 
41809  */
41810     getDragData : function(e) {
41811         var target = this.findItemFromChild(e.getTarget());
41812                 if(target) {
41813                         this.handleSelection(e);
41814                         var selNodes = this.getSelectedNodes();
41815             var dragData = {
41816                 source: this,
41817                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41818                 nodes: selNodes,
41819                 records: []
41820                         };
41821                         var selectedIndices = this.getSelectedIndexes();
41822                         for (var i = 0; i < selectedIndices.length; i++) {
41823                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41824                         }
41825                         if (selNodes.length == 1) {
41826                                 dragData.ddel = target.cloneNode(true); // the div element
41827                         } else {
41828                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41829                                 div.className = 'multi-proxy';
41830                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41831                                         div.appendChild(selNodes[i].cloneNode(true));
41832                                 }
41833                                 dragData.ddel = div;
41834                         }
41835             //console.log(dragData)
41836             //console.log(dragData.ddel.innerHTML)
41837                         return dragData;
41838                 }
41839         //console.log('nodragData')
41840                 return false;
41841     },
41842     
41843 /**     Specify to which ddGroup items in this DDView may be dragged. */
41844     setDraggable: function(ddGroup) {
41845         if (ddGroup instanceof Array) {
41846                 Roo.each(ddGroup, this.setDraggable, this);
41847                 return;
41848         }
41849         if (this.dragZone) {
41850                 this.dragZone.addToGroup(ddGroup);
41851         } else {
41852                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41853                                 containerScroll: true,
41854                                 ddGroup: ddGroup 
41855
41856                         });
41857 //                      Draggability implies selection. DragZone's mousedown selects the element.
41858                         if (!this.multiSelect) { this.singleSelect = true; }
41859
41860 //                      Wire the DragZone's handlers up to methods in *this*
41861                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41862                 }
41863     },
41864
41865 /**     Specify from which ddGroup this DDView accepts drops. */
41866     setDroppable: function(ddGroup) {
41867         if (ddGroup instanceof Array) {
41868                 Roo.each(ddGroup, this.setDroppable, this);
41869                 return;
41870         }
41871         if (this.dropZone) {
41872                 this.dropZone.addToGroup(ddGroup);
41873         } else {
41874                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41875                                 containerScroll: true,
41876                                 ddGroup: ddGroup
41877                         });
41878
41879 //                      Wire the DropZone's handlers up to methods in *this*
41880                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41881                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41882                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41883                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41884                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41885                 }
41886     },
41887
41888 /**     Decide whether to drop above or below a View node. */
41889     getDropPoint : function(e, n, dd){
41890         if (n == this.el.dom) { return "above"; }
41891                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41892                 var c = t + (b - t) / 2;
41893                 var y = Roo.lib.Event.getPageY(e);
41894                 if(y <= c) {
41895                         return "above";
41896                 }else{
41897                         return "below";
41898                 }
41899     },
41900
41901     onNodeEnter : function(n, dd, e, data){
41902                 return false;
41903     },
41904     
41905     onNodeOver : function(n, dd, e, data){
41906                 var pt = this.getDropPoint(e, n, dd);
41907                 // set the insert point style on the target node
41908                 var dragElClass = this.dropNotAllowed;
41909                 if (pt) {
41910                         var targetElClass;
41911                         if (pt == "above"){
41912                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41913                                 targetElClass = "x-view-drag-insert-above";
41914                         } else {
41915                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41916                                 targetElClass = "x-view-drag-insert-below";
41917                         }
41918                         if (this.lastInsertClass != targetElClass){
41919                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41920                                 this.lastInsertClass = targetElClass;
41921                         }
41922                 }
41923                 return dragElClass;
41924         },
41925
41926     onNodeOut : function(n, dd, e, data){
41927                 this.removeDropIndicators(n);
41928     },
41929
41930     onNodeDrop : function(n, dd, e, data){
41931         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41932                 return false;
41933         }
41934         var pt = this.getDropPoint(e, n, dd);
41935                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41936                 if (pt == "below") { insertAt++; }
41937                 for (var i = 0; i < data.records.length; i++) {
41938                         var r = data.records[i];
41939                         var dup = this.store.getById(r.id);
41940                         if (dup && (dd != this.dragZone)) {
41941                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41942                         } else {
41943                                 if (data.copy) {
41944                                         this.store.insert(insertAt++, r.copy());
41945                                 } else {
41946                                         data.source.isDirtyFlag = true;
41947                                         r.store.remove(r);
41948                                         this.store.insert(insertAt++, r);
41949                                 }
41950                                 this.isDirtyFlag = true;
41951                         }
41952                 }
41953                 this.dragZone.cachedTarget = null;
41954                 return true;
41955     },
41956
41957     removeDropIndicators : function(n){
41958                 if(n){
41959                         Roo.fly(n).removeClass([
41960                                 "x-view-drag-insert-above",
41961                                 "x-view-drag-insert-below"]);
41962                         this.lastInsertClass = "_noclass";
41963                 }
41964     },
41965
41966 /**
41967  *      Utility method. Add a delete option to the DDView's context menu.
41968  *      @param {String} imageUrl The URL of the "delete" icon image.
41969  */
41970         setDeletable: function(imageUrl) {
41971                 if (!this.singleSelect && !this.multiSelect) {
41972                         this.singleSelect = true;
41973                 }
41974                 var c = this.getContextMenu();
41975                 this.contextMenu.on("itemclick", function(item) {
41976                         switch (item.id) {
41977                                 case "delete":
41978                                         this.remove(this.getSelectedIndexes());
41979                                         break;
41980                         }
41981                 }, this);
41982                 this.contextMenu.add({
41983                         icon: imageUrl,
41984                         id: "delete",
41985                         text: 'Delete'
41986                 });
41987         },
41988         
41989 /**     Return the context menu for this DDView. */
41990         getContextMenu: function() {
41991                 if (!this.contextMenu) {
41992 //                      Create the View's context menu
41993                         this.contextMenu = new Roo.menu.Menu({
41994                                 id: this.id + "-contextmenu"
41995                         });
41996                         this.el.on("contextmenu", this.showContextMenu, this);
41997                 }
41998                 return this.contextMenu;
41999         },
42000         
42001         disableContextMenu: function() {
42002                 if (this.contextMenu) {
42003                         this.el.un("contextmenu", this.showContextMenu, this);
42004                 }
42005         },
42006
42007         showContextMenu: function(e, item) {
42008         item = this.findItemFromChild(e.getTarget());
42009                 if (item) {
42010                         e.stopEvent();
42011                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42012                         this.contextMenu.showAt(e.getXY());
42013             }
42014     },
42015
42016 /**
42017  *      Remove {@link Roo.data.Record}s at the specified indices.
42018  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42019  */
42020     remove: function(selectedIndices) {
42021                 selectedIndices = [].concat(selectedIndices);
42022                 for (var i = 0; i < selectedIndices.length; i++) {
42023                         var rec = this.store.getAt(selectedIndices[i]);
42024                         this.store.remove(rec);
42025                 }
42026     },
42027
42028 /**
42029  *      Double click fires the event, but also, if this is draggable, and there is only one other
42030  *      related DropZone, it transfers the selected node.
42031  */
42032     onDblClick : function(e){
42033         var item = this.findItemFromChild(e.getTarget());
42034         if(item){
42035             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42036                 return false;
42037             }
42038             if (this.dragGroup) {
42039                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42040                     while (targets.indexOf(this.dropZone) > -1) {
42041                             targets.remove(this.dropZone);
42042                                 }
42043                     if (targets.length == 1) {
42044                                         this.dragZone.cachedTarget = null;
42045                         var el = Roo.get(targets[0].getEl());
42046                         var box = el.getBox(true);
42047                         targets[0].onNodeDrop(el.dom, {
42048                                 target: el.dom,
42049                                 xy: [box.x, box.y + box.height - 1]
42050                         }, null, this.getDragData(e));
42051                     }
42052                 }
42053         }
42054     },
42055     
42056     handleSelection: function(e) {
42057                 this.dragZone.cachedTarget = null;
42058         var item = this.findItemFromChild(e.getTarget());
42059         if (!item) {
42060                 this.clearSelections(true);
42061                 return;
42062         }
42063                 if (item && (this.multiSelect || this.singleSelect)){
42064                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42065                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42066                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42067                                 this.unselect(item);
42068                         } else {
42069                                 this.select(item, this.multiSelect && e.ctrlKey);
42070                                 this.lastSelection = item;
42071                         }
42072                 }
42073     },
42074
42075     onItemClick : function(item, index, e){
42076                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42077                         return false;
42078                 }
42079                 return true;
42080     },
42081
42082     unselect : function(nodeInfo, suppressEvent){
42083                 var node = this.getNode(nodeInfo);
42084                 if(node && this.isSelected(node)){
42085                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42086                                 Roo.fly(node).removeClass(this.selectedClass);
42087                                 this.selections.remove(node);
42088                                 if(!suppressEvent){
42089                                         this.fireEvent("selectionchange", this, this.selections);
42090                                 }
42091                         }
42092                 }
42093     }
42094 });
42095 /*
42096  * Based on:
42097  * Ext JS Library 1.1.1
42098  * Copyright(c) 2006-2007, Ext JS, LLC.
42099  *
42100  * Originally Released Under LGPL - original licence link has changed is not relivant.
42101  *
42102  * Fork - LGPL
42103  * <script type="text/javascript">
42104  */
42105  
42106 /**
42107  * @class Roo.LayoutManager
42108  * @extends Roo.util.Observable
42109  * Base class for layout managers.
42110  */
42111 Roo.LayoutManager = function(container, config){
42112     Roo.LayoutManager.superclass.constructor.call(this);
42113     this.el = Roo.get(container);
42114     // ie scrollbar fix
42115     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42116         document.body.scroll = "no";
42117     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42118         this.el.position('relative');
42119     }
42120     this.id = this.el.id;
42121     this.el.addClass("x-layout-container");
42122     /** false to disable window resize monitoring @type Boolean */
42123     this.monitorWindowResize = true;
42124     this.regions = {};
42125     this.addEvents({
42126         /**
42127          * @event layout
42128          * Fires when a layout is performed. 
42129          * @param {Roo.LayoutManager} this
42130          */
42131         "layout" : true,
42132         /**
42133          * @event regionresized
42134          * Fires when the user resizes a region. 
42135          * @param {Roo.LayoutRegion} region The resized region
42136          * @param {Number} newSize The new size (width for east/west, height for north/south)
42137          */
42138         "regionresized" : true,
42139         /**
42140          * @event regioncollapsed
42141          * Fires when a region is collapsed. 
42142          * @param {Roo.LayoutRegion} region The collapsed region
42143          */
42144         "regioncollapsed" : true,
42145         /**
42146          * @event regionexpanded
42147          * Fires when a region is expanded.  
42148          * @param {Roo.LayoutRegion} region The expanded region
42149          */
42150         "regionexpanded" : true
42151     });
42152     this.updating = false;
42153     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42154 };
42155
42156 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42157     /**
42158      * Returns true if this layout is currently being updated
42159      * @return {Boolean}
42160      */
42161     isUpdating : function(){
42162         return this.updating; 
42163     },
42164     
42165     /**
42166      * Suspend the LayoutManager from doing auto-layouts while
42167      * making multiple add or remove calls
42168      */
42169     beginUpdate : function(){
42170         this.updating = true;    
42171     },
42172     
42173     /**
42174      * Restore auto-layouts and optionally disable the manager from performing a layout
42175      * @param {Boolean} noLayout true to disable a layout update 
42176      */
42177     endUpdate : function(noLayout){
42178         this.updating = false;
42179         if(!noLayout){
42180             this.layout();
42181         }    
42182     },
42183     
42184     layout: function(){
42185         
42186     },
42187     
42188     onRegionResized : function(region, newSize){
42189         this.fireEvent("regionresized", region, newSize);
42190         this.layout();
42191     },
42192     
42193     onRegionCollapsed : function(region){
42194         this.fireEvent("regioncollapsed", region);
42195     },
42196     
42197     onRegionExpanded : function(region){
42198         this.fireEvent("regionexpanded", region);
42199     },
42200         
42201     /**
42202      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42203      * performs box-model adjustments.
42204      * @return {Object} The size as an object {width: (the width), height: (the height)}
42205      */
42206     getViewSize : function(){
42207         var size;
42208         if(this.el.dom != document.body){
42209             size = this.el.getSize();
42210         }else{
42211             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42212         }
42213         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42214         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42215         return size;
42216     },
42217     
42218     /**
42219      * Returns the Element this layout is bound to.
42220      * @return {Roo.Element}
42221      */
42222     getEl : function(){
42223         return this.el;
42224     },
42225     
42226     /**
42227      * Returns the specified region.
42228      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42229      * @return {Roo.LayoutRegion}
42230      */
42231     getRegion : function(target){
42232         return this.regions[target.toLowerCase()];
42233     },
42234     
42235     onWindowResize : function(){
42236         if(this.monitorWindowResize){
42237             this.layout();
42238         }
42239     }
42240 });/*
42241  * Based on:
42242  * Ext JS Library 1.1.1
42243  * Copyright(c) 2006-2007, Ext JS, LLC.
42244  *
42245  * Originally Released Under LGPL - original licence link has changed is not relivant.
42246  *
42247  * Fork - LGPL
42248  * <script type="text/javascript">
42249  */
42250 /**
42251  * @class Roo.BorderLayout
42252  * @extends Roo.LayoutManager
42253  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42254  * please see: <br><br>
42255  * <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>
42256  * <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>
42257  * Example:
42258  <pre><code>
42259  var layout = new Roo.BorderLayout(document.body, {
42260     north: {
42261         initialSize: 25,
42262         titlebar: false
42263     },
42264     west: {
42265         split:true,
42266         initialSize: 200,
42267         minSize: 175,
42268         maxSize: 400,
42269         titlebar: true,
42270         collapsible: true
42271     },
42272     east: {
42273         split:true,
42274         initialSize: 202,
42275         minSize: 175,
42276         maxSize: 400,
42277         titlebar: true,
42278         collapsible: true
42279     },
42280     south: {
42281         split:true,
42282         initialSize: 100,
42283         minSize: 100,
42284         maxSize: 200,
42285         titlebar: true,
42286         collapsible: true
42287     },
42288     center: {
42289         titlebar: true,
42290         autoScroll:true,
42291         resizeTabs: true,
42292         minTabWidth: 50,
42293         preferredTabWidth: 150
42294     }
42295 });
42296
42297 // shorthand
42298 var CP = Roo.ContentPanel;
42299
42300 layout.beginUpdate();
42301 layout.add("north", new CP("north", "North"));
42302 layout.add("south", new CP("south", {title: "South", closable: true}));
42303 layout.add("west", new CP("west", {title: "West"}));
42304 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42305 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42306 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42307 layout.getRegion("center").showPanel("center1");
42308 layout.endUpdate();
42309 </code></pre>
42310
42311 <b>The container the layout is rendered into can be either the body element or any other element.
42312 If it is not the body element, the container needs to either be an absolute positioned element,
42313 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42314 the container size if it is not the body element.</b>
42315
42316 * @constructor
42317 * Create a new BorderLayout
42318 * @param {String/HTMLElement/Element} container The container this layout is bound to
42319 * @param {Object} config Configuration options
42320  */
42321 Roo.BorderLayout = function(container, config){
42322     config = config || {};
42323     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42324     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42325     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42326         var target = this.factory.validRegions[i];
42327         if(config[target]){
42328             this.addRegion(target, config[target]);
42329         }
42330     }
42331 };
42332
42333 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42334     /**
42335      * Creates and adds a new region if it doesn't already exist.
42336      * @param {String} target The target region key (north, south, east, west or center).
42337      * @param {Object} config The regions config object
42338      * @return {BorderLayoutRegion} The new region
42339      */
42340     addRegion : function(target, config){
42341         if(!this.regions[target]){
42342             var r = this.factory.create(target, this, config);
42343             this.bindRegion(target, r);
42344         }
42345         return this.regions[target];
42346     },
42347
42348     // private (kinda)
42349     bindRegion : function(name, r){
42350         this.regions[name] = r;
42351         r.on("visibilitychange", this.layout, this);
42352         r.on("paneladded", this.layout, this);
42353         r.on("panelremoved", this.layout, this);
42354         r.on("invalidated", this.layout, this);
42355         r.on("resized", this.onRegionResized, this);
42356         r.on("collapsed", this.onRegionCollapsed, this);
42357         r.on("expanded", this.onRegionExpanded, this);
42358     },
42359
42360     /**
42361      * Performs a layout update.
42362      */
42363     layout : function(){
42364         if(this.updating) return;
42365         var size = this.getViewSize();
42366         var w = size.width;
42367         var h = size.height;
42368         var centerW = w;
42369         var centerH = h;
42370         var centerY = 0;
42371         var centerX = 0;
42372         //var x = 0, y = 0;
42373
42374         var rs = this.regions;
42375         var north = rs["north"];
42376         var south = rs["south"]; 
42377         var west = rs["west"];
42378         var east = rs["east"];
42379         var center = rs["center"];
42380         //if(this.hideOnLayout){ // not supported anymore
42381             //c.el.setStyle("display", "none");
42382         //}
42383         if(north && north.isVisible()){
42384             var b = north.getBox();
42385             var m = north.getMargins();
42386             b.width = w - (m.left+m.right);
42387             b.x = m.left;
42388             b.y = m.top;
42389             centerY = b.height + b.y + m.bottom;
42390             centerH -= centerY;
42391             north.updateBox(this.safeBox(b));
42392         }
42393         if(south && south.isVisible()){
42394             var b = south.getBox();
42395             var m = south.getMargins();
42396             b.width = w - (m.left+m.right);
42397             b.x = m.left;
42398             var totalHeight = (b.height + m.top + m.bottom);
42399             b.y = h - totalHeight + m.top;
42400             centerH -= totalHeight;
42401             south.updateBox(this.safeBox(b));
42402         }
42403         if(west && west.isVisible()){
42404             var b = west.getBox();
42405             var m = west.getMargins();
42406             b.height = centerH - (m.top+m.bottom);
42407             b.x = m.left;
42408             b.y = centerY + m.top;
42409             var totalWidth = (b.width + m.left + m.right);
42410             centerX += totalWidth;
42411             centerW -= totalWidth;
42412             west.updateBox(this.safeBox(b));
42413         }
42414         if(east && east.isVisible()){
42415             var b = east.getBox();
42416             var m = east.getMargins();
42417             b.height = centerH - (m.top+m.bottom);
42418             var totalWidth = (b.width + m.left + m.right);
42419             b.x = w - totalWidth + m.left;
42420             b.y = centerY + m.top;
42421             centerW -= totalWidth;
42422             east.updateBox(this.safeBox(b));
42423         }
42424         if(center){
42425             var m = center.getMargins();
42426             var centerBox = {
42427                 x: centerX + m.left,
42428                 y: centerY + m.top,
42429                 width: centerW - (m.left+m.right),
42430                 height: centerH - (m.top+m.bottom)
42431             };
42432             //if(this.hideOnLayout){
42433                 //center.el.setStyle("display", "block");
42434             //}
42435             center.updateBox(this.safeBox(centerBox));
42436         }
42437         this.el.repaint();
42438         this.fireEvent("layout", this);
42439     },
42440
42441     // private
42442     safeBox : function(box){
42443         box.width = Math.max(0, box.width);
42444         box.height = Math.max(0, box.height);
42445         return box;
42446     },
42447
42448     /**
42449      * Adds a ContentPanel (or subclass) to this layout.
42450      * @param {String} target The target region key (north, south, east, west or center).
42451      * @param {Roo.ContentPanel} panel The panel to add
42452      * @return {Roo.ContentPanel} The added panel
42453      */
42454     add : function(target, panel){
42455          
42456         target = target.toLowerCase();
42457         return this.regions[target].add(panel);
42458     },
42459
42460     /**
42461      * Remove a ContentPanel (or subclass) to this layout.
42462      * @param {String} target The target region key (north, south, east, west or center).
42463      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42464      * @return {Roo.ContentPanel} The removed panel
42465      */
42466     remove : function(target, panel){
42467         target = target.toLowerCase();
42468         return this.regions[target].remove(panel);
42469     },
42470
42471     /**
42472      * Searches all regions for a panel with the specified id
42473      * @param {String} panelId
42474      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42475      */
42476     findPanel : function(panelId){
42477         var rs = this.regions;
42478         for(var target in rs){
42479             if(typeof rs[target] != "function"){
42480                 var p = rs[target].getPanel(panelId);
42481                 if(p){
42482                     return p;
42483                 }
42484             }
42485         }
42486         return null;
42487     },
42488
42489     /**
42490      * Searches all regions for a panel with the specified id and activates (shows) it.
42491      * @param {String/ContentPanel} panelId The panels id or the panel itself
42492      * @return {Roo.ContentPanel} The shown panel or null
42493      */
42494     showPanel : function(panelId) {
42495       var rs = this.regions;
42496       for(var target in rs){
42497          var r = rs[target];
42498          if(typeof r != "function"){
42499             if(r.hasPanel(panelId)){
42500                return r.showPanel(panelId);
42501             }
42502          }
42503       }
42504       return null;
42505    },
42506
42507    /**
42508      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42509      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42510      */
42511     restoreState : function(provider){
42512         if(!provider){
42513             provider = Roo.state.Manager;
42514         }
42515         var sm = new Roo.LayoutStateManager();
42516         sm.init(this, provider);
42517     },
42518
42519     /**
42520      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42521      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42522      * a valid ContentPanel config object.  Example:
42523      * <pre><code>
42524 // Create the main layout
42525 var layout = new Roo.BorderLayout('main-ct', {
42526     west: {
42527         split:true,
42528         minSize: 175,
42529         titlebar: true
42530     },
42531     center: {
42532         title:'Components'
42533     }
42534 }, 'main-ct');
42535
42536 // Create and add multiple ContentPanels at once via configs
42537 layout.batchAdd({
42538    west: {
42539        id: 'source-files',
42540        autoCreate:true,
42541        title:'Ext Source Files',
42542        autoScroll:true,
42543        fitToFrame:true
42544    },
42545    center : {
42546        el: cview,
42547        autoScroll:true,
42548        fitToFrame:true,
42549        toolbar: tb,
42550        resizeEl:'cbody'
42551    }
42552 });
42553 </code></pre>
42554      * @param {Object} regions An object containing ContentPanel configs by region name
42555      */
42556     batchAdd : function(regions){
42557         this.beginUpdate();
42558         for(var rname in regions){
42559             var lr = this.regions[rname];
42560             if(lr){
42561                 this.addTypedPanels(lr, regions[rname]);
42562             }
42563         }
42564         this.endUpdate();
42565     },
42566
42567     // private
42568     addTypedPanels : function(lr, ps){
42569         if(typeof ps == 'string'){
42570             lr.add(new Roo.ContentPanel(ps));
42571         }
42572         else if(ps instanceof Array){
42573             for(var i =0, len = ps.length; i < len; i++){
42574                 this.addTypedPanels(lr, ps[i]);
42575             }
42576         }
42577         else if(!ps.events){ // raw config?
42578             var el = ps.el;
42579             delete ps.el; // prevent conflict
42580             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42581         }
42582         else {  // panel object assumed!
42583             lr.add(ps);
42584         }
42585     },
42586     /**
42587      * Adds a xtype elements to the layout.
42588      * <pre><code>
42589
42590 layout.addxtype({
42591        xtype : 'ContentPanel',
42592        region: 'west',
42593        items: [ .... ]
42594    }
42595 );
42596
42597 layout.addxtype({
42598         xtype : 'NestedLayoutPanel',
42599         region: 'west',
42600         layout: {
42601            center: { },
42602            west: { }   
42603         },
42604         items : [ ... list of content panels or nested layout panels.. ]
42605    }
42606 );
42607 </code></pre>
42608      * @param {Object} cfg Xtype definition of item to add.
42609      */
42610     addxtype : function(cfg)
42611     {
42612         // basically accepts a pannel...
42613         // can accept a layout region..!?!?
42614        // console.log('BorderLayout add ' + cfg.xtype)
42615         
42616         if (!cfg.xtype.match(/Panel$/)) {
42617             return false;
42618         }
42619         var ret = false;
42620         var region = cfg.region;
42621         delete cfg.region;
42622         
42623           
42624         var xitems = [];
42625         if (cfg.items) {
42626             xitems = cfg.items;
42627             delete cfg.items;
42628         }
42629         
42630         
42631         switch(cfg.xtype) 
42632         {
42633             case 'ContentPanel':  // ContentPanel (el, cfg)
42634                 if(cfg.autoCreate) {
42635                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42636                 } else {
42637                     var el = this.el.createChild();
42638                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42639                 }
42640                 
42641                 this.add(region, ret);
42642                 break;
42643             
42644             
42645             case 'TreePanel': // our new panel!
42646                 cfg.el = this.el.createChild();
42647                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42648                 this.add(region, ret);
42649                 break;
42650             
42651             case 'NestedLayoutPanel': 
42652                 // create a new Layout (which is  a Border Layout...
42653                 var el = this.el.createChild();
42654                 var clayout = cfg.layout;
42655                 delete cfg.layout;
42656                 clayout.items   = clayout.items  || [];
42657                 // replace this exitems with the clayout ones..
42658                 xitems = clayout.items;
42659                  
42660                 
42661                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42662                     cfg.background = false;
42663                 }
42664                 var layout = new Roo.BorderLayout(el, clayout);
42665                 
42666                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42667                 //console.log('adding nested layout panel '  + cfg.toSource());
42668                 this.add(region, ret);
42669                 
42670                 break;
42671                 
42672             case 'GridPanel': 
42673             
42674                 // needs grid and region
42675                 
42676                 //var el = this.getRegion(region).el.createChild();
42677                 var el = this.el.createChild();
42678                 // create the grid first...
42679                 
42680                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42681                 delete cfg.grid;
42682                 if (region == 'center' && this.active ) {
42683                     cfg.background = false;
42684                 }
42685                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42686                 
42687                 this.add(region, ret);
42688                 if (cfg.background) {
42689                     ret.on('activate', function(gp) {
42690                         if (!gp.grid.rendered) {
42691                             gp.grid.render();
42692                         }
42693                     });
42694                 } else {
42695                     grid.render();
42696                 }
42697                 break;
42698            
42699                
42700                 
42701                 
42702             default: 
42703                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42704                 return;
42705              // GridPanel (grid, cfg)
42706             
42707         }
42708         this.beginUpdate();
42709         // add children..
42710         Roo.each(xitems, function(i)  {
42711             ret.addxtype(i);
42712         });
42713         this.endUpdate();
42714         return ret;
42715         
42716     }
42717 });
42718
42719 /**
42720  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42721  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42722  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42723  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42724  * <pre><code>
42725 // shorthand
42726 var CP = Roo.ContentPanel;
42727
42728 var layout = Roo.BorderLayout.create({
42729     north: {
42730         initialSize: 25,
42731         titlebar: false,
42732         panels: [new CP("north", "North")]
42733     },
42734     west: {
42735         split:true,
42736         initialSize: 200,
42737         minSize: 175,
42738         maxSize: 400,
42739         titlebar: true,
42740         collapsible: true,
42741         panels: [new CP("west", {title: "West"})]
42742     },
42743     east: {
42744         split:true,
42745         initialSize: 202,
42746         minSize: 175,
42747         maxSize: 400,
42748         titlebar: true,
42749         collapsible: true,
42750         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42751     },
42752     south: {
42753         split:true,
42754         initialSize: 100,
42755         minSize: 100,
42756         maxSize: 200,
42757         titlebar: true,
42758         collapsible: true,
42759         panels: [new CP("south", {title: "South", closable: true})]
42760     },
42761     center: {
42762         titlebar: true,
42763         autoScroll:true,
42764         resizeTabs: true,
42765         minTabWidth: 50,
42766         preferredTabWidth: 150,
42767         panels: [
42768             new CP("center1", {title: "Close Me", closable: true}),
42769             new CP("center2", {title: "Center Panel", closable: false})
42770         ]
42771     }
42772 }, document.body);
42773
42774 layout.getRegion("center").showPanel("center1");
42775 </code></pre>
42776  * @param config
42777  * @param targetEl
42778  */
42779 Roo.BorderLayout.create = function(config, targetEl){
42780     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42781     layout.beginUpdate();
42782     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42783     for(var j = 0, jlen = regions.length; j < jlen; j++){
42784         var lr = regions[j];
42785         if(layout.regions[lr] && config[lr].panels){
42786             var r = layout.regions[lr];
42787             var ps = config[lr].panels;
42788             layout.addTypedPanels(r, ps);
42789         }
42790     }
42791     layout.endUpdate();
42792     return layout;
42793 };
42794
42795 // private
42796 Roo.BorderLayout.RegionFactory = {
42797     // private
42798     validRegions : ["north","south","east","west","center"],
42799
42800     // private
42801     create : function(target, mgr, config){
42802         target = target.toLowerCase();
42803         if(config.lightweight || config.basic){
42804             return new Roo.BasicLayoutRegion(mgr, config, target);
42805         }
42806         switch(target){
42807             case "north":
42808                 return new Roo.NorthLayoutRegion(mgr, config);
42809             case "south":
42810                 return new Roo.SouthLayoutRegion(mgr, config);
42811             case "east":
42812                 return new Roo.EastLayoutRegion(mgr, config);
42813             case "west":
42814                 return new Roo.WestLayoutRegion(mgr, config);
42815             case "center":
42816                 return new Roo.CenterLayoutRegion(mgr, config);
42817         }
42818         throw 'Layout region "'+target+'" not supported.';
42819     }
42820 };/*
42821  * Based on:
42822  * Ext JS Library 1.1.1
42823  * Copyright(c) 2006-2007, Ext JS, LLC.
42824  *
42825  * Originally Released Under LGPL - original licence link has changed is not relivant.
42826  *
42827  * Fork - LGPL
42828  * <script type="text/javascript">
42829  */
42830  
42831 /**
42832  * @class Roo.BasicLayoutRegion
42833  * @extends Roo.util.Observable
42834  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42835  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42836  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42837  */
42838 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42839     this.mgr = mgr;
42840     this.position  = pos;
42841     this.events = {
42842         /**
42843          * @scope Roo.BasicLayoutRegion
42844          */
42845         
42846         /**
42847          * @event beforeremove
42848          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42849          * @param {Roo.LayoutRegion} this
42850          * @param {Roo.ContentPanel} panel The panel
42851          * @param {Object} e The cancel event object
42852          */
42853         "beforeremove" : true,
42854         /**
42855          * @event invalidated
42856          * Fires when the layout for this region is changed.
42857          * @param {Roo.LayoutRegion} this
42858          */
42859         "invalidated" : true,
42860         /**
42861          * @event visibilitychange
42862          * Fires when this region is shown or hidden 
42863          * @param {Roo.LayoutRegion} this
42864          * @param {Boolean} visibility true or false
42865          */
42866         "visibilitychange" : true,
42867         /**
42868          * @event paneladded
42869          * Fires when a panel is added. 
42870          * @param {Roo.LayoutRegion} this
42871          * @param {Roo.ContentPanel} panel The panel
42872          */
42873         "paneladded" : true,
42874         /**
42875          * @event panelremoved
42876          * Fires when a panel is removed. 
42877          * @param {Roo.LayoutRegion} this
42878          * @param {Roo.ContentPanel} panel The panel
42879          */
42880         "panelremoved" : true,
42881         /**
42882          * @event collapsed
42883          * Fires when this region is collapsed.
42884          * @param {Roo.LayoutRegion} this
42885          */
42886         "collapsed" : true,
42887         /**
42888          * @event expanded
42889          * Fires when this region is expanded.
42890          * @param {Roo.LayoutRegion} this
42891          */
42892         "expanded" : true,
42893         /**
42894          * @event slideshow
42895          * Fires when this region is slid into view.
42896          * @param {Roo.LayoutRegion} this
42897          */
42898         "slideshow" : true,
42899         /**
42900          * @event slidehide
42901          * Fires when this region slides out of view. 
42902          * @param {Roo.LayoutRegion} this
42903          */
42904         "slidehide" : true,
42905         /**
42906          * @event panelactivated
42907          * Fires when a panel is activated. 
42908          * @param {Roo.LayoutRegion} this
42909          * @param {Roo.ContentPanel} panel The activated panel
42910          */
42911         "panelactivated" : true,
42912         /**
42913          * @event resized
42914          * Fires when the user resizes this region. 
42915          * @param {Roo.LayoutRegion} this
42916          * @param {Number} newSize The new size (width for east/west, height for north/south)
42917          */
42918         "resized" : true
42919     };
42920     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42921     this.panels = new Roo.util.MixedCollection();
42922     this.panels.getKey = this.getPanelId.createDelegate(this);
42923     this.box = null;
42924     this.activePanel = null;
42925     // ensure listeners are added...
42926     
42927     if (config.listeners || config.events) {
42928         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42929             listeners : config.listeners || {},
42930             events : config.events || {}
42931         });
42932     }
42933     
42934     if(skipConfig !== true){
42935         this.applyConfig(config);
42936     }
42937 };
42938
42939 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42940     getPanelId : function(p){
42941         return p.getId();
42942     },
42943     
42944     applyConfig : function(config){
42945         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42946         this.config = config;
42947         
42948     },
42949     
42950     /**
42951      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42952      * the width, for horizontal (north, south) the height.
42953      * @param {Number} newSize The new width or height
42954      */
42955     resizeTo : function(newSize){
42956         var el = this.el ? this.el :
42957                  (this.activePanel ? this.activePanel.getEl() : null);
42958         if(el){
42959             switch(this.position){
42960                 case "east":
42961                 case "west":
42962                     el.setWidth(newSize);
42963                     this.fireEvent("resized", this, newSize);
42964                 break;
42965                 case "north":
42966                 case "south":
42967                     el.setHeight(newSize);
42968                     this.fireEvent("resized", this, newSize);
42969                 break;                
42970             }
42971         }
42972     },
42973     
42974     getBox : function(){
42975         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42976     },
42977     
42978     getMargins : function(){
42979         return this.margins;
42980     },
42981     
42982     updateBox : function(box){
42983         this.box = box;
42984         var el = this.activePanel.getEl();
42985         el.dom.style.left = box.x + "px";
42986         el.dom.style.top = box.y + "px";
42987         this.activePanel.setSize(box.width, box.height);
42988     },
42989     
42990     /**
42991      * Returns the container element for this region.
42992      * @return {Roo.Element}
42993      */
42994     getEl : function(){
42995         return this.activePanel;
42996     },
42997     
42998     /**
42999      * Returns true if this region is currently visible.
43000      * @return {Boolean}
43001      */
43002     isVisible : function(){
43003         return this.activePanel ? true : false;
43004     },
43005     
43006     setActivePanel : function(panel){
43007         panel = this.getPanel(panel);
43008         if(this.activePanel && this.activePanel != panel){
43009             this.activePanel.setActiveState(false);
43010             this.activePanel.getEl().setLeftTop(-10000,-10000);
43011         }
43012         this.activePanel = panel;
43013         panel.setActiveState(true);
43014         if(this.box){
43015             panel.setSize(this.box.width, this.box.height);
43016         }
43017         this.fireEvent("panelactivated", this, panel);
43018         this.fireEvent("invalidated");
43019     },
43020     
43021     /**
43022      * Show the specified panel.
43023      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43024      * @return {Roo.ContentPanel} The shown panel or null
43025      */
43026     showPanel : function(panel){
43027         if(panel = this.getPanel(panel)){
43028             this.setActivePanel(panel);
43029         }
43030         return panel;
43031     },
43032     
43033     /**
43034      * Get the active panel for this region.
43035      * @return {Roo.ContentPanel} The active panel or null
43036      */
43037     getActivePanel : function(){
43038         return this.activePanel;
43039     },
43040     
43041     /**
43042      * Add the passed ContentPanel(s)
43043      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43044      * @return {Roo.ContentPanel} The panel added (if only one was added)
43045      */
43046     add : function(panel){
43047         if(arguments.length > 1){
43048             for(var i = 0, len = arguments.length; i < len; i++) {
43049                 this.add(arguments[i]);
43050             }
43051             return null;
43052         }
43053         if(this.hasPanel(panel)){
43054             this.showPanel(panel);
43055             return panel;
43056         }
43057         var el = panel.getEl();
43058         if(el.dom.parentNode != this.mgr.el.dom){
43059             this.mgr.el.dom.appendChild(el.dom);
43060         }
43061         if(panel.setRegion){
43062             panel.setRegion(this);
43063         }
43064         this.panels.add(panel);
43065         el.setStyle("position", "absolute");
43066         if(!panel.background){
43067             this.setActivePanel(panel);
43068             if(this.config.initialSize && this.panels.getCount()==1){
43069                 this.resizeTo(this.config.initialSize);
43070             }
43071         }
43072         this.fireEvent("paneladded", this, panel);
43073         return panel;
43074     },
43075     
43076     /**
43077      * Returns true if the panel is in this region.
43078      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43079      * @return {Boolean}
43080      */
43081     hasPanel : function(panel){
43082         if(typeof panel == "object"){ // must be panel obj
43083             panel = panel.getId();
43084         }
43085         return this.getPanel(panel) ? true : false;
43086     },
43087     
43088     /**
43089      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43090      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43091      * @param {Boolean} preservePanel Overrides the config preservePanel option
43092      * @return {Roo.ContentPanel} The panel that was removed
43093      */
43094     remove : function(panel, preservePanel){
43095         panel = this.getPanel(panel);
43096         if(!panel){
43097             return null;
43098         }
43099         var e = {};
43100         this.fireEvent("beforeremove", this, panel, e);
43101         if(e.cancel === true){
43102             return null;
43103         }
43104         var panelId = panel.getId();
43105         this.panels.removeKey(panelId);
43106         return panel;
43107     },
43108     
43109     /**
43110      * Returns the panel specified or null if it's not in this region.
43111      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43112      * @return {Roo.ContentPanel}
43113      */
43114     getPanel : function(id){
43115         if(typeof id == "object"){ // must be panel obj
43116             return id;
43117         }
43118         return this.panels.get(id);
43119     },
43120     
43121     /**
43122      * Returns this regions position (north/south/east/west/center).
43123      * @return {String} 
43124      */
43125     getPosition: function(){
43126         return this.position;    
43127     }
43128 });/*
43129  * Based on:
43130  * Ext JS Library 1.1.1
43131  * Copyright(c) 2006-2007, Ext JS, LLC.
43132  *
43133  * Originally Released Under LGPL - original licence link has changed is not relivant.
43134  *
43135  * Fork - LGPL
43136  * <script type="text/javascript">
43137  */
43138  
43139 /**
43140  * @class Roo.LayoutRegion
43141  * @extends Roo.BasicLayoutRegion
43142  * This class represents a region in a layout manager.
43143  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43144  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43145  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43146  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43147  * @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})
43148  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43149  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43150  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43151  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43152  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43153  * @cfg {String} title The title for the region (overrides panel titles)
43154  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43155  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43156  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43157  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43158  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43159  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43160  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43161  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43162  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43163  * @cfg {Boolean} showPin True to show a pin button
43164 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43165 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43166 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43167 * @cfg {Number} width  For East/West panels
43168 * @cfg {Number} height For North/South panels
43169 * @cfg {Boolean} split To show the splitter
43170  */
43171 Roo.LayoutRegion = function(mgr, config, pos){
43172     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43173     var dh = Roo.DomHelper;
43174     /** This region's container element 
43175     * @type Roo.Element */
43176     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43177     /** This region's title element 
43178     * @type Roo.Element */
43179
43180     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43181         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43182         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43183     ]}, true);
43184     this.titleEl.enableDisplayMode();
43185     /** This region's title text element 
43186     * @type HTMLElement */
43187     this.titleTextEl = this.titleEl.dom.firstChild;
43188     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43189     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43190     this.closeBtn.enableDisplayMode();
43191     this.closeBtn.on("click", this.closeClicked, this);
43192     this.closeBtn.hide();
43193
43194     this.createBody(config);
43195     this.visible = true;
43196     this.collapsed = false;
43197
43198     if(config.hideWhenEmpty){
43199         this.hide();
43200         this.on("paneladded", this.validateVisibility, this);
43201         this.on("panelremoved", this.validateVisibility, this);
43202     }
43203     this.applyConfig(config);
43204 };
43205
43206 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43207
43208     createBody : function(){
43209         /** This region's body element 
43210         * @type Roo.Element */
43211         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43212     },
43213
43214     applyConfig : function(c){
43215         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43216             var dh = Roo.DomHelper;
43217             if(c.titlebar !== false){
43218                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43219                 this.collapseBtn.on("click", this.collapse, this);
43220                 this.collapseBtn.enableDisplayMode();
43221
43222                 if(c.showPin === true || this.showPin){
43223                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43224                     this.stickBtn.enableDisplayMode();
43225                     this.stickBtn.on("click", this.expand, this);
43226                     this.stickBtn.hide();
43227                 }
43228             }
43229             /** This region's collapsed element
43230             * @type Roo.Element */
43231             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43232                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43233             ]}, true);
43234             if(c.floatable !== false){
43235                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43236                this.collapsedEl.on("click", this.collapseClick, this);
43237             }
43238
43239             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43240                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43241                    id: "message", unselectable: "on", style:{"float":"left"}});
43242                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43243              }
43244             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43245             this.expandBtn.on("click", this.expand, this);
43246         }
43247         if(this.collapseBtn){
43248             this.collapseBtn.setVisible(c.collapsible == true);
43249         }
43250         this.cmargins = c.cmargins || this.cmargins ||
43251                          (this.position == "west" || this.position == "east" ?
43252                              {top: 0, left: 2, right:2, bottom: 0} :
43253                              {top: 2, left: 0, right:0, bottom: 2});
43254         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43255         this.bottomTabs = c.tabPosition != "top";
43256         this.autoScroll = c.autoScroll || false;
43257         if(this.autoScroll){
43258             this.bodyEl.setStyle("overflow", "auto");
43259         }else{
43260             this.bodyEl.setStyle("overflow", "hidden");
43261         }
43262         //if(c.titlebar !== false){
43263             if((!c.titlebar && !c.title) || c.titlebar === false){
43264                 this.titleEl.hide();
43265             }else{
43266                 this.titleEl.show();
43267                 if(c.title){
43268                     this.titleTextEl.innerHTML = c.title;
43269                 }
43270             }
43271         //}
43272         this.duration = c.duration || .30;
43273         this.slideDuration = c.slideDuration || .45;
43274         this.config = c;
43275         if(c.collapsed){
43276             this.collapse(true);
43277         }
43278         if(c.hidden){
43279             this.hide();
43280         }
43281     },
43282     /**
43283      * Returns true if this region is currently visible.
43284      * @return {Boolean}
43285      */
43286     isVisible : function(){
43287         return this.visible;
43288     },
43289
43290     /**
43291      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43292      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43293      */
43294     setCollapsedTitle : function(title){
43295         title = title || "&#160;";
43296         if(this.collapsedTitleTextEl){
43297             this.collapsedTitleTextEl.innerHTML = title;
43298         }
43299     },
43300
43301     getBox : function(){
43302         var b;
43303         if(!this.collapsed){
43304             b = this.el.getBox(false, true);
43305         }else{
43306             b = this.collapsedEl.getBox(false, true);
43307         }
43308         return b;
43309     },
43310
43311     getMargins : function(){
43312         return this.collapsed ? this.cmargins : this.margins;
43313     },
43314
43315     highlight : function(){
43316         this.el.addClass("x-layout-panel-dragover");
43317     },
43318
43319     unhighlight : function(){
43320         this.el.removeClass("x-layout-panel-dragover");
43321     },
43322
43323     updateBox : function(box){
43324         this.box = box;
43325         if(!this.collapsed){
43326             this.el.dom.style.left = box.x + "px";
43327             this.el.dom.style.top = box.y + "px";
43328             this.updateBody(box.width, box.height);
43329         }else{
43330             this.collapsedEl.dom.style.left = box.x + "px";
43331             this.collapsedEl.dom.style.top = box.y + "px";
43332             this.collapsedEl.setSize(box.width, box.height);
43333         }
43334         if(this.tabs){
43335             this.tabs.autoSizeTabs();
43336         }
43337     },
43338
43339     updateBody : function(w, h){
43340         if(w !== null){
43341             this.el.setWidth(w);
43342             w -= this.el.getBorderWidth("rl");
43343             if(this.config.adjustments){
43344                 w += this.config.adjustments[0];
43345             }
43346         }
43347         if(h !== null){
43348             this.el.setHeight(h);
43349             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43350             h -= this.el.getBorderWidth("tb");
43351             if(this.config.adjustments){
43352                 h += this.config.adjustments[1];
43353             }
43354             this.bodyEl.setHeight(h);
43355             if(this.tabs){
43356                 h = this.tabs.syncHeight(h);
43357             }
43358         }
43359         if(this.panelSize){
43360             w = w !== null ? w : this.panelSize.width;
43361             h = h !== null ? h : this.panelSize.height;
43362         }
43363         if(this.activePanel){
43364             var el = this.activePanel.getEl();
43365             w = w !== null ? w : el.getWidth();
43366             h = h !== null ? h : el.getHeight();
43367             this.panelSize = {width: w, height: h};
43368             this.activePanel.setSize(w, h);
43369         }
43370         if(Roo.isIE && this.tabs){
43371             this.tabs.el.repaint();
43372         }
43373     },
43374
43375     /**
43376      * Returns the container element for this region.
43377      * @return {Roo.Element}
43378      */
43379     getEl : function(){
43380         return this.el;
43381     },
43382
43383     /**
43384      * Hides this region.
43385      */
43386     hide : function(){
43387         if(!this.collapsed){
43388             this.el.dom.style.left = "-2000px";
43389             this.el.hide();
43390         }else{
43391             this.collapsedEl.dom.style.left = "-2000px";
43392             this.collapsedEl.hide();
43393         }
43394         this.visible = false;
43395         this.fireEvent("visibilitychange", this, false);
43396     },
43397
43398     /**
43399      * Shows this region if it was previously hidden.
43400      */
43401     show : function(){
43402         if(!this.collapsed){
43403             this.el.show();
43404         }else{
43405             this.collapsedEl.show();
43406         }
43407         this.visible = true;
43408         this.fireEvent("visibilitychange", this, true);
43409     },
43410
43411     closeClicked : function(){
43412         if(this.activePanel){
43413             this.remove(this.activePanel);
43414         }
43415     },
43416
43417     collapseClick : function(e){
43418         if(this.isSlid){
43419            e.stopPropagation();
43420            this.slideIn();
43421         }else{
43422            e.stopPropagation();
43423            this.slideOut();
43424         }
43425     },
43426
43427     /**
43428      * Collapses this region.
43429      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43430      */
43431     collapse : function(skipAnim){
43432         if(this.collapsed) return;
43433         this.collapsed = true;
43434         if(this.split){
43435             this.split.el.hide();
43436         }
43437         if(this.config.animate && skipAnim !== true){
43438             this.fireEvent("invalidated", this);
43439             this.animateCollapse();
43440         }else{
43441             this.el.setLocation(-20000,-20000);
43442             this.el.hide();
43443             this.collapsedEl.show();
43444             this.fireEvent("collapsed", this);
43445             this.fireEvent("invalidated", this);
43446         }
43447     },
43448
43449     animateCollapse : function(){
43450         // overridden
43451     },
43452
43453     /**
43454      * Expands this region if it was previously collapsed.
43455      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43456      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43457      */
43458     expand : function(e, skipAnim){
43459         if(e) e.stopPropagation();
43460         if(!this.collapsed || this.el.hasActiveFx()) return;
43461         if(this.isSlid){
43462             this.afterSlideIn();
43463             skipAnim = true;
43464         }
43465         this.collapsed = false;
43466         if(this.config.animate && skipAnim !== true){
43467             this.animateExpand();
43468         }else{
43469             this.el.show();
43470             if(this.split){
43471                 this.split.el.show();
43472             }
43473             this.collapsedEl.setLocation(-2000,-2000);
43474             this.collapsedEl.hide();
43475             this.fireEvent("invalidated", this);
43476             this.fireEvent("expanded", this);
43477         }
43478     },
43479
43480     animateExpand : function(){
43481         // overridden
43482     },
43483
43484     initTabs : function(){
43485         this.bodyEl.setStyle("overflow", "hidden");
43486         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43487             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43488             disableTooltips: this.config.disableTabTips
43489         });
43490         if(this.config.hideTabs){
43491             ts.stripWrap.setDisplayed(false);
43492         }
43493         this.tabs = ts;
43494         ts.resizeTabs = this.config.resizeTabs === true;
43495         ts.minTabWidth = this.config.minTabWidth || 40;
43496         ts.maxTabWidth = this.config.maxTabWidth || 250;
43497         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43498         ts.monitorResize = false;
43499         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43500         ts.bodyEl.addClass('x-layout-tabs-body');
43501         this.panels.each(this.initPanelAsTab, this);
43502     },
43503
43504     initPanelAsTab : function(panel){
43505         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43506                     this.config.closeOnTab && panel.isClosable());
43507         if(panel.tabTip !== undefined){
43508             ti.setTooltip(panel.tabTip);
43509         }
43510         ti.on("activate", function(){
43511               this.setActivePanel(panel);
43512         }, this);
43513         if(this.config.closeOnTab){
43514             ti.on("beforeclose", function(t, e){
43515                 e.cancel = true;
43516                 this.remove(panel);
43517             }, this);
43518         }
43519         return ti;
43520     },
43521
43522     updatePanelTitle : function(panel, title){
43523         if(this.activePanel == panel){
43524             this.updateTitle(title);
43525         }
43526         if(this.tabs){
43527             var ti = this.tabs.getTab(panel.getEl().id);
43528             ti.setText(title);
43529             if(panel.tabTip !== undefined){
43530                 ti.setTooltip(panel.tabTip);
43531             }
43532         }
43533     },
43534
43535     updateTitle : function(title){
43536         if(this.titleTextEl && !this.config.title){
43537             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43538         }
43539     },
43540
43541     setActivePanel : function(panel){
43542         panel = this.getPanel(panel);
43543         if(this.activePanel && this.activePanel != panel){
43544             this.activePanel.setActiveState(false);
43545         }
43546         this.activePanel = panel;
43547         panel.setActiveState(true);
43548         if(this.panelSize){
43549             panel.setSize(this.panelSize.width, this.panelSize.height);
43550         }
43551         if(this.closeBtn){
43552             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43553         }
43554         this.updateTitle(panel.getTitle());
43555         if(this.tabs){
43556             this.fireEvent("invalidated", this);
43557         }
43558         this.fireEvent("panelactivated", this, panel);
43559     },
43560
43561     /**
43562      * Shows the specified panel.
43563      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43564      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43565      */
43566     showPanel : function(panel){
43567         if(panel = this.getPanel(panel)){
43568             if(this.tabs){
43569                 var tab = this.tabs.getTab(panel.getEl().id);
43570                 if(tab.isHidden()){
43571                     this.tabs.unhideTab(tab.id);
43572                 }
43573                 tab.activate();
43574             }else{
43575                 this.setActivePanel(panel);
43576             }
43577         }
43578         return panel;
43579     },
43580
43581     /**
43582      * Get the active panel for this region.
43583      * @return {Roo.ContentPanel} The active panel or null
43584      */
43585     getActivePanel : function(){
43586         return this.activePanel;
43587     },
43588
43589     validateVisibility : function(){
43590         if(this.panels.getCount() < 1){
43591             this.updateTitle("&#160;");
43592             this.closeBtn.hide();
43593             this.hide();
43594         }else{
43595             if(!this.isVisible()){
43596                 this.show();
43597             }
43598         }
43599     },
43600
43601     /**
43602      * Adds the passed ContentPanel(s) to this region.
43603      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43604      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43605      */
43606     add : function(panel){
43607         if(arguments.length > 1){
43608             for(var i = 0, len = arguments.length; i < len; i++) {
43609                 this.add(arguments[i]);
43610             }
43611             return null;
43612         }
43613         if(this.hasPanel(panel)){
43614             this.showPanel(panel);
43615             return panel;
43616         }
43617         panel.setRegion(this);
43618         this.panels.add(panel);
43619         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43620             this.bodyEl.dom.appendChild(panel.getEl().dom);
43621             if(panel.background !== true){
43622                 this.setActivePanel(panel);
43623             }
43624             this.fireEvent("paneladded", this, panel);
43625             return panel;
43626         }
43627         if(!this.tabs){
43628             this.initTabs();
43629         }else{
43630             this.initPanelAsTab(panel);
43631         }
43632         if(panel.background !== true){
43633             this.tabs.activate(panel.getEl().id);
43634         }
43635         this.fireEvent("paneladded", this, panel);
43636         return panel;
43637     },
43638
43639     /**
43640      * Hides the tab for the specified panel.
43641      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43642      */
43643     hidePanel : function(panel){
43644         if(this.tabs && (panel = this.getPanel(panel))){
43645             this.tabs.hideTab(panel.getEl().id);
43646         }
43647     },
43648
43649     /**
43650      * Unhides the tab for a previously hidden panel.
43651      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43652      */
43653     unhidePanel : function(panel){
43654         if(this.tabs && (panel = this.getPanel(panel))){
43655             this.tabs.unhideTab(panel.getEl().id);
43656         }
43657     },
43658
43659     clearPanels : function(){
43660         while(this.panels.getCount() > 0){
43661              this.remove(this.panels.first());
43662         }
43663     },
43664
43665     /**
43666      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43667      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43668      * @param {Boolean} preservePanel Overrides the config preservePanel option
43669      * @return {Roo.ContentPanel} The panel that was removed
43670      */
43671     remove : function(panel, preservePanel){
43672         panel = this.getPanel(panel);
43673         if(!panel){
43674             return null;
43675         }
43676         var e = {};
43677         this.fireEvent("beforeremove", this, panel, e);
43678         if(e.cancel === true){
43679             return null;
43680         }
43681         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43682         var panelId = panel.getId();
43683         this.panels.removeKey(panelId);
43684         if(preservePanel){
43685             document.body.appendChild(panel.getEl().dom);
43686         }
43687         if(this.tabs){
43688             this.tabs.removeTab(panel.getEl().id);
43689         }else if (!preservePanel){
43690             this.bodyEl.dom.removeChild(panel.getEl().dom);
43691         }
43692         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43693             var p = this.panels.first();
43694             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43695             tempEl.appendChild(p.getEl().dom);
43696             this.bodyEl.update("");
43697             this.bodyEl.dom.appendChild(p.getEl().dom);
43698             tempEl = null;
43699             this.updateTitle(p.getTitle());
43700             this.tabs = null;
43701             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43702             this.setActivePanel(p);
43703         }
43704         panel.setRegion(null);
43705         if(this.activePanel == panel){
43706             this.activePanel = null;
43707         }
43708         if(this.config.autoDestroy !== false && preservePanel !== true){
43709             try{panel.destroy();}catch(e){}
43710         }
43711         this.fireEvent("panelremoved", this, panel);
43712         return panel;
43713     },
43714
43715     /**
43716      * Returns the TabPanel component used by this region
43717      * @return {Roo.TabPanel}
43718      */
43719     getTabs : function(){
43720         return this.tabs;
43721     },
43722
43723     createTool : function(parentEl, className){
43724         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43725             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43726         btn.addClassOnOver("x-layout-tools-button-over");
43727         return btn;
43728     }
43729 });/*
43730  * Based on:
43731  * Ext JS Library 1.1.1
43732  * Copyright(c) 2006-2007, Ext JS, LLC.
43733  *
43734  * Originally Released Under LGPL - original licence link has changed is not relivant.
43735  *
43736  * Fork - LGPL
43737  * <script type="text/javascript">
43738  */
43739  
43740
43741
43742 /**
43743  * @class Roo.SplitLayoutRegion
43744  * @extends Roo.LayoutRegion
43745  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43746  */
43747 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43748     this.cursor = cursor;
43749     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43750 };
43751
43752 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43753     splitTip : "Drag to resize.",
43754     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43755     useSplitTips : false,
43756
43757     applyConfig : function(config){
43758         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43759         if(config.split){
43760             if(!this.split){
43761                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43762                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43763                 /** The SplitBar for this region 
43764                 * @type Roo.SplitBar */
43765                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43766                 this.split.on("moved", this.onSplitMove, this);
43767                 this.split.useShim = config.useShim === true;
43768                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43769                 if(this.useSplitTips){
43770                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43771                 }
43772                 if(config.collapsible){
43773                     this.split.el.on("dblclick", this.collapse,  this);
43774                 }
43775             }
43776             if(typeof config.minSize != "undefined"){
43777                 this.split.minSize = config.minSize;
43778             }
43779             if(typeof config.maxSize != "undefined"){
43780                 this.split.maxSize = config.maxSize;
43781             }
43782             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43783                 this.hideSplitter();
43784             }
43785         }
43786     },
43787
43788     getHMaxSize : function(){
43789          var cmax = this.config.maxSize || 10000;
43790          var center = this.mgr.getRegion("center");
43791          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43792     },
43793
43794     getVMaxSize : function(){
43795          var cmax = this.config.maxSize || 10000;
43796          var center = this.mgr.getRegion("center");
43797          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43798     },
43799
43800     onSplitMove : function(split, newSize){
43801         this.fireEvent("resized", this, newSize);
43802     },
43803     
43804     /** 
43805      * Returns the {@link Roo.SplitBar} for this region.
43806      * @return {Roo.SplitBar}
43807      */
43808     getSplitBar : function(){
43809         return this.split;
43810     },
43811     
43812     hide : function(){
43813         this.hideSplitter();
43814         Roo.SplitLayoutRegion.superclass.hide.call(this);
43815     },
43816
43817     hideSplitter : function(){
43818         if(this.split){
43819             this.split.el.setLocation(-2000,-2000);
43820             this.split.el.hide();
43821         }
43822     },
43823
43824     show : function(){
43825         if(this.split){
43826             this.split.el.show();
43827         }
43828         Roo.SplitLayoutRegion.superclass.show.call(this);
43829     },
43830     
43831     beforeSlide: function(){
43832         if(Roo.isGecko){// firefox overflow auto bug workaround
43833             this.bodyEl.clip();
43834             if(this.tabs) this.tabs.bodyEl.clip();
43835             if(this.activePanel){
43836                 this.activePanel.getEl().clip();
43837                 
43838                 if(this.activePanel.beforeSlide){
43839                     this.activePanel.beforeSlide();
43840                 }
43841             }
43842         }
43843     },
43844     
43845     afterSlide : function(){
43846         if(Roo.isGecko){// firefox overflow auto bug workaround
43847             this.bodyEl.unclip();
43848             if(this.tabs) this.tabs.bodyEl.unclip();
43849             if(this.activePanel){
43850                 this.activePanel.getEl().unclip();
43851                 if(this.activePanel.afterSlide){
43852                     this.activePanel.afterSlide();
43853                 }
43854             }
43855         }
43856     },
43857
43858     initAutoHide : function(){
43859         if(this.autoHide !== false){
43860             if(!this.autoHideHd){
43861                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43862                 this.autoHideHd = {
43863                     "mouseout": function(e){
43864                         if(!e.within(this.el, true)){
43865                             st.delay(500);
43866                         }
43867                     },
43868                     "mouseover" : function(e){
43869                         st.cancel();
43870                     },
43871                     scope : this
43872                 };
43873             }
43874             this.el.on(this.autoHideHd);
43875         }
43876     },
43877
43878     clearAutoHide : function(){
43879         if(this.autoHide !== false){
43880             this.el.un("mouseout", this.autoHideHd.mouseout);
43881             this.el.un("mouseover", this.autoHideHd.mouseover);
43882         }
43883     },
43884
43885     clearMonitor : function(){
43886         Roo.get(document).un("click", this.slideInIf, this);
43887     },
43888
43889     // these names are backwards but not changed for compat
43890     slideOut : function(){
43891         if(this.isSlid || this.el.hasActiveFx()){
43892             return;
43893         }
43894         this.isSlid = true;
43895         if(this.collapseBtn){
43896             this.collapseBtn.hide();
43897         }
43898         this.closeBtnState = this.closeBtn.getStyle('display');
43899         this.closeBtn.hide();
43900         if(this.stickBtn){
43901             this.stickBtn.show();
43902         }
43903         this.el.show();
43904         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43905         this.beforeSlide();
43906         this.el.setStyle("z-index", 10001);
43907         this.el.slideIn(this.getSlideAnchor(), {
43908             callback: function(){
43909                 this.afterSlide();
43910                 this.initAutoHide();
43911                 Roo.get(document).on("click", this.slideInIf, this);
43912                 this.fireEvent("slideshow", this);
43913             },
43914             scope: this,
43915             block: true
43916         });
43917     },
43918
43919     afterSlideIn : function(){
43920         this.clearAutoHide();
43921         this.isSlid = false;
43922         this.clearMonitor();
43923         this.el.setStyle("z-index", "");
43924         if(this.collapseBtn){
43925             this.collapseBtn.show();
43926         }
43927         this.closeBtn.setStyle('display', this.closeBtnState);
43928         if(this.stickBtn){
43929             this.stickBtn.hide();
43930         }
43931         this.fireEvent("slidehide", this);
43932     },
43933
43934     slideIn : function(cb){
43935         if(!this.isSlid || this.el.hasActiveFx()){
43936             Roo.callback(cb);
43937             return;
43938         }
43939         this.isSlid = false;
43940         this.beforeSlide();
43941         this.el.slideOut(this.getSlideAnchor(), {
43942             callback: function(){
43943                 this.el.setLeftTop(-10000, -10000);
43944                 this.afterSlide();
43945                 this.afterSlideIn();
43946                 Roo.callback(cb);
43947             },
43948             scope: this,
43949             block: true
43950         });
43951     },
43952     
43953     slideInIf : function(e){
43954         if(!e.within(this.el)){
43955             this.slideIn();
43956         }
43957     },
43958
43959     animateCollapse : function(){
43960         this.beforeSlide();
43961         this.el.setStyle("z-index", 20000);
43962         var anchor = this.getSlideAnchor();
43963         this.el.slideOut(anchor, {
43964             callback : function(){
43965                 this.el.setStyle("z-index", "");
43966                 this.collapsedEl.slideIn(anchor, {duration:.3});
43967                 this.afterSlide();
43968                 this.el.setLocation(-10000,-10000);
43969                 this.el.hide();
43970                 this.fireEvent("collapsed", this);
43971             },
43972             scope: this,
43973             block: true
43974         });
43975     },
43976
43977     animateExpand : function(){
43978         this.beforeSlide();
43979         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43980         this.el.setStyle("z-index", 20000);
43981         this.collapsedEl.hide({
43982             duration:.1
43983         });
43984         this.el.slideIn(this.getSlideAnchor(), {
43985             callback : function(){
43986                 this.el.setStyle("z-index", "");
43987                 this.afterSlide();
43988                 if(this.split){
43989                     this.split.el.show();
43990                 }
43991                 this.fireEvent("invalidated", this);
43992                 this.fireEvent("expanded", this);
43993             },
43994             scope: this,
43995             block: true
43996         });
43997     },
43998
43999     anchors : {
44000         "west" : "left",
44001         "east" : "right",
44002         "north" : "top",
44003         "south" : "bottom"
44004     },
44005
44006     sanchors : {
44007         "west" : "l",
44008         "east" : "r",
44009         "north" : "t",
44010         "south" : "b"
44011     },
44012
44013     canchors : {
44014         "west" : "tl-tr",
44015         "east" : "tr-tl",
44016         "north" : "tl-bl",
44017         "south" : "bl-tl"
44018     },
44019
44020     getAnchor : function(){
44021         return this.anchors[this.position];
44022     },
44023
44024     getCollapseAnchor : function(){
44025         return this.canchors[this.position];
44026     },
44027
44028     getSlideAnchor : function(){
44029         return this.sanchors[this.position];
44030     },
44031
44032     getAlignAdj : function(){
44033         var cm = this.cmargins;
44034         switch(this.position){
44035             case "west":
44036                 return [0, 0];
44037             break;
44038             case "east":
44039                 return [0, 0];
44040             break;
44041             case "north":
44042                 return [0, 0];
44043             break;
44044             case "south":
44045                 return [0, 0];
44046             break;
44047         }
44048     },
44049
44050     getExpandAdj : function(){
44051         var c = this.collapsedEl, cm = this.cmargins;
44052         switch(this.position){
44053             case "west":
44054                 return [-(cm.right+c.getWidth()+cm.left), 0];
44055             break;
44056             case "east":
44057                 return [cm.right+c.getWidth()+cm.left, 0];
44058             break;
44059             case "north":
44060                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44061             break;
44062             case "south":
44063                 return [0, cm.top+cm.bottom+c.getHeight()];
44064             break;
44065         }
44066     }
44067 });/*
44068  * Based on:
44069  * Ext JS Library 1.1.1
44070  * Copyright(c) 2006-2007, Ext JS, LLC.
44071  *
44072  * Originally Released Under LGPL - original licence link has changed is not relivant.
44073  *
44074  * Fork - LGPL
44075  * <script type="text/javascript">
44076  */
44077 /*
44078  * These classes are private internal classes
44079  */
44080 Roo.CenterLayoutRegion = function(mgr, config){
44081     Roo.LayoutRegion.call(this, mgr, config, "center");
44082     this.visible = true;
44083     this.minWidth = config.minWidth || 20;
44084     this.minHeight = config.minHeight || 20;
44085 };
44086
44087 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44088     hide : function(){
44089         // center panel can't be hidden
44090     },
44091     
44092     show : function(){
44093         // center panel can't be hidden
44094     },
44095     
44096     getMinWidth: function(){
44097         return this.minWidth;
44098     },
44099     
44100     getMinHeight: function(){
44101         return this.minHeight;
44102     }
44103 });
44104
44105
44106 Roo.NorthLayoutRegion = function(mgr, config){
44107     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44108     if(this.split){
44109         this.split.placement = Roo.SplitBar.TOP;
44110         this.split.orientation = Roo.SplitBar.VERTICAL;
44111         this.split.el.addClass("x-layout-split-v");
44112     }
44113     var size = config.initialSize || config.height;
44114     if(typeof size != "undefined"){
44115         this.el.setHeight(size);
44116     }
44117 };
44118 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44119     orientation: Roo.SplitBar.VERTICAL,
44120     getBox : function(){
44121         if(this.collapsed){
44122             return this.collapsedEl.getBox();
44123         }
44124         var box = this.el.getBox();
44125         if(this.split){
44126             box.height += this.split.el.getHeight();
44127         }
44128         return box;
44129     },
44130     
44131     updateBox : function(box){
44132         if(this.split && !this.collapsed){
44133             box.height -= this.split.el.getHeight();
44134             this.split.el.setLeft(box.x);
44135             this.split.el.setTop(box.y+box.height);
44136             this.split.el.setWidth(box.width);
44137         }
44138         if(this.collapsed){
44139             this.updateBody(box.width, null);
44140         }
44141         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44142     }
44143 });
44144
44145 Roo.SouthLayoutRegion = function(mgr, config){
44146     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44147     if(this.split){
44148         this.split.placement = Roo.SplitBar.BOTTOM;
44149         this.split.orientation = Roo.SplitBar.VERTICAL;
44150         this.split.el.addClass("x-layout-split-v");
44151     }
44152     var size = config.initialSize || config.height;
44153     if(typeof size != "undefined"){
44154         this.el.setHeight(size);
44155     }
44156 };
44157 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44158     orientation: Roo.SplitBar.VERTICAL,
44159     getBox : function(){
44160         if(this.collapsed){
44161             return this.collapsedEl.getBox();
44162         }
44163         var box = this.el.getBox();
44164         if(this.split){
44165             var sh = this.split.el.getHeight();
44166             box.height += sh;
44167             box.y -= sh;
44168         }
44169         return box;
44170     },
44171     
44172     updateBox : function(box){
44173         if(this.split && !this.collapsed){
44174             var sh = this.split.el.getHeight();
44175             box.height -= sh;
44176             box.y += sh;
44177             this.split.el.setLeft(box.x);
44178             this.split.el.setTop(box.y-sh);
44179             this.split.el.setWidth(box.width);
44180         }
44181         if(this.collapsed){
44182             this.updateBody(box.width, null);
44183         }
44184         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44185     }
44186 });
44187
44188 Roo.EastLayoutRegion = function(mgr, config){
44189     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44190     if(this.split){
44191         this.split.placement = Roo.SplitBar.RIGHT;
44192         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44193         this.split.el.addClass("x-layout-split-h");
44194     }
44195     var size = config.initialSize || config.width;
44196     if(typeof size != "undefined"){
44197         this.el.setWidth(size);
44198     }
44199 };
44200 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44201     orientation: Roo.SplitBar.HORIZONTAL,
44202     getBox : function(){
44203         if(this.collapsed){
44204             return this.collapsedEl.getBox();
44205         }
44206         var box = this.el.getBox();
44207         if(this.split){
44208             var sw = this.split.el.getWidth();
44209             box.width += sw;
44210             box.x -= sw;
44211         }
44212         return box;
44213     },
44214
44215     updateBox : function(box){
44216         if(this.split && !this.collapsed){
44217             var sw = this.split.el.getWidth();
44218             box.width -= sw;
44219             this.split.el.setLeft(box.x);
44220             this.split.el.setTop(box.y);
44221             this.split.el.setHeight(box.height);
44222             box.x += sw;
44223         }
44224         if(this.collapsed){
44225             this.updateBody(null, box.height);
44226         }
44227         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44228     }
44229 });
44230
44231 Roo.WestLayoutRegion = function(mgr, config){
44232     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44233     if(this.split){
44234         this.split.placement = Roo.SplitBar.LEFT;
44235         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44236         this.split.el.addClass("x-layout-split-h");
44237     }
44238     var size = config.initialSize || config.width;
44239     if(typeof size != "undefined"){
44240         this.el.setWidth(size);
44241     }
44242 };
44243 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44244     orientation: Roo.SplitBar.HORIZONTAL,
44245     getBox : function(){
44246         if(this.collapsed){
44247             return this.collapsedEl.getBox();
44248         }
44249         var box = this.el.getBox();
44250         if(this.split){
44251             box.width += this.split.el.getWidth();
44252         }
44253         return box;
44254     },
44255     
44256     updateBox : function(box){
44257         if(this.split && !this.collapsed){
44258             var sw = this.split.el.getWidth();
44259             box.width -= sw;
44260             this.split.el.setLeft(box.x+box.width);
44261             this.split.el.setTop(box.y);
44262             this.split.el.setHeight(box.height);
44263         }
44264         if(this.collapsed){
44265             this.updateBody(null, box.height);
44266         }
44267         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44268     }
44269 });
44270 /*
44271  * Based on:
44272  * Ext JS Library 1.1.1
44273  * Copyright(c) 2006-2007, Ext JS, LLC.
44274  *
44275  * Originally Released Under LGPL - original licence link has changed is not relivant.
44276  *
44277  * Fork - LGPL
44278  * <script type="text/javascript">
44279  */
44280  
44281  
44282 /*
44283  * Private internal class for reading and applying state
44284  */
44285 Roo.LayoutStateManager = function(layout){
44286      // default empty state
44287      this.state = {
44288         north: {},
44289         south: {},
44290         east: {},
44291         west: {}       
44292     };
44293 };
44294
44295 Roo.LayoutStateManager.prototype = {
44296     init : function(layout, provider){
44297         this.provider = provider;
44298         var state = provider.get(layout.id+"-layout-state");
44299         if(state){
44300             var wasUpdating = layout.isUpdating();
44301             if(!wasUpdating){
44302                 layout.beginUpdate();
44303             }
44304             for(var key in state){
44305                 if(typeof state[key] != "function"){
44306                     var rstate = state[key];
44307                     var r = layout.getRegion(key);
44308                     if(r && rstate){
44309                         if(rstate.size){
44310                             r.resizeTo(rstate.size);
44311                         }
44312                         if(rstate.collapsed == true){
44313                             r.collapse(true);
44314                         }else{
44315                             r.expand(null, true);
44316                         }
44317                     }
44318                 }
44319             }
44320             if(!wasUpdating){
44321                 layout.endUpdate();
44322             }
44323             this.state = state; 
44324         }
44325         this.layout = layout;
44326         layout.on("regionresized", this.onRegionResized, this);
44327         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44328         layout.on("regionexpanded", this.onRegionExpanded, this);
44329     },
44330     
44331     storeState : function(){
44332         this.provider.set(this.layout.id+"-layout-state", this.state);
44333     },
44334     
44335     onRegionResized : function(region, newSize){
44336         this.state[region.getPosition()].size = newSize;
44337         this.storeState();
44338     },
44339     
44340     onRegionCollapsed : function(region){
44341         this.state[region.getPosition()].collapsed = true;
44342         this.storeState();
44343     },
44344     
44345     onRegionExpanded : function(region){
44346         this.state[region.getPosition()].collapsed = false;
44347         this.storeState();
44348     }
44349 };/*
44350  * Based on:
44351  * Ext JS Library 1.1.1
44352  * Copyright(c) 2006-2007, Ext JS, LLC.
44353  *
44354  * Originally Released Under LGPL - original licence link has changed is not relivant.
44355  *
44356  * Fork - LGPL
44357  * <script type="text/javascript">
44358  */
44359 /**
44360  * @class Roo.ContentPanel
44361  * @extends Roo.util.Observable
44362  * A basic ContentPanel element.
44363  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44364  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44365  * @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
44366  * @cfg {Boolean} closable True if the panel can be closed/removed
44367  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44368  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44369  * @cfg {Toolbar} toolbar A toolbar for this panel
44370  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44371  * @cfg {String} title The title for this panel
44372  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44373  * @cfg {String} url Calls {@link #setUrl} with this value
44374  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44375  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44376  * @constructor
44377  * Create a new ContentPanel.
44378  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44379  * @param {String/Object} config A string to set only the title or a config object
44380  * @param {String} content (optional) Set the HTML content for this panel
44381  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44382  */
44383 Roo.ContentPanel = function(el, config, content){
44384     
44385      
44386     /*
44387     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44388         config = el;
44389         el = Roo.id();
44390     }
44391     if (config && config.parentLayout) { 
44392         el = config.parentLayout.el.createChild(); 
44393     }
44394     */
44395     if(el.autoCreate){ // xtype is available if this is called from factory
44396         config = el;
44397         el = Roo.id();
44398     }
44399     this.el = Roo.get(el);
44400     if(!this.el && config && config.autoCreate){
44401         if(typeof config.autoCreate == "object"){
44402             if(!config.autoCreate.id){
44403                 config.autoCreate.id = config.id||el;
44404             }
44405             this.el = Roo.DomHelper.append(document.body,
44406                         config.autoCreate, true);
44407         }else{
44408             this.el = Roo.DomHelper.append(document.body,
44409                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44410         }
44411     }
44412     this.closable = false;
44413     this.loaded = false;
44414     this.active = false;
44415     if(typeof config == "string"){
44416         this.title = config;
44417     }else{
44418         Roo.apply(this, config);
44419     }
44420     
44421     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44422         this.wrapEl = this.el.wrap();    
44423         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44424         
44425     }
44426     
44427     
44428     
44429     if(this.resizeEl){
44430         this.resizeEl = Roo.get(this.resizeEl, true);
44431     }else{
44432         this.resizeEl = this.el;
44433     }
44434     this.addEvents({
44435         /**
44436          * @event activate
44437          * Fires when this panel is activated. 
44438          * @param {Roo.ContentPanel} this
44439          */
44440         "activate" : true,
44441         /**
44442          * @event deactivate
44443          * Fires when this panel is activated. 
44444          * @param {Roo.ContentPanel} this
44445          */
44446         "deactivate" : true,
44447
44448         /**
44449          * @event resize
44450          * Fires when this panel is resized if fitToFrame is true.
44451          * @param {Roo.ContentPanel} this
44452          * @param {Number} width The width after any component adjustments
44453          * @param {Number} height The height after any component adjustments
44454          */
44455         "resize" : true
44456     });
44457     if(this.autoScroll){
44458         this.resizeEl.setStyle("overflow", "auto");
44459     }
44460     content = content || this.content;
44461     if(content){
44462         this.setContent(content);
44463     }
44464     if(config && config.url){
44465         this.setUrl(this.url, this.params, this.loadOnce);
44466     }
44467     
44468     
44469     
44470     Roo.ContentPanel.superclass.constructor.call(this);
44471 };
44472
44473 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44474     tabTip:'',
44475     setRegion : function(region){
44476         this.region = region;
44477         if(region){
44478            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44479         }else{
44480            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44481         } 
44482     },
44483     
44484     /**
44485      * Returns the toolbar for this Panel if one was configured. 
44486      * @return {Roo.Toolbar} 
44487      */
44488     getToolbar : function(){
44489         return this.toolbar;
44490     },
44491     
44492     setActiveState : function(active){
44493         this.active = active;
44494         if(!active){
44495             this.fireEvent("deactivate", this);
44496         }else{
44497             this.fireEvent("activate", this);
44498         }
44499     },
44500     /**
44501      * Updates this panel's element
44502      * @param {String} content The new content
44503      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44504     */
44505     setContent : function(content, loadScripts){
44506         this.el.update(content, loadScripts);
44507     },
44508
44509     ignoreResize : function(w, h){
44510         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44511             return true;
44512         }else{
44513             this.lastSize = {width: w, height: h};
44514             return false;
44515         }
44516     },
44517     /**
44518      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44519      * @return {Roo.UpdateManager} The UpdateManager
44520      */
44521     getUpdateManager : function(){
44522         return this.el.getUpdateManager();
44523     },
44524      /**
44525      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44526      * @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:
44527 <pre><code>
44528 panel.load({
44529     url: "your-url.php",
44530     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44531     callback: yourFunction,
44532     scope: yourObject, //(optional scope)
44533     discardUrl: false,
44534     nocache: false,
44535     text: "Loading...",
44536     timeout: 30,
44537     scripts: false
44538 });
44539 </code></pre>
44540      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44541      * 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.
44542      * @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}
44543      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44544      * @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.
44545      * @return {Roo.ContentPanel} this
44546      */
44547     load : function(){
44548         var um = this.el.getUpdateManager();
44549         um.update.apply(um, arguments);
44550         return this;
44551     },
44552
44553
44554     /**
44555      * 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.
44556      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44557      * @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)
44558      * @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)
44559      * @return {Roo.UpdateManager} The UpdateManager
44560      */
44561     setUrl : function(url, params, loadOnce){
44562         if(this.refreshDelegate){
44563             this.removeListener("activate", this.refreshDelegate);
44564         }
44565         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44566         this.on("activate", this.refreshDelegate);
44567         return this.el.getUpdateManager();
44568     },
44569     
44570     _handleRefresh : function(url, params, loadOnce){
44571         if(!loadOnce || !this.loaded){
44572             var updater = this.el.getUpdateManager();
44573             updater.update(url, params, this._setLoaded.createDelegate(this));
44574         }
44575     },
44576     
44577     _setLoaded : function(){
44578         this.loaded = true;
44579     }, 
44580     
44581     /**
44582      * Returns this panel's id
44583      * @return {String} 
44584      */
44585     getId : function(){
44586         return this.el.id;
44587     },
44588     
44589     /** 
44590      * Returns this panel's element - used by regiosn to add.
44591      * @return {Roo.Element} 
44592      */
44593     getEl : function(){
44594         return this.wrapEl || this.el;
44595     },
44596     
44597     adjustForComponents : function(width, height){
44598         if(this.resizeEl != this.el){
44599             width -= this.el.getFrameWidth('lr');
44600             height -= this.el.getFrameWidth('tb');
44601         }
44602         if(this.toolbar){
44603             var te = this.toolbar.getEl();
44604             height -= te.getHeight();
44605             te.setWidth(width);
44606         }
44607         if(this.adjustments){
44608             width += this.adjustments[0];
44609             height += this.adjustments[1];
44610         }
44611         return {"width": width, "height": height};
44612     },
44613     
44614     setSize : function(width, height){
44615         if(this.fitToFrame && !this.ignoreResize(width, height)){
44616             if(this.fitContainer && this.resizeEl != this.el){
44617                 this.el.setSize(width, height);
44618             }
44619             var size = this.adjustForComponents(width, height);
44620             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44621             this.fireEvent('resize', this, size.width, size.height);
44622         }
44623     },
44624     
44625     /**
44626      * Returns this panel's title
44627      * @return {String} 
44628      */
44629     getTitle : function(){
44630         return this.title;
44631     },
44632     
44633     /**
44634      * Set this panel's title
44635      * @param {String} title
44636      */
44637     setTitle : function(title){
44638         this.title = title;
44639         if(this.region){
44640             this.region.updatePanelTitle(this, title);
44641         }
44642     },
44643     
44644     /**
44645      * Returns true is this panel was configured to be closable
44646      * @return {Boolean} 
44647      */
44648     isClosable : function(){
44649         return this.closable;
44650     },
44651     
44652     beforeSlide : function(){
44653         this.el.clip();
44654         this.resizeEl.clip();
44655     },
44656     
44657     afterSlide : function(){
44658         this.el.unclip();
44659         this.resizeEl.unclip();
44660     },
44661     
44662     /**
44663      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44664      *   Will fail silently if the {@link #setUrl} method has not been called.
44665      *   This does not activate the panel, just updates its content.
44666      */
44667     refresh : function(){
44668         if(this.refreshDelegate){
44669            this.loaded = false;
44670            this.refreshDelegate();
44671         }
44672     },
44673     
44674     /**
44675      * Destroys this panel
44676      */
44677     destroy : function(){
44678         this.el.removeAllListeners();
44679         var tempEl = document.createElement("span");
44680         tempEl.appendChild(this.el.dom);
44681         tempEl.innerHTML = "";
44682         this.el.remove();
44683         this.el = null;
44684     },
44685     
44686       /**
44687      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44688      * <pre><code>
44689
44690 layout.addxtype({
44691        xtype : 'Form',
44692        items: [ .... ]
44693    }
44694 );
44695
44696 </code></pre>
44697      * @param {Object} cfg Xtype definition of item to add.
44698      */
44699     
44700     addxtype : function(cfg) {
44701         // add form..
44702         if (cfg.xtype.match(/^Form$/)) {
44703             var el = this.el.createChild();
44704
44705             this.form = new  Roo.form.Form(cfg);
44706             
44707             
44708             if ( this.form.allItems.length) this.form.render(el.dom);
44709             return this.form;
44710         }
44711         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
44712             // views..
44713             cfg.el = this.el;
44714             // factory?
44715             var ret = new Roo[cfg.xtype](cfg);
44716             ret.render(false, ''); // render blank..
44717             return ret;
44718             
44719         }
44720         return false;
44721         
44722     }
44723 });
44724
44725 /**
44726  * @class Roo.GridPanel
44727  * @extends Roo.ContentPanel
44728  * @constructor
44729  * Create a new GridPanel.
44730  * @param {Roo.grid.Grid} grid The grid for this panel
44731  * @param {String/Object} config A string to set only the panel's title, or a config object
44732  */
44733 Roo.GridPanel = function(grid, config){
44734     
44735   
44736     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44737         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44738         
44739     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44740     
44741     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44742     
44743     if(this.toolbar){
44744         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44745     }
44746     // xtype created footer. - not sure if will work as we normally have to render first..
44747     if (this.footer && !this.footer.el && this.footer.xtype) {
44748         
44749         this.footer.container = this.grid.getView().getFooterPanel(true);
44750         this.footer.dataSource = this.grid.dataSource;
44751         this.footer = Roo.factory(this.footer, Roo);
44752         
44753     }
44754     
44755     grid.monitorWindowResize = false; // turn off autosizing
44756     grid.autoHeight = false;
44757     grid.autoWidth = false;
44758     this.grid = grid;
44759     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44760 };
44761
44762 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44763     getId : function(){
44764         return this.grid.id;
44765     },
44766     
44767     /**
44768      * Returns the grid for this panel
44769      * @return {Roo.grid.Grid} 
44770      */
44771     getGrid : function(){
44772         return this.grid;    
44773     },
44774     
44775     setSize : function(width, height){
44776         if(!this.ignoreResize(width, height)){
44777             var grid = this.grid;
44778             var size = this.adjustForComponents(width, height);
44779             grid.getGridEl().setSize(size.width, size.height);
44780             grid.autoSize();
44781         }
44782     },
44783     
44784     beforeSlide : function(){
44785         this.grid.getView().scroller.clip();
44786     },
44787     
44788     afterSlide : function(){
44789         this.grid.getView().scroller.unclip();
44790     },
44791     
44792     destroy : function(){
44793         this.grid.destroy();
44794         delete this.grid;
44795         Roo.GridPanel.superclass.destroy.call(this); 
44796     }
44797 });
44798
44799
44800 /**
44801  * @class Roo.NestedLayoutPanel
44802  * @extends Roo.ContentPanel
44803  * @constructor
44804  * Create a new NestedLayoutPanel.
44805  * 
44806  * 
44807  * @param {Roo.BorderLayout} layout The layout for this panel
44808  * @param {String/Object} config A string to set only the title or a config object
44809  */
44810 Roo.NestedLayoutPanel = function(layout, config)
44811 {
44812     // construct with only one argument..
44813     /* FIXME - implement nicer consturctors
44814     if (layout.layout) {
44815         config = layout;
44816         layout = config.layout;
44817         delete config.layout;
44818     }
44819     if (layout.xtype && !layout.getEl) {
44820         // then layout needs constructing..
44821         layout = Roo.factory(layout, Roo);
44822     }
44823     */
44824     
44825     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44826     
44827     layout.monitorWindowResize = false; // turn off autosizing
44828     this.layout = layout;
44829     this.layout.getEl().addClass("x-layout-nested-layout");
44830     
44831     
44832     
44833 };
44834
44835 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44836
44837     setSize : function(width, height){
44838         if(!this.ignoreResize(width, height)){
44839             var size = this.adjustForComponents(width, height);
44840             var el = this.layout.getEl();
44841             el.setSize(size.width, size.height);
44842             var touch = el.dom.offsetWidth;
44843             this.layout.layout();
44844             // ie requires a double layout on the first pass
44845             if(Roo.isIE && !this.initialized){
44846                 this.initialized = true;
44847                 this.layout.layout();
44848             }
44849         }
44850     },
44851     
44852     // activate all subpanels if not currently active..
44853     
44854     setActiveState : function(active){
44855         this.active = active;
44856         if(!active){
44857             this.fireEvent("deactivate", this);
44858             return;
44859         }
44860         
44861         this.fireEvent("activate", this);
44862         // not sure if this should happen before or after..
44863         if (!this.layout) {
44864             return; // should not happen..
44865         }
44866         var reg = false;
44867         for (var r in this.layout.regions) {
44868             reg = this.layout.getRegion(r);
44869             if (reg.getActivePanel()) {
44870                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44871                 reg.setActivePanel(reg.getActivePanel());
44872                 continue;
44873             }
44874             if (!reg.panels.length) {
44875                 continue;
44876             }
44877             reg.showPanel(reg.getPanel(0));
44878         }
44879         
44880         
44881         
44882         
44883     },
44884     
44885     /**
44886      * Returns the nested BorderLayout for this panel
44887      * @return {Roo.BorderLayout} 
44888      */
44889     getLayout : function(){
44890         return this.layout;
44891     },
44892     
44893      /**
44894      * Adds a xtype elements to the layout of the nested panel
44895      * <pre><code>
44896
44897 panel.addxtype({
44898        xtype : 'ContentPanel',
44899        region: 'west',
44900        items: [ .... ]
44901    }
44902 );
44903
44904 panel.addxtype({
44905         xtype : 'NestedLayoutPanel',
44906         region: 'west',
44907         layout: {
44908            center: { },
44909            west: { }   
44910         },
44911         items : [ ... list of content panels or nested layout panels.. ]
44912    }
44913 );
44914 </code></pre>
44915      * @param {Object} cfg Xtype definition of item to add.
44916      */
44917     addxtype : function(cfg) {
44918         return this.layout.addxtype(cfg);
44919     
44920     }
44921 });
44922
44923 Roo.ScrollPanel = function(el, config, content){
44924     config = config || {};
44925     config.fitToFrame = true;
44926     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44927     
44928     this.el.dom.style.overflow = "hidden";
44929     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44930     this.el.removeClass("x-layout-inactive-content");
44931     this.el.on("mousewheel", this.onWheel, this);
44932
44933     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44934     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44935     up.unselectable(); down.unselectable();
44936     up.on("click", this.scrollUp, this);
44937     down.on("click", this.scrollDown, this);
44938     up.addClassOnOver("x-scroller-btn-over");
44939     down.addClassOnOver("x-scroller-btn-over");
44940     up.addClassOnClick("x-scroller-btn-click");
44941     down.addClassOnClick("x-scroller-btn-click");
44942     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44943
44944     this.resizeEl = this.el;
44945     this.el = wrap; this.up = up; this.down = down;
44946 };
44947
44948 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44949     increment : 100,
44950     wheelIncrement : 5,
44951     scrollUp : function(){
44952         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44953     },
44954
44955     scrollDown : function(){
44956         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44957     },
44958
44959     afterScroll : function(){
44960         var el = this.resizeEl;
44961         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44962         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44963         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44964     },
44965
44966     setSize : function(){
44967         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44968         this.afterScroll();
44969     },
44970
44971     onWheel : function(e){
44972         var d = e.getWheelDelta();
44973         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44974         this.afterScroll();
44975         e.stopEvent();
44976     },
44977
44978     setContent : function(content, loadScripts){
44979         this.resizeEl.update(content, loadScripts);
44980     }
44981
44982 });
44983
44984
44985
44986
44987
44988
44989
44990
44991
44992 /**
44993  * @class Roo.TreePanel
44994  * @extends Roo.ContentPanel
44995  * @constructor
44996  * Create a new TreePanel.
44997  * @param {String/Object} config A string to set only the panel's title, or a config object
44998  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
44999  */
45000 Roo.TreePanel = function(config){
45001     var el = config.el;
45002     var tree = config.tree;
45003     delete config.tree; 
45004     delete config.el; // hopefull!
45005     Roo.TreePanel.superclass.constructor.call(this, el, config);
45006     var treeEl = el.createChild();
45007     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45008     //console.log(tree);
45009     this.on('activate', function()
45010     {
45011         if (this.tree.rendered) {
45012             return;
45013         }
45014         //console.log('render tree');
45015         this.tree.render();
45016     });
45017     
45018     this.on('resize',  function (cp, w, h) {
45019             this.tree.innerCt.setWidth(w);
45020             this.tree.innerCt.setHeight(h);
45021             this.tree.innerCt.setStyle('overflow-y', 'auto');
45022     });
45023
45024         
45025     
45026 };
45027
45028 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
45029
45030
45031
45032
45033
45034
45035
45036
45037
45038
45039
45040 /*
45041  * Based on:
45042  * Ext JS Library 1.1.1
45043  * Copyright(c) 2006-2007, Ext JS, LLC.
45044  *
45045  * Originally Released Under LGPL - original licence link has changed is not relivant.
45046  *
45047  * Fork - LGPL
45048  * <script type="text/javascript">
45049  */
45050  
45051
45052 /**
45053  * @class Roo.ReaderLayout
45054  * @extends Roo.BorderLayout
45055  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45056  * center region containing two nested regions (a top one for a list view and one for item preview below),
45057  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45058  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45059  * expedites the setup of the overall layout and regions for this common application style.
45060  * Example:
45061  <pre><code>
45062 var reader = new Roo.ReaderLayout();
45063 var CP = Roo.ContentPanel;  // shortcut for adding
45064
45065 reader.beginUpdate();
45066 reader.add("north", new CP("north", "North"));
45067 reader.add("west", new CP("west", {title: "West"}));
45068 reader.add("east", new CP("east", {title: "East"}));
45069
45070 reader.regions.listView.add(new CP("listView", "List"));
45071 reader.regions.preview.add(new CP("preview", "Preview"));
45072 reader.endUpdate();
45073 </code></pre>
45074 * @constructor
45075 * Create a new ReaderLayout
45076 * @param {Object} config Configuration options
45077 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45078 * document.body if omitted)
45079 */
45080 Roo.ReaderLayout = function(config, renderTo){
45081     var c = config || {size:{}};
45082     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45083         north: c.north !== false ? Roo.apply({
45084             split:false,
45085             initialSize: 32,
45086             titlebar: false
45087         }, c.north) : false,
45088         west: c.west !== false ? Roo.apply({
45089             split:true,
45090             initialSize: 200,
45091             minSize: 175,
45092             maxSize: 400,
45093             titlebar: true,
45094             collapsible: true,
45095             animate: true,
45096             margins:{left:5,right:0,bottom:5,top:5},
45097             cmargins:{left:5,right:5,bottom:5,top:5}
45098         }, c.west) : false,
45099         east: c.east !== false ? Roo.apply({
45100             split:true,
45101             initialSize: 200,
45102             minSize: 175,
45103             maxSize: 400,
45104             titlebar: true,
45105             collapsible: true,
45106             animate: true,
45107             margins:{left:0,right:5,bottom:5,top:5},
45108             cmargins:{left:5,right:5,bottom:5,top:5}
45109         }, c.east) : false,
45110         center: Roo.apply({
45111             tabPosition: 'top',
45112             autoScroll:false,
45113             closeOnTab: true,
45114             titlebar:false,
45115             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45116         }, c.center)
45117     });
45118
45119     this.el.addClass('x-reader');
45120
45121     this.beginUpdate();
45122
45123     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45124         south: c.preview !== false ? Roo.apply({
45125             split:true,
45126             initialSize: 200,
45127             minSize: 100,
45128             autoScroll:true,
45129             collapsible:true,
45130             titlebar: true,
45131             cmargins:{top:5,left:0, right:0, bottom:0}
45132         }, c.preview) : false,
45133         center: Roo.apply({
45134             autoScroll:false,
45135             titlebar:false,
45136             minHeight:200
45137         }, c.listView)
45138     });
45139     this.add('center', new Roo.NestedLayoutPanel(inner,
45140             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45141
45142     this.endUpdate();
45143
45144     this.regions.preview = inner.getRegion('south');
45145     this.regions.listView = inner.getRegion('center');
45146 };
45147
45148 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45149  * Based on:
45150  * Ext JS Library 1.1.1
45151  * Copyright(c) 2006-2007, Ext JS, LLC.
45152  *
45153  * Originally Released Under LGPL - original licence link has changed is not relivant.
45154  *
45155  * Fork - LGPL
45156  * <script type="text/javascript">
45157  */
45158  
45159 /**
45160  * @class Roo.grid.Grid
45161  * @extends Roo.util.Observable
45162  * This class represents the primary interface of a component based grid control.
45163  * <br><br>Usage:<pre><code>
45164  var grid = new Roo.grid.Grid("my-container-id", {
45165      ds: myDataStore,
45166      cm: myColModel,
45167      selModel: mySelectionModel,
45168      autoSizeColumns: true,
45169      monitorWindowResize: false,
45170      trackMouseOver: true
45171  });
45172  // set any options
45173  grid.render();
45174  * </code></pre>
45175  * <b>Common Problems:</b><br/>
45176  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45177  * element will correct this<br/>
45178  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45179  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45180  * are unpredictable.<br/>
45181  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45182  * grid to calculate dimensions/offsets.<br/>
45183   * @constructor
45184  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45185  * The container MUST have some type of size defined for the grid to fill. The container will be
45186  * automatically set to position relative if it isn't already.
45187  * @param {Object} config A config object that sets properties on this grid.
45188  */
45189 Roo.grid.Grid = function(container, config){
45190         // initialize the container
45191         this.container = Roo.get(container);
45192         this.container.update("");
45193         this.container.setStyle("overflow", "hidden");
45194     this.container.addClass('x-grid-container');
45195
45196     this.id = this.container.id;
45197
45198     Roo.apply(this, config);
45199     // check and correct shorthanded configs
45200     if(this.ds){
45201         this.dataSource = this.ds;
45202         delete this.ds;
45203     }
45204     if(this.cm){
45205         this.colModel = this.cm;
45206         delete this.cm;
45207     }
45208     if(this.sm){
45209         this.selModel = this.sm;
45210         delete this.sm;
45211     }
45212
45213     if (this.selModel) {
45214         this.selModel = Roo.factory(this.selModel, Roo.grid);
45215         this.sm = this.selModel;
45216         this.sm.xmodule = this.xmodule || false;
45217     }
45218     if (typeof(this.colModel.config) == 'undefined') {
45219         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45220         this.cm = this.colModel;
45221         this.cm.xmodule = this.xmodule || false;
45222     }
45223     if (this.dataSource) {
45224         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45225         this.ds = this.dataSource;
45226         this.ds.xmodule = this.xmodule || false;
45227         
45228     }
45229     
45230     
45231     
45232     if(this.width){
45233         this.container.setWidth(this.width);
45234     }
45235
45236     if(this.height){
45237         this.container.setHeight(this.height);
45238     }
45239     /** @private */
45240         this.addEvents({
45241             // raw events
45242             /**
45243              * @event click
45244              * The raw click event for the entire grid.
45245              * @param {Roo.EventObject} e
45246              */
45247             "click" : true,
45248             /**
45249              * @event dblclick
45250              * The raw dblclick event for the entire grid.
45251              * @param {Roo.EventObject} e
45252              */
45253             "dblclick" : true,
45254             /**
45255              * @event contextmenu
45256              * The raw contextmenu event for the entire grid.
45257              * @param {Roo.EventObject} e
45258              */
45259             "contextmenu" : true,
45260             /**
45261              * @event mousedown
45262              * The raw mousedown event for the entire grid.
45263              * @param {Roo.EventObject} e
45264              */
45265             "mousedown" : true,
45266             /**
45267              * @event mouseup
45268              * The raw mouseup event for the entire grid.
45269              * @param {Roo.EventObject} e
45270              */
45271             "mouseup" : true,
45272             /**
45273              * @event mouseover
45274              * The raw mouseover event for the entire grid.
45275              * @param {Roo.EventObject} e
45276              */
45277             "mouseover" : true,
45278             /**
45279              * @event mouseout
45280              * The raw mouseout event for the entire grid.
45281              * @param {Roo.EventObject} e
45282              */
45283             "mouseout" : true,
45284             /**
45285              * @event keypress
45286              * The raw keypress event for the entire grid.
45287              * @param {Roo.EventObject} e
45288              */
45289             "keypress" : true,
45290             /**
45291              * @event keydown
45292              * The raw keydown event for the entire grid.
45293              * @param {Roo.EventObject} e
45294              */
45295             "keydown" : true,
45296
45297             // custom events
45298
45299             /**
45300              * @event cellclick
45301              * Fires when a cell is clicked
45302              * @param {Grid} this
45303              * @param {Number} rowIndex
45304              * @param {Number} columnIndex
45305              * @param {Roo.EventObject} e
45306              */
45307             "cellclick" : true,
45308             /**
45309              * @event celldblclick
45310              * Fires when a cell is double clicked
45311              * @param {Grid} this
45312              * @param {Number} rowIndex
45313              * @param {Number} columnIndex
45314              * @param {Roo.EventObject} e
45315              */
45316             "celldblclick" : true,
45317             /**
45318              * @event rowclick
45319              * Fires when a row is clicked
45320              * @param {Grid} this
45321              * @param {Number} rowIndex
45322              * @param {Roo.EventObject} e
45323              */
45324             "rowclick" : true,
45325             /**
45326              * @event rowdblclick
45327              * Fires when a row is double clicked
45328              * @param {Grid} this
45329              * @param {Number} rowIndex
45330              * @param {Roo.EventObject} e
45331              */
45332             "rowdblclick" : true,
45333             /**
45334              * @event headerclick
45335              * Fires when a header is clicked
45336              * @param {Grid} this
45337              * @param {Number} columnIndex
45338              * @param {Roo.EventObject} e
45339              */
45340             "headerclick" : true,
45341             /**
45342              * @event headerdblclick
45343              * Fires when a header cell is double clicked
45344              * @param {Grid} this
45345              * @param {Number} columnIndex
45346              * @param {Roo.EventObject} e
45347              */
45348             "headerdblclick" : true,
45349             /**
45350              * @event rowcontextmenu
45351              * Fires when a row is right clicked
45352              * @param {Grid} this
45353              * @param {Number} rowIndex
45354              * @param {Roo.EventObject} e
45355              */
45356             "rowcontextmenu" : true,
45357             /**
45358          * @event cellcontextmenu
45359          * Fires when a cell is right clicked
45360          * @param {Grid} this
45361          * @param {Number} rowIndex
45362          * @param {Number} cellIndex
45363          * @param {Roo.EventObject} e
45364          */
45365          "cellcontextmenu" : true,
45366             /**
45367              * @event headercontextmenu
45368              * Fires when a header is right clicked
45369              * @param {Grid} this
45370              * @param {Number} columnIndex
45371              * @param {Roo.EventObject} e
45372              */
45373             "headercontextmenu" : true,
45374             /**
45375              * @event bodyscroll
45376              * Fires when the body element is scrolled
45377              * @param {Number} scrollLeft
45378              * @param {Number} scrollTop
45379              */
45380             "bodyscroll" : true,
45381             /**
45382              * @event columnresize
45383              * Fires when the user resizes a column
45384              * @param {Number} columnIndex
45385              * @param {Number} newSize
45386              */
45387             "columnresize" : true,
45388             /**
45389              * @event columnmove
45390              * Fires when the user moves a column
45391              * @param {Number} oldIndex
45392              * @param {Number} newIndex
45393              */
45394             "columnmove" : true,
45395             /**
45396              * @event startdrag
45397              * Fires when row(s) start being dragged
45398              * @param {Grid} this
45399              * @param {Roo.GridDD} dd The drag drop object
45400              * @param {event} e The raw browser event
45401              */
45402             "startdrag" : true,
45403             /**
45404              * @event enddrag
45405              * Fires when a drag operation is complete
45406              * @param {Grid} this
45407              * @param {Roo.GridDD} dd The drag drop object
45408              * @param {event} e The raw browser event
45409              */
45410             "enddrag" : true,
45411             /**
45412              * @event dragdrop
45413              * Fires when dragged row(s) are dropped on a valid DD target
45414              * @param {Grid} this
45415              * @param {Roo.GridDD} dd The drag drop object
45416              * @param {String} targetId The target drag drop object
45417              * @param {event} e The raw browser event
45418              */
45419             "dragdrop" : true,
45420             /**
45421              * @event dragover
45422              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45423              * @param {Grid} this
45424              * @param {Roo.GridDD} dd The drag drop object
45425              * @param {String} targetId The target drag drop object
45426              * @param {event} e The raw browser event
45427              */
45428             "dragover" : true,
45429             /**
45430              * @event dragenter
45431              *  Fires when the dragged row(s) first cross another DD target while being dragged
45432              * @param {Grid} this
45433              * @param {Roo.GridDD} dd The drag drop object
45434              * @param {String} targetId The target drag drop object
45435              * @param {event} e The raw browser event
45436              */
45437             "dragenter" : true,
45438             /**
45439              * @event dragout
45440              * Fires when the dragged row(s) leave another DD target while being dragged
45441              * @param {Grid} this
45442              * @param {Roo.GridDD} dd The drag drop object
45443              * @param {String} targetId The target drag drop object
45444              * @param {event} e The raw browser event
45445              */
45446             "dragout" : true,
45447         /**
45448          * @event render
45449          * Fires when the grid is rendered
45450          * @param {Grid} grid
45451          */
45452         render : true
45453     });
45454
45455     Roo.grid.Grid.superclass.constructor.call(this);
45456 };
45457 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45458     /**
45459      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45460          */
45461         minColumnWidth : 25,
45462
45463     /**
45464          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45465          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45466          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45467          */
45468         autoSizeColumns : false,
45469
45470         /**
45471          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45472          */
45473         autoSizeHeaders : true,
45474
45475         /**
45476          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45477          */
45478         monitorWindowResize : true,
45479
45480         /**
45481          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45482          * rows measured to get a columns size. Default is 0 (all rows).
45483          */
45484         maxRowsToMeasure : 0,
45485
45486         /**
45487          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45488          */
45489         trackMouseOver : true,
45490
45491         /**
45492          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45493          */
45494         enableDragDrop : false,
45495
45496         /**
45497          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45498          */
45499         enableColumnMove : true,
45500
45501         /**
45502          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45503          */
45504         enableColumnHide : true,
45505
45506         /**
45507          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45508          */
45509         enableRowHeightSync : false,
45510
45511         /**
45512          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45513          */
45514         stripeRows : true,
45515
45516         /**
45517          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45518          */
45519         autoHeight : false,
45520
45521     /**
45522      * @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.
45523      */
45524     autoExpandColumn : false,
45525
45526     /**
45527     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45528     * Default is 50.
45529     */
45530     autoExpandMin : 50,
45531
45532     /**
45533     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45534     */
45535     autoExpandMax : 1000,
45536
45537     /**
45538          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45539          */
45540         view : null,
45541
45542         /**
45543      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45544          */
45545         loadMask : false,
45546
45547     // private
45548     rendered : false,
45549
45550     /**
45551     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45552     * of a fixed width. Default is false.
45553     */
45554     /**
45555     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45556     */
45557     /**
45558      * Called once after all setup has been completed and the grid is ready to be rendered.
45559      * @return {Roo.grid.Grid} this
45560      */
45561     render : function(){
45562         var c = this.container;
45563         // try to detect autoHeight/width mode
45564         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45565             this.autoHeight = true;
45566         }
45567         var view = this.getView();
45568         view.init(this);
45569
45570         c.on("click", this.onClick, this);
45571         c.on("dblclick", this.onDblClick, this);
45572         c.on("contextmenu", this.onContextMenu, this);
45573         c.on("keydown", this.onKeyDown, this);
45574
45575         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45576
45577         this.getSelectionModel().init(this);
45578
45579         view.render();
45580
45581         if(this.loadMask){
45582             this.loadMask = new Roo.LoadMask(this.container,
45583                     Roo.apply({store:this.dataSource}, this.loadMask));
45584         }
45585         
45586         
45587         if (this.toolbar && this.toolbar.xtype) {
45588             this.toolbar.container = this.getView().getHeaderPanel(true);
45589             this.toolbar = new Ext.Toolbar(this.toolbar);
45590         }
45591         if (this.footer && this.footer.xtype) {
45592             this.footer.dataSource = this.getDataSource();
45593             this.footer.container = this.getView().getFooterPanel(true);
45594             this.footer = Roo.factory(this.footer, Roo);
45595         }
45596         this.rendered = true;
45597         this.fireEvent('render', this);
45598         return this;
45599     },
45600
45601         /**
45602          * Reconfigures the grid to use a different Store and Column Model.
45603          * The View will be bound to the new objects and refreshed.
45604          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45605          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45606          */
45607     reconfigure : function(dataSource, colModel){
45608         if(this.loadMask){
45609             this.loadMask.destroy();
45610             this.loadMask = new Roo.LoadMask(this.container,
45611                     Roo.apply({store:dataSource}, this.loadMask));
45612         }
45613         this.view.bind(dataSource, colModel);
45614         this.dataSource = dataSource;
45615         this.colModel = colModel;
45616         this.view.refresh(true);
45617     },
45618
45619     // private
45620     onKeyDown : function(e){
45621         this.fireEvent("keydown", e);
45622     },
45623
45624     /**
45625      * Destroy this grid.
45626      * @param {Boolean} removeEl True to remove the element
45627      */
45628     destroy : function(removeEl, keepListeners){
45629         if(this.loadMask){
45630             this.loadMask.destroy();
45631         }
45632         var c = this.container;
45633         c.removeAllListeners();
45634         this.view.destroy();
45635         this.colModel.purgeListeners();
45636         if(!keepListeners){
45637             this.purgeListeners();
45638         }
45639         c.update("");
45640         if(removeEl === true){
45641             c.remove();
45642         }
45643     },
45644
45645     // private
45646     processEvent : function(name, e){
45647         this.fireEvent(name, e);
45648         var t = e.getTarget();
45649         var v = this.view;
45650         var header = v.findHeaderIndex(t);
45651         if(header !== false){
45652             this.fireEvent("header" + name, this, header, e);
45653         }else{
45654             var row = v.findRowIndex(t);
45655             var cell = v.findCellIndex(t);
45656             if(row !== false){
45657                 this.fireEvent("row" + name, this, row, e);
45658                 if(cell !== false){
45659                     this.fireEvent("cell" + name, this, row, cell, e);
45660                 }
45661             }
45662         }
45663     },
45664
45665     // private
45666     onClick : function(e){
45667         this.processEvent("click", e);
45668     },
45669
45670     // private
45671     onContextMenu : function(e, t){
45672         this.processEvent("contextmenu", e);
45673     },
45674
45675     // private
45676     onDblClick : function(e){
45677         this.processEvent("dblclick", e);
45678     },
45679
45680     // private
45681     walkCells : function(row, col, step, fn, scope){
45682         var cm = this.colModel, clen = cm.getColumnCount();
45683         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45684         if(step < 0){
45685             if(col < 0){
45686                 row--;
45687                 first = false;
45688             }
45689             while(row >= 0){
45690                 if(!first){
45691                     col = clen-1;
45692                 }
45693                 first = false;
45694                 while(col >= 0){
45695                     if(fn.call(scope || this, row, col, cm) === true){
45696                         return [row, col];
45697                     }
45698                     col--;
45699                 }
45700                 row--;
45701             }
45702         } else {
45703             if(col >= clen){
45704                 row++;
45705                 first = false;
45706             }
45707             while(row < rlen){
45708                 if(!first){
45709                     col = 0;
45710                 }
45711                 first = false;
45712                 while(col < clen){
45713                     if(fn.call(scope || this, row, col, cm) === true){
45714                         return [row, col];
45715                     }
45716                     col++;
45717                 }
45718                 row++;
45719             }
45720         }
45721         return null;
45722     },
45723
45724     // private
45725     getSelections : function(){
45726         return this.selModel.getSelections();
45727     },
45728
45729     /**
45730      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45731      * but if manual update is required this method will initiate it.
45732      */
45733     autoSize : function(){
45734         if(this.rendered){
45735             this.view.layout();
45736             if(this.view.adjustForScroll){
45737                 this.view.adjustForScroll();
45738             }
45739         }
45740     },
45741
45742     /**
45743      * Returns the grid's underlying element.
45744      * @return {Element} The element
45745      */
45746     getGridEl : function(){
45747         return this.container;
45748     },
45749
45750     // private for compatibility, overridden by editor grid
45751     stopEditing : function(){},
45752
45753     /**
45754      * Returns the grid's SelectionModel.
45755      * @return {SelectionModel}
45756      */
45757     getSelectionModel : function(){
45758         if(!this.selModel){
45759             this.selModel = new Roo.grid.RowSelectionModel();
45760         }
45761         return this.selModel;
45762     },
45763
45764     /**
45765      * Returns the grid's DataSource.
45766      * @return {DataSource}
45767      */
45768     getDataSource : function(){
45769         return this.dataSource;
45770     },
45771
45772     /**
45773      * Returns the grid's ColumnModel.
45774      * @return {ColumnModel}
45775      */
45776     getColumnModel : function(){
45777         return this.colModel;
45778     },
45779
45780     /**
45781      * Returns the grid's GridView object.
45782      * @return {GridView}
45783      */
45784     getView : function(){
45785         if(!this.view){
45786             this.view = new Roo.grid.GridView(this.viewConfig);
45787         }
45788         return this.view;
45789     },
45790     /**
45791      * Called to get grid's drag proxy text, by default returns this.ddText.
45792      * @return {String}
45793      */
45794     getDragDropText : function(){
45795         var count = this.selModel.getCount();
45796         return String.format(this.ddText, count, count == 1 ? '' : 's');
45797     }
45798 });
45799 /**
45800  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45801  * %0 is replaced with the number of selected rows.
45802  * @type String
45803  */
45804 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45805  * Based on:
45806  * Ext JS Library 1.1.1
45807  * Copyright(c) 2006-2007, Ext JS, LLC.
45808  *
45809  * Originally Released Under LGPL - original licence link has changed is not relivant.
45810  *
45811  * Fork - LGPL
45812  * <script type="text/javascript">
45813  */
45814  
45815 Roo.grid.AbstractGridView = function(){
45816         this.grid = null;
45817         
45818         this.events = {
45819             "beforerowremoved" : true,
45820             "beforerowsinserted" : true,
45821             "beforerefresh" : true,
45822             "rowremoved" : true,
45823             "rowsinserted" : true,
45824             "rowupdated" : true,
45825             "refresh" : true
45826         };
45827     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45828 };
45829
45830 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45831     rowClass : "x-grid-row",
45832     cellClass : "x-grid-cell",
45833     tdClass : "x-grid-td",
45834     hdClass : "x-grid-hd",
45835     splitClass : "x-grid-hd-split",
45836     
45837         init: function(grid){
45838         this.grid = grid;
45839                 var cid = this.grid.getGridEl().id;
45840         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45841         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45842         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45843         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45844         },
45845         
45846         getColumnRenderers : function(){
45847         var renderers = [];
45848         var cm = this.grid.colModel;
45849         var colCount = cm.getColumnCount();
45850         for(var i = 0; i < colCount; i++){
45851             renderers[i] = cm.getRenderer(i);
45852         }
45853         return renderers;
45854     },
45855     
45856     getColumnIds : function(){
45857         var ids = [];
45858         var cm = this.grid.colModel;
45859         var colCount = cm.getColumnCount();
45860         for(var i = 0; i < colCount; i++){
45861             ids[i] = cm.getColumnId(i);
45862         }
45863         return ids;
45864     },
45865     
45866     getDataIndexes : function(){
45867         if(!this.indexMap){
45868             this.indexMap = this.buildIndexMap();
45869         }
45870         return this.indexMap.colToData;
45871     },
45872     
45873     getColumnIndexByDataIndex : function(dataIndex){
45874         if(!this.indexMap){
45875             this.indexMap = this.buildIndexMap();
45876         }
45877         return this.indexMap.dataToCol[dataIndex];
45878     },
45879     
45880     /**
45881      * Set a css style for a column dynamically. 
45882      * @param {Number} colIndex The index of the column
45883      * @param {String} name The css property name
45884      * @param {String} value The css value
45885      */
45886     setCSSStyle : function(colIndex, name, value){
45887         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45888         Roo.util.CSS.updateRule(selector, name, value);
45889     },
45890     
45891     generateRules : function(cm){
45892         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45893         Roo.util.CSS.removeStyleSheet(rulesId);
45894         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45895             var cid = cm.getColumnId(i);
45896             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45897                          this.tdSelector, cid, " {\n}\n",
45898                          this.hdSelector, cid, " {\n}\n",
45899                          this.splitSelector, cid, " {\n}\n");
45900         }
45901         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45902     }
45903 });/*
45904  * Based on:
45905  * Ext JS Library 1.1.1
45906  * Copyright(c) 2006-2007, Ext JS, LLC.
45907  *
45908  * Originally Released Under LGPL - original licence link has changed is not relivant.
45909  *
45910  * Fork - LGPL
45911  * <script type="text/javascript">
45912  */
45913
45914 // private
45915 // This is a support class used internally by the Grid components
45916 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45917     this.grid = grid;
45918     this.view = grid.getView();
45919     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45920     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45921     if(hd2){
45922         this.setHandleElId(Roo.id(hd));
45923         this.setOuterHandleElId(Roo.id(hd2));
45924     }
45925     this.scroll = false;
45926 };
45927 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45928     maxDragWidth: 120,
45929     getDragData : function(e){
45930         var t = Roo.lib.Event.getTarget(e);
45931         var h = this.view.findHeaderCell(t);
45932         if(h){
45933             return {ddel: h.firstChild, header:h};
45934         }
45935         return false;
45936     },
45937
45938     onInitDrag : function(e){
45939         this.view.headersDisabled = true;
45940         var clone = this.dragData.ddel.cloneNode(true);
45941         clone.id = Roo.id();
45942         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45943         this.proxy.update(clone);
45944         return true;
45945     },
45946
45947     afterValidDrop : function(){
45948         var v = this.view;
45949         setTimeout(function(){
45950             v.headersDisabled = false;
45951         }, 50);
45952     },
45953
45954     afterInvalidDrop : function(){
45955         var v = this.view;
45956         setTimeout(function(){
45957             v.headersDisabled = false;
45958         }, 50);
45959     }
45960 });
45961 /*
45962  * Based on:
45963  * Ext JS Library 1.1.1
45964  * Copyright(c) 2006-2007, Ext JS, LLC.
45965  *
45966  * Originally Released Under LGPL - original licence link has changed is not relivant.
45967  *
45968  * Fork - LGPL
45969  * <script type="text/javascript">
45970  */
45971 // private
45972 // This is a support class used internally by the Grid components
45973 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45974     this.grid = grid;
45975     this.view = grid.getView();
45976     // split the proxies so they don't interfere with mouse events
45977     this.proxyTop = Roo.DomHelper.append(document.body, {
45978         cls:"col-move-top", html:"&#160;"
45979     }, true);
45980     this.proxyBottom = Roo.DomHelper.append(document.body, {
45981         cls:"col-move-bottom", html:"&#160;"
45982     }, true);
45983     this.proxyTop.hide = this.proxyBottom.hide = function(){
45984         this.setLeftTop(-100,-100);
45985         this.setStyle("visibility", "hidden");
45986     };
45987     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45988     // temporarily disabled
45989     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
45990     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
45991 };
45992 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
45993     proxyOffsets : [-4, -9],
45994     fly: Roo.Element.fly,
45995
45996     getTargetFromEvent : function(e){
45997         var t = Roo.lib.Event.getTarget(e);
45998         var cindex = this.view.findCellIndex(t);
45999         if(cindex !== false){
46000             return this.view.getHeaderCell(cindex);
46001         }
46002     },
46003
46004     nextVisible : function(h){
46005         var v = this.view, cm = this.grid.colModel;
46006         h = h.nextSibling;
46007         while(h){
46008             if(!cm.isHidden(v.getCellIndex(h))){
46009                 return h;
46010             }
46011             h = h.nextSibling;
46012         }
46013         return null;
46014     },
46015
46016     prevVisible : function(h){
46017         var v = this.view, cm = this.grid.colModel;
46018         h = h.prevSibling;
46019         while(h){
46020             if(!cm.isHidden(v.getCellIndex(h))){
46021                 return h;
46022             }
46023             h = h.prevSibling;
46024         }
46025         return null;
46026     },
46027
46028     positionIndicator : function(h, n, e){
46029         var x = Roo.lib.Event.getPageX(e);
46030         var r = Roo.lib.Dom.getRegion(n.firstChild);
46031         var px, pt, py = r.top + this.proxyOffsets[1];
46032         if((r.right - x) <= (r.right-r.left)/2){
46033             px = r.right+this.view.borderWidth;
46034             pt = "after";
46035         }else{
46036             px = r.left;
46037             pt = "before";
46038         }
46039         var oldIndex = this.view.getCellIndex(h);
46040         var newIndex = this.view.getCellIndex(n);
46041
46042         if(this.grid.colModel.isFixed(newIndex)){
46043             return false;
46044         }
46045
46046         var locked = this.grid.colModel.isLocked(newIndex);
46047
46048         if(pt == "after"){
46049             newIndex++;
46050         }
46051         if(oldIndex < newIndex){
46052             newIndex--;
46053         }
46054         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46055             return false;
46056         }
46057         px +=  this.proxyOffsets[0];
46058         this.proxyTop.setLeftTop(px, py);
46059         this.proxyTop.show();
46060         if(!this.bottomOffset){
46061             this.bottomOffset = this.view.mainHd.getHeight();
46062         }
46063         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46064         this.proxyBottom.show();
46065         return pt;
46066     },
46067
46068     onNodeEnter : function(n, dd, e, data){
46069         if(data.header != n){
46070             this.positionIndicator(data.header, n, e);
46071         }
46072     },
46073
46074     onNodeOver : function(n, dd, e, data){
46075         var result = false;
46076         if(data.header != n){
46077             result = this.positionIndicator(data.header, n, e);
46078         }
46079         if(!result){
46080             this.proxyTop.hide();
46081             this.proxyBottom.hide();
46082         }
46083         return result ? this.dropAllowed : this.dropNotAllowed;
46084     },
46085
46086     onNodeOut : function(n, dd, e, data){
46087         this.proxyTop.hide();
46088         this.proxyBottom.hide();
46089     },
46090
46091     onNodeDrop : function(n, dd, e, data){
46092         var h = data.header;
46093         if(h != n){
46094             var cm = this.grid.colModel;
46095             var x = Roo.lib.Event.getPageX(e);
46096             var r = Roo.lib.Dom.getRegion(n.firstChild);
46097             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46098             var oldIndex = this.view.getCellIndex(h);
46099             var newIndex = this.view.getCellIndex(n);
46100             var locked = cm.isLocked(newIndex);
46101             if(pt == "after"){
46102                 newIndex++;
46103             }
46104             if(oldIndex < newIndex){
46105                 newIndex--;
46106             }
46107             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46108                 return false;
46109             }
46110             cm.setLocked(oldIndex, locked, true);
46111             cm.moveColumn(oldIndex, newIndex);
46112             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46113             return true;
46114         }
46115         return false;
46116     }
46117 });
46118 /*
46119  * Based on:
46120  * Ext JS Library 1.1.1
46121  * Copyright(c) 2006-2007, Ext JS, LLC.
46122  *
46123  * Originally Released Under LGPL - original licence link has changed is not relivant.
46124  *
46125  * Fork - LGPL
46126  * <script type="text/javascript">
46127  */
46128   
46129 /**
46130  * @class Roo.grid.GridView
46131  * @extends Roo.util.Observable
46132  *
46133  * @constructor
46134  * @param {Object} config
46135  */
46136 Roo.grid.GridView = function(config){
46137     Roo.grid.GridView.superclass.constructor.call(this);
46138     this.el = null;
46139
46140     Roo.apply(this, config);
46141 };
46142
46143 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46144
46145     /**
46146      * Override this function to apply custom css classes to rows during rendering
46147      * @param {Record} record The record
46148      * @param {Number} index
46149      * @method getRowClass
46150      */
46151     rowClass : "x-grid-row",
46152
46153     cellClass : "x-grid-col",
46154
46155     tdClass : "x-grid-td",
46156
46157     hdClass : "x-grid-hd",
46158
46159     splitClass : "x-grid-split",
46160
46161     sortClasses : ["sort-asc", "sort-desc"],
46162
46163     enableMoveAnim : false,
46164
46165     hlColor: "C3DAF9",
46166
46167     dh : Roo.DomHelper,
46168
46169     fly : Roo.Element.fly,
46170
46171     css : Roo.util.CSS,
46172
46173     borderWidth: 1,
46174
46175     splitOffset: 3,
46176
46177     scrollIncrement : 22,
46178
46179     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46180
46181     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46182
46183     bind : function(ds, cm){
46184         if(this.ds){
46185             this.ds.un("load", this.onLoad, this);
46186             this.ds.un("datachanged", this.onDataChange, this);
46187             this.ds.un("add", this.onAdd, this);
46188             this.ds.un("remove", this.onRemove, this);
46189             this.ds.un("update", this.onUpdate, this);
46190             this.ds.un("clear", this.onClear, this);
46191         }
46192         if(ds){
46193             ds.on("load", this.onLoad, this);
46194             ds.on("datachanged", this.onDataChange, this);
46195             ds.on("add", this.onAdd, this);
46196             ds.on("remove", this.onRemove, this);
46197             ds.on("update", this.onUpdate, this);
46198             ds.on("clear", this.onClear, this);
46199         }
46200         this.ds = ds;
46201
46202         if(this.cm){
46203             this.cm.un("widthchange", this.onColWidthChange, this);
46204             this.cm.un("headerchange", this.onHeaderChange, this);
46205             this.cm.un("hiddenchange", this.onHiddenChange, this);
46206             this.cm.un("columnmoved", this.onColumnMove, this);
46207             this.cm.un("columnlockchange", this.onColumnLock, this);
46208         }
46209         if(cm){
46210             this.generateRules(cm);
46211             cm.on("widthchange", this.onColWidthChange, this);
46212             cm.on("headerchange", this.onHeaderChange, this);
46213             cm.on("hiddenchange", this.onHiddenChange, this);
46214             cm.on("columnmoved", this.onColumnMove, this);
46215             cm.on("columnlockchange", this.onColumnLock, this);
46216         }
46217         this.cm = cm;
46218     },
46219
46220     init: function(grid){
46221                 Roo.grid.GridView.superclass.init.call(this, grid);
46222
46223                 this.bind(grid.dataSource, grid.colModel);
46224
46225             grid.on("headerclick", this.handleHeaderClick, this);
46226
46227         if(grid.trackMouseOver){
46228             grid.on("mouseover", this.onRowOver, this);
46229                 grid.on("mouseout", this.onRowOut, this);
46230             }
46231             grid.cancelTextSelection = function(){};
46232                 this.gridId = grid.id;
46233
46234                 var tpls = this.templates || {};
46235
46236                 if(!tpls.master){
46237                     tpls.master = new Roo.Template(
46238                        '<div class="x-grid" hidefocus="true">',
46239                           '<div class="x-grid-topbar"></div>',
46240                           '<div class="x-grid-scroller"><div></div></div>',
46241                           '<div class="x-grid-locked">',
46242                               '<div class="x-grid-header">{lockedHeader}</div>',
46243                               '<div class="x-grid-body">{lockedBody}</div>',
46244                           "</div>",
46245                           '<div class="x-grid-viewport">',
46246                               '<div class="x-grid-header">{header}</div>',
46247                               '<div class="x-grid-body">{body}</div>',
46248                           "</div>",
46249                           '<div class="x-grid-bottombar"></div>',
46250                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46251                           '<div class="x-grid-resize-proxy">&#160;</div>',
46252                        "</div>"
46253                     );
46254                     tpls.master.disableformats = true;
46255                 }
46256
46257                 if(!tpls.header){
46258                     tpls.header = new Roo.Template(
46259                        '<table border="0" cellspacing="0" cellpadding="0">',
46260                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46261                        "</table>{splits}"
46262                     );
46263                     tpls.header.disableformats = true;
46264                 }
46265                 tpls.header.compile();
46266
46267                 if(!tpls.hcell){
46268                     tpls.hcell = new Roo.Template(
46269                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46270                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46271                         "</div></td>"
46272                      );
46273                      tpls.hcell.disableFormats = true;
46274                 }
46275                 tpls.hcell.compile();
46276
46277                 if(!tpls.hsplit){
46278                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46279                     tpls.hsplit.disableFormats = true;
46280                 }
46281                 tpls.hsplit.compile();
46282
46283                 if(!tpls.body){
46284                     tpls.body = new Roo.Template(
46285                        '<table border="0" cellspacing="0" cellpadding="0">',
46286                        "<tbody>{rows}</tbody>",
46287                        "</table>"
46288                     );
46289                     tpls.body.disableFormats = true;
46290                 }
46291                 tpls.body.compile();
46292
46293                 if(!tpls.row){
46294                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46295                     tpls.row.disableFormats = true;
46296                 }
46297                 tpls.row.compile();
46298
46299                 if(!tpls.cell){
46300                     tpls.cell = new Roo.Template(
46301                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46302                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46303                         "</td>"
46304                     );
46305             tpls.cell.disableFormats = true;
46306         }
46307                 tpls.cell.compile();
46308
46309                 this.templates = tpls;
46310         },
46311
46312         // remap these for backwards compat
46313     onColWidthChange : function(){
46314         this.updateColumns.apply(this, arguments);
46315     },
46316     onHeaderChange : function(){
46317         this.updateHeaders.apply(this, arguments);
46318     }, 
46319     onHiddenChange : function(){
46320         this.handleHiddenChange.apply(this, arguments);
46321     },
46322     onColumnMove : function(){
46323         this.handleColumnMove.apply(this, arguments);
46324     },
46325     onColumnLock : function(){
46326         this.handleLockChange.apply(this, arguments);
46327     },
46328
46329     onDataChange : function(){
46330         this.refresh();
46331         this.updateHeaderSortState();
46332     },
46333
46334         onClear : function(){
46335         this.refresh();
46336     },
46337
46338         onUpdate : function(ds, record){
46339         this.refreshRow(record);
46340     },
46341
46342     refreshRow : function(record){
46343         var ds = this.ds, index;
46344         if(typeof record == 'number'){
46345             index = record;
46346             record = ds.getAt(index);
46347         }else{
46348             index = ds.indexOf(record);
46349         }
46350         this.insertRows(ds, index, index, true);
46351         this.onRemove(ds, record, index+1, true);
46352         this.syncRowHeights(index, index);
46353         this.layout();
46354         this.fireEvent("rowupdated", this, index, record);
46355     },
46356
46357     onAdd : function(ds, records, index){
46358         this.insertRows(ds, index, index + (records.length-1));
46359     },
46360
46361     onRemove : function(ds, record, index, isUpdate){
46362         if(isUpdate !== true){
46363             this.fireEvent("beforerowremoved", this, index, record);
46364         }
46365         var bt = this.getBodyTable(), lt = this.getLockedTable();
46366         if(bt.rows[index]){
46367             bt.firstChild.removeChild(bt.rows[index]);
46368         }
46369         if(lt.rows[index]){
46370             lt.firstChild.removeChild(lt.rows[index]);
46371         }
46372         if(isUpdate !== true){
46373             this.stripeRows(index);
46374             this.syncRowHeights(index, index);
46375             this.layout();
46376             this.fireEvent("rowremoved", this, index, record);
46377         }
46378     },
46379
46380     onLoad : function(){
46381         this.scrollToTop();
46382     },
46383
46384     /**
46385      * Scrolls the grid to the top
46386      */
46387     scrollToTop : function(){
46388         if(this.scroller){
46389             this.scroller.dom.scrollTop = 0;
46390             this.syncScroll();
46391         }
46392     },
46393
46394     /**
46395      * Gets a panel in the header of the grid that can be used for toolbars etc.
46396      * After modifying the contents of this panel a call to grid.autoSize() may be
46397      * required to register any changes in size.
46398      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46399      * @return Roo.Element
46400      */
46401     getHeaderPanel : function(doShow){
46402         if(doShow){
46403             this.headerPanel.show();
46404         }
46405         return this.headerPanel;
46406         },
46407
46408         /**
46409      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46410      * After modifying the contents of this panel a call to grid.autoSize() may be
46411      * required to register any changes in size.
46412      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46413      * @return Roo.Element
46414      */
46415     getFooterPanel : function(doShow){
46416         if(doShow){
46417             this.footerPanel.show();
46418         }
46419         return this.footerPanel;
46420         },
46421
46422         initElements : function(){
46423             var E = Roo.Element;
46424             var el = this.grid.getGridEl().dom.firstChild;
46425             var cs = el.childNodes;
46426
46427             this.el = new E(el);
46428             this.headerPanel = new E(el.firstChild);
46429             this.headerPanel.enableDisplayMode("block");
46430
46431         this.scroller = new E(cs[1]);
46432             this.scrollSizer = new E(this.scroller.dom.firstChild);
46433
46434             this.lockedWrap = new E(cs[2]);
46435             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46436             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46437
46438             this.mainWrap = new E(cs[3]);
46439             this.mainHd = new E(this.mainWrap.dom.firstChild);
46440             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46441
46442             this.footerPanel = new E(cs[4]);
46443             this.footerPanel.enableDisplayMode("block");
46444
46445         this.focusEl = new E(cs[5]);
46446         this.focusEl.swallowEvent("click", true);
46447         this.resizeProxy = new E(cs[6]);
46448
46449             this.headerSelector = String.format(
46450                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46451                this.lockedHd.id, this.mainHd.id
46452             );
46453
46454             this.splitterSelector = String.format(
46455                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46456                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46457             );
46458     },
46459     idToCssName : function(s)
46460     {
46461         return s.replace(/[^a-z0-9]+/ig, '-');
46462     },
46463
46464         getHeaderCell : function(index){
46465             return Roo.DomQuery.select(this.headerSelector)[index];
46466         },
46467
46468         getHeaderCellMeasure : function(index){
46469             return this.getHeaderCell(index).firstChild;
46470         },
46471
46472         getHeaderCellText : function(index){
46473             return this.getHeaderCell(index).firstChild.firstChild;
46474         },
46475
46476         getLockedTable : function(){
46477             return this.lockedBody.dom.firstChild;
46478         },
46479
46480         getBodyTable : function(){
46481             return this.mainBody.dom.firstChild;
46482         },
46483
46484         getLockedRow : function(index){
46485             return this.getLockedTable().rows[index];
46486         },
46487
46488         getRow : function(index){
46489             return this.getBodyTable().rows[index];
46490         },
46491
46492         getRowComposite : function(index){
46493             if(!this.rowEl){
46494                 this.rowEl = new Roo.CompositeElementLite();
46495             }
46496         var els = [], lrow, mrow;
46497         if(lrow = this.getLockedRow(index)){
46498             els.push(lrow);
46499         }
46500         if(mrow = this.getRow(index)){
46501             els.push(mrow);
46502         }
46503         this.rowEl.elements = els;
46504             return this.rowEl;
46505         },
46506
46507         getCell : function(rowIndex, colIndex){
46508             var locked = this.cm.getLockedCount();
46509             var source;
46510             if(colIndex < locked){
46511                 source = this.lockedBody.dom.firstChild;
46512             }else{
46513                 source = this.mainBody.dom.firstChild;
46514                 colIndex -= locked;
46515             }
46516         return source.rows[rowIndex].childNodes[colIndex];
46517         },
46518
46519         getCellText : function(rowIndex, colIndex){
46520             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46521         },
46522
46523         getCellBox : function(cell){
46524             var b = this.fly(cell).getBox();
46525         if(Roo.isOpera){ // opera fails to report the Y
46526             b.y = cell.offsetTop + this.mainBody.getY();
46527         }
46528         return b;
46529     },
46530
46531     getCellIndex : function(cell){
46532         var id = String(cell.className).match(this.cellRE);
46533         if(id){
46534             return parseInt(id[1], 10);
46535         }
46536         return 0;
46537     },
46538
46539     findHeaderIndex : function(n){
46540         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46541         return r ? this.getCellIndex(r) : false;
46542     },
46543
46544     findHeaderCell : function(n){
46545         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46546         return r ? r : false;
46547     },
46548
46549     findRowIndex : function(n){
46550         if(!n){
46551             return false;
46552         }
46553         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46554         return r ? r.rowIndex : false;
46555     },
46556
46557     findCellIndex : function(node){
46558         var stop = this.el.dom;
46559         while(node && node != stop){
46560             if(this.findRE.test(node.className)){
46561                 return this.getCellIndex(node);
46562             }
46563             node = node.parentNode;
46564         }
46565         return false;
46566     },
46567
46568     getColumnId : function(index){
46569             return this.cm.getColumnId(index);
46570         },
46571
46572         getSplitters : function(){
46573             if(this.splitterSelector){
46574                return Roo.DomQuery.select(this.splitterSelector);
46575             }else{
46576                 return null;
46577             }
46578         },
46579
46580         getSplitter : function(index){
46581             return this.getSplitters()[index];
46582         },
46583
46584     onRowOver : function(e, t){
46585         var row;
46586         if((row = this.findRowIndex(t)) !== false){
46587             this.getRowComposite(row).addClass("x-grid-row-over");
46588         }
46589     },
46590
46591     onRowOut : function(e, t){
46592         var row;
46593         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46594             this.getRowComposite(row).removeClass("x-grid-row-over");
46595         }
46596     },
46597
46598     renderHeaders : function(){
46599             var cm = this.cm;
46600         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46601         var cb = [], lb = [], sb = [], lsb = [], p = {};
46602         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46603             p.cellId = "x-grid-hd-0-" + i;
46604             p.splitId = "x-grid-csplit-0-" + i;
46605             p.id = cm.getColumnId(i);
46606             p.title = cm.getColumnTooltip(i) || "";
46607             p.value = cm.getColumnHeader(i) || "";
46608             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46609             if(!cm.isLocked(i)){
46610                 cb[cb.length] = ct.apply(p);
46611                 sb[sb.length] = st.apply(p);
46612             }else{
46613                 lb[lb.length] = ct.apply(p);
46614                 lsb[lsb.length] = st.apply(p);
46615             }
46616         }
46617         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46618                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46619         },
46620
46621         updateHeaders : function(){
46622         var html = this.renderHeaders();
46623         this.lockedHd.update(html[0]);
46624         this.mainHd.update(html[1]);
46625     },
46626
46627     /**
46628      * Focuses the specified row.
46629      * @param {Number} row The row index
46630      */
46631     focusRow : function(row){
46632         var x = this.scroller.dom.scrollLeft;
46633         this.focusCell(row, 0, false);
46634         this.scroller.dom.scrollLeft = x;
46635     },
46636
46637     /**
46638      * Focuses the specified cell.
46639      * @param {Number} row The row index
46640      * @param {Number} col The column index
46641      * @param {Boolean} hscroll false to disable horizontal scrolling
46642      */
46643     focusCell : function(row, col, hscroll){
46644         var el = this.ensureVisible(row, col, hscroll);
46645         this.focusEl.alignTo(el, "tl-tl");
46646         if(Roo.isGecko){
46647             this.focusEl.focus();
46648         }else{
46649             this.focusEl.focus.defer(1, this.focusEl);
46650         }
46651     },
46652
46653     /**
46654      * Scrolls the specified cell into view
46655      * @param {Number} row The row index
46656      * @param {Number} col The column index
46657      * @param {Boolean} hscroll false to disable horizontal scrolling
46658      */
46659     ensureVisible : function(row, col, hscroll){
46660         if(typeof row != "number"){
46661             row = row.rowIndex;
46662         }
46663         if(row < 0 && row >= this.ds.getCount()){
46664             return;
46665         }
46666         col = (col !== undefined ? col : 0);
46667         var cm = this.grid.colModel;
46668         while(cm.isHidden(col)){
46669             col++;
46670         }
46671
46672         var el = this.getCell(row, col);
46673         if(!el){
46674             return;
46675         }
46676         var c = this.scroller.dom;
46677
46678         var ctop = parseInt(el.offsetTop, 10);
46679         var cleft = parseInt(el.offsetLeft, 10);
46680         var cbot = ctop + el.offsetHeight;
46681         var cright = cleft + el.offsetWidth;
46682
46683         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46684         var stop = parseInt(c.scrollTop, 10);
46685         var sleft = parseInt(c.scrollLeft, 10);
46686         var sbot = stop + ch;
46687         var sright = sleft + c.clientWidth;
46688
46689         if(ctop < stop){
46690                 c.scrollTop = ctop;
46691         }else if(cbot > sbot){
46692             c.scrollTop = cbot-ch;
46693         }
46694
46695         if(hscroll !== false){
46696             if(cleft < sleft){
46697                 c.scrollLeft = cleft;
46698             }else if(cright > sright){
46699                 c.scrollLeft = cright-c.clientWidth;
46700             }
46701         }
46702         return el;
46703     },
46704
46705     updateColumns : function(){
46706         this.grid.stopEditing();
46707         var cm = this.grid.colModel, colIds = this.getColumnIds();
46708         //var totalWidth = cm.getTotalWidth();
46709         var pos = 0;
46710         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46711             //if(cm.isHidden(i)) continue;
46712             var w = cm.getColumnWidth(i);
46713             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46714             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46715         }
46716         this.updateSplitters();
46717     },
46718
46719     generateRules : function(cm){
46720         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46721         Roo.util.CSS.removeStyleSheet(rulesId);
46722         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46723             var cid = cm.getColumnId(i);
46724             var align = '';
46725             if(cm.config[i].align){
46726                 align = 'text-align:'+cm.config[i].align+';';
46727             }
46728             var hidden = '';
46729             if(cm.isHidden(i)){
46730                 hidden = 'display:none;';
46731             }
46732             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46733             ruleBuf.push(
46734                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46735                     this.hdSelector, cid, " {\n", align, width, "}\n",
46736                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46737                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46738         }
46739         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46740     },
46741
46742     updateSplitters : function(){
46743         var cm = this.cm, s = this.getSplitters();
46744         if(s){ // splitters not created yet
46745             var pos = 0, locked = true;
46746             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46747                 if(cm.isHidden(i)) continue;
46748                 var w = cm.getColumnWidth(i);
46749                 if(!cm.isLocked(i) && locked){
46750                     pos = 0;
46751                     locked = false;
46752                 }
46753                 pos += w;
46754                 s[i].style.left = (pos-this.splitOffset) + "px";
46755             }
46756         }
46757     },
46758
46759     handleHiddenChange : function(colModel, colIndex, hidden){
46760         if(hidden){
46761             this.hideColumn(colIndex);
46762         }else{
46763             this.unhideColumn(colIndex);
46764         }
46765     },
46766
46767     hideColumn : function(colIndex){
46768         var cid = this.getColumnId(colIndex);
46769         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46770         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46771         if(Roo.isSafari){
46772             this.updateHeaders();
46773         }
46774         this.updateSplitters();
46775         this.layout();
46776     },
46777
46778     unhideColumn : function(colIndex){
46779         var cid = this.getColumnId(colIndex);
46780         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46781         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46782
46783         if(Roo.isSafari){
46784             this.updateHeaders();
46785         }
46786         this.updateSplitters();
46787         this.layout();
46788     },
46789
46790     insertRows : function(dm, firstRow, lastRow, isUpdate){
46791         if(firstRow == 0 && lastRow == dm.getCount()-1){
46792             this.refresh();
46793         }else{
46794             if(!isUpdate){
46795                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46796             }
46797             var s = this.getScrollState();
46798             var markup = this.renderRows(firstRow, lastRow);
46799             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46800             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46801             this.restoreScroll(s);
46802             if(!isUpdate){
46803                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46804                 this.syncRowHeights(firstRow, lastRow);
46805                 this.stripeRows(firstRow);
46806                 this.layout();
46807             }
46808         }
46809     },
46810
46811     bufferRows : function(markup, target, index){
46812         var before = null, trows = target.rows, tbody = target.tBodies[0];
46813         if(index < trows.length){
46814             before = trows[index];
46815         }
46816         var b = document.createElement("div");
46817         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46818         var rows = b.firstChild.rows;
46819         for(var i = 0, len = rows.length; i < len; i++){
46820             if(before){
46821                 tbody.insertBefore(rows[0], before);
46822             }else{
46823                 tbody.appendChild(rows[0]);
46824             }
46825         }
46826         b.innerHTML = "";
46827         b = null;
46828     },
46829
46830     deleteRows : function(dm, firstRow, lastRow){
46831         if(dm.getRowCount()<1){
46832             this.fireEvent("beforerefresh", this);
46833             this.mainBody.update("");
46834             this.lockedBody.update("");
46835             this.fireEvent("refresh", this);
46836         }else{
46837             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46838             var bt = this.getBodyTable();
46839             var tbody = bt.firstChild;
46840             var rows = bt.rows;
46841             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46842                 tbody.removeChild(rows[firstRow]);
46843             }
46844             this.stripeRows(firstRow);
46845             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46846         }
46847     },
46848
46849     updateRows : function(dataSource, firstRow, lastRow){
46850         var s = this.getScrollState();
46851         this.refresh();
46852         this.restoreScroll(s);
46853     },
46854
46855     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46856         if(!noRefresh){
46857            this.refresh();
46858         }
46859         this.updateHeaderSortState();
46860     },
46861
46862     getScrollState : function(){
46863         var sb = this.scroller.dom;
46864         return {left: sb.scrollLeft, top: sb.scrollTop};
46865     },
46866
46867     stripeRows : function(startRow){
46868         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46869             return;
46870         }
46871         startRow = startRow || 0;
46872         var rows = this.getBodyTable().rows;
46873         var lrows = this.getLockedTable().rows;
46874         var cls = ' x-grid-row-alt ';
46875         for(var i = startRow, len = rows.length; i < len; i++){
46876             var row = rows[i], lrow = lrows[i];
46877             var isAlt = ((i+1) % 2 == 0);
46878             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46879             if(isAlt == hasAlt){
46880                 continue;
46881             }
46882             if(isAlt){
46883                 row.className += " x-grid-row-alt";
46884             }else{
46885                 row.className = row.className.replace("x-grid-row-alt", "");
46886             }
46887             if(lrow){
46888                 lrow.className = row.className;
46889             }
46890         }
46891     },
46892
46893     restoreScroll : function(state){
46894         var sb = this.scroller.dom;
46895         sb.scrollLeft = state.left;
46896         sb.scrollTop = state.top;
46897         this.syncScroll();
46898     },
46899
46900     syncScroll : function(){
46901         var sb = this.scroller.dom;
46902         var sh = this.mainHd.dom;
46903         var bs = this.mainBody.dom;
46904         var lv = this.lockedBody.dom;
46905         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46906         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46907     },
46908
46909     handleScroll : function(e){
46910         this.syncScroll();
46911         var sb = this.scroller.dom;
46912         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46913         e.stopEvent();
46914     },
46915
46916     handleWheel : function(e){
46917         var d = e.getWheelDelta();
46918         this.scroller.dom.scrollTop -= d*22;
46919         // set this here to prevent jumpy scrolling on large tables
46920         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46921         e.stopEvent();
46922     },
46923
46924     renderRows : function(startRow, endRow){
46925         // pull in all the crap needed to render rows
46926         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46927         var colCount = cm.getColumnCount();
46928
46929         if(ds.getCount() < 1){
46930             return ["", ""];
46931         }
46932
46933         // build a map for all the columns
46934         var cs = [];
46935         for(var i = 0; i < colCount; i++){
46936             var name = cm.getDataIndex(i);
46937             cs[i] = {
46938                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46939                 renderer : cm.getRenderer(i),
46940                 id : cm.getColumnId(i),
46941                 locked : cm.isLocked(i)
46942             };
46943         }
46944
46945         startRow = startRow || 0;
46946         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46947
46948         // records to render
46949         var rs = ds.getRange(startRow, endRow);
46950
46951         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46952     },
46953
46954     // As much as I hate to duplicate code, this was branched because FireFox really hates
46955     // [].join("") on strings. The performance difference was substantial enough to
46956     // branch this function
46957     doRender : Roo.isGecko ?
46958             function(cs, rs, ds, startRow, colCount, stripe){
46959                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46960                 // buffers
46961                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46962                 for(var j = 0, len = rs.length; j < len; j++){
46963                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46964                     for(var i = 0; i < colCount; i++){
46965                         c = cs[i];
46966                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46967                         p.id = c.id;
46968                         p.css = p.attr = "";
46969                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46970                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46971                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46972                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46973                         }
46974                         var markup = ct.apply(p);
46975                         if(!c.locked){
46976                             cb+= markup;
46977                         }else{
46978                             lcb+= markup;
46979                         }
46980                     }
46981                     var alt = [];
46982                     if(stripe && ((rowIndex+1) % 2 == 0)){
46983                         alt[0] = "x-grid-row-alt";
46984                     }
46985                     if(r.dirty){
46986                         alt[1] = " x-grid-dirty-row";
46987                     }
46988                     rp.cells = lcb;
46989                     if(this.getRowClass){
46990                         alt[2] = this.getRowClass(r, rowIndex);
46991                     }
46992                     rp.alt = alt.join(" ");
46993                     lbuf+= rt.apply(rp);
46994                     rp.cells = cb;
46995                     buf+=  rt.apply(rp);
46996                 }
46997                 return [lbuf, buf];
46998             } :
46999             function(cs, rs, ds, startRow, colCount, stripe){
47000                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47001                 // buffers
47002                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47003                 for(var j = 0, len = rs.length; j < len; j++){
47004                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47005                     for(var i = 0; i < colCount; i++){
47006                         c = cs[i];
47007                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47008                         p.id = c.id;
47009                         p.css = p.attr = "";
47010                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47011                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47012                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47013                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47014                         }
47015                         var markup = ct.apply(p);
47016                         if(!c.locked){
47017                             cb[cb.length] = markup;
47018                         }else{
47019                             lcb[lcb.length] = markup;
47020                         }
47021                     }
47022                     var alt = [];
47023                     if(stripe && ((rowIndex+1) % 2 == 0)){
47024                         alt[0] = "x-grid-row-alt";
47025                     }
47026                     if(r.dirty){
47027                         alt[1] = " x-grid-dirty-row";
47028                     }
47029                     rp.cells = lcb;
47030                     if(this.getRowClass){
47031                         alt[2] = this.getRowClass(r, rowIndex);
47032                     }
47033                     rp.alt = alt.join(" ");
47034                     rp.cells = lcb.join("");
47035                     lbuf[lbuf.length] = rt.apply(rp);
47036                     rp.cells = cb.join("");
47037                     buf[buf.length] =  rt.apply(rp);
47038                 }
47039                 return [lbuf.join(""), buf.join("")];
47040             },
47041
47042     renderBody : function(){
47043         var markup = this.renderRows();
47044         var bt = this.templates.body;
47045         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47046     },
47047
47048     /**
47049      * Refreshes the grid
47050      * @param {Boolean} headersToo
47051      */
47052     refresh : function(headersToo){
47053         this.fireEvent("beforerefresh", this);
47054         this.grid.stopEditing();
47055         var result = this.renderBody();
47056         this.lockedBody.update(result[0]);
47057         this.mainBody.update(result[1]);
47058         if(headersToo === true){
47059             this.updateHeaders();
47060             this.updateColumns();
47061             this.updateSplitters();
47062             this.updateHeaderSortState();
47063         }
47064         this.syncRowHeights();
47065         this.layout();
47066         this.fireEvent("refresh", this);
47067     },
47068
47069     handleColumnMove : function(cm, oldIndex, newIndex){
47070         this.indexMap = null;
47071         var s = this.getScrollState();
47072         this.refresh(true);
47073         this.restoreScroll(s);
47074         this.afterMove(newIndex);
47075     },
47076
47077     afterMove : function(colIndex){
47078         if(this.enableMoveAnim && Roo.enableFx){
47079             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47080         }
47081     },
47082
47083     updateCell : function(dm, rowIndex, dataIndex){
47084         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47085         if(typeof colIndex == "undefined"){ // not present in grid
47086             return;
47087         }
47088         var cm = this.grid.colModel;
47089         var cell = this.getCell(rowIndex, colIndex);
47090         var cellText = this.getCellText(rowIndex, colIndex);
47091
47092         var p = {
47093             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47094             id : cm.getColumnId(colIndex),
47095             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47096         };
47097         var renderer = cm.getRenderer(colIndex);
47098         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47099         if(typeof val == "undefined" || val === "") val = "&#160;";
47100         cellText.innerHTML = val;
47101         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47102         this.syncRowHeights(rowIndex, rowIndex);
47103     },
47104
47105     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47106         var maxWidth = 0;
47107         if(this.grid.autoSizeHeaders){
47108             var h = this.getHeaderCellMeasure(colIndex);
47109             maxWidth = Math.max(maxWidth, h.scrollWidth);
47110         }
47111         var tb, index;
47112         if(this.cm.isLocked(colIndex)){
47113             tb = this.getLockedTable();
47114             index = colIndex;
47115         }else{
47116             tb = this.getBodyTable();
47117             index = colIndex - this.cm.getLockedCount();
47118         }
47119         if(tb && tb.rows){
47120             var rows = tb.rows;
47121             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47122             for(var i = 0; i < stopIndex; i++){
47123                 var cell = rows[i].childNodes[index].firstChild;
47124                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47125             }
47126         }
47127         return maxWidth + /*margin for error in IE*/ 5;
47128     },
47129     /**
47130      * Autofit a column to its content.
47131      * @param {Number} colIndex
47132      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47133      */
47134      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47135          if(this.cm.isHidden(colIndex)){
47136              return; // can't calc a hidden column
47137          }
47138         if(forceMinSize){
47139             var cid = this.cm.getColumnId(colIndex);
47140             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47141            if(this.grid.autoSizeHeaders){
47142                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47143            }
47144         }
47145         var newWidth = this.calcColumnWidth(colIndex);
47146         this.cm.setColumnWidth(colIndex,
47147             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47148         if(!suppressEvent){
47149             this.grid.fireEvent("columnresize", colIndex, newWidth);
47150         }
47151     },
47152
47153     /**
47154      * Autofits all columns to their content and then expands to fit any extra space in the grid
47155      */
47156      autoSizeColumns : function(){
47157         var cm = this.grid.colModel;
47158         var colCount = cm.getColumnCount();
47159         for(var i = 0; i < colCount; i++){
47160             this.autoSizeColumn(i, true, true);
47161         }
47162         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47163             this.fitColumns();
47164         }else{
47165             this.updateColumns();
47166             this.layout();
47167         }
47168     },
47169
47170     /**
47171      * Autofits all columns to the grid's width proportionate with their current size
47172      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47173      */
47174     fitColumns : function(reserveScrollSpace){
47175         var cm = this.grid.colModel;
47176         var colCount = cm.getColumnCount();
47177         var cols = [];
47178         var width = 0;
47179         var i, w;
47180         for (i = 0; i < colCount; i++){
47181             if(!cm.isHidden(i) && !cm.isFixed(i)){
47182                 w = cm.getColumnWidth(i);
47183                 cols.push(i);
47184                 cols.push(w);
47185                 width += w;
47186             }
47187         }
47188         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47189         if(reserveScrollSpace){
47190             avail -= 17;
47191         }
47192         var frac = (avail - cm.getTotalWidth())/width;
47193         while (cols.length){
47194             w = cols.pop();
47195             i = cols.pop();
47196             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47197         }
47198         this.updateColumns();
47199         this.layout();
47200     },
47201
47202     onRowSelect : function(rowIndex){
47203         var row = this.getRowComposite(rowIndex);
47204         row.addClass("x-grid-row-selected");
47205     },
47206
47207     onRowDeselect : function(rowIndex){
47208         var row = this.getRowComposite(rowIndex);
47209         row.removeClass("x-grid-row-selected");
47210     },
47211
47212     onCellSelect : function(row, col){
47213         var cell = this.getCell(row, col);
47214         if(cell){
47215             Roo.fly(cell).addClass("x-grid-cell-selected");
47216         }
47217     },
47218
47219     onCellDeselect : function(row, col){
47220         var cell = this.getCell(row, col);
47221         if(cell){
47222             Roo.fly(cell).removeClass("x-grid-cell-selected");
47223         }
47224     },
47225
47226     updateHeaderSortState : function(){
47227         var state = this.ds.getSortState();
47228         if(!state){
47229             return;
47230         }
47231         this.sortState = state;
47232         var sortColumn = this.cm.findColumnIndex(state.field);
47233         if(sortColumn != -1){
47234             var sortDir = state.direction;
47235             var sc = this.sortClasses;
47236             var hds = this.el.select(this.headerSelector).removeClass(sc);
47237             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47238         }
47239     },
47240
47241     handleHeaderClick : function(g, index){
47242         if(this.headersDisabled){
47243             return;
47244         }
47245         var dm = g.dataSource, cm = g.colModel;
47246             if(!cm.isSortable(index)){
47247             return;
47248         }
47249             g.stopEditing();
47250         dm.sort(cm.getDataIndex(index));
47251     },
47252
47253
47254     destroy : function(){
47255         if(this.colMenu){
47256             this.colMenu.removeAll();
47257             Roo.menu.MenuMgr.unregister(this.colMenu);
47258             this.colMenu.getEl().remove();
47259             delete this.colMenu;
47260         }
47261         if(this.hmenu){
47262             this.hmenu.removeAll();
47263             Roo.menu.MenuMgr.unregister(this.hmenu);
47264             this.hmenu.getEl().remove();
47265             delete this.hmenu;
47266         }
47267         if(this.grid.enableColumnMove){
47268             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47269             if(dds){
47270                 for(var dd in dds){
47271                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47272                         var elid = dds[dd].dragElId;
47273                         dds[dd].unreg();
47274                         Roo.get(elid).remove();
47275                     } else if(dds[dd].config.isTarget){
47276                         dds[dd].proxyTop.remove();
47277                         dds[dd].proxyBottom.remove();
47278                         dds[dd].unreg();
47279                     }
47280                     if(Roo.dd.DDM.locationCache[dd]){
47281                         delete Roo.dd.DDM.locationCache[dd];
47282                     }
47283                 }
47284                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47285             }
47286         }
47287         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47288         this.bind(null, null);
47289         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47290     },
47291
47292     handleLockChange : function(){
47293         this.refresh(true);
47294     },
47295
47296     onDenyColumnLock : function(){
47297
47298     },
47299
47300     onDenyColumnHide : function(){
47301
47302     },
47303
47304     handleHdMenuClick : function(item){
47305         var index = this.hdCtxIndex;
47306         var cm = this.cm, ds = this.ds;
47307         switch(item.id){
47308             case "asc":
47309                 ds.sort(cm.getDataIndex(index), "ASC");
47310                 break;
47311             case "desc":
47312                 ds.sort(cm.getDataIndex(index), "DESC");
47313                 break;
47314             case "lock":
47315                 var lc = cm.getLockedCount();
47316                 if(cm.getColumnCount(true) <= lc+1){
47317                     this.onDenyColumnLock();
47318                     return;
47319                 }
47320                 if(lc != index){
47321                     cm.setLocked(index, true, true);
47322                     cm.moveColumn(index, lc);
47323                     this.grid.fireEvent("columnmove", index, lc);
47324                 }else{
47325                     cm.setLocked(index, true);
47326                 }
47327             break;
47328             case "unlock":
47329                 var lc = cm.getLockedCount();
47330                 if((lc-1) != index){
47331                     cm.setLocked(index, false, true);
47332                     cm.moveColumn(index, lc-1);
47333                     this.grid.fireEvent("columnmove", index, lc-1);
47334                 }else{
47335                     cm.setLocked(index, false);
47336                 }
47337             break;
47338             default:
47339                 index = cm.getIndexById(item.id.substr(4));
47340                 if(index != -1){
47341                     if(item.checked && cm.getColumnCount(true) <= 1){
47342                         this.onDenyColumnHide();
47343                         return false;
47344                     }
47345                     cm.setHidden(index, item.checked);
47346                 }
47347         }
47348         return true;
47349     },
47350
47351     beforeColMenuShow : function(){
47352         var cm = this.cm,  colCount = cm.getColumnCount();
47353         this.colMenu.removeAll();
47354         for(var i = 0; i < colCount; i++){
47355             this.colMenu.add(new Roo.menu.CheckItem({
47356                 id: "col-"+cm.getColumnId(i),
47357                 text: cm.getColumnHeader(i),
47358                 checked: !cm.isHidden(i),
47359                 hideOnClick:false
47360             }));
47361         }
47362     },
47363
47364     handleHdCtx : function(g, index, e){
47365         e.stopEvent();
47366         var hd = this.getHeaderCell(index);
47367         this.hdCtxIndex = index;
47368         var ms = this.hmenu.items, cm = this.cm;
47369         ms.get("asc").setDisabled(!cm.isSortable(index));
47370         ms.get("desc").setDisabled(!cm.isSortable(index));
47371         if(this.grid.enableColLock !== false){
47372             ms.get("lock").setDisabled(cm.isLocked(index));
47373             ms.get("unlock").setDisabled(!cm.isLocked(index));
47374         }
47375         this.hmenu.show(hd, "tl-bl");
47376     },
47377
47378     handleHdOver : function(e){
47379         var hd = this.findHeaderCell(e.getTarget());
47380         if(hd && !this.headersDisabled){
47381             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47382                this.fly(hd).addClass("x-grid-hd-over");
47383             }
47384         }
47385     },
47386
47387     handleHdOut : function(e){
47388         var hd = this.findHeaderCell(e.getTarget());
47389         if(hd){
47390             this.fly(hd).removeClass("x-grid-hd-over");
47391         }
47392     },
47393
47394     handleSplitDblClick : function(e, t){
47395         var i = this.getCellIndex(t);
47396         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47397             this.autoSizeColumn(i, true);
47398             this.layout();
47399         }
47400     },
47401
47402     render : function(){
47403
47404         var cm = this.cm;
47405         var colCount = cm.getColumnCount();
47406
47407         if(this.grid.monitorWindowResize === true){
47408             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47409         }
47410         var header = this.renderHeaders();
47411         var body = this.templates.body.apply({rows:""});
47412         var html = this.templates.master.apply({
47413             lockedBody: body,
47414             body: body,
47415             lockedHeader: header[0],
47416             header: header[1]
47417         });
47418
47419         //this.updateColumns();
47420
47421         this.grid.getGridEl().dom.innerHTML = html;
47422
47423         this.initElements();
47424
47425         this.scroller.on("scroll", this.handleScroll, this);
47426         this.lockedBody.on("mousewheel", this.handleWheel, this);
47427         this.mainBody.on("mousewheel", this.handleWheel, this);
47428
47429         this.mainHd.on("mouseover", this.handleHdOver, this);
47430         this.mainHd.on("mouseout", this.handleHdOut, this);
47431         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47432                 {delegate: "."+this.splitClass});
47433
47434         this.lockedHd.on("mouseover", this.handleHdOver, this);
47435         this.lockedHd.on("mouseout", this.handleHdOut, this);
47436         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47437                 {delegate: "."+this.splitClass});
47438
47439         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47440             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47441         }
47442
47443         this.updateSplitters();
47444
47445         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47446             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47447             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47448         }
47449
47450         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47451             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47452             this.hmenu.add(
47453                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47454                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47455             );
47456             if(this.grid.enableColLock !== false){
47457                 this.hmenu.add('-',
47458                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47459                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47460                 );
47461             }
47462             if(this.grid.enableColumnHide !== false){
47463
47464                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47465                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47466                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47467
47468                 this.hmenu.add('-',
47469                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47470                 );
47471             }
47472             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47473
47474             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47475         }
47476
47477         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47478             this.dd = new Roo.grid.GridDragZone(this.grid, {
47479                 ddGroup : this.grid.ddGroup || 'GridDD'
47480             });
47481         }
47482
47483         /*
47484         for(var i = 0; i < colCount; i++){
47485             if(cm.isHidden(i)){
47486                 this.hideColumn(i);
47487             }
47488             if(cm.config[i].align){
47489                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47490                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47491             }
47492         }*/
47493         
47494         this.updateHeaderSortState();
47495
47496         this.beforeInitialResize();
47497         this.layout(true);
47498
47499         // two part rendering gives faster view to the user
47500         this.renderPhase2.defer(1, this);
47501     },
47502
47503     renderPhase2 : function(){
47504         // render the rows now
47505         this.refresh();
47506         if(this.grid.autoSizeColumns){
47507             this.autoSizeColumns();
47508         }
47509     },
47510
47511     beforeInitialResize : function(){
47512
47513     },
47514
47515     onColumnSplitterMoved : function(i, w){
47516         this.userResized = true;
47517         var cm = this.grid.colModel;
47518         cm.setColumnWidth(i, w, true);
47519         var cid = cm.getColumnId(i);
47520         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47521         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47522         this.updateSplitters();
47523         this.layout();
47524         this.grid.fireEvent("columnresize", i, w);
47525     },
47526
47527     syncRowHeights : function(startIndex, endIndex){
47528         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47529             startIndex = startIndex || 0;
47530             var mrows = this.getBodyTable().rows;
47531             var lrows = this.getLockedTable().rows;
47532             var len = mrows.length-1;
47533             endIndex = Math.min(endIndex || len, len);
47534             for(var i = startIndex; i <= endIndex; i++){
47535                 var m = mrows[i], l = lrows[i];
47536                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47537                 m.style.height = l.style.height = h + "px";
47538             }
47539         }
47540     },
47541
47542     layout : function(initialRender, is2ndPass){
47543         var g = this.grid;
47544         var auto = g.autoHeight;
47545         var scrollOffset = 16;
47546         var c = g.getGridEl(), cm = this.cm,
47547                 expandCol = g.autoExpandColumn,
47548                 gv = this;
47549         //c.beginMeasure();
47550
47551         if(!c.dom.offsetWidth){ // display:none?
47552             if(initialRender){
47553                 this.lockedWrap.show();
47554                 this.mainWrap.show();
47555             }
47556             return;
47557         }
47558
47559         var hasLock = this.cm.isLocked(0);
47560
47561         var tbh = this.headerPanel.getHeight();
47562         var bbh = this.footerPanel.getHeight();
47563
47564         if(auto){
47565             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47566             var newHeight = ch + c.getBorderWidth("tb");
47567             if(g.maxHeight){
47568                 newHeight = Math.min(g.maxHeight, newHeight);
47569             }
47570             c.setHeight(newHeight);
47571         }
47572
47573         if(g.autoWidth){
47574             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47575         }
47576
47577         var s = this.scroller;
47578
47579         var csize = c.getSize(true);
47580
47581         this.el.setSize(csize.width, csize.height);
47582
47583         this.headerPanel.setWidth(csize.width);
47584         this.footerPanel.setWidth(csize.width);
47585
47586         var hdHeight = this.mainHd.getHeight();
47587         var vw = csize.width;
47588         var vh = csize.height - (tbh + bbh);
47589
47590         s.setSize(vw, vh);
47591
47592         var bt = this.getBodyTable();
47593         var ltWidth = hasLock ?
47594                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47595
47596         var scrollHeight = bt.offsetHeight;
47597         var scrollWidth = ltWidth + bt.offsetWidth;
47598         var vscroll = false, hscroll = false;
47599
47600         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47601
47602         var lw = this.lockedWrap, mw = this.mainWrap;
47603         var lb = this.lockedBody, mb = this.mainBody;
47604
47605         setTimeout(function(){
47606             var t = s.dom.offsetTop;
47607             var w = s.dom.clientWidth,
47608                 h = s.dom.clientHeight;
47609
47610             lw.setTop(t);
47611             lw.setSize(ltWidth, h);
47612
47613             mw.setLeftTop(ltWidth, t);
47614             mw.setSize(w-ltWidth, h);
47615
47616             lb.setHeight(h-hdHeight);
47617             mb.setHeight(h-hdHeight);
47618
47619             if(is2ndPass !== true && !gv.userResized && expandCol){
47620                 // high speed resize without full column calculation
47621                 
47622                 var ci = cm.getIndexById(expandCol);
47623                 if (ci < 0) {
47624                     ci = cm.findColumnIndex(expandCol);
47625                 }
47626                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47627                 var expandId = cm.getColumnId(ci);
47628                 var  tw = cm.getTotalWidth(false);
47629                 var currentWidth = cm.getColumnWidth(ci);
47630                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47631                 if(currentWidth != cw){
47632                     cm.setColumnWidth(ci, cw, true);
47633                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47634                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47635                     gv.updateSplitters();
47636                     gv.layout(false, true);
47637                 }
47638             }
47639
47640             if(initialRender){
47641                 lw.show();
47642                 mw.show();
47643             }
47644             //c.endMeasure();
47645         }, 10);
47646     },
47647
47648     onWindowResize : function(){
47649         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47650             return;
47651         }
47652         this.layout();
47653     },
47654
47655     appendFooter : function(parentEl){
47656         return null;
47657     },
47658
47659     sortAscText : "Sort Ascending",
47660     sortDescText : "Sort Descending",
47661     lockText : "Lock Column",
47662     unlockText : "Unlock Column",
47663     columnsText : "Columns"
47664 });
47665
47666
47667 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47668     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47669     this.proxy.el.addClass('x-grid3-col-dd');
47670 };
47671
47672 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47673     handleMouseDown : function(e){
47674
47675     },
47676
47677     callHandleMouseDown : function(e){
47678         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47679     }
47680 });
47681 /*
47682  * Based on:
47683  * Ext JS Library 1.1.1
47684  * Copyright(c) 2006-2007, Ext JS, LLC.
47685  *
47686  * Originally Released Under LGPL - original licence link has changed is not relivant.
47687  *
47688  * Fork - LGPL
47689  * <script type="text/javascript">
47690  */
47691  
47692 // private
47693 // This is a support class used internally by the Grid components
47694 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47695     this.grid = grid;
47696     this.view = grid.getView();
47697     this.proxy = this.view.resizeProxy;
47698     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47699         "gridSplitters" + this.grid.getGridEl().id, {
47700         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47701     });
47702     this.setHandleElId(Roo.id(hd));
47703     this.setOuterHandleElId(Roo.id(hd2));
47704     this.scroll = false;
47705 };
47706 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47707     fly: Roo.Element.fly,
47708
47709     b4StartDrag : function(x, y){
47710         this.view.headersDisabled = true;
47711         this.proxy.setHeight(this.view.mainWrap.getHeight());
47712         var w = this.cm.getColumnWidth(this.cellIndex);
47713         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47714         this.resetConstraints();
47715         this.setXConstraint(minw, 1000);
47716         this.setYConstraint(0, 0);
47717         this.minX = x - minw;
47718         this.maxX = x + 1000;
47719         this.startPos = x;
47720         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47721     },
47722
47723
47724     handleMouseDown : function(e){
47725         ev = Roo.EventObject.setEvent(e);
47726         var t = this.fly(ev.getTarget());
47727         if(t.hasClass("x-grid-split")){
47728             this.cellIndex = this.view.getCellIndex(t.dom);
47729             this.split = t.dom;
47730             this.cm = this.grid.colModel;
47731             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47732                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47733             }
47734         }
47735     },
47736
47737     endDrag : function(e){
47738         this.view.headersDisabled = false;
47739         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47740         var diff = endX - this.startPos;
47741         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47742     },
47743
47744     autoOffset : function(){
47745         this.setDelta(0,0);
47746     }
47747 });/*
47748  * Based on:
47749  * Ext JS Library 1.1.1
47750  * Copyright(c) 2006-2007, Ext JS, LLC.
47751  *
47752  * Originally Released Under LGPL - original licence link has changed is not relivant.
47753  *
47754  * Fork - LGPL
47755  * <script type="text/javascript">
47756  */
47757  
47758 // private
47759 // This is a support class used internally by the Grid components
47760 Roo.grid.GridDragZone = function(grid, config){
47761     this.view = grid.getView();
47762     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47763     if(this.view.lockedBody){
47764         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47765         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47766     }
47767     this.scroll = false;
47768     this.grid = grid;
47769     this.ddel = document.createElement('div');
47770     this.ddel.className = 'x-grid-dd-wrap';
47771 };
47772
47773 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47774     ddGroup : "GridDD",
47775
47776     getDragData : function(e){
47777         var t = Roo.lib.Event.getTarget(e);
47778         var rowIndex = this.view.findRowIndex(t);
47779         if(rowIndex !== false){
47780             var sm = this.grid.selModel;
47781             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47782               //  sm.mouseDown(e, t);
47783             //}
47784             if (e.hasModifier()){
47785                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47786             }
47787             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47788         }
47789         return false;
47790     },
47791
47792     onInitDrag : function(e){
47793         var data = this.dragData;
47794         this.ddel.innerHTML = this.grid.getDragDropText();
47795         this.proxy.update(this.ddel);
47796         // fire start drag?
47797     },
47798
47799     afterRepair : function(){
47800         this.dragging = false;
47801     },
47802
47803     getRepairXY : function(e, data){
47804         return false;
47805     },
47806
47807     onEndDrag : function(data, e){
47808         // fire end drag?
47809     },
47810
47811     onValidDrop : function(dd, e, id){
47812         // fire drag drop?
47813         this.hideProxy();
47814     },
47815
47816     beforeInvalidDrop : function(e, id){
47817
47818     }
47819 });/*
47820  * Based on:
47821  * Ext JS Library 1.1.1
47822  * Copyright(c) 2006-2007, Ext JS, LLC.
47823  *
47824  * Originally Released Under LGPL - original licence link has changed is not relivant.
47825  *
47826  * Fork - LGPL
47827  * <script type="text/javascript">
47828  */
47829  
47830
47831 /**
47832  * @class Roo.grid.ColumnModel
47833  * @extends Roo.util.Observable
47834  * This is the default implementation of a ColumnModel used by the Grid. It defines
47835  * the columns in the grid.
47836  * <br>Usage:<br>
47837  <pre><code>
47838  var colModel = new Roo.grid.ColumnModel([
47839         {header: "Ticker", width: 60, sortable: true, locked: true},
47840         {header: "Company Name", width: 150, sortable: true},
47841         {header: "Market Cap.", width: 100, sortable: true},
47842         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47843         {header: "Employees", width: 100, sortable: true, resizable: false}
47844  ]);
47845  </code></pre>
47846  * <p>
47847  
47848  * The config options listed for this class are options which may appear in each
47849  * individual column definition.
47850  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47851  * @constructor
47852  * @param {Object} config An Array of column config objects. See this class's
47853  * config objects for details.
47854 */
47855 Roo.grid.ColumnModel = function(config){
47856         /**
47857      * The config passed into the constructor
47858      */
47859     this.config = config;
47860     this.lookup = {};
47861
47862     // if no id, create one
47863     // if the column does not have a dataIndex mapping,
47864     // map it to the order it is in the config
47865     for(var i = 0, len = config.length; i < len; i++){
47866         var c = config[i];
47867         if(typeof c.dataIndex == "undefined"){
47868             c.dataIndex = i;
47869         }
47870         if(typeof c.renderer == "string"){
47871             c.renderer = Roo.util.Format[c.renderer];
47872         }
47873         if(typeof c.id == "undefined"){
47874             c.id = Roo.id();
47875         }
47876         if(c.editor && c.editor.xtype){
47877             c.editor  = Roo.factory(c.editor, Roo.grid);
47878         }
47879         if(c.editor && c.editor.isFormField){
47880             c.editor = new Roo.grid.GridEditor(c.editor);
47881         }
47882         this.lookup[c.id] = c;
47883     }
47884
47885     /**
47886      * The width of columns which have no width specified (defaults to 100)
47887      * @type Number
47888      */
47889     this.defaultWidth = 100;
47890
47891     /**
47892      * Default sortable of columns which have no sortable specified (defaults to false)
47893      * @type Boolean
47894      */
47895     this.defaultSortable = false;
47896
47897     this.addEvents({
47898         /**
47899              * @event widthchange
47900              * Fires when the width of a column changes.
47901              * @param {ColumnModel} this
47902              * @param {Number} columnIndex The column index
47903              * @param {Number} newWidth The new width
47904              */
47905             "widthchange": true,
47906         /**
47907              * @event headerchange
47908              * Fires when the text of a header changes.
47909              * @param {ColumnModel} this
47910              * @param {Number} columnIndex The column index
47911              * @param {Number} newText The new header text
47912              */
47913             "headerchange": true,
47914         /**
47915              * @event hiddenchange
47916              * Fires when a column is hidden or "unhidden".
47917              * @param {ColumnModel} this
47918              * @param {Number} columnIndex The column index
47919              * @param {Boolean} hidden true if hidden, false otherwise
47920              */
47921             "hiddenchange": true,
47922             /**
47923          * @event columnmoved
47924          * Fires when a column is moved.
47925          * @param {ColumnModel} this
47926          * @param {Number} oldIndex
47927          * @param {Number} newIndex
47928          */
47929         "columnmoved" : true,
47930         /**
47931          * @event columlockchange
47932          * Fires when a column's locked state is changed
47933          * @param {ColumnModel} this
47934          * @param {Number} colIndex
47935          * @param {Boolean} locked true if locked
47936          */
47937         "columnlockchange" : true
47938     });
47939     Roo.grid.ColumnModel.superclass.constructor.call(this);
47940 };
47941 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47942     /**
47943      * @cfg {String} header The header text to display in the Grid view.
47944      */
47945     /**
47946      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47947      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47948      * specified, the column's index is used as an index into the Record's data Array.
47949      */
47950     /**
47951      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47952      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47953      */
47954     /**
47955      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47956      * Defaults to the value of the {@link #defaultSortable} property.
47957      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47958      */
47959     /**
47960      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47961      */
47962     /**
47963      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47964      */
47965     /**
47966      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47967      */
47968     /**
47969      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47970      */
47971     /**
47972      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47973      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47974      * default renderer uses the raw data value.
47975      */
47976        /**
47977      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
47978      */
47979     /**
47980      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
47981      */
47982
47983     /**
47984      * Returns the id of the column at the specified index.
47985      * @param {Number} index The column index
47986      * @return {String} the id
47987      */
47988     getColumnId : function(index){
47989         return this.config[index].id;
47990     },
47991
47992     /**
47993      * Returns the column for a specified id.
47994      * @param {String} id The column id
47995      * @return {Object} the column
47996      */
47997     getColumnById : function(id){
47998         return this.lookup[id];
47999     },
48000
48001     /**
48002      * Returns the index for a specified column id.
48003      * @param {String} id The column id
48004      * @return {Number} the index, or -1 if not found
48005      */
48006     getIndexById : function(id){
48007         for(var i = 0, len = this.config.length; i < len; i++){
48008             if(this.config[i].id == id){
48009                 return i;
48010             }
48011         }
48012         return -1;
48013     },
48014     /**
48015      * Returns the index for a specified column dataIndex.
48016      * @param {String} dataIndex The column dataIndex
48017      * @return {Number} the index, or -1 if not found
48018      */
48019     
48020     findColumnIndex : function(dataIndex){
48021         for(var i = 0, len = this.config.length; i < len; i++){
48022             if(this.config[i].dataIndex == dataIndex){
48023                 return i;
48024             }
48025         }
48026         return -1;
48027     },
48028     
48029     
48030     moveColumn : function(oldIndex, newIndex){
48031         var c = this.config[oldIndex];
48032         this.config.splice(oldIndex, 1);
48033         this.config.splice(newIndex, 0, c);
48034         this.dataMap = null;
48035         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48036     },
48037
48038     isLocked : function(colIndex){
48039         return this.config[colIndex].locked === true;
48040     },
48041
48042     setLocked : function(colIndex, value, suppressEvent){
48043         if(this.isLocked(colIndex) == value){
48044             return;
48045         }
48046         this.config[colIndex].locked = value;
48047         if(!suppressEvent){
48048             this.fireEvent("columnlockchange", this, colIndex, value);
48049         }
48050     },
48051
48052     getTotalLockedWidth : function(){
48053         var totalWidth = 0;
48054         for(var i = 0; i < this.config.length; i++){
48055             if(this.isLocked(i) && !this.isHidden(i)){
48056                 this.totalWidth += this.getColumnWidth(i);
48057             }
48058         }
48059         return totalWidth;
48060     },
48061
48062     getLockedCount : function(){
48063         for(var i = 0, len = this.config.length; i < len; i++){
48064             if(!this.isLocked(i)){
48065                 return i;
48066             }
48067         }
48068     },
48069
48070     /**
48071      * Returns the number of columns.
48072      * @return {Number}
48073      */
48074     getColumnCount : function(visibleOnly){
48075         if(visibleOnly === true){
48076             var c = 0;
48077             for(var i = 0, len = this.config.length; i < len; i++){
48078                 if(!this.isHidden(i)){
48079                     c++;
48080                 }
48081             }
48082             return c;
48083         }
48084         return this.config.length;
48085     },
48086
48087     /**
48088      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48089      * @param {Function} fn
48090      * @param {Object} scope (optional)
48091      * @return {Array} result
48092      */
48093     getColumnsBy : function(fn, scope){
48094         var r = [];
48095         for(var i = 0, len = this.config.length; i < len; i++){
48096             var c = this.config[i];
48097             if(fn.call(scope||this, c, i) === true){
48098                 r[r.length] = c;
48099             }
48100         }
48101         return r;
48102     },
48103
48104     /**
48105      * Returns true if the specified column is sortable.
48106      * @param {Number} col The column index
48107      * @return {Boolean}
48108      */
48109     isSortable : function(col){
48110         if(typeof this.config[col].sortable == "undefined"){
48111             return this.defaultSortable;
48112         }
48113         return this.config[col].sortable;
48114     },
48115
48116     /**
48117      * Returns the rendering (formatting) function defined for the column.
48118      * @param {Number} col The column index.
48119      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48120      */
48121     getRenderer : function(col){
48122         if(!this.config[col].renderer){
48123             return Roo.grid.ColumnModel.defaultRenderer;
48124         }
48125         return this.config[col].renderer;
48126     },
48127
48128     /**
48129      * Sets the rendering (formatting) function for a column.
48130      * @param {Number} col The column index
48131      * @param {Function} fn The function to use to process the cell's raw data
48132      * to return HTML markup for the grid view. The render function is called with
48133      * the following parameters:<ul>
48134      * <li>Data value.</li>
48135      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48136      * <li>css A CSS style string to apply to the table cell.</li>
48137      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48138      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48139      * <li>Row index</li>
48140      * <li>Column index</li>
48141      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48142      */
48143     setRenderer : function(col, fn){
48144         this.config[col].renderer = fn;
48145     },
48146
48147     /**
48148      * Returns the width for the specified column.
48149      * @param {Number} col The column index
48150      * @return {Number}
48151      */
48152     getColumnWidth : function(col){
48153         return this.config[col].width || this.defaultWidth;
48154     },
48155
48156     /**
48157      * Sets the width for a column.
48158      * @param {Number} col The column index
48159      * @param {Number} width The new width
48160      */
48161     setColumnWidth : function(col, width, suppressEvent){
48162         this.config[col].width = width;
48163         this.totalWidth = null;
48164         if(!suppressEvent){
48165              this.fireEvent("widthchange", this, col, width);
48166         }
48167     },
48168
48169     /**
48170      * Returns the total width of all columns.
48171      * @param {Boolean} includeHidden True to include hidden column widths
48172      * @return {Number}
48173      */
48174     getTotalWidth : function(includeHidden){
48175         if(!this.totalWidth){
48176             this.totalWidth = 0;
48177             for(var i = 0, len = this.config.length; i < len; i++){
48178                 if(includeHidden || !this.isHidden(i)){
48179                     this.totalWidth += this.getColumnWidth(i);
48180                 }
48181             }
48182         }
48183         return this.totalWidth;
48184     },
48185
48186     /**
48187      * Returns the header for the specified column.
48188      * @param {Number} col The column index
48189      * @return {String}
48190      */
48191     getColumnHeader : function(col){
48192         return this.config[col].header;
48193     },
48194
48195     /**
48196      * Sets the header for a column.
48197      * @param {Number} col The column index
48198      * @param {String} header The new header
48199      */
48200     setColumnHeader : function(col, header){
48201         this.config[col].header = header;
48202         this.fireEvent("headerchange", this, col, header);
48203     },
48204
48205     /**
48206      * Returns the tooltip for the specified column.
48207      * @param {Number} col The column index
48208      * @return {String}
48209      */
48210     getColumnTooltip : function(col){
48211             return this.config[col].tooltip;
48212     },
48213     /**
48214      * Sets the tooltip for a column.
48215      * @param {Number} col The column index
48216      * @param {String} tooltip The new tooltip
48217      */
48218     setColumnTooltip : function(col, tooltip){
48219             this.config[col].tooltip = tooltip;
48220     },
48221
48222     /**
48223      * Returns the dataIndex for the specified column.
48224      * @param {Number} col The column index
48225      * @return {Number}
48226      */
48227     getDataIndex : function(col){
48228         return this.config[col].dataIndex;
48229     },
48230
48231     /**
48232      * Sets the dataIndex for a column.
48233      * @param {Number} col The column index
48234      * @param {Number} dataIndex The new dataIndex
48235      */
48236     setDataIndex : function(col, dataIndex){
48237         this.config[col].dataIndex = dataIndex;
48238     },
48239
48240     
48241     
48242     /**
48243      * Returns true if the cell is editable.
48244      * @param {Number} colIndex The column index
48245      * @param {Number} rowIndex The row index
48246      * @return {Boolean}
48247      */
48248     isCellEditable : function(colIndex, rowIndex){
48249         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48250     },
48251
48252     /**
48253      * Returns the editor defined for the cell/column.
48254      * return false or null to disable editing.
48255      * @param {Number} colIndex The column index
48256      * @param {Number} rowIndex The row index
48257      * @return {Object}
48258      */
48259     getCellEditor : function(colIndex, rowIndex){
48260         return this.config[colIndex].editor;
48261     },
48262
48263     /**
48264      * Sets if a column is editable.
48265      * @param {Number} col The column index
48266      * @param {Boolean} editable True if the column is editable
48267      */
48268     setEditable : function(col, editable){
48269         this.config[col].editable = editable;
48270     },
48271
48272
48273     /**
48274      * Returns true if the column is hidden.
48275      * @param {Number} colIndex The column index
48276      * @return {Boolean}
48277      */
48278     isHidden : function(colIndex){
48279         return this.config[colIndex].hidden;
48280     },
48281
48282
48283     /**
48284      * Returns true if the column width cannot be changed
48285      */
48286     isFixed : function(colIndex){
48287         return this.config[colIndex].fixed;
48288     },
48289
48290     /**
48291      * Returns true if the column can be resized
48292      * @return {Boolean}
48293      */
48294     isResizable : function(colIndex){
48295         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48296     },
48297     /**
48298      * Sets if a column is hidden.
48299      * @param {Number} colIndex The column index
48300      * @param {Boolean} hidden True if the column is hidden
48301      */
48302     setHidden : function(colIndex, hidden){
48303         this.config[colIndex].hidden = hidden;
48304         this.totalWidth = null;
48305         this.fireEvent("hiddenchange", this, colIndex, hidden);
48306     },
48307
48308     /**
48309      * Sets the editor for a column.
48310      * @param {Number} col The column index
48311      * @param {Object} editor The editor object
48312      */
48313     setEditor : function(col, editor){
48314         this.config[col].editor = editor;
48315     }
48316 });
48317
48318 Roo.grid.ColumnModel.defaultRenderer = function(value){
48319         if(typeof value == "string" && value.length < 1){
48320             return "&#160;";
48321         }
48322         return value;
48323 };
48324
48325 // Alias for backwards compatibility
48326 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48327 /*
48328  * Based on:
48329  * Ext JS Library 1.1.1
48330  * Copyright(c) 2006-2007, Ext JS, LLC.
48331  *
48332  * Originally Released Under LGPL - original licence link has changed is not relivant.
48333  *
48334  * Fork - LGPL
48335  * <script type="text/javascript">
48336  */
48337
48338 /**
48339  * @class Roo.grid.AbstractSelectionModel
48340  * @extends Roo.util.Observable
48341  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48342  * implemented by descendant classes.  This class should not be directly instantiated.
48343  * @constructor
48344  */
48345 Roo.grid.AbstractSelectionModel = function(){
48346     this.locked = false;
48347     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48348 };
48349
48350 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48351     /** @ignore Called by the grid automatically. Do not call directly. */
48352     init : function(grid){
48353         this.grid = grid;
48354         this.initEvents();
48355     },
48356
48357     /**
48358      * Locks the selections.
48359      */
48360     lock : function(){
48361         this.locked = true;
48362     },
48363
48364     /**
48365      * Unlocks the selections.
48366      */
48367     unlock : function(){
48368         this.locked = false;
48369     },
48370
48371     /**
48372      * Returns true if the selections are locked.
48373      * @return {Boolean}
48374      */
48375     isLocked : function(){
48376         return this.locked;
48377     }
48378 });/*
48379  * Based on:
48380  * Ext JS Library 1.1.1
48381  * Copyright(c) 2006-2007, Ext JS, LLC.
48382  *
48383  * Originally Released Under LGPL - original licence link has changed is not relivant.
48384  *
48385  * Fork - LGPL
48386  * <script type="text/javascript">
48387  */
48388 /**
48389  * @extends Roo.grid.AbstractSelectionModel
48390  * @class Roo.grid.RowSelectionModel
48391  * The default SelectionModel used by {@link Roo.grid.Grid}.
48392  * It supports multiple selections and keyboard selection/navigation. 
48393  * @constructor
48394  * @param {Object} config
48395  */
48396 Roo.grid.RowSelectionModel = function(config){
48397     Roo.apply(this, config);
48398     this.selections = new Roo.util.MixedCollection(false, function(o){
48399         return o.id;
48400     });
48401
48402     this.last = false;
48403     this.lastActive = false;
48404
48405     this.addEvents({
48406         /**
48407              * @event selectionchange
48408              * Fires when the selection changes
48409              * @param {SelectionModel} this
48410              */
48411             "selectionchange" : true,
48412         /**
48413              * @event afterselectionchange
48414              * Fires after the selection changes (eg. by key press or clicking)
48415              * @param {SelectionModel} this
48416              */
48417             "afterselectionchange" : true,
48418         /**
48419              * @event beforerowselect
48420              * Fires when a row is selected being selected, return false to cancel.
48421              * @param {SelectionModel} this
48422              * @param {Number} rowIndex The selected index
48423              * @param {Boolean} keepExisting False if other selections will be cleared
48424              */
48425             "beforerowselect" : true,
48426         /**
48427              * @event rowselect
48428              * Fires when a row is selected.
48429              * @param {SelectionModel} this
48430              * @param {Number} rowIndex The selected index
48431              * @param {Roo.data.Record} r The record
48432              */
48433             "rowselect" : true,
48434         /**
48435              * @event rowdeselect
48436              * Fires when a row is deselected.
48437              * @param {SelectionModel} this
48438              * @param {Number} rowIndex The selected index
48439              */
48440         "rowdeselect" : true
48441     });
48442     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48443     this.locked = false;
48444 };
48445
48446 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48447     /**
48448      * @cfg {Boolean} singleSelect
48449      * True to allow selection of only one row at a time (defaults to false)
48450      */
48451     singleSelect : false,
48452
48453     // private
48454     initEvents : function(){
48455
48456         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48457             this.grid.on("mousedown", this.handleMouseDown, this);
48458         }else{ // allow click to work like normal
48459             this.grid.on("rowclick", this.handleDragableRowClick, this);
48460         }
48461
48462         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48463             "up" : function(e){
48464                 if(!e.shiftKey){
48465                     this.selectPrevious(e.shiftKey);
48466                 }else if(this.last !== false && this.lastActive !== false){
48467                     var last = this.last;
48468                     this.selectRange(this.last,  this.lastActive-1);
48469                     this.grid.getView().focusRow(this.lastActive);
48470                     if(last !== false){
48471                         this.last = last;
48472                     }
48473                 }else{
48474                     this.selectFirstRow();
48475                 }
48476                 this.fireEvent("afterselectionchange", this);
48477             },
48478             "down" : function(e){
48479                 if(!e.shiftKey){
48480                     this.selectNext(e.shiftKey);
48481                 }else if(this.last !== false && this.lastActive !== false){
48482                     var last = this.last;
48483                     this.selectRange(this.last,  this.lastActive+1);
48484                     this.grid.getView().focusRow(this.lastActive);
48485                     if(last !== false){
48486                         this.last = last;
48487                     }
48488                 }else{
48489                     this.selectFirstRow();
48490                 }
48491                 this.fireEvent("afterselectionchange", this);
48492             },
48493             scope: this
48494         });
48495
48496         var view = this.grid.view;
48497         view.on("refresh", this.onRefresh, this);
48498         view.on("rowupdated", this.onRowUpdated, this);
48499         view.on("rowremoved", this.onRemove, this);
48500     },
48501
48502     // private
48503     onRefresh : function(){
48504         var ds = this.grid.dataSource, i, v = this.grid.view;
48505         var s = this.selections;
48506         s.each(function(r){
48507             if((i = ds.indexOfId(r.id)) != -1){
48508                 v.onRowSelect(i);
48509             }else{
48510                 s.remove(r);
48511             }
48512         });
48513     },
48514
48515     // private
48516     onRemove : function(v, index, r){
48517         this.selections.remove(r);
48518     },
48519
48520     // private
48521     onRowUpdated : function(v, index, r){
48522         if(this.isSelected(r)){
48523             v.onRowSelect(index);
48524         }
48525     },
48526
48527     /**
48528      * Select records.
48529      * @param {Array} records The records to select
48530      * @param {Boolean} keepExisting (optional) True to keep existing selections
48531      */
48532     selectRecords : function(records, keepExisting){
48533         if(!keepExisting){
48534             this.clearSelections();
48535         }
48536         var ds = this.grid.dataSource;
48537         for(var i = 0, len = records.length; i < len; i++){
48538             this.selectRow(ds.indexOf(records[i]), true);
48539         }
48540     },
48541
48542     /**
48543      * Gets the number of selected rows.
48544      * @return {Number}
48545      */
48546     getCount : function(){
48547         return this.selections.length;
48548     },
48549
48550     /**
48551      * Selects the first row in the grid.
48552      */
48553     selectFirstRow : function(){
48554         this.selectRow(0);
48555     },
48556
48557     /**
48558      * Select the last row.
48559      * @param {Boolean} keepExisting (optional) True to keep existing selections
48560      */
48561     selectLastRow : function(keepExisting){
48562         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48563     },
48564
48565     /**
48566      * Selects the row immediately following the last selected row.
48567      * @param {Boolean} keepExisting (optional) True to keep existing selections
48568      */
48569     selectNext : function(keepExisting){
48570         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48571             this.selectRow(this.last+1, keepExisting);
48572             this.grid.getView().focusRow(this.last);
48573         }
48574     },
48575
48576     /**
48577      * Selects the row that precedes the last selected row.
48578      * @param {Boolean} keepExisting (optional) True to keep existing selections
48579      */
48580     selectPrevious : function(keepExisting){
48581         if(this.last){
48582             this.selectRow(this.last-1, keepExisting);
48583             this.grid.getView().focusRow(this.last);
48584         }
48585     },
48586
48587     /**
48588      * Returns the selected records
48589      * @return {Array} Array of selected records
48590      */
48591     getSelections : function(){
48592         return [].concat(this.selections.items);
48593     },
48594
48595     /**
48596      * Returns the first selected record.
48597      * @return {Record}
48598      */
48599     getSelected : function(){
48600         return this.selections.itemAt(0);
48601     },
48602
48603
48604     /**
48605      * Clears all selections.
48606      */
48607     clearSelections : function(fast){
48608         if(this.locked) return;
48609         if(fast !== true){
48610             var ds = this.grid.dataSource;
48611             var s = this.selections;
48612             s.each(function(r){
48613                 this.deselectRow(ds.indexOfId(r.id));
48614             }, this);
48615             s.clear();
48616         }else{
48617             this.selections.clear();
48618         }
48619         this.last = false;
48620     },
48621
48622
48623     /**
48624      * Selects all rows.
48625      */
48626     selectAll : function(){
48627         if(this.locked) return;
48628         this.selections.clear();
48629         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48630             this.selectRow(i, true);
48631         }
48632     },
48633
48634     /**
48635      * Returns True if there is a selection.
48636      * @return {Boolean}
48637      */
48638     hasSelection : function(){
48639         return this.selections.length > 0;
48640     },
48641
48642     /**
48643      * Returns True if the specified row is selected.
48644      * @param {Number/Record} record The record or index of the record to check
48645      * @return {Boolean}
48646      */
48647     isSelected : function(index){
48648         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48649         return (r && this.selections.key(r.id) ? true : false);
48650     },
48651
48652     /**
48653      * Returns True if the specified record id is selected.
48654      * @param {String} id The id of record to check
48655      * @return {Boolean}
48656      */
48657     isIdSelected : function(id){
48658         return (this.selections.key(id) ? true : false);
48659     },
48660
48661     // private
48662     handleMouseDown : function(e, t){
48663         var view = this.grid.getView(), rowIndex;
48664         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48665             return;
48666         };
48667         if(e.shiftKey && this.last !== false){
48668             var last = this.last;
48669             this.selectRange(last, rowIndex, e.ctrlKey);
48670             this.last = last; // reset the last
48671             view.focusRow(rowIndex);
48672         }else{
48673             var isSelected = this.isSelected(rowIndex);
48674             if(e.button !== 0 && isSelected){
48675                 view.focusRow(rowIndex);
48676             }else if(e.ctrlKey && isSelected){
48677                 this.deselectRow(rowIndex);
48678             }else if(!isSelected){
48679                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48680                 view.focusRow(rowIndex);
48681             }
48682         }
48683         this.fireEvent("afterselectionchange", this);
48684     },
48685     // private
48686     handleDragableRowClick :  function(grid, rowIndex, e) 
48687     {
48688         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48689             this.selectRow(rowIndex, false);
48690             grid.view.focusRow(rowIndex);
48691              this.fireEvent("afterselectionchange", this);
48692         }
48693     },
48694     
48695     /**
48696      * Selects multiple rows.
48697      * @param {Array} rows Array of the indexes of the row to select
48698      * @param {Boolean} keepExisting (optional) True to keep existing selections
48699      */
48700     selectRows : function(rows, keepExisting){
48701         if(!keepExisting){
48702             this.clearSelections();
48703         }
48704         for(var i = 0, len = rows.length; i < len; i++){
48705             this.selectRow(rows[i], true);
48706         }
48707     },
48708
48709     /**
48710      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48711      * @param {Number} startRow The index of the first row in the range
48712      * @param {Number} endRow The index of the last row in the range
48713      * @param {Boolean} keepExisting (optional) True to retain existing selections
48714      */
48715     selectRange : function(startRow, endRow, keepExisting){
48716         if(this.locked) return;
48717         if(!keepExisting){
48718             this.clearSelections();
48719         }
48720         if(startRow <= endRow){
48721             for(var i = startRow; i <= endRow; i++){
48722                 this.selectRow(i, true);
48723             }
48724         }else{
48725             for(var i = startRow; i >= endRow; i--){
48726                 this.selectRow(i, true);
48727             }
48728         }
48729     },
48730
48731     /**
48732      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48733      * @param {Number} startRow The index of the first row in the range
48734      * @param {Number} endRow The index of the last row in the range
48735      */
48736     deselectRange : function(startRow, endRow, preventViewNotify){
48737         if(this.locked) return;
48738         for(var i = startRow; i <= endRow; i++){
48739             this.deselectRow(i, preventViewNotify);
48740         }
48741     },
48742
48743     /**
48744      * Selects a row.
48745      * @param {Number} row The index of the row to select
48746      * @param {Boolean} keepExisting (optional) True to keep existing selections
48747      */
48748     selectRow : function(index, keepExisting, preventViewNotify){
48749         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48750         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48751             if(!keepExisting || this.singleSelect){
48752                 this.clearSelections();
48753             }
48754             var r = this.grid.dataSource.getAt(index);
48755             this.selections.add(r);
48756             this.last = this.lastActive = index;
48757             if(!preventViewNotify){
48758                 this.grid.getView().onRowSelect(index);
48759             }
48760             this.fireEvent("rowselect", this, index, r);
48761             this.fireEvent("selectionchange", this);
48762         }
48763     },
48764
48765     /**
48766      * Deselects a row.
48767      * @param {Number} row The index of the row to deselect
48768      */
48769     deselectRow : function(index, preventViewNotify){
48770         if(this.locked) return;
48771         if(this.last == index){
48772             this.last = false;
48773         }
48774         if(this.lastActive == index){
48775             this.lastActive = false;
48776         }
48777         var r = this.grid.dataSource.getAt(index);
48778         this.selections.remove(r);
48779         if(!preventViewNotify){
48780             this.grid.getView().onRowDeselect(index);
48781         }
48782         this.fireEvent("rowdeselect", this, index);
48783         this.fireEvent("selectionchange", this);
48784     },
48785
48786     // private
48787     restoreLast : function(){
48788         if(this._last){
48789             this.last = this._last;
48790         }
48791     },
48792
48793     // private
48794     acceptsNav : function(row, col, cm){
48795         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48796     },
48797
48798     // private
48799     onEditorKey : function(field, e){
48800         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48801         if(k == e.TAB){
48802             e.stopEvent();
48803             ed.completeEdit();
48804             if(e.shiftKey){
48805                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48806             }else{
48807                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48808             }
48809         }else if(k == e.ENTER && !e.ctrlKey){
48810             e.stopEvent();
48811             ed.completeEdit();
48812             if(e.shiftKey){
48813                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48814             }else{
48815                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48816             }
48817         }else if(k == e.ESC){
48818             ed.cancelEdit();
48819         }
48820         if(newCell){
48821             g.startEditing(newCell[0], newCell[1]);
48822         }
48823     }
48824 });/*
48825  * Based on:
48826  * Ext JS Library 1.1.1
48827  * Copyright(c) 2006-2007, Ext JS, LLC.
48828  *
48829  * Originally Released Under LGPL - original licence link has changed is not relivant.
48830  *
48831  * Fork - LGPL
48832  * <script type="text/javascript">
48833  */
48834 /**
48835  * @class Roo.grid.CellSelectionModel
48836  * @extends Roo.grid.AbstractSelectionModel
48837  * This class provides the basic implementation for cell selection in a grid.
48838  * @constructor
48839  * @param {Object} config The object containing the configuration of this model.
48840  */
48841 Roo.grid.CellSelectionModel = function(config){
48842     Roo.apply(this, config);
48843
48844     this.selection = null;
48845
48846     this.addEvents({
48847         /**
48848              * @event beforerowselect
48849              * Fires before a cell is selected.
48850              * @param {SelectionModel} this
48851              * @param {Number} rowIndex The selected row index
48852              * @param {Number} colIndex The selected cell index
48853              */
48854             "beforecellselect" : true,
48855         /**
48856              * @event cellselect
48857              * Fires when a cell is selected.
48858              * @param {SelectionModel} this
48859              * @param {Number} rowIndex The selected row index
48860              * @param {Number} colIndex The selected cell index
48861              */
48862             "cellselect" : true,
48863         /**
48864              * @event selectionchange
48865              * Fires when the active selection changes.
48866              * @param {SelectionModel} this
48867              * @param {Object} selection null for no selection or an object (o) with two properties
48868                 <ul>
48869                 <li>o.record: the record object for the row the selection is in</li>
48870                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48871                 </ul>
48872              */
48873             "selectionchange" : true
48874     });
48875     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48876 };
48877
48878 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48879
48880     /** @ignore */
48881     initEvents : function(){
48882         this.grid.on("mousedown", this.handleMouseDown, this);
48883         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48884         var view = this.grid.view;
48885         view.on("refresh", this.onViewChange, this);
48886         view.on("rowupdated", this.onRowUpdated, this);
48887         view.on("beforerowremoved", this.clearSelections, this);
48888         view.on("beforerowsinserted", this.clearSelections, this);
48889         if(this.grid.isEditor){
48890             this.grid.on("beforeedit", this.beforeEdit,  this);
48891         }
48892     },
48893
48894         //private
48895     beforeEdit : function(e){
48896         this.select(e.row, e.column, false, true, e.record);
48897     },
48898
48899         //private
48900     onRowUpdated : function(v, index, r){
48901         if(this.selection && this.selection.record == r){
48902             v.onCellSelect(index, this.selection.cell[1]);
48903         }
48904     },
48905
48906         //private
48907     onViewChange : function(){
48908         this.clearSelections(true);
48909     },
48910
48911         /**
48912          * Returns the currently selected cell,.
48913          * @return {Array} The selected cell (row, column) or null if none selected.
48914          */
48915     getSelectedCell : function(){
48916         return this.selection ? this.selection.cell : null;
48917     },
48918
48919     /**
48920      * Clears all selections.
48921      * @param {Boolean} true to prevent the gridview from being notified about the change.
48922      */
48923     clearSelections : function(preventNotify){
48924         var s = this.selection;
48925         if(s){
48926             if(preventNotify !== true){
48927                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48928             }
48929             this.selection = null;
48930             this.fireEvent("selectionchange", this, null);
48931         }
48932     },
48933
48934     /**
48935      * Returns true if there is a selection.
48936      * @return {Boolean}
48937      */
48938     hasSelection : function(){
48939         return this.selection ? true : false;
48940     },
48941
48942     /** @ignore */
48943     handleMouseDown : function(e, t){
48944         var v = this.grid.getView();
48945         if(this.isLocked()){
48946             return;
48947         };
48948         var row = v.findRowIndex(t);
48949         var cell = v.findCellIndex(t);
48950         if(row !== false && cell !== false){
48951             this.select(row, cell);
48952         }
48953     },
48954
48955     /**
48956      * Selects a cell.
48957      * @param {Number} rowIndex
48958      * @param {Number} collIndex
48959      */
48960     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48961         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48962             this.clearSelections();
48963             r = r || this.grid.dataSource.getAt(rowIndex);
48964             this.selection = {
48965                 record : r,
48966                 cell : [rowIndex, colIndex]
48967             };
48968             if(!preventViewNotify){
48969                 var v = this.grid.getView();
48970                 v.onCellSelect(rowIndex, colIndex);
48971                 if(preventFocus !== true){
48972                     v.focusCell(rowIndex, colIndex);
48973                 }
48974             }
48975             this.fireEvent("cellselect", this, rowIndex, colIndex);
48976             this.fireEvent("selectionchange", this, this.selection);
48977         }
48978     },
48979
48980         //private
48981     isSelectable : function(rowIndex, colIndex, cm){
48982         return !cm.isHidden(colIndex);
48983     },
48984
48985     /** @ignore */
48986     handleKeyDown : function(e){
48987         if(!e.isNavKeyPress()){
48988             return;
48989         }
48990         var g = this.grid, s = this.selection;
48991         if(!s){
48992             e.stopEvent();
48993             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
48994             if(cell){
48995                 this.select(cell[0], cell[1]);
48996             }
48997             return;
48998         }
48999         var sm = this;
49000         var walk = function(row, col, step){
49001             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49002         };
49003         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49004         var newCell;
49005
49006         switch(k){
49007              case e.TAB:
49008                  if(e.shiftKey){
49009                      newCell = walk(r, c-1, -1);
49010                  }else{
49011                      newCell = walk(r, c+1, 1);
49012                  }
49013              break;
49014              case e.DOWN:
49015                  newCell = walk(r+1, c, 1);
49016              break;
49017              case e.UP:
49018                  newCell = walk(r-1, c, -1);
49019              break;
49020              case e.RIGHT:
49021                  newCell = walk(r, c+1, 1);
49022              break;
49023              case e.LEFT:
49024                  newCell = walk(r, c-1, -1);
49025              break;
49026              case e.ENTER:
49027                  if(g.isEditor && !g.editing){
49028                     g.startEditing(r, c);
49029                     e.stopEvent();
49030                     return;
49031                 }
49032              break;
49033         };
49034         if(newCell){
49035             this.select(newCell[0], newCell[1]);
49036             e.stopEvent();
49037         }
49038     },
49039
49040     acceptsNav : function(row, col, cm){
49041         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49042     },
49043
49044     onEditorKey : function(field, e){
49045         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49046         if(k == e.TAB){
49047             if(e.shiftKey){
49048                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49049             }else{
49050                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49051             }
49052             e.stopEvent();
49053         }else if(k == e.ENTER && !e.ctrlKey){
49054             ed.completeEdit();
49055             e.stopEvent();
49056         }else if(k == e.ESC){
49057             ed.cancelEdit();
49058         }
49059         if(newCell){
49060             g.startEditing(newCell[0], newCell[1]);
49061         }
49062     }
49063 });/*
49064  * Based on:
49065  * Ext JS Library 1.1.1
49066  * Copyright(c) 2006-2007, Ext JS, LLC.
49067  *
49068  * Originally Released Under LGPL - original licence link has changed is not relivant.
49069  *
49070  * Fork - LGPL
49071  * <script type="text/javascript">
49072  */
49073  
49074 /**
49075  * @class Roo.grid.EditorGrid
49076  * @extends Roo.grid.Grid
49077  * Class for creating and editable grid.
49078  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49079  * The container MUST have some type of size defined for the grid to fill. The container will be 
49080  * automatically set to position relative if it isn't already.
49081  * @param {Object} dataSource The data model to bind to
49082  * @param {Object} colModel The column model with info about this grid's columns
49083  */
49084 Roo.grid.EditorGrid = function(container, config){
49085     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49086     this.getGridEl().addClass("xedit-grid");
49087
49088     if(!this.selModel){
49089         this.selModel = new Roo.grid.CellSelectionModel();
49090     }
49091
49092     this.activeEditor = null;
49093
49094         this.addEvents({
49095             /**
49096              * @event beforeedit
49097              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49098              * <ul style="padding:5px;padding-left:16px;">
49099              * <li>grid - This grid</li>
49100              * <li>record - The record being edited</li>
49101              * <li>field - The field name being edited</li>
49102              * <li>value - The value for the field being edited.</li>
49103              * <li>row - The grid row index</li>
49104              * <li>column - The grid column index</li>
49105              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49106              * </ul>
49107              * @param {Object} e An edit event (see above for description)
49108              */
49109             "beforeedit" : true,
49110             /**
49111              * @event afteredit
49112              * Fires after a cell is edited. <br />
49113              * <ul style="padding:5px;padding-left:16px;">
49114              * <li>grid - This grid</li>
49115              * <li>record - The record being edited</li>
49116              * <li>field - The field name being edited</li>
49117              * <li>value - The value being set</li>
49118              * <li>originalValue - The original value for the field, before the edit.</li>
49119              * <li>row - The grid row index</li>
49120              * <li>column - The grid column index</li>
49121              * </ul>
49122              * @param {Object} e An edit event (see above for description)
49123              */
49124             "afteredit" : true,
49125             /**
49126              * @event validateedit
49127              * Fires after a cell is edited, but before the value is set in the record. 
49128          * You can use this to modify the value being set in the field, Return false
49129              * to cancel the change. The edit event object has the following properties <br />
49130              * <ul style="padding:5px;padding-left:16px;">
49131          * <li>editor - This editor</li>
49132              * <li>grid - This grid</li>
49133              * <li>record - The record being edited</li>
49134              * <li>field - The field name being edited</li>
49135              * <li>value - The value being set</li>
49136              * <li>originalValue - The original value for the field, before the edit.</li>
49137              * <li>row - The grid row index</li>
49138              * <li>column - The grid column index</li>
49139              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49140              * </ul>
49141              * @param {Object} e An edit event (see above for description)
49142              */
49143             "validateedit" : true
49144         });
49145     this.on("bodyscroll", this.stopEditing,  this);
49146     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49147 };
49148
49149 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49150     /**
49151      * @cfg {Number} clicksToEdit
49152      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49153      */
49154     clicksToEdit: 2,
49155
49156     // private
49157     isEditor : true,
49158     // private
49159     trackMouseOver: false, // causes very odd FF errors
49160
49161     onCellDblClick : function(g, row, col){
49162         this.startEditing(row, col);
49163     },
49164
49165     onEditComplete : function(ed, value, startValue){
49166         this.editing = false;
49167         this.activeEditor = null;
49168         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49169         var r = ed.record;
49170         var field = this.colModel.getDataIndex(ed.col);
49171         var e = {
49172             grid: this,
49173             record: r,
49174             field: field,
49175             originalValue: startValue,
49176             value: value,
49177             row: ed.row,
49178             column: ed.col,
49179             cancel:false,
49180             editor: ed
49181         };
49182         if(String(value) !== String(startValue)){
49183             
49184             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49185                 r.set(field, e.value);
49186                 delete e.cancel; //?? why!!!
49187                 this.fireEvent("afteredit", e);
49188             }
49189         } else {
49190             this.fireEvent("afteredit", e); // always fir it!
49191         }
49192         this.view.focusCell(ed.row, ed.col);
49193     },
49194
49195     /**
49196      * Starts editing the specified for the specified row/column
49197      * @param {Number} rowIndex
49198      * @param {Number} colIndex
49199      */
49200     startEditing : function(row, col){
49201         this.stopEditing();
49202         if(this.colModel.isCellEditable(col, row)){
49203             this.view.ensureVisible(row, col, true);
49204             var r = this.dataSource.getAt(row);
49205             var field = this.colModel.getDataIndex(col);
49206             var e = {
49207                 grid: this,
49208                 record: r,
49209                 field: field,
49210                 value: r.data[field],
49211                 row: row,
49212                 column: col,
49213                 cancel:false
49214             };
49215             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49216                 this.editing = true;
49217                 var ed = this.colModel.getCellEditor(col, row);
49218                 
49219                 if (!ed) {
49220                     return;
49221                 }
49222                 if(!ed.rendered){
49223                     ed.render(ed.parentEl || document.body);
49224                 }
49225                 ed.field.reset();
49226                 (function(){ // complex but required for focus issues in safari, ie and opera
49227                     ed.row = row;
49228                     ed.col = col;
49229                     ed.record = r;
49230                     ed.on("complete", this.onEditComplete, this, {single: true});
49231                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49232                     this.activeEditor = ed;
49233                     var v = r.data[field];
49234                     ed.startEdit(this.view.getCell(row, col), v);
49235                 }).defer(50, this);
49236             }
49237         }
49238     },
49239         
49240     /**
49241      * Stops any active editing
49242      */
49243     stopEditing : function(){
49244         if(this.activeEditor){
49245             this.activeEditor.completeEdit();
49246         }
49247         this.activeEditor = null;
49248     }
49249 });/*
49250  * Based on:
49251  * Ext JS Library 1.1.1
49252  * Copyright(c) 2006-2007, Ext JS, LLC.
49253  *
49254  * Originally Released Under LGPL - original licence link has changed is not relivant.
49255  *
49256  * Fork - LGPL
49257  * <script type="text/javascript">
49258  */
49259
49260 // private - not really -- you end up using it !
49261 // This is a support class used internally by the Grid components
49262
49263 /**
49264  * @class Roo.grid.GridEditor
49265  * @extends Roo.Editor
49266  * Class for creating and editable grid elements.
49267  * @param {Object} config any settings (must include field)
49268  */
49269 Roo.grid.GridEditor = function(field, config){
49270     if (!config && field.field) {
49271         config = field;
49272         field = Roo.factory(config.field, Roo.form);
49273     }
49274     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49275     field.monitorTab = false;
49276 };
49277
49278 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49279     
49280     /**
49281      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49282      */
49283     
49284     alignment: "tl-tl",
49285     autoSize: "width",
49286     hideEl : false,
49287     cls: "x-small-editor x-grid-editor",
49288     shim:false,
49289     shadow:"frame"
49290 });/*
49291  * Based on:
49292  * Ext JS Library 1.1.1
49293  * Copyright(c) 2006-2007, Ext JS, LLC.
49294  *
49295  * Originally Released Under LGPL - original licence link has changed is not relivant.
49296  *
49297  * Fork - LGPL
49298  * <script type="text/javascript">
49299  */
49300   
49301
49302   
49303 Roo.grid.PropertyRecord = Roo.data.Record.create([
49304     {name:'name',type:'string'},  'value'
49305 ]);
49306
49307
49308 Roo.grid.PropertyStore = function(grid, source){
49309     this.grid = grid;
49310     this.store = new Roo.data.Store({
49311         recordType : Roo.grid.PropertyRecord
49312     });
49313     this.store.on('update', this.onUpdate,  this);
49314     if(source){
49315         this.setSource(source);
49316     }
49317     Roo.grid.PropertyStore.superclass.constructor.call(this);
49318 };
49319
49320
49321
49322 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49323     setSource : function(o){
49324         this.source = o;
49325         this.store.removeAll();
49326         var data = [];
49327         for(var k in o){
49328             if(this.isEditableValue(o[k])){
49329                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49330             }
49331         }
49332         this.store.loadRecords({records: data}, {}, true);
49333     },
49334
49335     onUpdate : function(ds, record, type){
49336         if(type == Roo.data.Record.EDIT){
49337             var v = record.data['value'];
49338             var oldValue = record.modified['value'];
49339             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49340                 this.source[record.id] = v;
49341                 record.commit();
49342                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49343             }else{
49344                 record.reject();
49345             }
49346         }
49347     },
49348
49349     getProperty : function(row){
49350        return this.store.getAt(row);
49351     },
49352
49353     isEditableValue: function(val){
49354         if(val && val instanceof Date){
49355             return true;
49356         }else if(typeof val == 'object' || typeof val == 'function'){
49357             return false;
49358         }
49359         return true;
49360     },
49361
49362     setValue : function(prop, value){
49363         this.source[prop] = value;
49364         this.store.getById(prop).set('value', value);
49365     },
49366
49367     getSource : function(){
49368         return this.source;
49369     }
49370 });
49371
49372 Roo.grid.PropertyColumnModel = function(grid, store){
49373     this.grid = grid;
49374     var g = Roo.grid;
49375     g.PropertyColumnModel.superclass.constructor.call(this, [
49376         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49377         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49378     ]);
49379     this.store = store;
49380     this.bselect = Roo.DomHelper.append(document.body, {
49381         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49382             {tag: 'option', value: 'true', html: 'true'},
49383             {tag: 'option', value: 'false', html: 'false'}
49384         ]
49385     });
49386     Roo.id(this.bselect);
49387     var f = Roo.form;
49388     this.editors = {
49389         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49390         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49391         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49392         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49393         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49394     };
49395     this.renderCellDelegate = this.renderCell.createDelegate(this);
49396     this.renderPropDelegate = this.renderProp.createDelegate(this);
49397 };
49398
49399 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49400     
49401     
49402     nameText : 'Name',
49403     valueText : 'Value',
49404     
49405     dateFormat : 'm/j/Y',
49406     
49407     
49408     renderDate : function(dateVal){
49409         return dateVal.dateFormat(this.dateFormat);
49410     },
49411
49412     renderBool : function(bVal){
49413         return bVal ? 'true' : 'false';
49414     },
49415
49416     isCellEditable : function(colIndex, rowIndex){
49417         return colIndex == 1;
49418     },
49419
49420     getRenderer : function(col){
49421         return col == 1 ?
49422             this.renderCellDelegate : this.renderPropDelegate;
49423     },
49424
49425     renderProp : function(v){
49426         return this.getPropertyName(v);
49427     },
49428
49429     renderCell : function(val){
49430         var rv = val;
49431         if(val instanceof Date){
49432             rv = this.renderDate(val);
49433         }else if(typeof val == 'boolean'){
49434             rv = this.renderBool(val);
49435         }
49436         return Roo.util.Format.htmlEncode(rv);
49437     },
49438
49439     getPropertyName : function(name){
49440         var pn = this.grid.propertyNames;
49441         return pn && pn[name] ? pn[name] : name;
49442     },
49443
49444     getCellEditor : function(colIndex, rowIndex){
49445         var p = this.store.getProperty(rowIndex);
49446         var n = p.data['name'], val = p.data['value'];
49447         
49448         if(typeof(this.grid.customEditors[n]) == 'string'){
49449             return this.editors[this.grid.customEditors[n]];
49450         }
49451         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49452             return this.grid.customEditors[n];
49453         }
49454         if(val instanceof Date){
49455             return this.editors['date'];
49456         }else if(typeof val == 'number'){
49457             return this.editors['number'];
49458         }else if(typeof val == 'boolean'){
49459             return this.editors['boolean'];
49460         }else{
49461             return this.editors['string'];
49462         }
49463     }
49464 });
49465
49466 /**
49467  * @class Roo.grid.PropertyGrid
49468  * @extends Roo.grid.EditorGrid
49469  * This class represents the  interface of a component based property grid control.
49470  * <br><br>Usage:<pre><code>
49471  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49472       
49473  });
49474  // set any options
49475  grid.render();
49476  * </code></pre>
49477   
49478  * @constructor
49479  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49480  * The container MUST have some type of size defined for the grid to fill. The container will be
49481  * automatically set to position relative if it isn't already.
49482  * @param {Object} config A config object that sets properties on this grid.
49483  */
49484 Roo.grid.PropertyGrid = function(container, config){
49485     config = config || {};
49486     var store = new Roo.grid.PropertyStore(this);
49487     this.store = store;
49488     var cm = new Roo.grid.PropertyColumnModel(this, store);
49489     store.store.sort('name', 'ASC');
49490     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49491         ds: store.store,
49492         cm: cm,
49493         enableColLock:false,
49494         enableColumnMove:false,
49495         stripeRows:false,
49496         trackMouseOver: false,
49497         clicksToEdit:1
49498     }, config));
49499     this.getGridEl().addClass('x-props-grid');
49500     this.lastEditRow = null;
49501     this.on('columnresize', this.onColumnResize, this);
49502     this.addEvents({
49503          /**
49504              * @event beforepropertychange
49505              * Fires before a property changes (return false to stop?)
49506              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49507              * @param {String} id Record Id
49508              * @param {String} newval New Value
49509          * @param {String} oldval Old Value
49510              */
49511         "beforepropertychange": true,
49512         /**
49513              * @event propertychange
49514              * Fires after a property changes
49515              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49516              * @param {String} id Record Id
49517              * @param {String} newval New Value
49518          * @param {String} oldval Old Value
49519              */
49520         "propertychange": true
49521     });
49522     this.customEditors = this.customEditors || {};
49523 };
49524 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49525     
49526      /**
49527      * @cfg {Object} customEditors map of colnames=> custom editors.
49528      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49529      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49530      * false disables editing of the field.
49531          */
49532     
49533       /**
49534      * @cfg {Object} propertyNames map of property Names to their displayed value
49535          */
49536     
49537     render : function(){
49538         Roo.grid.PropertyGrid.superclass.render.call(this);
49539         this.autoSize.defer(100, this);
49540     },
49541
49542     autoSize : function(){
49543         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49544         if(this.view){
49545             this.view.fitColumns();
49546         }
49547     },
49548
49549     onColumnResize : function(){
49550         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49551         this.autoSize();
49552     },
49553     /**
49554      * Sets the data for the Grid
49555      * accepts a Key => Value object of all the elements avaiable.
49556      * @param {Object} data  to appear in grid.
49557      */
49558     setSource : function(source){
49559         this.store.setSource(source);
49560         //this.autoSize();
49561     },
49562     /**
49563      * Gets all the data from the grid.
49564      * @return {Object} data  data stored in grid
49565      */
49566     getSource : function(){
49567         return this.store.getSource();
49568     }
49569 });/*
49570  * Based on:
49571  * Ext JS Library 1.1.1
49572  * Copyright(c) 2006-2007, Ext JS, LLC.
49573  *
49574  * Originally Released Under LGPL - original licence link has changed is not relivant.
49575  *
49576  * Fork - LGPL
49577  * <script type="text/javascript">
49578  */
49579  
49580 /**
49581  * @class Roo.LoadMask
49582  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49583  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49584  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49585  * element's UpdateManager load indicator and will be destroyed after the initial load.
49586  * @constructor
49587  * Create a new LoadMask
49588  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49589  * @param {Object} config The config object
49590  */
49591 Roo.LoadMask = function(el, config){
49592     this.el = Roo.get(el);
49593     Roo.apply(this, config);
49594     if(this.store){
49595         this.store.on('beforeload', this.onBeforeLoad, this);
49596         this.store.on('load', this.onLoad, this);
49597         this.store.on('loadexception', this.onLoad, this);
49598         this.removeMask = false;
49599     }else{
49600         var um = this.el.getUpdateManager();
49601         um.showLoadIndicator = false; // disable the default indicator
49602         um.on('beforeupdate', this.onBeforeLoad, this);
49603         um.on('update', this.onLoad, this);
49604         um.on('failure', this.onLoad, this);
49605         this.removeMask = true;
49606     }
49607 };
49608
49609 Roo.LoadMask.prototype = {
49610     /**
49611      * @cfg {Boolean} removeMask
49612      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49613      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49614      */
49615     /**
49616      * @cfg {String} msg
49617      * The text to display in a centered loading message box (defaults to 'Loading...')
49618      */
49619     msg : 'Loading...',
49620     /**
49621      * @cfg {String} msgCls
49622      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49623      */
49624     msgCls : 'x-mask-loading',
49625
49626     /**
49627      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49628      * @type Boolean
49629      */
49630     disabled: false,
49631
49632     /**
49633      * Disables the mask to prevent it from being displayed
49634      */
49635     disable : function(){
49636        this.disabled = true;
49637     },
49638
49639     /**
49640      * Enables the mask so that it can be displayed
49641      */
49642     enable : function(){
49643         this.disabled = false;
49644     },
49645
49646     // private
49647     onLoad : function(){
49648         this.el.unmask(this.removeMask);
49649     },
49650
49651     // private
49652     onBeforeLoad : function(){
49653         if(!this.disabled){
49654             this.el.mask(this.msg, this.msgCls);
49655         }
49656     },
49657
49658     // private
49659     destroy : function(){
49660         if(this.store){
49661             this.store.un('beforeload', this.onBeforeLoad, this);
49662             this.store.un('load', this.onLoad, this);
49663             this.store.un('loadexception', this.onLoad, this);
49664         }else{
49665             var um = this.el.getUpdateManager();
49666             um.un('beforeupdate', this.onBeforeLoad, this);
49667             um.un('update', this.onLoad, this);
49668             um.un('failure', this.onLoad, this);
49669         }
49670     }
49671 };/*
49672  * Based on:
49673  * Ext JS Library 1.1.1
49674  * Copyright(c) 2006-2007, Ext JS, LLC.
49675  *
49676  * Originally Released Under LGPL - original licence link has changed is not relivant.
49677  *
49678  * Fork - LGPL
49679  * <script type="text/javascript">
49680  */
49681 Roo.XTemplate = function(){
49682     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49683     var s = this.html;
49684
49685     s = ['<tpl>', s, '</tpl>'].join('');
49686
49687     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49688
49689     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49690     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49691     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49692     var m, id = 0;
49693     var tpls = [];
49694
49695     while(m = s.match(re)){
49696        var m2 = m[0].match(nameRe);
49697        var m3 = m[0].match(ifRe);
49698        var m4 = m[0].match(execRe);
49699        var exp = null, fn = null, exec = null;
49700        var name = m2 && m2[1] ? m2[1] : '';
49701        if(m3){
49702            exp = m3 && m3[1] ? m3[1] : null;
49703            if(exp){
49704                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49705            }
49706        }
49707        if(m4){
49708            exp = m4 && m4[1] ? m4[1] : null;
49709            if(exp){
49710                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49711            }
49712        }
49713        if(name){
49714            switch(name){
49715                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49716                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49717                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49718            }
49719        }
49720        tpls.push({
49721             id: id,
49722             target: name,
49723             exec: exec,
49724             test: fn,
49725             body: m[1]||''
49726         });
49727        s = s.replace(m[0], '{xtpl'+ id + '}');
49728        ++id;
49729     }
49730     for(var i = tpls.length-1; i >= 0; --i){
49731         this.compileTpl(tpls[i]);
49732     }
49733     this.master = tpls[tpls.length-1];
49734     this.tpls = tpls;
49735 };
49736 Roo.extend(Roo.XTemplate, Roo.Template, {
49737
49738     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49739
49740     applySubTemplate : function(id, values, parent){
49741         var t = this.tpls[id];
49742         if(t.test && !t.test.call(this, values, parent)){
49743             return '';
49744         }
49745         if(t.exec && t.exec.call(this, values, parent)){
49746             return '';
49747         }
49748         var vs = t.target ? t.target.call(this, values, parent) : values;
49749         parent = t.target ? values : parent;
49750         if(t.target && vs instanceof Array){
49751             var buf = [];
49752             for(var i = 0, len = vs.length; i < len; i++){
49753                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49754             }
49755             return buf.join('');
49756         }
49757         return t.compiled.call(this, vs, parent);
49758     },
49759
49760     compileTpl : function(tpl){
49761         var fm = Roo.util.Format;
49762         var useF = this.disableFormats !== true;
49763         var sep = Roo.isGecko ? "+" : ",";
49764         var fn = function(m, name, format, args){
49765             if(name.substr(0, 4) == 'xtpl'){
49766                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49767             }
49768             var v;
49769             if(name.indexOf('.') != -1){
49770                 v = name;
49771             }else{
49772                 v = "values['" + name + "']";
49773             }
49774             if(format && useF){
49775                 args = args ? ',' + args : "";
49776                 if(format.substr(0, 5) != "this."){
49777                     format = "fm." + format + '(';
49778                 }else{
49779                     format = 'this.call("'+ format.substr(5) + '", ';
49780                     args = ", values";
49781                 }
49782             }else{
49783                 args= ''; format = "("+v+" === undefined ? '' : ";
49784             }
49785             return "'"+ sep + format + v + args + ")"+sep+"'";
49786         };
49787         var body;
49788         // branched to use + in gecko and [].join() in others
49789         if(Roo.isGecko){
49790             body = "tpl.compiled = function(values, parent){ return '" +
49791                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49792                     "';};";
49793         }else{
49794             body = ["tpl.compiled = function(values, parent){ return ['"];
49795             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49796             body.push("'].join('');};");
49797             body = body.join('');
49798         }
49799         /** eval:var:zzzzzzz */
49800         eval(body);
49801         return this;
49802     },
49803
49804     applyTemplate : function(values){
49805         return this.master.compiled.call(this, values, {});
49806         var s = this.subs;
49807     },
49808
49809     apply : function(){
49810         return this.applyTemplate.apply(this, arguments);
49811     },
49812
49813     compile : function(){return this;}
49814 });
49815
49816 Roo.XTemplate.from = function(el){
49817     el = Roo.getDom(el);
49818     return new Roo.XTemplate(el.value || el.innerHTML);
49819 };/*
49820  * Original code for Roojs - LGPL
49821  * <script type="text/javascript">
49822  */
49823  
49824 /**
49825  * @class Roo.XComponent
49826  * A delayed Element creator...
49827  * 
49828  * Mypart.xyx = new Roo.XComponent({
49829
49830     parent : 'Mypart.xyz', // empty == document.element.!!
49831     order : '001',
49832     name : 'xxxx'
49833     region : 'xxxx'
49834     disabled : function() {} 
49835      
49836     tree : function() { // return an tree of xtype declared components
49837         var MODULE = this;
49838         return 
49839         {
49840             xtype : 'NestedLayoutPanel',
49841             // technicall
49842         }
49843      ]
49844  *})
49845  * @extends Roo.util.Observable
49846  * @constructor
49847  * @param cfg {Object} configuration of component
49848  * 
49849  */
49850 Roo.XComponent = function(cfg) {
49851     Roo.apply(this, cfg);
49852     this.addEvents({ 
49853         /**
49854              * @event built
49855              * Fires when this the componnt is built
49856              * @param {Roo.XComponent} c the component
49857              */
49858         'built' : true,
49859         /**
49860              * @event buildcomplete
49861              * Fires on the top level element when all elements have been built
49862              * @param {Roo.XComponent} c the top level component.
49863          */
49864         'buildcomplete' : true,
49865         
49866     });
49867     
49868     Roo.XComponent.register(this);
49869     this.modules = false;
49870     this.el = false; // where the layout goes..
49871     
49872     
49873 }
49874 Roo.extend(Roo.XComponent, Roo.util.Observable, {
49875     /**
49876      * @property el
49877      * The created element (with Roo.factory())
49878      * @type {Roo.Layout}
49879      */
49880     el  : false,
49881     
49882     /**
49883      * @property el
49884      * for BC  - use el in new code
49885      * @type {Roo.Layout}
49886      */
49887     panel : false,
49888     
49889     /**
49890      * @property layout
49891      * for BC  - use el in new code
49892      * @type {Roo.Layout}
49893      */
49894     layout : false,
49895     
49896      /**
49897      * @cfg {Function|boolean} disabled
49898      * If this module is disabled by some rule, return true from the funtion
49899      */
49900     disabled : false,
49901     
49902     /**
49903      * @cfg {String} parent 
49904      * Name of parent element which it get xtype added to..
49905      */
49906     parent: false,
49907     
49908     /**
49909      * @cfg {String} order
49910      * Used to set the order in which elements are created (usefull for multiple tabs)
49911      */
49912     
49913     order : false,
49914     /**
49915      * @cfg {String} name
49916      * String to display while loading.
49917      */
49918     name : false,
49919     /**
49920      * @cfg {Array} items
49921      * A single item array - the first element is the root of the tree..
49922      * It's done this way to stay compatible with the Xtype system...
49923      */
49924     items : false,
49925      
49926      
49927     
49928 });
49929
49930 Roo.apply(Roo.XComponent, {
49931     
49932     /**
49933      * @property  buildCompleted
49934      * True when the builder has completed building the interface.
49935      * @type Boolean
49936      */
49937     buildCompleted : false,
49938      
49939     /**
49940      * @property  topModule
49941      * the upper most module - uses document.element as it's constructor.
49942      * @type Object
49943      */
49944      
49945     topModule  : false,
49946       
49947     /**
49948      * @property  modules
49949      * array of modules to be created by registration system.
49950      * @type Roo.XComponent
49951      */
49952     
49953     modules : [],
49954       
49955     
49956     /**
49957      * Register components to be built later.
49958      *
49959      * This solves the following issues
49960      * - Building is not done on page load, but after an authentication process has occured.
49961      * - Interface elements are registered on page load
49962      * - Parent Interface elements may not be loaded before child, so this handles that..
49963      * 
49964      *
49965      * example:
49966      * 
49967      * MyApp.register({
49968           order : '000001',
49969           module : 'Pman.Tab.projectMgr',
49970           region : 'center',
49971           parent : 'Pman.layout',
49972           disabled : false,  // or use a function..
49973         })
49974      
49975      * * @param {Object} details about module
49976      */
49977     register : function(obj) {
49978         this.modules.push(obj);
49979          
49980     },
49981     /**
49982      * convert a string to an object..
49983      * 
49984      */
49985     
49986     toObject : function(str)
49987     {
49988         if (!str || typeof(str) == 'object') {
49989             return str;
49990         }
49991         var ar = str.split('.');
49992         var rt, o;
49993         rt = ar.shift();
49994             /** eval:var:o */
49995         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
49996         if (o === false) {
49997             throw "Module not found : " + str;
49998         }
49999         Roo.each(ar, function(e) {
50000             if (typeof(o[e]) == 'undefined') {
50001                 throw "Module not found : " + str;
50002             }
50003             o = o[e];
50004         });
50005         return o;
50006         
50007     },
50008     
50009     
50010     /**
50011      * move modules into their correct place in the tree..
50012      * 
50013      */
50014     preBuild : function ()
50015     {
50016         
50017         Roo.each(this.modules , function (obj)
50018         {
50019             obj.parent = this.toObject(obj.parent);
50020             
50021             if (!obj.parent) {
50022                 this.topModule = obj;
50023                 return;
50024             }
50025             
50026             if (!obj.parent.modules) {
50027                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50028                     function(o) { return o.order + '' }
50029                 );
50030             }
50031             
50032             obj.parent.modules.add(obj);
50033         }, this);
50034     },
50035     
50036      /**
50037      * make a list of modules to build.
50038      * @return {Array} list of modules. 
50039      */ 
50040     
50041     buildOrder : function()
50042     {
50043         var _this = this;
50044         var cmp = function(a,b) {   
50045             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50046         };
50047         
50048         if (!this.topModule || !this.topModule.modules) {
50049             throw "No top level modules to build";
50050         }
50051        
50052         // make a flat list in order of modules to build.
50053         var mods = [ this.topModule ];
50054         
50055         
50056         // add modules to their parents..
50057         var addMod = function(m) {
50058            // console.log(m.modKey);
50059             
50060             mods.push(m);
50061             if (m.modules) {
50062                 m.modules.keySort('ASC',  cmp );
50063                 m.modules.each(addMod);
50064             }
50065             // not sure if this is used any more..
50066             if (m.finalize) {
50067                 m.finalize.name = m.name + " (clean up) ";
50068                 mods.push(m.finalize);
50069             }
50070             
50071         }
50072         this.topModule.modules.keySort('ASC',  cmp );
50073         this.topModule.modules.each(addMod);
50074         return mods;
50075     },
50076     
50077      /**
50078      * Build the registered modules.
50079      * @param {Object} parent element.
50080      * @param {Function} optional method to call after module has been added.
50081      * 
50082      */ 
50083    
50084     build : function() 
50085     {
50086         
50087         this.preBuild();
50088         var mods = this.buildOrder();
50089       
50090         //this.allmods = mods;
50091         //console.log(mods);
50092         //return;
50093         if (!mods.length) { // should not happen
50094             throw "NO modules!!!";
50095         }
50096         
50097         
50098         
50099         // flash it up as modal - so we store the mask!?
50100         Roo.MessageBox.show({ title: 'loading' });
50101         Roo.MessageBox.show({
50102            title: "Please wait...",
50103            msg: "Building Interface...",
50104            width:450,
50105            progress:true,
50106            closable:false,
50107            modal: false
50108           
50109         });
50110         var total = mods.length;
50111         
50112         var _this = this;
50113         var progressRun = function() {
50114             if (!mods.length) {
50115                 console.log('hide?');
50116                 Roo.MessageBox.hide();
50117                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50118                 return;    
50119             }
50120             
50121             var m = mods.shift();
50122             console.log(m);
50123             if (typeof(m) == 'function') { // not sure if this is supported any more..
50124                 m.call(this);
50125                 return progressRun.defer(10, _this);
50126             } 
50127             
50128             Roo.MessageBox.updateProgress(
50129                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50130                     " of " + total + 
50131                     (m.name ? (' - ' + m.name) : '')
50132                     );
50133             
50134          
50135             
50136             var disabled = (typeof(m.disabled) == 'function') ?
50137                 m.disabled.call(m.module.disabled) : m.disabled;    
50138             
50139             
50140             if (disabled) {
50141                 return progressRun(); // we do not update the display!
50142             }
50143             
50144             if (!m.parent) {
50145                 // it's a top level one..
50146                 var layoutbase = new Ext.BorderLayout(document.body, {
50147                
50148                     center: {
50149                          titlebar: false,
50150                          autoScroll:false,
50151                          closeOnTab: true,
50152                          tabPosition: 'top',
50153                          //resizeTabs: true,
50154                          alwaysShowTabs: true,
50155                          minTabWidth: 140
50156                     }
50157                 });
50158                 var tree = m.tree();
50159                 tree.region = 'center';
50160                 m.el = layoutbase.addxtype(tree);
50161                 m.panel = m.el;
50162                 m.layout = m.panel.layout;    
50163                 return progressRun.defer(10, _this);
50164             }
50165             
50166             var tree = m.tree();
50167             tree.region = tree.region || m.region;
50168             m.el = m.parent.el.addxtype(tree);
50169             m.fireEvent('built', m);
50170             m.panel = m.el;
50171             m.layout = m.panel.layout;    
50172             progressRun.defer(10, _this); 
50173             
50174         }
50175         progressRun.defer(1, _this);
50176      
50177         
50178         
50179     }
50180      
50181    
50182     
50183     
50184 });
50185  //<script type="text/javascript">
50186
50187
50188 /**
50189  * @class Roo.Login
50190  * @extends Roo.LayoutDialog
50191  * A generic Login Dialog..... - only one needed in theory!?!?
50192  *
50193  * Fires XComponent builder on success...
50194  * 
50195  * Sends 
50196  *    username,password, lang = for login actions.
50197  *    check = 1 for periodic checking that sesion is valid.
50198  *    passwordRequest = email request password
50199  *    logout = 1 = to logout
50200  * 
50201  * Affects: (this id="????" elements)
50202  *   loading  (removed) (used to indicate application is loading)
50203  *   loading-mask (hides) (used to hide application when it's building loading)
50204  *   
50205  * 
50206  * Usage: 
50207  *    
50208  * 
50209  * Myapp.login = Roo.Login({
50210      url: xxxx,
50211    
50212      realm : 'Myapp', 
50213      
50214      
50215      method : 'POST',
50216      
50217      
50218      * 
50219  })
50220  * 
50221  * 
50222  * 
50223  **/
50224  
50225 Roo.Login = function(cfg)
50226 {
50227     this.addEvents({
50228         'refreshed' : true,
50229     });
50230     
50231     Roo.apply(this,cfg);
50232     
50233     Roo.onReady(function() {
50234         this.onLoad();
50235     }, this);
50236     // call parent..
50237     
50238    
50239     Roo.Login.superclass.constructor.call(this, this);
50240     //this.addxtype(this.items[0]);
50241     
50242     
50243 }
50244
50245
50246 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50247     
50248     /**
50249      * @cfg {String} method
50250      * Method used to query for login details.
50251      */
50252     
50253     method : 'POST',
50254     /**
50255      * @cfg {String} url
50256      * URL to query login data. - eg. baseURL + '/Login.php'
50257      */
50258     url : '',
50259     
50260     /**
50261      * @property user
50262      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50263      * @type {Object} 
50264      */
50265     user : false,
50266     /**
50267      * @property checkFails
50268      * Number of times we have attempted to get authentication check, and failed.
50269      * @type {Number} 
50270      */
50271     checkFails : 0,
50272       /**
50273      * @property intervalID
50274      * The window interval that does the constant login checking.
50275      * @type {Number} 
50276      */
50277     intervalID : 0,
50278     
50279     
50280     onLoad : function() // called on page load...
50281     {
50282         // load 
50283          
50284         if (Roo.get('loading')) { // clear any loading indicator..
50285             Roo.get('loading').remove();
50286         }
50287         
50288         //this.switchLang('en'); // set the language to english..
50289        
50290         this.check({
50291             success:  function(response, opts)  {  // check successfull...
50292             
50293                 var res = this.processResponse(response);
50294                 this.checkFails =0;
50295                 if (!res.success) { // error!
50296                     this.checkFails = 5;
50297                     //console.log('call failure');
50298                     return this.failure(response,opts);
50299                 }
50300                 
50301                 if (!res.data.id) { // id=0 == login failure.
50302                     return this.show();
50303                 }
50304                 
50305                               
50306                         //console.log(success);
50307                 this.fillAuth(res.data);   
50308                 this.checkFails =0;
50309                 Roo.XComponent.build();
50310             },
50311             failure : this.show
50312         });
50313         
50314     }, 
50315     
50316     
50317     check: function(cfg) // called every so often to refresh cookie etc..
50318     {
50319         if (cfg.again) { // could be undefined..
50320             this.checkFails++;
50321         } else {
50322             this.checkFails = 0;
50323         }
50324         var _this = this;
50325         if (this.sending) {
50326             if ( this.checkFails > 4) {
50327                 Roo.MessageBox.alert("Error",  
50328                     "Error getting authentication status. - try reloading, or wait a while", function() {
50329                         _this.sending = false;
50330                     }); 
50331                 return;
50332             }
50333             cfg.again = true;
50334             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50335             return;
50336         }
50337         this.sending = true;
50338         
50339         Roo.Ajax.request({  
50340             url: this.url,
50341             params: {
50342                 getAuthUser: true
50343             },  
50344             method: this.method,
50345             success:  cfg.success || this.success,
50346             failure : cfg.failure || this.failure,
50347             scope : this,
50348             callCfg : cfg
50349               
50350         });  
50351     }, 
50352     
50353     
50354     logout: function()
50355     {
50356         window.onbeforeunload = function() { }; // false does not work for IE..
50357         this.user = false;
50358         var _this = this;
50359         
50360         Roo.Ajax.request({  
50361             url: this.url,
50362             params: {
50363                 logout: 1
50364             },  
50365             method: 'GET',
50366             failure : function() {
50367                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50368                     document.location = document.location.toString() + '?ts=' + Math.random();
50369                 });
50370                 
50371             },
50372             success : function() {
50373                 _this.user = false;
50374                 this.checkFails =0;
50375                 // fixme..
50376                 document.location = document.location.toString() + '?ts=' + Math.random();
50377             }
50378               
50379               
50380         }); 
50381     },
50382     
50383     processResponse : function (response)
50384     {
50385         var res = '';
50386         try {
50387             res = Roo.decode(response.responseText);
50388             // oops...
50389             if (typeof(res) != 'object') {
50390                 res = { success : false, errorMsg : res, errors : true };
50391             }
50392             if (typeof(res.success) == 'undefined') {
50393                 res.success = false;
50394             }
50395             
50396         } catch(e) {
50397             res = { success : false,  errorMsg : response.responseText, errors : true };
50398         }
50399         return res;
50400     },
50401     
50402     success : function(response, opts)  // check successfull...
50403     {  
50404         this.sending = false;
50405         var res = this.processResponse(response);
50406         if (!res.success) {
50407             return this.failure(response, opts);
50408         }
50409         if (!res.data || !res.data.id) {
50410             return this.failure(response,opts);
50411         }
50412         //console.log(res);
50413         this.fillAuth(res.data);
50414         
50415         this.checkFails =0;
50416         
50417     },
50418     
50419     
50420     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50421     {
50422         this.authUser = -1;
50423         this.sending = false;
50424         var res = this.processResponse(response);
50425         //console.log(res);
50426         if ( this.checkFails > 2) {
50427         
50428             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50429                 "Error getting authentication status. - try reloading"); 
50430             return;
50431         }
50432         opts.callCfg.again = true;
50433         this.check.defer(1000, this, [ opts.callCfg ]);
50434         return;  
50435     },
50436     
50437     
50438     
50439     fillAuth: function(au) {
50440         this.startAuthCheck();
50441         this.authUserId = au.id;
50442         this.authUser = au;
50443         this.lastChecked = new Date();
50444         this.fireEvent('refreshed', au);
50445         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50446         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50447         au.lang = au.lang || 'en';
50448         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50449         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50450         this.switchLang(au.lang );
50451         
50452      
50453         // open system... - -on setyp..
50454         if (this.authUserId  < 0) {
50455             Roo.MessageBox.alert("Warning", 
50456                 "This is an open system - please set up a admin user with a password.");  
50457         }
50458          
50459         //Pman.onload(); // which should do nothing if it's a re-auth result...
50460         
50461              
50462     },
50463     
50464     startAuthCheck : function() // starter for timeout checking..
50465     {
50466         if (this.intervalID) { // timer already in place...
50467             return false;
50468         }
50469         var _this = this;
50470         this.intervalID =  window.setInterval(function() {
50471               _this.check(false);
50472             }, 120000); // every 120 secs = 2mins..
50473         
50474         
50475     },
50476          
50477     
50478     switchLang : function (lang) 
50479     {
50480         _T = typeof(_T) == 'undefined' ? false : _T;
50481           if (!_T || !lang.length) {
50482             return;
50483         }
50484         
50485         if (!_T && lang != 'en') {
50486             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50487             return;
50488         }
50489         
50490         if (typeof(_T.en) == 'undefined') {
50491             _T.en = {};
50492             Roo.apply(_T.en, _T);
50493         }
50494         
50495         if (typeof(_T[lang]) == 'undefined') {
50496             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50497             return;
50498         }
50499         
50500         
50501         Roo.apply(_T, _T[lang]);
50502         // just need to set the text values for everything...
50503         var _this = this;
50504         /* this will not work ...
50505         if (this.form) { 
50506             
50507                
50508             function formLabel(name, val) {
50509                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50510             }
50511             
50512             formLabel('password', "Password"+':');
50513             formLabel('username', "Email Address"+':');
50514             formLabel('lang', "Language"+':');
50515             this.dialog.setTitle("Login");
50516             this.dialog.buttons[0].setText("Forgot Password");
50517             this.dialog.buttons[1].setText("Login");
50518         }
50519         */
50520         
50521         
50522     },
50523     
50524     
50525     title: "Login",
50526     modal: true,
50527     width:  350,
50528     //height: 230,
50529     height: 180,
50530     shadow: true,
50531     minWidth:200,
50532     minHeight:180,
50533     //proxyDrag: true,
50534     closable: false,
50535     draggable: false,
50536     collapsible: false,
50537     resizable: false,
50538     center: {  // needed??
50539         autoScroll:false,
50540         titlebar: false,
50541        // tabPosition: 'top',
50542         hideTabs: true,
50543         closeOnTab: true,
50544         alwaysShowTabs: false
50545     } ,
50546     listeners : {
50547         
50548         show  : function(dlg)
50549         {
50550             //console.log(this);
50551             this.form = this.layout.getRegion('center').activePanel.form;
50552             this.form.dialog = dlg;
50553             this.buttons[0].form = this.form;
50554             this.buttons[0].dialog = dlg
50555             this.buttons[1].form = this.form;
50556             this.buttons[1].dialog = dlg;
50557            
50558            //this.resizeToLogo.defer(1000,this);
50559             // this is all related to resizing for logos..
50560             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50561            //// if (!sz) {
50562              //   this.resizeToLogo.defer(1000,this);
50563              //   return;
50564            // }
50565             //var w = Ext.lib.Dom.getViewWidth() - 100;
50566             //var h = Ext.lib.Dom.getViewHeight() - 100;
50567             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50568             //this.center();
50569             if (this.disabled) {
50570                 this.hide();
50571                 return;
50572             }
50573             
50574             if (this.user.id < 0) { // used for inital setup situations.
50575                 return;
50576             }
50577             
50578             if (this.intervalID) {
50579                 // remove the timer
50580                 window.clearInterval(this.intervalID);
50581                 this.intervalID = false;
50582             }
50583             
50584             
50585             if (Roo.get('loading')) {
50586                 Roo.get('loading').remove();
50587             }
50588             if (Roo.get('loading-mask')) {
50589                 Roo.get('loading-mask').hide();
50590             }
50591             
50592             //incomming._node = tnode;
50593             this.form.reset();
50594             //this.dialog.modal = !modal;
50595             //this.dialog.show();
50596             this.el.unmask(); 
50597             
50598             
50599             this.form.setValues({
50600                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50601                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50602             });
50603             
50604             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50605             if (this.form.findField('username').getValue().length > 0 ){
50606                 this.form.findField('password').focus();
50607             } else {
50608                this.form.findField('username').focus();
50609             }
50610     
50611         }
50612     },
50613     items : [
50614          {
50615        
50616             xtype : 'ContentPanel',
50617             xns : Roo,
50618             region: 'center',
50619             fitToFrame : true,
50620             
50621             items : [
50622     
50623                 {
50624                
50625                     xtype : 'Form',
50626                     xns : Roo.form,
50627                     labelWidth: 100,
50628                     style : 'margin: 10px;',
50629                     
50630                     listeners : {
50631                         actionfailed : function(f, act) {
50632                             // form can return { errors: .... }
50633                                 
50634                             //act.result.errors // invalid form element list...
50635                             //act.result.errorMsg// invalid form element list...
50636                             
50637                             this.dialog.el.unmask();
50638                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50639                                         "Login failed - communication error - try again.");
50640                                       
50641                         },
50642                         actioncomplete: function(re, act) {
50643                              
50644                             Roo.state.Manager.set(
50645                                 this.dialog.realm + '.username',  
50646                                     this.findField('username').getValue()
50647                             );
50648                             Roo.state.Manager.set(
50649                                 this.dialog.realm + '.lang',  
50650                                 this.findField('lang').getValue() 
50651                             );
50652                             
50653                             this.dialog.fillAuth(act.result.data);
50654                               
50655                             this.dialog.hide();
50656                             
50657                             if (Roo.get('loading-mask')) {
50658                                 Roo.get('loading-mask').show();
50659                             }
50660                             Roo.XComponent.build();
50661                             
50662                              
50663                             
50664                         }
50665                     },
50666                     items : [
50667                         {
50668                             xtype : 'TextField',
50669                             xns : Roo.form,
50670                             fieldLabel: "Email Address",
50671                             name: 'username',
50672                             width:200,
50673                             autoCreate : {tag: "input", type: "text", size: "20"}
50674                         },
50675                         {
50676                             xtype : 'TextField',
50677                             xns : Roo.form,
50678                             fieldLabel: "Password",
50679                             inputType: 'password',
50680                             name: 'password',
50681                             width:200,
50682                             autoCreate : {tag: "input", type: "text", size: "20"},
50683                             listeners : {
50684                                 specialkey : function(e,ev) {
50685                                     if (ev.keyCode == 13) {
50686                                         this.form.dialog.el.mask("Logging in");
50687                                         this.form.doAction('submit', {
50688                                             url: this.form.dialog.url,
50689                                             method: this.form.dialog.method,
50690                                         });
50691                                     }
50692                                 }
50693                             }  
50694                         },
50695                         {
50696                             xtype : 'ComboBox',
50697                             xns : Roo.form,
50698                             fieldLabel: "Language",
50699                             name : 'langdisp',
50700                             store: {
50701                                 xtype : 'SimpleStore',
50702                                 fields: ['lang', 'ldisp'],
50703                                 data : [
50704                                     [ 'en', 'English' ],
50705                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50706                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50707                                 ]
50708                             },
50709                             
50710                             valueField : 'lang',
50711                             hiddenName:  'lang',
50712                             width: 200,
50713                             displayField:'ldisp',
50714                             typeAhead: false,
50715                             editable: false,
50716                             mode: 'local',
50717                             triggerAction: 'all',
50718                             emptyText:'Select a Language...',
50719                             selectOnFocus:true,
50720                             listeners : {
50721                                 select :  function(cb, rec, ix) {
50722                                     this.form.switchLang(rec.data.lang);
50723                                 }
50724                             }
50725                         
50726                         }
50727                     ]
50728                 }
50729                   
50730                 
50731             ]
50732         }
50733     ],
50734     buttons : [
50735         {
50736             xtype : 'Button',
50737             xns : 'Roo',
50738             text : "Forgot Password",
50739             listeners : {
50740                 click : function() {
50741                     //console.log(this);
50742                     var n = this.form.findField('username').getValue();
50743                     if (!n.length) {
50744                         Roo.MessageBox.alert("Error", "Fill in your email address");
50745                         return;
50746                     }
50747                     Roo.Ajax.request({
50748                         url: this.dialog.url,
50749                         params: {
50750                             passwordRequest: n
50751                         },
50752                         method: this.dialog.method,
50753                         success:  function(response, opts)  {  // check successfull...
50754                         
50755                             var res = this.dialog.processResponse(response);
50756                             if (!res.success) { // error!
50757                                Roo.MessageBox.alert("Error" ,
50758                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50759                                return;
50760                             }
50761                             Roo.MessageBox.alert("Notice" ,
50762                                 "Please check you email for the Password Reset message");
50763                         },
50764                         failure : function() {
50765                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50766                         }
50767                         
50768                     });
50769                 }
50770             }
50771         },
50772         {
50773             xtype : 'Button',
50774             xns : 'Roo',
50775             text : "Login",
50776             listeners : {
50777                 
50778                 click : function () {
50779                         
50780                     this.dialog.el.mask("Logging in");
50781                     this.form.doAction('submit', {
50782                             url: this.dialog.url,
50783                             method: this.dialog.method
50784                     });
50785                 }
50786             }
50787         }
50788     ]
50789   
50790   
50791 })
50792  
50793
50794
50795