sync
[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          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * 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.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * 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]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac))
571     });
572
573
574 })();
575
576 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
577                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
578 /*
579  * Based on:
580  * Ext JS Library 1.1.1
581  * Copyright(c) 2006-2007, Ext JS, LLC.
582  *
583  * Originally Released Under LGPL - original licence link has changed is not relivant.
584  *
585  * Fork - LGPL
586  * <script type="text/javascript">
587  */
588
589 (function() {    
590     // wrappedn so fnCleanup is not in global scope...
591     if(Roo.isIE) {
592         function fnCleanUp() {
593             var p = Function.prototype;
594             delete p.createSequence;
595             delete p.defer;
596             delete p.createDelegate;
597             delete p.createCallback;
598             delete p.createInterceptor;
599
600             window.detachEvent("onunload", fnCleanUp);
601         }
602         window.attachEvent("onunload", fnCleanUp);
603     }
604 })();
605
606
607 /**
608  * @class Function
609  * These functions are available on every Function object (any JavaScript function).
610  */
611 Roo.apply(Function.prototype, {
612      /**
613      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
614      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
615      * Will create a function that is bound to those 2 args.
616      * @return {Function} The new function
617     */
618     createCallback : function(/*args...*/){
619         // make args available, in function below
620         var args = arguments;
621         var method = this;
622         return function() {
623             return method.apply(window, args);
624         };
625     },
626
627     /**
628      * Creates a delegate (callback) that sets the scope to obj.
629      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
630      * Will create a function that is automatically scoped to this.
631      * @param {Object} obj (optional) The object for which the scope is set
632      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
633      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
634      *                                             if a number the args are inserted at the specified position
635      * @return {Function} The new function
636      */
637     createDelegate : function(obj, args, appendArgs){
638         var method = this;
639         return function() {
640             var callArgs = args || arguments;
641             if(appendArgs === true){
642                 callArgs = Array.prototype.slice.call(arguments, 0);
643                 callArgs = callArgs.concat(args);
644             }else if(typeof appendArgs == "number"){
645                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
646                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
647                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
648             }
649             return method.apply(obj || window, callArgs);
650         };
651     },
652
653     /**
654      * Calls this function after the number of millseconds specified.
655      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
656      * @param {Object} obj (optional) The object for which the scope is set
657      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
658      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
659      *                                             if a number the args are inserted at the specified position
660      * @return {Number} The timeout id that can be used with clearTimeout
661      */
662     defer : function(millis, obj, args, appendArgs){
663         var fn = this.createDelegate(obj, args, appendArgs);
664         if(millis){
665             return setTimeout(fn, millis);
666         }
667         fn();
668         return 0;
669     },
670     /**
671      * Create a combined function call sequence of the original function + the passed function.
672      * The resulting function returns the results of the original function.
673      * The passed fcn is called with the parameters of the original function
674      * @param {Function} fcn The function to sequence
675      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
676      * @return {Function} The new function
677      */
678     createSequence : function(fcn, scope){
679         if(typeof fcn != "function"){
680             return this;
681         }
682         var method = this;
683         return function() {
684             var retval = method.apply(this || window, arguments);
685             fcn.apply(scope || this || window, arguments);
686             return retval;
687         };
688     },
689
690     /**
691      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
692      * The resulting function returns the results of the original function.
693      * The passed fcn is called with the parameters of the original function.
694      * @addon
695      * @param {Function} fcn The function to call before the original
696      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
697      * @return {Function} The new function
698      */
699     createInterceptor : function(fcn, scope){
700         if(typeof fcn != "function"){
701             return this;
702         }
703         var method = this;
704         return function() {
705             fcn.target = this;
706             fcn.method = method;
707             if(fcn.apply(scope || this || window, arguments) === false){
708                 return;
709             }
710             return method.apply(this || window, arguments);
711         };
712     }
713 });
714 /*
715  * Based on:
716  * Ext JS Library 1.1.1
717  * Copyright(c) 2006-2007, Ext JS, LLC.
718  *
719  * Originally Released Under LGPL - original licence link has changed is not relivant.
720  *
721  * Fork - LGPL
722  * <script type="text/javascript">
723  */
724
725 Roo.applyIf(String, {
726     
727     /** @scope String */
728     
729     /**
730      * Escapes the passed string for ' and \
731      * @param {String} string The string to escape
732      * @return {String} The escaped string
733      * @static
734      */
735     escape : function(string) {
736         return string.replace(/('|\\)/g, "\\$1");
737     },
738
739     /**
740      * Pads the left side of a string with a specified character.  This is especially useful
741      * for normalizing number and date strings.  Example usage:
742      * <pre><code>
743 var s = String.leftPad('123', 5, '0');
744 // s now contains the string: '00123'
745 </code></pre>
746      * @param {String} string The original string
747      * @param {Number} size The total length of the output string
748      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
749      * @return {String} The padded string
750      * @static
751      */
752     leftPad : function (val, size, ch) {
753         var result = new String(val);
754         if(ch === null || ch === undefined || ch === '') {
755             ch = " ";
756         }
757         while (result.length < size) {
758             result = ch + result;
759         }
760         return result;
761     },
762
763     /**
764      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
765      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
766      * <pre><code>
767 var cls = 'my-class', text = 'Some text';
768 var s = String.format('<div class="{0}">{1}</div>', cls, text);
769 // s now contains the string: '<div class="my-class">Some text</div>'
770 </code></pre>
771      * @param {String} string The tokenized string to be formatted
772      * @param {String} value1 The value to replace token {0}
773      * @param {String} value2 Etc...
774      * @return {String} The formatted string
775      * @static
776      */
777     format : function(format){
778         var args = Array.prototype.slice.call(arguments, 1);
779         return format.replace(/\{(\d+)\}/g, function(m, i){
780             return Roo.util.Format.htmlEncode(args[i]);
781         });
782     }
783 });
784
785 /**
786  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
787  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
788  * they are already different, the first value passed in is returned.  Note that this method returns the new value
789  * but does not change the current string.
790  * <pre><code>
791 // alternate sort directions
792 sort = sort.toggle('ASC', 'DESC');
793
794 // instead of conditional logic:
795 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
796 </code></pre>
797  * @param {String} value The value to compare to the current string
798  * @param {String} other The new value to use if the string already equals the first value passed in
799  * @return {String} The new value
800  */
801  
802 String.prototype.toggle = function(value, other){
803     return this == value ? other : value;
804 };/*
805  * Based on:
806  * Ext JS Library 1.1.1
807  * Copyright(c) 2006-2007, Ext JS, LLC.
808  *
809  * Originally Released Under LGPL - original licence link has changed is not relivant.
810  *
811  * Fork - LGPL
812  * <script type="text/javascript">
813  */
814
815  /**
816  * @class Number
817  */
818 Roo.applyIf(Number.prototype, {
819     /**
820      * Checks whether or not the current number is within a desired range.  If the number is already within the
821      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
822      * exceeded.  Note that this method returns the constrained value but does not change the current number.
823      * @param {Number} min The minimum number in the range
824      * @param {Number} max The maximum number in the range
825      * @return {Number} The constrained value if outside the range, otherwise the current value
826      */
827     constrain : function(min, max){
828         return Math.min(Math.max(this, min), max);
829     }
830 });/*
831  * Based on:
832  * Ext JS Library 1.1.1
833  * Copyright(c) 2006-2007, Ext JS, LLC.
834  *
835  * Originally Released Under LGPL - original licence link has changed is not relivant.
836  *
837  * Fork - LGPL
838  * <script type="text/javascript">
839  */
840  /**
841  * @class Array
842  */
843 Roo.applyIf(Array.prototype, {
844     /**
845      * Checks whether or not the specified object exists in the array.
846      * @param {Object} o The object to check for
847      * @return {Number} The index of o in the array (or -1 if it is not found)
848      */
849     indexOf : function(o){
850        for (var i = 0, len = this.length; i < len; i++){
851               if(this[i] == o) return i;
852        }
853            return -1;
854     },
855
856     /**
857      * Removes the specified object from the array.  If the object is not found nothing happens.
858      * @param {Object} o The object to remove
859      */
860     remove : function(o){
861        var index = this.indexOf(o);
862        if(index != -1){
863            this.splice(index, 1);
864        }
865     },
866     /**
867      * Map (JS 1.6 compatibility)
868      * @param {Function} function  to call
869      */
870     map : function(fun )
871     {
872         var len = this.length >>> 0;
873         if (typeof fun != "function")
874             throw new TypeError();
875
876         var res = new Array(len);
877         var thisp = arguments[1];
878         for (var i = 0; i < len; i++)
879         {
880             if (i in this)
881                 res[i] = fun.call(thisp, this[i], i, this);
882         }
883
884         return res;
885     }
886     
887 });
888
889
890  /*
891  * Based on:
892  * Ext JS Library 1.1.1
893  * Copyright(c) 2006-2007, Ext JS, LLC.
894  *
895  * Originally Released Under LGPL - original licence link has changed is not relivant.
896  *
897  * Fork - LGPL
898  * <script type="text/javascript">
899  */
900
901 /**
902  * @class Date
903  *
904  * The date parsing and format syntax is a subset of
905  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
906  * supported will provide results equivalent to their PHP versions.
907  *
908  * Following is the list of all currently supported formats:
909  *<pre>
910 Sample date:
911 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
912
913 Format  Output      Description
914 ------  ----------  --------------------------------------------------------------
915   d      10         Day of the month, 2 digits with leading zeros
916   D      Wed        A textual representation of a day, three letters
917   j      10         Day of the month without leading zeros
918   l      Wednesday  A full textual representation of the day of the week
919   S      th         English ordinal day of month suffix, 2 chars (use with j)
920   w      3          Numeric representation of the day of the week
921   z      9          The julian date, or day of the year (0-365)
922   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
923   F      January    A full textual representation of the month
924   m      01         Numeric representation of a month, with leading zeros
925   M      Jan        Month name abbreviation, three letters
926   n      1          Numeric representation of a month, without leading zeros
927   t      31         Number of days in the given month
928   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
929   Y      2007       A full numeric representation of a year, 4 digits
930   y      07         A two digit representation of a year
931   a      pm         Lowercase Ante meridiem and Post meridiem
932   A      PM         Uppercase Ante meridiem and Post meridiem
933   g      3          12-hour format of an hour without leading zeros
934   G      15         24-hour format of an hour without leading zeros
935   h      03         12-hour format of an hour with leading zeros
936   H      15         24-hour format of an hour with leading zeros
937   i      05         Minutes with leading zeros
938   s      01         Seconds, with leading zeros
939   O      -0600      Difference to Greenwich time (GMT) in hours
940   T      CST        Timezone setting of the machine running the code
941   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
942 </pre>
943  *
944  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
945  * <pre><code>
946 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
947 document.write(dt.format('Y-m-d'));                         //2007-01-10
948 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
949 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
950  </code></pre>
951  *
952  * Here are some standard date/time patterns that you might find helpful.  They
953  * are not part of the source of Date.js, but to use them you can simply copy this
954  * block of code into any script that is included after Date.js and they will also become
955  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
956  * <pre><code>
957 Date.patterns = {
958     ISO8601Long:"Y-m-d H:i:s",
959     ISO8601Short:"Y-m-d",
960     ShortDate: "n/j/Y",
961     LongDate: "l, F d, Y",
962     FullDateTime: "l, F d, Y g:i:s A",
963     MonthDay: "F d",
964     ShortTime: "g:i A",
965     LongTime: "g:i:s A",
966     SortableDateTime: "Y-m-d\\TH:i:s",
967     UniversalSortableDateTime: "Y-m-d H:i:sO",
968     YearMonth: "F, Y"
969 };
970 </code></pre>
971  *
972  * Example usage:
973  * <pre><code>
974 var dt = new Date();
975 document.write(dt.format(Date.patterns.ShortDate));
976  </code></pre>
977  */
978
979 /*
980  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
981  * They generate precompiled functions from date formats instead of parsing and
982  * processing the pattern every time you format a date.  These functions are available
983  * on every Date object (any javascript function).
984  *
985  * The original article and download are here:
986  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
987  *
988  */
989  
990  
991  // was in core
992 /**
993  Returns the number of milliseconds between this date and date
994  @param {Date} date (optional) Defaults to now
995  @return {Number} The diff in milliseconds
996  @member Date getElapsed
997  */
998 Date.prototype.getElapsed = function(date) {
999         return Math.abs((date || new Date()).getTime()-this.getTime());
1000 };
1001 // was in date file..
1002
1003
1004 // private
1005 Date.parseFunctions = {count:0};
1006 // private
1007 Date.parseRegexes = [];
1008 // private
1009 Date.formatFunctions = {count:0};
1010
1011 // private
1012 Date.prototype.dateFormat = function(format) {
1013     if (Date.formatFunctions[format] == null) {
1014         Date.createNewFormat(format);
1015     }
1016     var func = Date.formatFunctions[format];
1017     return this[func]();
1018 };
1019
1020
1021 /**
1022  * Formats a date given the supplied format string
1023  * @param {String} format The format string
1024  * @return {String} The formatted date
1025  * @method
1026  */
1027 Date.prototype.format = Date.prototype.dateFormat;
1028
1029 // private
1030 Date.createNewFormat = function(format) {
1031     var funcName = "format" + Date.formatFunctions.count++;
1032     Date.formatFunctions[format] = funcName;
1033     var code = "Date.prototype." + funcName + " = function(){return ";
1034     var special = false;
1035     var ch = '';
1036     for (var i = 0; i < format.length; ++i) {
1037         ch = format.charAt(i);
1038         if (!special && ch == "\\") {
1039             special = true;
1040         }
1041         else if (special) {
1042             special = false;
1043             code += "'" + String.escape(ch) + "' + ";
1044         }
1045         else {
1046             code += Date.getFormatCode(ch);
1047         }
1048     }
1049     /** eval:var:zzzzzzzzzzzzz */
1050     eval(code.substring(0, code.length - 3) + ";}");
1051 };
1052
1053 // private
1054 Date.getFormatCode = function(character) {
1055     switch (character) {
1056     case "d":
1057         return "String.leftPad(this.getDate(), 2, '0') + ";
1058     case "D":
1059         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1060     case "j":
1061         return "this.getDate() + ";
1062     case "l":
1063         return "Date.dayNames[this.getDay()] + ";
1064     case "S":
1065         return "this.getSuffix() + ";
1066     case "w":
1067         return "this.getDay() + ";
1068     case "z":
1069         return "this.getDayOfYear() + ";
1070     case "W":
1071         return "this.getWeekOfYear() + ";
1072     case "F":
1073         return "Date.monthNames[this.getMonth()] + ";
1074     case "m":
1075         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1076     case "M":
1077         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1078     case "n":
1079         return "(this.getMonth() + 1) + ";
1080     case "t":
1081         return "this.getDaysInMonth() + ";
1082     case "L":
1083         return "(this.isLeapYear() ? 1 : 0) + ";
1084     case "Y":
1085         return "this.getFullYear() + ";
1086     case "y":
1087         return "('' + this.getFullYear()).substring(2, 4) + ";
1088     case "a":
1089         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1090     case "A":
1091         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1092     case "g":
1093         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1094     case "G":
1095         return "this.getHours() + ";
1096     case "h":
1097         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1098     case "H":
1099         return "String.leftPad(this.getHours(), 2, '0') + ";
1100     case "i":
1101         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1102     case "s":
1103         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1104     case "O":
1105         return "this.getGMTOffset() + ";
1106     case "T":
1107         return "this.getTimezone() + ";
1108     case "Z":
1109         return "(this.getTimezoneOffset() * -60) + ";
1110     default:
1111         return "'" + String.escape(character) + "' + ";
1112     }
1113 };
1114
1115 /**
1116  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1117  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1118  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1119  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1120  * string or the parse operation will fail.
1121  * Example Usage:
1122 <pre><code>
1123 //dt = Fri May 25 2007 (current date)
1124 var dt = new Date();
1125
1126 //dt = Thu May 25 2006 (today's month/day in 2006)
1127 dt = Date.parseDate("2006", "Y");
1128
1129 //dt = Sun Jan 15 2006 (all date parts specified)
1130 dt = Date.parseDate("2006-1-15", "Y-m-d");
1131
1132 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1133 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1134 </code></pre>
1135  * @param {String} input The unparsed date as a string
1136  * @param {String} format The format the date is in
1137  * @return {Date} The parsed date
1138  * @static
1139  */
1140 Date.parseDate = function(input, format) {
1141     if (Date.parseFunctions[format] == null) {
1142         Date.createParser(format);
1143     }
1144     var func = Date.parseFunctions[format];
1145     return Date[func](input);
1146 };
1147 /**
1148  * @private
1149  */
1150 Date.createParser = function(format) {
1151     var funcName = "parse" + Date.parseFunctions.count++;
1152     var regexNum = Date.parseRegexes.length;
1153     var currentGroup = 1;
1154     Date.parseFunctions[format] = funcName;
1155
1156     var code = "Date." + funcName + " = function(input){\n"
1157         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1158         + "var d = new Date();\n"
1159         + "y = d.getFullYear();\n"
1160         + "m = d.getMonth();\n"
1161         + "d = d.getDate();\n"
1162         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1163         + "if (results && results.length > 0) {";
1164     var regex = "";
1165
1166     var special = false;
1167     var ch = '';
1168     for (var i = 0; i < format.length; ++i) {
1169         ch = format.charAt(i);
1170         if (!special && ch == "\\") {
1171             special = true;
1172         }
1173         else if (special) {
1174             special = false;
1175             regex += String.escape(ch);
1176         }
1177         else {
1178             var obj = Date.formatCodeToRegex(ch, currentGroup);
1179             currentGroup += obj.g;
1180             regex += obj.s;
1181             if (obj.g && obj.c) {
1182                 code += obj.c;
1183             }
1184         }
1185     }
1186
1187     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1188         + "{v = new Date(y, m, d, h, i, s);}\n"
1189         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1190         + "{v = new Date(y, m, d, h, i);}\n"
1191         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1192         + "{v = new Date(y, m, d, h);}\n"
1193         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1194         + "{v = new Date(y, m, d);}\n"
1195         + "else if (y >= 0 && m >= 0)\n"
1196         + "{v = new Date(y, m);}\n"
1197         + "else if (y >= 0)\n"
1198         + "{v = new Date(y);}\n"
1199         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1200         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1201         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1202         + ";}";
1203
1204     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1205     /** eval:var:zzzzzzzzzzzzz */
1206     eval(code);
1207 };
1208
1209 // private
1210 Date.formatCodeToRegex = function(character, currentGroup) {
1211     switch (character) {
1212     case "D":
1213         return {g:0,
1214         c:null,
1215         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1216     case "j":
1217         return {g:1,
1218             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1219             s:"(\\d{1,2})"}; // day of month without leading zeroes
1220     case "d":
1221         return {g:1,
1222             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1223             s:"(\\d{2})"}; // day of month with leading zeroes
1224     case "l":
1225         return {g:0,
1226             c:null,
1227             s:"(?:" + Date.dayNames.join("|") + ")"};
1228     case "S":
1229         return {g:0,
1230             c:null,
1231             s:"(?:st|nd|rd|th)"};
1232     case "w":
1233         return {g:0,
1234             c:null,
1235             s:"\\d"};
1236     case "z":
1237         return {g:0,
1238             c:null,
1239             s:"(?:\\d{1,3})"};
1240     case "W":
1241         return {g:0,
1242             c:null,
1243             s:"(?:\\d{2})"};
1244     case "F":
1245         return {g:1,
1246             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1247             s:"(" + Date.monthNames.join("|") + ")"};
1248     case "M":
1249         return {g:1,
1250             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1251             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1252     case "n":
1253         return {g:1,
1254             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1255             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1256     case "m":
1257         return {g:1,
1258             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1259             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1260     case "t":
1261         return {g:0,
1262             c:null,
1263             s:"\\d{1,2}"};
1264     case "L":
1265         return {g:0,
1266             c:null,
1267             s:"(?:1|0)"};
1268     case "Y":
1269         return {g:1,
1270             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1271             s:"(\\d{4})"};
1272     case "y":
1273         return {g:1,
1274             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1275                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1276             s:"(\\d{1,2})"};
1277     case "a":
1278         return {g:1,
1279             c:"if (results[" + currentGroup + "] == 'am') {\n"
1280                 + "if (h == 12) { h = 0; }\n"
1281                 + "} else { if (h < 12) { h += 12; }}",
1282             s:"(am|pm)"};
1283     case "A":
1284         return {g:1,
1285             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1286                 + "if (h == 12) { h = 0; }\n"
1287                 + "} else { if (h < 12) { h += 12; }}",
1288             s:"(AM|PM)"};
1289     case "g":
1290     case "G":
1291         return {g:1,
1292             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1294     case "h":
1295     case "H":
1296         return {g:1,
1297             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1299     case "i":
1300         return {g:1,
1301             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"};
1303     case "s":
1304         return {g:1,
1305             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1306             s:"(\\d{2})"};
1307     case "O":
1308         return {g:1,
1309             c:[
1310                 "o = results[", currentGroup, "];\n",
1311                 "var sn = o.substring(0,1);\n", // get + / - sign
1312                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1313                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1314                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1315                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1316             ].join(""),
1317             s:"([+\-]\\d{4})"};
1318     case "T":
1319         return {g:0,
1320             c:null,
1321             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1322     case "Z":
1323         return {g:1,
1324             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1325                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1326             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1327     default:
1328         return {g:0,
1329             c:null,
1330             s:String.escape(character)};
1331     }
1332 };
1333
1334 /**
1335  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1336  * @return {String} The abbreviated timezone name (e.g. 'CST')
1337  */
1338 Date.prototype.getTimezone = function() {
1339     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1340 };
1341
1342 /**
1343  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1344  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1345  */
1346 Date.prototype.getGMTOffset = function() {
1347     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1348         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1349         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1350 };
1351
1352 /**
1353  * Get the numeric day number of the year, adjusted for leap year.
1354  * @return {Number} 0 through 364 (365 in leap years)
1355  */
1356 Date.prototype.getDayOfYear = function() {
1357     var num = 0;
1358     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1359     for (var i = 0; i < this.getMonth(); ++i) {
1360         num += Date.daysInMonth[i];
1361     }
1362     return num + this.getDate() - 1;
1363 };
1364
1365 /**
1366  * Get the string representation of the numeric week number of the year
1367  * (equivalent to the format specifier 'W').
1368  * @return {String} '00' through '52'
1369  */
1370 Date.prototype.getWeekOfYear = function() {
1371     // Skip to Thursday of this week
1372     var now = this.getDayOfYear() + (4 - this.getDay());
1373     // Find the first Thursday of the year
1374     var jan1 = new Date(this.getFullYear(), 0, 1);
1375     var then = (7 - jan1.getDay() + 4);
1376     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1377 };
1378
1379 /**
1380  * Whether or not the current date is in a leap year.
1381  * @return {Boolean} True if the current date is in a leap year, else false
1382  */
1383 Date.prototype.isLeapYear = function() {
1384     var year = this.getFullYear();
1385     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1386 };
1387
1388 /**
1389  * Get the first day of the current month, adjusted for leap year.  The returned value
1390  * is the numeric day index within the week (0-6) which can be used in conjunction with
1391  * the {@link #monthNames} array to retrieve the textual day name.
1392  * Example:
1393  *<pre><code>
1394 var dt = new Date('1/10/2007');
1395 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1396 </code></pre>
1397  * @return {Number} The day number (0-6)
1398  */
1399 Date.prototype.getFirstDayOfMonth = function() {
1400     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1401     return (day < 0) ? (day + 7) : day;
1402 };
1403
1404 /**
1405  * Get the last day of the current month, adjusted for leap year.  The returned value
1406  * is the numeric day index within the week (0-6) which can be used in conjunction with
1407  * the {@link #monthNames} array to retrieve the textual day name.
1408  * Example:
1409  *<pre><code>
1410 var dt = new Date('1/10/2007');
1411 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getLastDayOfMonth = function() {
1416     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420
1421 /**
1422  * Get the first date of this date's month
1423  * @return {Date}
1424  */
1425 Date.prototype.getFirstDateOfMonth = function() {
1426     return new Date(this.getFullYear(), this.getMonth(), 1);
1427 };
1428
1429 /**
1430  * Get the last date of this date's month
1431  * @return {Date}
1432  */
1433 Date.prototype.getLastDateOfMonth = function() {
1434     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1435 };
1436 /**
1437  * Get the number of days in the current month, adjusted for leap year.
1438  * @return {Number} The number of days in the month
1439  */
1440 Date.prototype.getDaysInMonth = function() {
1441     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1442     return Date.daysInMonth[this.getMonth()];
1443 };
1444
1445 /**
1446  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1447  * @return {String} 'st, 'nd', 'rd' or 'th'
1448  */
1449 Date.prototype.getSuffix = function() {
1450     switch (this.getDate()) {
1451         case 1:
1452         case 21:
1453         case 31:
1454             return "st";
1455         case 2:
1456         case 22:
1457             return "nd";
1458         case 3:
1459         case 23:
1460             return "rd";
1461         default:
1462             return "th";
1463     }
1464 };
1465
1466 // private
1467 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1468
1469 /**
1470  * An array of textual month names.
1471  * Override these values for international dates, for example...
1472  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1473  * @type Array
1474  * @static
1475  */
1476 Date.monthNames =
1477    ["January",
1478     "February",
1479     "March",
1480     "April",
1481     "May",
1482     "June",
1483     "July",
1484     "August",
1485     "September",
1486     "October",
1487     "November",
1488     "December"];
1489
1490 /**
1491  * An array of textual day names.
1492  * Override these values for international dates, for example...
1493  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1494  * @type Array
1495  * @static
1496  */
1497 Date.dayNames =
1498    ["Sunday",
1499     "Monday",
1500     "Tuesday",
1501     "Wednesday",
1502     "Thursday",
1503     "Friday",
1504     "Saturday"];
1505
1506 // private
1507 Date.y2kYear = 50;
1508 // private
1509 Date.monthNumbers = {
1510     Jan:0,
1511     Feb:1,
1512     Mar:2,
1513     Apr:3,
1514     May:4,
1515     Jun:5,
1516     Jul:6,
1517     Aug:7,
1518     Sep:8,
1519     Oct:9,
1520     Nov:10,
1521     Dec:11};
1522
1523 /**
1524  * Creates and returns a new Date instance with the exact same date value as the called instance.
1525  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1526  * variable will also be changed.  When the intention is to create a new variable that will not
1527  * modify the original instance, you should create a clone.
1528  *
1529  * Example of correctly cloning a date:
1530  * <pre><code>
1531 //wrong way:
1532 var orig = new Date('10/1/2006');
1533 var copy = orig;
1534 copy.setDate(5);
1535 document.write(orig);  //returns 'Thu Oct 05 2006'!
1536
1537 //correct way:
1538 var orig = new Date('10/1/2006');
1539 var copy = orig.clone();
1540 copy.setDate(5);
1541 document.write(orig);  //returns 'Thu Oct 01 2006'
1542 </code></pre>
1543  * @return {Date} The new Date instance
1544  */
1545 Date.prototype.clone = function() {
1546         return new Date(this.getTime());
1547 };
1548
1549 /**
1550  * Clears any time information from this date
1551  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1552  @return {Date} this or the clone
1553  */
1554 Date.prototype.clearTime = function(clone){
1555     if(clone){
1556         return this.clone().clearTime();
1557     }
1558     this.setHours(0);
1559     this.setMinutes(0);
1560     this.setSeconds(0);
1561     this.setMilliseconds(0);
1562     return this;
1563 };
1564
1565 // private
1566 // safari setMonth is broken
1567 if(Roo.isSafari){
1568     Date.brokenSetMonth = Date.prototype.setMonth;
1569         Date.prototype.setMonth = function(num){
1570                 if(num <= -1){
1571                         var n = Math.ceil(-num);
1572                         var back_year = Math.ceil(n/12);
1573                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1574                         this.setFullYear(this.getFullYear() - back_year);
1575                         return Date.brokenSetMonth.call(this, month);
1576                 } else {
1577                         return Date.brokenSetMonth.apply(this, arguments);
1578                 }
1579         };
1580 }
1581
1582 /** Date interval constant 
1583 * @static 
1584 * @type String */
1585 Date.MILLI = "ms";
1586 /** Date interval constant 
1587 * @static 
1588 * @type String */
1589 Date.SECOND = "s";
1590 /** Date interval constant 
1591 * @static 
1592 * @type String */
1593 Date.MINUTE = "mi";
1594 /** Date interval constant 
1595 * @static 
1596 * @type String */
1597 Date.HOUR = "h";
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.DAY = "d";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.MONTH = "mo";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.YEAR = "y";
1610
1611 /**
1612  * Provides a convenient method of performing basic date arithmetic.  This method
1613  * does not modify the Date instance being called - it creates and returns
1614  * a new Date instance containing the resulting date value.
1615  *
1616  * Examples:
1617  * <pre><code>
1618 //Basic usage:
1619 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1620 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1621
1622 //Negative values will subtract correctly:
1623 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1624 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1625
1626 //You can even chain several calls together in one line!
1627 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1628 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1629  </code></pre>
1630  *
1631  * @param {String} interval   A valid date interval enum value
1632  * @param {Number} value      The amount to add to the current date
1633  * @return {Date} The new Date instance
1634  */
1635 Date.prototype.add = function(interval, value){
1636   var d = this.clone();
1637   if (!interval || value === 0) return d;
1638   switch(interval.toLowerCase()){
1639     case Date.MILLI:
1640       d.setMilliseconds(this.getMilliseconds() + value);
1641       break;
1642     case Date.SECOND:
1643       d.setSeconds(this.getSeconds() + value);
1644       break;
1645     case Date.MINUTE:
1646       d.setMinutes(this.getMinutes() + value);
1647       break;
1648     case Date.HOUR:
1649       d.setHours(this.getHours() + value);
1650       break;
1651     case Date.DAY:
1652       d.setDate(this.getDate() + value);
1653       break;
1654     case Date.MONTH:
1655       var day = this.getDate();
1656       if(day > 28){
1657           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1658       }
1659       d.setDate(day);
1660       d.setMonth(this.getMonth() + value);
1661       break;
1662     case Date.YEAR:
1663       d.setFullYear(this.getFullYear() + value);
1664       break;
1665   }
1666   return d;
1667 };/*
1668  * Based on:
1669  * Ext JS Library 1.1.1
1670  * Copyright(c) 2006-2007, Ext JS, LLC.
1671  *
1672  * Originally Released Under LGPL - original licence link has changed is not relivant.
1673  *
1674  * Fork - LGPL
1675  * <script type="text/javascript">
1676  */
1677
1678 Roo.lib.Dom = {
1679     getViewWidth : function(full) {
1680         return full ? this.getDocumentWidth() : this.getViewportWidth();
1681     },
1682
1683     getViewHeight : function(full) {
1684         return full ? this.getDocumentHeight() : this.getViewportHeight();
1685     },
1686
1687     getDocumentHeight: function() {
1688         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1689         return Math.max(scrollHeight, this.getViewportHeight());
1690     },
1691
1692     getDocumentWidth: function() {
1693         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1694         return Math.max(scrollWidth, this.getViewportWidth());
1695     },
1696
1697     getViewportHeight: function() {
1698         var height = self.innerHeight;
1699         var mode = document.compatMode;
1700
1701         if ((mode || Roo.isIE) && !Roo.isOpera) {
1702             height = (mode == "CSS1Compat") ?
1703                      document.documentElement.clientHeight :
1704                      document.body.clientHeight;
1705         }
1706
1707         return height;
1708     },
1709
1710     getViewportWidth: function() {
1711         var width = self.innerWidth;
1712         var mode = document.compatMode;
1713
1714         if (mode || Roo.isIE) {
1715             width = (mode == "CSS1Compat") ?
1716                     document.documentElement.clientWidth :
1717                     document.body.clientWidth;
1718         }
1719         return width;
1720     },
1721
1722     isAncestor : function(p, c) {
1723         p = Roo.getDom(p);
1724         c = Roo.getDom(c);
1725         if (!p || !c) {
1726             return false;
1727         }
1728
1729         if (p.contains && !Roo.isSafari) {
1730             return p.contains(c);
1731         } else if (p.compareDocumentPosition) {
1732             return !!(p.compareDocumentPosition(c) & 16);
1733         } else {
1734             var parent = c.parentNode;
1735             while (parent) {
1736                 if (parent == p) {
1737                     return true;
1738                 }
1739                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1740                     return false;
1741                 }
1742                 parent = parent.parentNode;
1743             }
1744             return false;
1745         }
1746     },
1747
1748     getRegion : function(el) {
1749         return Roo.lib.Region.getRegion(el);
1750     },
1751
1752     getY : function(el) {
1753         return this.getXY(el)[1];
1754     },
1755
1756     getX : function(el) {
1757         return this.getXY(el)[0];
1758     },
1759
1760     getXY : function(el) {
1761         var p, pe, b, scroll, bd = document.body;
1762         el = Roo.getDom(el);
1763         var fly = Roo.lib.AnimBase.fly;
1764         if (el.getBoundingClientRect) {
1765             b = el.getBoundingClientRect();
1766             scroll = fly(document).getScroll();
1767             return [b.left + scroll.left, b.top + scroll.top];
1768         }
1769         var x = 0, y = 0;
1770
1771         p = el;
1772
1773         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1774
1775         while (p) {
1776
1777             x += p.offsetLeft;
1778             y += p.offsetTop;
1779
1780             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1781                 hasAbsolute = true;
1782             }
1783
1784             if (Roo.isGecko) {
1785                 pe = fly(p);
1786
1787                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1788                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1789
1790
1791                 x += bl;
1792                 y += bt;
1793
1794
1795                 if (p != el && pe.getStyle('overflow') != 'visible') {
1796                     x += bl;
1797                     y += bt;
1798                 }
1799             }
1800             p = p.offsetParent;
1801         }
1802
1803         if (Roo.isSafari && hasAbsolute) {
1804             x -= bd.offsetLeft;
1805             y -= bd.offsetTop;
1806         }
1807
1808         if (Roo.isGecko && !hasAbsolute) {
1809             var dbd = fly(bd);
1810             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1811             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1812         }
1813
1814         p = el.parentNode;
1815         while (p && p != bd) {
1816             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1817                 x -= p.scrollLeft;
1818                 y -= p.scrollTop;
1819             }
1820             p = p.parentNode;
1821         }
1822         return [x, y];
1823     },
1824  
1825   
1826
1827
1828     setXY : function(el, xy) {
1829         el = Roo.fly(el, '_setXY');
1830         el.position();
1831         var pts = el.translatePoints(xy);
1832         if (xy[0] !== false) {
1833             el.dom.style.left = pts.left + "px";
1834         }
1835         if (xy[1] !== false) {
1836             el.dom.style.top = pts.top + "px";
1837         }
1838     },
1839
1840     setX : function(el, x) {
1841         this.setXY(el, [x, false]);
1842     },
1843
1844     setY : function(el, y) {
1845         this.setXY(el, [false, y]);
1846     }
1847 };
1848 /*
1849  * Portions of this file are based on pieces of Yahoo User Interface Library
1850  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1851  * YUI licensed under the BSD License:
1852  * http://developer.yahoo.net/yui/license.txt
1853  * <script type="text/javascript">
1854  *
1855  */
1856
1857 Roo.lib.Event = function() {
1858     var loadComplete = false;
1859     var listeners = [];
1860     var unloadListeners = [];
1861     var retryCount = 0;
1862     var onAvailStack = [];
1863     var counter = 0;
1864     var lastError = null;
1865
1866     return {
1867         POLL_RETRYS: 200,
1868         POLL_INTERVAL: 20,
1869         EL: 0,
1870         TYPE: 1,
1871         FN: 2,
1872         WFN: 3,
1873         OBJ: 3,
1874         ADJ_SCOPE: 4,
1875         _interval: null,
1876
1877         startInterval: function() {
1878             if (!this._interval) {
1879                 var self = this;
1880                 var callback = function() {
1881                     self._tryPreloadAttach();
1882                 };
1883                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1884
1885             }
1886         },
1887
1888         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1889             onAvailStack.push({ id:         p_id,
1890                 fn:         p_fn,
1891                 obj:        p_obj,
1892                 override:   p_override,
1893                 checkReady: false    });
1894
1895             retryCount = this.POLL_RETRYS;
1896             this.startInterval();
1897         },
1898
1899
1900         addListener: function(el, eventName, fn) {
1901             el = Roo.getDom(el);
1902             if (!el || !fn) {
1903                 return false;
1904             }
1905
1906             if ("unload" == eventName) {
1907                 unloadListeners[unloadListeners.length] =
1908                 [el, eventName, fn];
1909                 return true;
1910             }
1911
1912             var wrappedFn = function(e) {
1913                 return fn(Roo.lib.Event.getEvent(e));
1914             };
1915
1916             var li = [el, eventName, fn, wrappedFn];
1917
1918             var index = listeners.length;
1919             listeners[index] = li;
1920
1921             this.doAdd(el, eventName, wrappedFn, false);
1922             return true;
1923
1924         },
1925
1926
1927         removeListener: function(el, eventName, fn) {
1928             var i, len;
1929
1930             el = Roo.getDom(el);
1931
1932             if(!fn) {
1933                 return this.purgeElement(el, false, eventName);
1934             }
1935
1936
1937             if ("unload" == eventName) {
1938
1939                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1940                     var li = unloadListeners[i];
1941                     if (li &&
1942                         li[0] == el &&
1943                         li[1] == eventName &&
1944                         li[2] == fn) {
1945                         unloadListeners.splice(i, 1);
1946                         return true;
1947                     }
1948                 }
1949
1950                 return false;
1951             }
1952
1953             var cacheItem = null;
1954
1955
1956             var index = arguments[3];
1957
1958             if ("undefined" == typeof index) {
1959                 index = this._getCacheIndex(el, eventName, fn);
1960             }
1961
1962             if (index >= 0) {
1963                 cacheItem = listeners[index];
1964             }
1965
1966             if (!el || !cacheItem) {
1967                 return false;
1968             }
1969
1970             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1971
1972             delete listeners[index][this.WFN];
1973             delete listeners[index][this.FN];
1974             listeners.splice(index, 1);
1975
1976             return true;
1977
1978         },
1979
1980
1981         getTarget: function(ev, resolveTextNode) {
1982             ev = ev.browserEvent || ev;
1983             var t = ev.target || ev.srcElement;
1984             return this.resolveTextNode(t);
1985         },
1986
1987
1988         resolveTextNode: function(node) {
1989             if (Roo.isSafari && node && 3 == node.nodeType) {
1990                 return node.parentNode;
1991             } else {
1992                 return node;
1993             }
1994         },
1995
1996
1997         getPageX: function(ev) {
1998             ev = ev.browserEvent || ev;
1999             var x = ev.pageX;
2000             if (!x && 0 !== x) {
2001                 x = ev.clientX || 0;
2002
2003                 if (Roo.isIE) {
2004                     x += this.getScroll()[1];
2005                 }
2006             }
2007
2008             return x;
2009         },
2010
2011
2012         getPageY: function(ev) {
2013             ev = ev.browserEvent || ev;
2014             var y = ev.pageY;
2015             if (!y && 0 !== y) {
2016                 y = ev.clientY || 0;
2017
2018                 if (Roo.isIE) {
2019                     y += this.getScroll()[0];
2020                 }
2021             }
2022
2023
2024             return y;
2025         },
2026
2027
2028         getXY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             return [this.getPageX(ev), this.getPageY(ev)];
2031         },
2032
2033
2034         getRelatedTarget: function(ev) {
2035             ev = ev.browserEvent || ev;
2036             var t = ev.relatedTarget;
2037             if (!t) {
2038                 if (ev.type == "mouseout") {
2039                     t = ev.toElement;
2040                 } else if (ev.type == "mouseover") {
2041                     t = ev.fromElement;
2042                 }
2043             }
2044
2045             return this.resolveTextNode(t);
2046         },
2047
2048
2049         getTime: function(ev) {
2050             ev = ev.browserEvent || ev;
2051             if (!ev.time) {
2052                 var t = new Date().getTime();
2053                 try {
2054                     ev.time = t;
2055                 } catch(ex) {
2056                     this.lastError = ex;
2057                     return t;
2058                 }
2059             }
2060
2061             return ev.time;
2062         },
2063
2064
2065         stopEvent: function(ev) {
2066             this.stopPropagation(ev);
2067             this.preventDefault(ev);
2068         },
2069
2070
2071         stopPropagation: function(ev) {
2072             ev = ev.browserEvent || ev;
2073             if (ev.stopPropagation) {
2074                 ev.stopPropagation();
2075             } else {
2076                 ev.cancelBubble = true;
2077             }
2078         },
2079
2080
2081         preventDefault: function(ev) {
2082             ev = ev.browserEvent || ev;
2083             if(ev.preventDefault) {
2084                 ev.preventDefault();
2085             } else {
2086                 ev.returnValue = false;
2087             }
2088         },
2089
2090
2091         getEvent: function(e) {
2092             var ev = e || window.event;
2093             if (!ev) {
2094                 var c = this.getEvent.caller;
2095                 while (c) {
2096                     ev = c.arguments[0];
2097                     if (ev && Event == ev.constructor) {
2098                         break;
2099                     }
2100                     c = c.caller;
2101                 }
2102             }
2103             return ev;
2104         },
2105
2106
2107         getCharCode: function(ev) {
2108             ev = ev.browserEvent || ev;
2109             return ev.charCode || ev.keyCode || 0;
2110         },
2111
2112
2113         _getCacheIndex: function(el, eventName, fn) {
2114             for (var i = 0,len = listeners.length; i < len; ++i) {
2115                 var li = listeners[i];
2116                 if (li &&
2117                     li[this.FN] == fn &&
2118                     li[this.EL] == el &&
2119                     li[this.TYPE] == eventName) {
2120                     return i;
2121                 }
2122             }
2123
2124             return -1;
2125         },
2126
2127
2128         elCache: {},
2129
2130
2131         getEl: function(id) {
2132             return document.getElementById(id);
2133         },
2134
2135
2136         clearCache: function() {
2137         },
2138
2139
2140         _load: function(e) {
2141             loadComplete = true;
2142             var EU = Roo.lib.Event;
2143
2144
2145             if (Roo.isIE) {
2146                 EU.doRemove(window, "load", EU._load);
2147             }
2148         },
2149
2150
2151         _tryPreloadAttach: function() {
2152
2153             if (this.locked) {
2154                 return false;
2155             }
2156
2157             this.locked = true;
2158
2159
2160             var tryAgain = !loadComplete;
2161             if (!tryAgain) {
2162                 tryAgain = (retryCount > 0);
2163             }
2164
2165
2166             var notAvail = [];
2167             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2168                 var item = onAvailStack[i];
2169                 if (item) {
2170                     var el = this.getEl(item.id);
2171
2172                     if (el) {
2173                         if (!item.checkReady ||
2174                             loadComplete ||
2175                             el.nextSibling ||
2176                             (document && document.body)) {
2177
2178                             var scope = el;
2179                             if (item.override) {
2180                                 if (item.override === true) {
2181                                     scope = item.obj;
2182                                 } else {
2183                                     scope = item.override;
2184                                 }
2185                             }
2186                             item.fn.call(scope, item.obj);
2187                             onAvailStack[i] = null;
2188                         }
2189                     } else {
2190                         notAvail.push(item);
2191                     }
2192                 }
2193             }
2194
2195             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2196
2197             if (tryAgain) {
2198
2199                 this.startInterval();
2200             } else {
2201                 clearInterval(this._interval);
2202                 this._interval = null;
2203             }
2204
2205             this.locked = false;
2206
2207             return true;
2208
2209         },
2210
2211
2212         purgeElement: function(el, recurse, eventName) {
2213             var elListeners = this.getListeners(el, eventName);
2214             if (elListeners) {
2215                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2216                     var l = elListeners[i];
2217                     this.removeListener(el, l.type, l.fn);
2218                 }
2219             }
2220
2221             if (recurse && el && el.childNodes) {
2222                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2223                     this.purgeElement(el.childNodes[i], recurse, eventName);
2224                 }
2225             }
2226         },
2227
2228
2229         getListeners: function(el, eventName) {
2230             var results = [], searchLists;
2231             if (!eventName) {
2232                 searchLists = [listeners, unloadListeners];
2233             } else if (eventName == "unload") {
2234                 searchLists = [unloadListeners];
2235             } else {
2236                 searchLists = [listeners];
2237             }
2238
2239             for (var j = 0; j < searchLists.length; ++j) {
2240                 var searchList = searchLists[j];
2241                 if (searchList && searchList.length > 0) {
2242                     for (var i = 0,len = searchList.length; i < len; ++i) {
2243                         var l = searchList[i];
2244                         if (l && l[this.EL] === el &&
2245                             (!eventName || eventName === l[this.TYPE])) {
2246                             results.push({
2247                                 type:   l[this.TYPE],
2248                                 fn:     l[this.FN],
2249                                 obj:    l[this.OBJ],
2250                                 adjust: l[this.ADJ_SCOPE],
2251                                 index:  i
2252                             });
2253                         }
2254                     }
2255                 }
2256             }
2257
2258             return (results.length) ? results : null;
2259         },
2260
2261
2262         _unload: function(e) {
2263
2264             var EU = Roo.lib.Event, i, j, l, len, index;
2265
2266             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2267                 l = unloadListeners[i];
2268                 if (l) {
2269                     var scope = window;
2270                     if (l[EU.ADJ_SCOPE]) {
2271                         if (l[EU.ADJ_SCOPE] === true) {
2272                             scope = l[EU.OBJ];
2273                         } else {
2274                             scope = l[EU.ADJ_SCOPE];
2275                         }
2276                     }
2277                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2278                     unloadListeners[i] = null;
2279                     l = null;
2280                     scope = null;
2281                 }
2282             }
2283
2284             unloadListeners = null;
2285
2286             if (listeners && listeners.length > 0) {
2287                 j = listeners.length;
2288                 while (j) {
2289                     index = j - 1;
2290                     l = listeners[index];
2291                     if (l) {
2292                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2293                                 l[EU.FN], index);
2294                     }
2295                     j = j - 1;
2296                 }
2297                 l = null;
2298
2299                 EU.clearCache();
2300             }
2301
2302             EU.doRemove(window, "unload", EU._unload);
2303
2304         },
2305
2306
2307         getScroll: function() {
2308             var dd = document.documentElement, db = document.body;
2309             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2310                 return [dd.scrollTop, dd.scrollLeft];
2311             } else if (db) {
2312                 return [db.scrollTop, db.scrollLeft];
2313             } else {
2314                 return [0, 0];
2315             }
2316         },
2317
2318
2319         doAdd: function () {
2320             if (window.addEventListener) {
2321                 return function(el, eventName, fn, capture) {
2322                     el.addEventListener(eventName, fn, (capture));
2323                 };
2324             } else if (window.attachEvent) {
2325                 return function(el, eventName, fn, capture) {
2326                     el.attachEvent("on" + eventName, fn);
2327                 };
2328             } else {
2329                 return function() {
2330                 };
2331             }
2332         }(),
2333
2334
2335         doRemove: function() {
2336             if (window.removeEventListener) {
2337                 return function (el, eventName, fn, capture) {
2338                     el.removeEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.detachEvent) {
2341                 return function (el, eventName, fn) {
2342                     el.detachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }()
2349     };
2350     
2351 }();
2352 (function() {     
2353    
2354     var E = Roo.lib.Event;
2355     E.on = E.addListener;
2356     E.un = E.removeListener;
2357
2358     if (document && document.body) {
2359         E._load();
2360     } else {
2361         E.doAdd(window, "load", E._load);
2362     }
2363     E.doAdd(window, "unload", E._unload);
2364     E._tryPreloadAttach();
2365 })();
2366
2367 /*
2368  * Portions of this file are based on pieces of Yahoo User Interface Library
2369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2370  * YUI licensed under the BSD License:
2371  * http://developer.yahoo.net/yui/license.txt
2372  * <script type="text/javascript">
2373  *
2374  */
2375
2376 (function() {
2377     
2378     Roo.lib.Ajax = {
2379         request : function(method, uri, cb, data, options) {
2380             if(options){
2381                 var hs = options.headers;
2382                 if(hs){
2383                     for(var h in hs){
2384                         if(hs.hasOwnProperty(h)){
2385                             this.initHeader(h, hs[h], false);
2386                         }
2387                     }
2388                 }
2389                 if(options.xmlData){
2390                     this.initHeader('Content-Type', 'text/xml', false);
2391                     method = 'POST';
2392                     data = options.xmlData;
2393                 }
2394             }
2395
2396             return this.asyncRequest(method, uri, cb, data);
2397         },
2398
2399         serializeForm : function(form) {
2400             if(typeof form == 'string') {
2401                 form = (document.getElementById(form) || document.forms[form]);
2402             }
2403
2404             var el, name, val, disabled, data = '', hasSubmit = false;
2405             for (var i = 0; i < form.elements.length; i++) {
2406                 el = form.elements[i];
2407                 disabled = form.elements[i].disabled;
2408                 name = form.elements[i].name;
2409                 val = form.elements[i].value;
2410
2411                 if (!disabled && name){
2412                     switch (el.type)
2413                             {
2414                         case 'select-one':
2415                         case 'select-multiple':
2416                             for (var j = 0; j < el.options.length; j++) {
2417                                 if (el.options[j].selected) {
2418                                     if (Roo.isIE) {
2419                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2420                                     }
2421                                     else {
2422                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2423                                     }
2424                                 }
2425                             }
2426                             break;
2427                         case 'radio':
2428                         case 'checkbox':
2429                             if (el.checked) {
2430                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             }
2432                             break;
2433                         case 'file':
2434
2435                         case undefined:
2436
2437                         case 'reset':
2438
2439                         case 'button':
2440
2441                             break;
2442                         case 'submit':
2443                             if(hasSubmit == false) {
2444                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2445                                 hasSubmit = true;
2446                             }
2447                             break;
2448                         default:
2449                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2450                             break;
2451                     }
2452                 }
2453             }
2454             data = data.substr(0, data.length - 1);
2455             return data;
2456         },
2457
2458         headers:{},
2459
2460         hasHeaders:false,
2461
2462         useDefaultHeader:true,
2463
2464         defaultPostHeader:'application/x-www-form-urlencoded',
2465
2466         useDefaultXhrHeader:true,
2467
2468         defaultXhrHeader:'XMLHttpRequest',
2469
2470         hasDefaultHeaders:true,
2471
2472         defaultHeaders:{},
2473
2474         poll:{},
2475
2476         timeout:{},
2477
2478         pollInterval:50,
2479
2480         transactionId:0,
2481
2482         setProgId:function(id)
2483         {
2484             this.activeX.unshift(id);
2485         },
2486
2487         setDefaultPostHeader:function(b)
2488         {
2489             this.useDefaultHeader = b;
2490         },
2491
2492         setDefaultXhrHeader:function(b)
2493         {
2494             this.useDefaultXhrHeader = b;
2495         },
2496
2497         setPollingInterval:function(i)
2498         {
2499             if (typeof i == 'number' && isFinite(i)) {
2500                 this.pollInterval = i;
2501             }
2502         },
2503
2504         createXhrObject:function(transactionId)
2505         {
2506             var obj,http;
2507             try
2508             {
2509
2510                 http = new XMLHttpRequest();
2511
2512                 obj = { conn:http, tId:transactionId };
2513             }
2514             catch(e)
2515             {
2516                 for (var i = 0; i < this.activeX.length; ++i) {
2517                     try
2518                     {
2519
2520                         http = new ActiveXObject(this.activeX[i]);
2521
2522                         obj = { conn:http, tId:transactionId };
2523                         break;
2524                     }
2525                     catch(e) {
2526                     }
2527                 }
2528             }
2529             finally
2530             {
2531                 return obj;
2532             }
2533         },
2534
2535         getConnectionObject:function()
2536         {
2537             var o;
2538             var tId = this.transactionId;
2539
2540             try
2541             {
2542                 o = this.createXhrObject(tId);
2543                 if (o) {
2544                     this.transactionId++;
2545                 }
2546             }
2547             catch(e) {
2548             }
2549             finally
2550             {
2551                 return o;
2552             }
2553         },
2554
2555         asyncRequest:function(method, uri, callback, postData)
2556         {
2557             var o = this.getConnectionObject();
2558
2559             if (!o) {
2560                 return null;
2561             }
2562             else {
2563                 o.conn.open(method, uri, true);
2564
2565                 if (this.useDefaultXhrHeader) {
2566                     if (!this.defaultHeaders['X-Requested-With']) {
2567                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2568                     }
2569                 }
2570
2571                 if(postData && this.useDefaultHeader){
2572                     this.initHeader('Content-Type', this.defaultPostHeader);
2573                 }
2574
2575                  if (this.hasDefaultHeaders || this.hasHeaders) {
2576                     this.setHeader(o);
2577                 }
2578
2579                 this.handleReadyState(o, callback);
2580                 o.conn.send(postData || null);
2581
2582                 return o;
2583             }
2584         },
2585
2586         handleReadyState:function(o, callback)
2587         {
2588             var oConn = this;
2589
2590             if (callback && callback.timeout) {
2591                 this.timeout[o.tId] = window.setTimeout(function() {
2592                     oConn.abort(o, callback, true);
2593                 }, callback.timeout);
2594             }
2595
2596             this.poll[o.tId] = window.setInterval(
2597                     function() {
2598                         if (o.conn && o.conn.readyState == 4) {
2599                             window.clearInterval(oConn.poll[o.tId]);
2600                             delete oConn.poll[o.tId];
2601
2602                             if(callback && callback.timeout) {
2603                                 window.clearTimeout(oConn.timeout[o.tId]);
2604                                 delete oConn.timeout[o.tId];
2605                             }
2606
2607                             oConn.handleTransactionResponse(o, callback);
2608                         }
2609                     }
2610                     , this.pollInterval);
2611         },
2612
2613         handleTransactionResponse:function(o, callback, isAbort)
2614         {
2615
2616             if (!callback) {
2617                 this.releaseObject(o);
2618                 return;
2619             }
2620
2621             var httpStatus, responseObject;
2622
2623             try
2624             {
2625                 if (o.conn.status !== undefined && o.conn.status != 0) {
2626                     httpStatus = o.conn.status;
2627                 }
2628                 else {
2629                     httpStatus = 13030;
2630                 }
2631             }
2632             catch(e) {
2633
2634
2635                 httpStatus = 13030;
2636             }
2637
2638             if (httpStatus >= 200 && httpStatus < 300) {
2639                 responseObject = this.createResponseObject(o, callback.argument);
2640                 if (callback.success) {
2641                     if (!callback.scope) {
2642                         callback.success(responseObject);
2643                     }
2644                     else {
2645
2646
2647                         callback.success.apply(callback.scope, [responseObject]);
2648                     }
2649                 }
2650             }
2651             else {
2652                 switch (httpStatus) {
2653
2654                     case 12002:
2655                     case 12029:
2656                     case 12030:
2657                     case 12031:
2658                     case 12152:
2659                     case 13030:
2660                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2661                         if (callback.failure) {
2662                             if (!callback.scope) {
2663                                 callback.failure(responseObject);
2664                             }
2665                             else {
2666                                 callback.failure.apply(callback.scope, [responseObject]);
2667                             }
2668                         }
2669                         break;
2670                     default:
2671                         responseObject = this.createResponseObject(o, callback.argument);
2672                         if (callback.failure) {
2673                             if (!callback.scope) {
2674                                 callback.failure(responseObject);
2675                             }
2676                             else {
2677                                 callback.failure.apply(callback.scope, [responseObject]);
2678                             }
2679                         }
2680                 }
2681             }
2682
2683             this.releaseObject(o);
2684             responseObject = null;
2685         },
2686
2687         createResponseObject:function(o, callbackArg)
2688         {
2689             var obj = {};
2690             var headerObj = {};
2691
2692             try
2693             {
2694                 var headerStr = o.conn.getAllResponseHeaders();
2695                 var header = headerStr.split('\n');
2696                 for (var i = 0; i < header.length; i++) {
2697                     var delimitPos = header[i].indexOf(':');
2698                     if (delimitPos != -1) {
2699                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2700                     }
2701                 }
2702             }
2703             catch(e) {
2704             }
2705
2706             obj.tId = o.tId;
2707             obj.status = o.conn.status;
2708             obj.statusText = o.conn.statusText;
2709             obj.getResponseHeader = headerObj;
2710             obj.getAllResponseHeaders = headerStr;
2711             obj.responseText = o.conn.responseText;
2712             obj.responseXML = o.conn.responseXML;
2713
2714             if (typeof callbackArg !== undefined) {
2715                 obj.argument = callbackArg;
2716             }
2717
2718             return obj;
2719         },
2720
2721         createExceptionObject:function(tId, callbackArg, isAbort)
2722         {
2723             var COMM_CODE = 0;
2724             var COMM_ERROR = 'communication failure';
2725             var ABORT_CODE = -1;
2726             var ABORT_ERROR = 'transaction aborted';
2727
2728             var obj = {};
2729
2730             obj.tId = tId;
2731             if (isAbort) {
2732                 obj.status = ABORT_CODE;
2733                 obj.statusText = ABORT_ERROR;
2734             }
2735             else {
2736                 obj.status = COMM_CODE;
2737                 obj.statusText = COMM_ERROR;
2738             }
2739
2740             if (callbackArg) {
2741                 obj.argument = callbackArg;
2742             }
2743
2744             return obj;
2745         },
2746
2747         initHeader:function(label, value, isDefault)
2748         {
2749             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2750
2751             if (headerObj[label] === undefined) {
2752                 headerObj[label] = value;
2753             }
2754             else {
2755
2756
2757                 headerObj[label] = value + "," + headerObj[label];
2758             }
2759
2760             if (isDefault) {
2761                 this.hasDefaultHeaders = true;
2762             }
2763             else {
2764                 this.hasHeaders = true;
2765             }
2766         },
2767
2768
2769         setHeader:function(o)
2770         {
2771             if (this.hasDefaultHeaders) {
2772                 for (var prop in this.defaultHeaders) {
2773                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2774                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2775                     }
2776                 }
2777             }
2778
2779             if (this.hasHeaders) {
2780                 for (var prop in this.headers) {
2781                     if (this.headers.hasOwnProperty(prop)) {
2782                         o.conn.setRequestHeader(prop, this.headers[prop]);
2783                     }
2784                 }
2785                 this.headers = {};
2786                 this.hasHeaders = false;
2787             }
2788         },
2789
2790         resetDefaultHeaders:function() {
2791             delete this.defaultHeaders;
2792             this.defaultHeaders = {};
2793             this.hasDefaultHeaders = false;
2794         },
2795
2796         abort:function(o, callback, isTimeout)
2797         {
2798             if(this.isCallInProgress(o)) {
2799                 o.conn.abort();
2800                 window.clearInterval(this.poll[o.tId]);
2801                 delete this.poll[o.tId];
2802                 if (isTimeout) {
2803                     delete this.timeout[o.tId];
2804                 }
2805
2806                 this.handleTransactionResponse(o, callback, true);
2807
2808                 return true;
2809             }
2810             else {
2811                 return false;
2812             }
2813         },
2814
2815
2816         isCallInProgress:function(o)
2817         {
2818             if (o && o.conn) {
2819                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2820             }
2821             else {
2822
2823                 return false;
2824             }
2825         },
2826
2827
2828         releaseObject:function(o)
2829         {
2830
2831             o.conn = null;
2832
2833             o = null;
2834         },
2835
2836         activeX:[
2837         'MSXML2.XMLHTTP.3.0',
2838         'MSXML2.XMLHTTP',
2839         'Microsoft.XMLHTTP'
2840         ]
2841
2842
2843     };
2844 })();/*
2845  * Portions of this file are based on pieces of Yahoo User Interface Library
2846  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2847  * YUI licensed under the BSD License:
2848  * http://developer.yahoo.net/yui/license.txt
2849  * <script type="text/javascript">
2850  *
2851  */
2852
2853 Roo.lib.Region = function(t, r, b, l) {
2854     this.top = t;
2855     this[1] = t;
2856     this.right = r;
2857     this.bottom = b;
2858     this.left = l;
2859     this[0] = l;
2860 };
2861
2862
2863 Roo.lib.Region.prototype = {
2864     contains : function(region) {
2865         return ( region.left >= this.left &&
2866                  region.right <= this.right &&
2867                  region.top >= this.top &&
2868                  region.bottom <= this.bottom    );
2869
2870     },
2871
2872     getArea : function() {
2873         return ( (this.bottom - this.top) * (this.right - this.left) );
2874     },
2875
2876     intersect : function(region) {
2877         var t = Math.max(this.top, region.top);
2878         var r = Math.min(this.right, region.right);
2879         var b = Math.min(this.bottom, region.bottom);
2880         var l = Math.max(this.left, region.left);
2881
2882         if (b >= t && r >= l) {
2883             return new Roo.lib.Region(t, r, b, l);
2884         } else {
2885             return null;
2886         }
2887     },
2888     union : function(region) {
2889         var t = Math.min(this.top, region.top);
2890         var r = Math.max(this.right, region.right);
2891         var b = Math.max(this.bottom, region.bottom);
2892         var l = Math.min(this.left, region.left);
2893
2894         return new Roo.lib.Region(t, r, b, l);
2895     },
2896
2897     adjust : function(t, l, b, r) {
2898         this.top += t;
2899         this.left += l;
2900         this.right += r;
2901         this.bottom += b;
2902         return this;
2903     }
2904 };
2905
2906 Roo.lib.Region.getRegion = function(el) {
2907     var p = Roo.lib.Dom.getXY(el);
2908
2909     var t = p[1];
2910     var r = p[0] + el.offsetWidth;
2911     var b = p[1] + el.offsetHeight;
2912     var l = p[0];
2913
2914     return new Roo.lib.Region(t, r, b, l);
2915 };
2916 /*
2917  * Portions of this file are based on pieces of Yahoo User Interface Library
2918  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2919  * YUI licensed under the BSD License:
2920  * http://developer.yahoo.net/yui/license.txt
2921  * <script type="text/javascript">
2922  *
2923  */
2924 //@@dep Roo.lib.Region
2925
2926
2927 Roo.lib.Point = function(x, y) {
2928     if (x instanceof Array) {
2929         y = x[1];
2930         x = x[0];
2931     }
2932     this.x = this.right = this.left = this[0] = x;
2933     this.y = this.top = this.bottom = this[1] = y;
2934 };
2935
2936 Roo.lib.Point.prototype = new Roo.lib.Region();
2937 /*
2938  * Portions of this file are based on pieces of Yahoo User Interface Library
2939  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2940  * YUI licensed under the BSD License:
2941  * http://developer.yahoo.net/yui/license.txt
2942  * <script type="text/javascript">
2943  *
2944  */
2945  
2946 (function() {   
2947
2948     Roo.lib.Anim = {
2949         scroll : function(el, args, duration, easing, cb, scope) {
2950             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2951         },
2952
2953         motion : function(el, args, duration, easing, cb, scope) {
2954             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2955         },
2956
2957         color : function(el, args, duration, easing, cb, scope) {
2958             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2959         },
2960
2961         run : function(el, args, duration, easing, cb, scope, type) {
2962             type = type || Roo.lib.AnimBase;
2963             if (typeof easing == "string") {
2964                 easing = Roo.lib.Easing[easing];
2965             }
2966             var anim = new type(el, args, duration, easing);
2967             anim.animateX(function() {
2968                 Roo.callback(cb, scope);
2969             });
2970             return anim;
2971         }
2972     };
2973 })();/*
2974  * Portions of this file are based on pieces of Yahoo User Interface Library
2975  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2976  * YUI licensed under the BSD License:
2977  * http://developer.yahoo.net/yui/license.txt
2978  * <script type="text/javascript">
2979  *
2980  */
2981
2982 (function() {    
2983     var libFlyweight;
2984     
2985     function fly(el) {
2986         if (!libFlyweight) {
2987             libFlyweight = new Roo.Element.Flyweight();
2988         }
2989         libFlyweight.dom = el;
2990         return libFlyweight;
2991     }
2992
2993     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2994     
2995    
2996     
2997     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2998         if (el) {
2999             this.init(el, attributes, duration, method);
3000         }
3001     };
3002
3003     Roo.lib.AnimBase.fly = fly;
3004     
3005     
3006     
3007     Roo.lib.AnimBase.prototype = {
3008
3009         toString: function() {
3010             var el = this.getEl();
3011             var id = el.id || el.tagName;
3012             return ("Anim " + id);
3013         },
3014
3015         patterns: {
3016             noNegatives:        /width|height|opacity|padding/i,
3017             offsetAttribute:  /^((width|height)|(top|left))$/,
3018             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3019             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3020         },
3021
3022
3023         doMethod: function(attr, start, end) {
3024             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3025         },
3026
3027
3028         setAttribute: function(attr, val, unit) {
3029             if (this.patterns.noNegatives.test(attr)) {
3030                 val = (val > 0) ? val : 0;
3031             }
3032
3033             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3034         },
3035
3036
3037         getAttribute: function(attr) {
3038             var el = this.getEl();
3039             var val = fly(el).getStyle(attr);
3040
3041             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3042                 return parseFloat(val);
3043             }
3044
3045             var a = this.patterns.offsetAttribute.exec(attr) || [];
3046             var pos = !!( a[3] );
3047             var box = !!( a[2] );
3048
3049
3050             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3051                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3052             } else {
3053                 val = 0;
3054             }
3055
3056             return val;
3057         },
3058
3059
3060         getDefaultUnit: function(attr) {
3061             if (this.patterns.defaultUnit.test(attr)) {
3062                 return 'px';
3063             }
3064
3065             return '';
3066         },
3067
3068         animateX : function(callback, scope) {
3069             var f = function() {
3070                 this.onComplete.removeListener(f);
3071                 if (typeof callback == "function") {
3072                     callback.call(scope || this, this);
3073                 }
3074             };
3075             this.onComplete.addListener(f, this);
3076             this.animate();
3077         },
3078
3079
3080         setRuntimeAttribute: function(attr) {
3081             var start;
3082             var end;
3083             var attributes = this.attributes;
3084
3085             this.runtimeAttributes[attr] = {};
3086
3087             var isset = function(prop) {
3088                 return (typeof prop !== 'undefined');
3089             };
3090
3091             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3092                 return false;
3093             }
3094
3095             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3096
3097
3098             if (isset(attributes[attr]['to'])) {
3099                 end = attributes[attr]['to'];
3100             } else if (isset(attributes[attr]['by'])) {
3101                 if (start.constructor == Array) {
3102                     end = [];
3103                     for (var i = 0, len = start.length; i < len; ++i) {
3104                         end[i] = start[i] + attributes[attr]['by'][i];
3105                     }
3106                 } else {
3107                     end = start + attributes[attr]['by'];
3108                 }
3109             }
3110
3111             this.runtimeAttributes[attr].start = start;
3112             this.runtimeAttributes[attr].end = end;
3113
3114
3115             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3116         },
3117
3118
3119         init: function(el, attributes, duration, method) {
3120
3121             var isAnimated = false;
3122
3123
3124             var startTime = null;
3125
3126
3127             var actualFrames = 0;
3128
3129
3130             el = Roo.getDom(el);
3131
3132
3133             this.attributes = attributes || {};
3134
3135
3136             this.duration = duration || 1;
3137
3138
3139             this.method = method || Roo.lib.Easing.easeNone;
3140
3141
3142             this.useSeconds = true;
3143
3144
3145             this.currentFrame = 0;
3146
3147
3148             this.totalFrames = Roo.lib.AnimMgr.fps;
3149
3150
3151             this.getEl = function() {
3152                 return el;
3153             };
3154
3155
3156             this.isAnimated = function() {
3157                 return isAnimated;
3158             };
3159
3160
3161             this.getStartTime = function() {
3162                 return startTime;
3163             };
3164
3165             this.runtimeAttributes = {};
3166
3167
3168             this.animate = function() {
3169                 if (this.isAnimated()) {
3170                     return false;
3171                 }
3172
3173                 this.currentFrame = 0;
3174
3175                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3176
3177                 Roo.lib.AnimMgr.registerElement(this);
3178             };
3179
3180
3181             this.stop = function(finish) {
3182                 if (finish) {
3183                     this.currentFrame = this.totalFrames;
3184                     this._onTween.fire();
3185                 }
3186                 Roo.lib.AnimMgr.stop(this);
3187             };
3188
3189             var onStart = function() {
3190                 this.onStart.fire();
3191
3192                 this.runtimeAttributes = {};
3193                 for (var attr in this.attributes) {
3194                     this.setRuntimeAttribute(attr);
3195                 }
3196
3197                 isAnimated = true;
3198                 actualFrames = 0;
3199                 startTime = new Date();
3200             };
3201
3202
3203             var onTween = function() {
3204                 var data = {
3205                     duration: new Date() - this.getStartTime(),
3206                     currentFrame: this.currentFrame
3207                 };
3208
3209                 data.toString = function() {
3210                     return (
3211                             'duration: ' + data.duration +
3212                             ', currentFrame: ' + data.currentFrame
3213                             );
3214                 };
3215
3216                 this.onTween.fire(data);
3217
3218                 var runtimeAttributes = this.runtimeAttributes;
3219
3220                 for (var attr in runtimeAttributes) {
3221                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3222                 }
3223
3224                 actualFrames += 1;
3225             };
3226
3227             var onComplete = function() {
3228                 var actual_duration = (new Date() - startTime) / 1000 ;
3229
3230                 var data = {
3231                     duration: actual_duration,
3232                     frames: actualFrames,
3233                     fps: actualFrames / actual_duration
3234                 };
3235
3236                 data.toString = function() {
3237                     return (
3238                             'duration: ' + data.duration +
3239                             ', frames: ' + data.frames +
3240                             ', fps: ' + data.fps
3241                             );
3242                 };
3243
3244                 isAnimated = false;
3245                 actualFrames = 0;
3246                 this.onComplete.fire(data);
3247             };
3248
3249
3250             this._onStart = new Roo.util.Event(this);
3251             this.onStart = new Roo.util.Event(this);
3252             this.onTween = new Roo.util.Event(this);
3253             this._onTween = new Roo.util.Event(this);
3254             this.onComplete = new Roo.util.Event(this);
3255             this._onComplete = new Roo.util.Event(this);
3256             this._onStart.addListener(onStart);
3257             this._onTween.addListener(onTween);
3258             this._onComplete.addListener(onComplete);
3259         }
3260     };
3261 })();
3262 /*
3263  * Portions of this file are based on pieces of Yahoo User Interface Library
3264  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3265  * YUI licensed under the BSD License:
3266  * http://developer.yahoo.net/yui/license.txt
3267  * <script type="text/javascript">
3268  *
3269  */
3270
3271 Roo.lib.AnimMgr = new function() {
3272
3273         var thread = null;
3274
3275
3276         var queue = [];
3277
3278
3279         var tweenCount = 0;
3280
3281
3282         this.fps = 1000;
3283
3284
3285         this.delay = 1;
3286
3287
3288         this.registerElement = function(tween) {
3289             queue[queue.length] = tween;
3290             tweenCount += 1;
3291             tween._onStart.fire();
3292             this.start();
3293         };
3294
3295
3296         this.unRegister = function(tween, index) {
3297             tween._onComplete.fire();
3298             index = index || getIndex(tween);
3299             if (index != -1) {
3300                 queue.splice(index, 1);
3301             }
3302
3303             tweenCount -= 1;
3304             if (tweenCount <= 0) {
3305                 this.stop();
3306             }
3307         };
3308
3309
3310         this.start = function() {
3311             if (thread === null) {
3312                 thread = setInterval(this.run, this.delay);
3313             }
3314         };
3315
3316
3317         this.stop = function(tween) {
3318             if (!tween) {
3319                 clearInterval(thread);
3320
3321                 for (var i = 0, len = queue.length; i < len; ++i) {
3322                     if (queue[0].isAnimated()) {
3323                         this.unRegister(queue[0], 0);
3324                     }
3325                 }
3326
3327                 queue = [];
3328                 thread = null;
3329                 tweenCount = 0;
3330             }
3331             else {
3332                 this.unRegister(tween);
3333             }
3334         };
3335
3336
3337         this.run = function() {
3338             for (var i = 0, len = queue.length; i < len; ++i) {
3339                 var tween = queue[i];
3340                 if (!tween || !tween.isAnimated()) {
3341                     continue;
3342                 }
3343
3344                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3345                 {
3346                     tween.currentFrame += 1;
3347
3348                     if (tween.useSeconds) {
3349                         correctFrame(tween);
3350                     }
3351                     tween._onTween.fire();
3352                 }
3353                 else {
3354                     Roo.lib.AnimMgr.stop(tween, i);
3355                 }
3356             }
3357         };
3358
3359         var getIndex = function(anim) {
3360             for (var i = 0, len = queue.length; i < len; ++i) {
3361                 if (queue[i] == anim) {
3362                     return i;
3363                 }
3364             }
3365             return -1;
3366         };
3367
3368
3369         var correctFrame = function(tween) {
3370             var frames = tween.totalFrames;
3371             var frame = tween.currentFrame;
3372             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3373             var elapsed = (new Date() - tween.getStartTime());
3374             var tweak = 0;
3375
3376             if (elapsed < tween.duration * 1000) {
3377                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3378             } else {
3379                 tweak = frames - (frame + 1);
3380             }
3381             if (tweak > 0 && isFinite(tweak)) {
3382                 if (tween.currentFrame + tweak >= frames) {
3383                     tweak = frames - (frame + 1);
3384                 }
3385
3386                 tween.currentFrame += tweak;
3387             }
3388         };
3389     };/*
3390  * Portions of this file are based on pieces of Yahoo User Interface Library
3391  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3392  * YUI licensed under the BSD License:
3393  * http://developer.yahoo.net/yui/license.txt
3394  * <script type="text/javascript">
3395  *
3396  */
3397 Roo.lib.Bezier = new function() {
3398
3399         this.getPosition = function(points, t) {
3400             var n = points.length;
3401             var tmp = [];
3402
3403             for (var i = 0; i < n; ++i) {
3404                 tmp[i] = [points[i][0], points[i][1]];
3405             }
3406
3407             for (var j = 1; j < n; ++j) {
3408                 for (i = 0; i < n - j; ++i) {
3409                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3410                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3411                 }
3412             }
3413
3414             return [ tmp[0][0], tmp[0][1] ];
3415
3416         };
3417     };/*
3418  * Portions of this file are based on pieces of Yahoo User Interface Library
3419  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3420  * YUI licensed under the BSD License:
3421  * http://developer.yahoo.net/yui/license.txt
3422  * <script type="text/javascript">
3423  *
3424  */
3425 (function() {
3426
3427     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3428         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3429     };
3430
3431     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3432
3433     var fly = Roo.lib.AnimBase.fly;
3434     var Y = Roo.lib;
3435     var superclass = Y.ColorAnim.superclass;
3436     var proto = Y.ColorAnim.prototype;
3437
3438     proto.toString = function() {
3439         var el = this.getEl();
3440         var id = el.id || el.tagName;
3441         return ("ColorAnim " + id);
3442     };
3443
3444     proto.patterns.color = /color$/i;
3445     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3446     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3447     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3448     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3449
3450
3451     proto.parseColor = function(s) {
3452         if (s.length == 3) {
3453             return s;
3454         }
3455
3456         var c = this.patterns.hex.exec(s);
3457         if (c && c.length == 4) {
3458             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3459         }
3460
3461         c = this.patterns.rgb.exec(s);
3462         if (c && c.length == 4) {
3463             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3464         }
3465
3466         c = this.patterns.hex3.exec(s);
3467         if (c && c.length == 4) {
3468             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3469         }
3470
3471         return null;
3472     };
3473     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3474     proto.getAttribute = function(attr) {
3475         var el = this.getEl();
3476         if (this.patterns.color.test(attr)) {
3477             var val = fly(el).getStyle(attr);
3478
3479             if (this.patterns.transparent.test(val)) {
3480                 var parent = el.parentNode;
3481                 val = fly(parent).getStyle(attr);
3482
3483                 while (parent && this.patterns.transparent.test(val)) {
3484                     parent = parent.parentNode;
3485                     val = fly(parent).getStyle(attr);
3486                     if (parent.tagName.toUpperCase() == 'HTML') {
3487                         val = '#fff';
3488                     }
3489                 }
3490             }
3491         } else {
3492             val = superclass.getAttribute.call(this, attr);
3493         }
3494
3495         return val;
3496     };
3497     proto.getAttribute = function(attr) {
3498         var el = this.getEl();
3499         if (this.patterns.color.test(attr)) {
3500             var val = fly(el).getStyle(attr);
3501
3502             if (this.patterns.transparent.test(val)) {
3503                 var parent = el.parentNode;
3504                 val = fly(parent).getStyle(attr);
3505
3506                 while (parent && this.patterns.transparent.test(val)) {
3507                     parent = parent.parentNode;
3508                     val = fly(parent).getStyle(attr);
3509                     if (parent.tagName.toUpperCase() == 'HTML') {
3510                         val = '#fff';
3511                     }
3512                 }
3513             }
3514         } else {
3515             val = superclass.getAttribute.call(this, attr);
3516         }
3517
3518         return val;
3519     };
3520
3521     proto.doMethod = function(attr, start, end) {
3522         var val;
3523
3524         if (this.patterns.color.test(attr)) {
3525             val = [];
3526             for (var i = 0, len = start.length; i < len; ++i) {
3527                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3528             }
3529
3530             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3531         }
3532         else {
3533             val = superclass.doMethod.call(this, attr, start, end);
3534         }
3535
3536         return val;
3537     };
3538
3539     proto.setRuntimeAttribute = function(attr) {
3540         superclass.setRuntimeAttribute.call(this, attr);
3541
3542         if (this.patterns.color.test(attr)) {
3543             var attributes = this.attributes;
3544             var start = this.parseColor(this.runtimeAttributes[attr].start);
3545             var end = this.parseColor(this.runtimeAttributes[attr].end);
3546
3547             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3548                 end = this.parseColor(attributes[attr].by);
3549
3550                 for (var i = 0, len = start.length; i < len; ++i) {
3551                     end[i] = start[i] + end[i];
3552                 }
3553             }
3554
3555             this.runtimeAttributes[attr].start = start;
3556             this.runtimeAttributes[attr].end = end;
3557         }
3558     };
3559 })();
3560
3561 /*
3562  * Portions of this file are based on pieces of Yahoo User Interface Library
3563  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3564  * YUI licensed under the BSD License:
3565  * http://developer.yahoo.net/yui/license.txt
3566  * <script type="text/javascript">
3567  *
3568  */
3569 Roo.lib.Easing = {
3570
3571
3572     easeNone: function (t, b, c, d) {
3573         return c * t / d + b;
3574     },
3575
3576
3577     easeIn: function (t, b, c, d) {
3578         return c * (t /= d) * t + b;
3579     },
3580
3581
3582     easeOut: function (t, b, c, d) {
3583         return -c * (t /= d) * (t - 2) + b;
3584     },
3585
3586
3587     easeBoth: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t + b;
3590         }
3591
3592         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3593     },
3594
3595
3596     easeInStrong: function (t, b, c, d) {
3597         return c * (t /= d) * t * t * t + b;
3598     },
3599
3600
3601     easeOutStrong: function (t, b, c, d) {
3602         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3603     },
3604
3605
3606     easeBothStrong: function (t, b, c, d) {
3607         if ((t /= d / 2) < 1) {
3608             return c / 2 * t * t * t * t + b;
3609         }
3610
3611         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3612     },
3613
3614
3615
3616     elasticIn: function (t, b, c, d, a, p) {
3617         if (t == 0) {
3618             return b;
3619         }
3620         if ((t /= d) == 1) {
3621             return b + c;
3622         }
3623         if (!p) {
3624             p = d * .3;
3625         }
3626
3627         if (!a || a < Math.abs(c)) {
3628             a = c;
3629             var s = p / 4;
3630         }
3631         else {
3632             var s = p / (2 * Math.PI) * Math.asin(c / a);
3633         }
3634
3635         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3636     },
3637
3638
3639     elasticOut: function (t, b, c, d, a, p) {
3640         if (t == 0) {
3641             return b;
3642         }
3643         if ((t /= d) == 1) {
3644             return b + c;
3645         }
3646         if (!p) {
3647             p = d * .3;
3648         }
3649
3650         if (!a || a < Math.abs(c)) {
3651             a = c;
3652             var s = p / 4;
3653         }
3654         else {
3655             var s = p / (2 * Math.PI) * Math.asin(c / a);
3656         }
3657
3658         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3659     },
3660
3661
3662     elasticBoth: function (t, b, c, d, a, p) {
3663         if (t == 0) {
3664             return b;
3665         }
3666
3667         if ((t /= d / 2) == 2) {
3668             return b + c;
3669         }
3670
3671         if (!p) {
3672             p = d * (.3 * 1.5);
3673         }
3674
3675         if (!a || a < Math.abs(c)) {
3676             a = c;
3677             var s = p / 4;
3678         }
3679         else {
3680             var s = p / (2 * Math.PI) * Math.asin(c / a);
3681         }
3682
3683         if (t < 1) {
3684             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3685                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3686         }
3687         return a * Math.pow(2, -10 * (t -= 1)) *
3688                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3689     },
3690
3691
3692
3693     backIn: function (t, b, c, d, s) {
3694         if (typeof s == 'undefined') {
3695             s = 1.70158;
3696         }
3697         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3698     },
3699
3700
3701     backOut: function (t, b, c, d, s) {
3702         if (typeof s == 'undefined') {
3703             s = 1.70158;
3704         }
3705         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3706     },
3707
3708
3709     backBoth: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713
3714         if ((t /= d / 2 ) < 1) {
3715             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3716         }
3717         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3718     },
3719
3720
3721     bounceIn: function (t, b, c, d) {
3722         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3723     },
3724
3725
3726     bounceOut: function (t, b, c, d) {
3727         if ((t /= d) < (1 / 2.75)) {
3728             return c * (7.5625 * t * t) + b;
3729         } else if (t < (2 / 2.75)) {
3730             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3731         } else if (t < (2.5 / 2.75)) {
3732             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3733         }
3734         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3735     },
3736
3737
3738     bounceBoth: function (t, b, c, d) {
3739         if (t < d / 2) {
3740             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3741         }
3742         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3743     }
3744 };/*
3745  * Portions of this file are based on pieces of Yahoo User Interface Library
3746  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3747  * YUI licensed under the BSD License:
3748  * http://developer.yahoo.net/yui/license.txt
3749  * <script type="text/javascript">
3750  *
3751  */
3752     (function() {
3753         Roo.lib.Motion = function(el, attributes, duration, method) {
3754             if (el) {
3755                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3756             }
3757         };
3758
3759         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3760
3761
3762         var Y = Roo.lib;
3763         var superclass = Y.Motion.superclass;
3764         var proto = Y.Motion.prototype;
3765
3766         proto.toString = function() {
3767             var el = this.getEl();
3768             var id = el.id || el.tagName;
3769             return ("Motion " + id);
3770         };
3771
3772         proto.patterns.points = /^points$/i;
3773
3774         proto.setAttribute = function(attr, val, unit) {
3775             if (this.patterns.points.test(attr)) {
3776                 unit = unit || 'px';
3777                 superclass.setAttribute.call(this, 'left', val[0], unit);
3778                 superclass.setAttribute.call(this, 'top', val[1], unit);
3779             } else {
3780                 superclass.setAttribute.call(this, attr, val, unit);
3781             }
3782         };
3783
3784         proto.getAttribute = function(attr) {
3785             if (this.patterns.points.test(attr)) {
3786                 var val = [
3787                         superclass.getAttribute.call(this, 'left'),
3788                         superclass.getAttribute.call(this, 'top')
3789                         ];
3790             } else {
3791                 val = superclass.getAttribute.call(this, attr);
3792             }
3793
3794             return val;
3795         };
3796
3797         proto.doMethod = function(attr, start, end) {
3798             var val = null;
3799
3800             if (this.patterns.points.test(attr)) {
3801                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3802                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3803             } else {
3804                 val = superclass.doMethod.call(this, attr, start, end);
3805             }
3806             return val;
3807         };
3808
3809         proto.setRuntimeAttribute = function(attr) {
3810             if (this.patterns.points.test(attr)) {
3811                 var el = this.getEl();
3812                 var attributes = this.attributes;
3813                 var start;
3814                 var control = attributes['points']['control'] || [];
3815                 var end;
3816                 var i, len;
3817
3818                 if (control.length > 0 && !(control[0] instanceof Array)) {
3819                     control = [control];
3820                 } else {
3821                     var tmp = [];
3822                     for (i = 0,len = control.length; i < len; ++i) {
3823                         tmp[i] = control[i];
3824                     }
3825                     control = tmp;
3826                 }
3827
3828                 Roo.fly(el).position();
3829
3830                 if (isset(attributes['points']['from'])) {
3831                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3832                 }
3833                 else {
3834                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3835                 }
3836
3837                 start = this.getAttribute('points');
3838
3839
3840                 if (isset(attributes['points']['to'])) {
3841                     end = translateValues.call(this, attributes['points']['to'], start);
3842
3843                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3844                     for (i = 0,len = control.length; i < len; ++i) {
3845                         control[i] = translateValues.call(this, control[i], start);
3846                     }
3847
3848
3849                 } else if (isset(attributes['points']['by'])) {
3850                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3851
3852                     for (i = 0,len = control.length; i < len; ++i) {
3853                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3854                     }
3855                 }
3856
3857                 this.runtimeAttributes[attr] = [start];
3858
3859                 if (control.length > 0) {
3860                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3861                 }
3862
3863                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3864             }
3865             else {
3866                 superclass.setRuntimeAttribute.call(this, attr);
3867             }
3868         };
3869
3870         var translateValues = function(val, start) {
3871             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3872             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3873
3874             return val;
3875         };
3876
3877         var isset = function(prop) {
3878             return (typeof prop !== 'undefined');
3879         };
3880     })();
3881 /*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Scroll = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Scroll.superclass;
3901         var proto = Y.Scroll.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Scroll " + id);
3907         };
3908
3909         proto.doMethod = function(attr, start, end) {
3910             var val = null;
3911
3912             if (attr == 'scroll') {
3913                 val = [
3914                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3915                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3916                         ];
3917
3918             } else {
3919                 val = superclass.doMethod.call(this, attr, start, end);
3920             }
3921             return val;
3922         };
3923
3924         proto.getAttribute = function(attr) {
3925             var val = null;
3926             var el = this.getEl();
3927
3928             if (attr == 'scroll') {
3929                 val = [ el.scrollLeft, el.scrollTop ];
3930             } else {
3931                 val = superclass.getAttribute.call(this, attr);
3932             }
3933
3934             return val;
3935         };
3936
3937         proto.setAttribute = function(attr, val, unit) {
3938             var el = this.getEl();
3939
3940             if (attr == 'scroll') {
3941                 el.scrollLeft = val[0];
3942                 el.scrollTop = val[1];
3943             } else {
3944                 superclass.setAttribute.call(this, attr, val, unit);
3945             }
3946         };
3947     })();
3948 /*
3949  * Based on:
3950  * Ext JS Library 1.1.1
3951  * Copyright(c) 2006-2007, Ext JS, LLC.
3952  *
3953  * Originally Released Under LGPL - original licence link has changed is not relivant.
3954  *
3955  * Fork - LGPL
3956  * <script type="text/javascript">
3957  */
3958  
3959
3960 /**
3961  * @class Roo.DomHelper
3962  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3963  * 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>.
3964  * @singleton
3965  */
3966 Roo.DomHelper = function(){
3967     var tempTableEl = null;
3968     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3969     var tableRe = /^table|tbody|tr|td$/i;
3970     var xmlns = {};
3971     // build as innerHTML where available
3972     /** @ignore */
3973     var createHtml = function(o){
3974         if(typeof o == 'string'){
3975             return o;
3976         }
3977         var b = "";
3978         if(!o.tag){
3979             o.tag = "div";
3980         }
3981         b += "<" + o.tag;
3982         for(var attr in o){
3983             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3984             if(attr == "style"){
3985                 var s = o["style"];
3986                 if(typeof s == "function"){
3987                     s = s.call();
3988                 }
3989                 if(typeof s == "string"){
3990                     b += ' style="' + s + '"';
3991                 }else if(typeof s == "object"){
3992                     b += ' style="';
3993                     for(var key in s){
3994                         if(typeof s[key] != "function"){
3995                             b += key + ":" + s[key] + ";";
3996                         }
3997                     }
3998                     b += '"';
3999                 }
4000             }else{
4001                 if(attr == "cls"){
4002                     b += ' class="' + o["cls"] + '"';
4003                 }else if(attr == "htmlFor"){
4004                     b += ' for="' + o["htmlFor"] + '"';
4005                 }else{
4006                     b += " " + attr + '="' + o[attr] + '"';
4007                 }
4008             }
4009         }
4010         if(emptyTags.test(o.tag)){
4011             b += "/>";
4012         }else{
4013             b += ">";
4014             var cn = o.children || o.cn;
4015             if(cn){
4016                 //http://bugs.kde.org/show_bug.cgi?id=71506
4017                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4018                     for(var i = 0, len = cn.length; i < len; i++) {
4019                         b += createHtml(cn[i], b);
4020                     }
4021                 }else{
4022                     b += createHtml(cn, b);
4023                 }
4024             }
4025             if(o.html){
4026                 b += o.html;
4027             }
4028             b += "</" + o.tag + ">";
4029         }
4030         return b;
4031     };
4032
4033     // build as dom
4034     /** @ignore */
4035     var createDom = function(o, parentNode){
4036          
4037         // defininition craeted..
4038         var ns = false;
4039         if (o.ns && o.ns != 'html') {
4040                
4041             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4042                 xmlns[o.ns] = o.xmlns;
4043                 ns = o.xmlns;
4044             }
4045             if (typeof(xmlns[o.ns]) == 'undefined') {
4046                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4047             }
4048             ns = xmlns[o.ns];
4049         }
4050         
4051         
4052         if (typeof(o) == 'string') {
4053             return parentNode.appendChild(document.createTextNode(o));
4054         }
4055         o.tag = o.tag || div;
4056         if (o.ns && Roo.isIE) {
4057             ns = false;
4058             o.tag = o.ns + ':' + o.tag;
4059             
4060         }
4061         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4062         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4063         for(var attr in o){
4064             
4065             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4066                     attr == "style" || typeof o[attr] == "function") continue;
4067                     
4068             if(attr=="cls" && Roo.isIE){
4069                 el.className = o["cls"];
4070             }else{
4071                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4072                 else el[attr] = o[attr];
4073             }
4074         }
4075         Roo.DomHelper.applyStyles(el, o.style);
4076         var cn = o.children || o.cn;
4077         if(cn){
4078             //http://bugs.kde.org/show_bug.cgi?id=71506
4079              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4080                 for(var i = 0, len = cn.length; i < len; i++) {
4081                     createDom(cn[i], el);
4082                 }
4083             }else{
4084                 createDom(cn, el);
4085             }
4086         }
4087         if(o.html){
4088             el.innerHTML = o.html;
4089         }
4090         if(parentNode){
4091            parentNode.appendChild(el);
4092         }
4093         return el;
4094     };
4095
4096     var ieTable = function(depth, s, h, e){
4097         tempTableEl.innerHTML = [s, h, e].join('');
4098         var i = -1, el = tempTableEl;
4099         while(++i < depth){
4100             el = el.firstChild;
4101         }
4102         return el;
4103     };
4104
4105     // kill repeat to save bytes
4106     var ts = '<table>',
4107         te = '</table>',
4108         tbs = ts+'<tbody>',
4109         tbe = '</tbody>'+te,
4110         trs = tbs + '<tr>',
4111         tre = '</tr>'+tbe;
4112
4113     /**
4114      * @ignore
4115      * Nasty code for IE's broken table implementation
4116      */
4117     var insertIntoTable = function(tag, where, el, html){
4118         if(!tempTableEl){
4119             tempTableEl = document.createElement('div');
4120         }
4121         var node;
4122         var before = null;
4123         if(tag == 'td'){
4124             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4125                 return;
4126             }
4127             if(where == 'beforebegin'){
4128                 before = el;
4129                 el = el.parentNode;
4130             } else{
4131                 before = el.nextSibling;
4132                 el = el.parentNode;
4133             }
4134             node = ieTable(4, trs, html, tre);
4135         }
4136         else if(tag == 'tr'){
4137             if(where == 'beforebegin'){
4138                 before = el;
4139                 el = el.parentNode;
4140                 node = ieTable(3, tbs, html, tbe);
4141             } else if(where == 'afterend'){
4142                 before = el.nextSibling;
4143                 el = el.parentNode;
4144                 node = ieTable(3, tbs, html, tbe);
4145             } else{ // INTO a TR
4146                 if(where == 'afterbegin'){
4147                     before = el.firstChild;
4148                 }
4149                 node = ieTable(4, trs, html, tre);
4150             }
4151         } else if(tag == 'tbody'){
4152             if(where == 'beforebegin'){
4153                 before = el;
4154                 el = el.parentNode;
4155                 node = ieTable(2, ts, html, te);
4156             } else if(where == 'afterend'){
4157                 before = el.nextSibling;
4158                 el = el.parentNode;
4159                 node = ieTable(2, ts, html, te);
4160             } else{
4161                 if(where == 'afterbegin'){
4162                     before = el.firstChild;
4163                 }
4164                 node = ieTable(3, tbs, html, tbe);
4165             }
4166         } else{ // TABLE
4167             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4168                 return;
4169             }
4170             if(where == 'afterbegin'){
4171                 before = el.firstChild;
4172             }
4173             node = ieTable(2, ts, html, te);
4174         }
4175         el.insertBefore(node, before);
4176         return node;
4177     };
4178
4179     return {
4180     /** True to force the use of DOM instead of html fragments @type Boolean */
4181     useDom : false,
4182
4183     /**
4184      * Returns the markup for the passed Element(s) config
4185      * @param {Object} o The Dom object spec (and children)
4186      * @return {String}
4187      */
4188     markup : function(o){
4189         return createHtml(o);
4190     },
4191
4192     /**
4193      * Applies a style specification to an element
4194      * @param {String/HTMLElement} el The element to apply styles to
4195      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4196      * a function which returns such a specification.
4197      */
4198     applyStyles : function(el, styles){
4199         if(styles){
4200            el = Roo.fly(el);
4201            if(typeof styles == "string"){
4202                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4203                var matches;
4204                while ((matches = re.exec(styles)) != null){
4205                    el.setStyle(matches[1], matches[2]);
4206                }
4207            }else if (typeof styles == "object"){
4208                for (var style in styles){
4209                   el.setStyle(style, styles[style]);
4210                }
4211            }else if (typeof styles == "function"){
4212                 Roo.DomHelper.applyStyles(el, styles.call());
4213            }
4214         }
4215     },
4216
4217     /**
4218      * Inserts an HTML fragment into the Dom
4219      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4220      * @param {HTMLElement} el The context element
4221      * @param {String} html The HTML fragmenet
4222      * @return {HTMLElement} The new node
4223      */
4224     insertHtml : function(where, el, html){
4225         where = where.toLowerCase();
4226         if(el.insertAdjacentHTML){
4227             if(tableRe.test(el.tagName)){
4228                 var rs;
4229                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4230                     return rs;
4231                 }
4232             }
4233             switch(where){
4234                 case "beforebegin":
4235                     el.insertAdjacentHTML('BeforeBegin', html);
4236                     return el.previousSibling;
4237                 case "afterbegin":
4238                     el.insertAdjacentHTML('AfterBegin', html);
4239                     return el.firstChild;
4240                 case "beforeend":
4241                     el.insertAdjacentHTML('BeforeEnd', html);
4242                     return el.lastChild;
4243                 case "afterend":
4244                     el.insertAdjacentHTML('AfterEnd', html);
4245                     return el.nextSibling;
4246             }
4247             throw 'Illegal insertion point -> "' + where + '"';
4248         }
4249         var range = el.ownerDocument.createRange();
4250         var frag;
4251         switch(where){
4252              case "beforebegin":
4253                 range.setStartBefore(el);
4254                 frag = range.createContextualFragment(html);
4255                 el.parentNode.insertBefore(frag, el);
4256                 return el.previousSibling;
4257              case "afterbegin":
4258                 if(el.firstChild){
4259                     range.setStartBefore(el.firstChild);
4260                     frag = range.createContextualFragment(html);
4261                     el.insertBefore(frag, el.firstChild);
4262                     return el.firstChild;
4263                 }else{
4264                     el.innerHTML = html;
4265                     return el.firstChild;
4266                 }
4267             case "beforeend":
4268                 if(el.lastChild){
4269                     range.setStartAfter(el.lastChild);
4270                     frag = range.createContextualFragment(html);
4271                     el.appendChild(frag);
4272                     return el.lastChild;
4273                 }else{
4274                     el.innerHTML = html;
4275                     return el.lastChild;
4276                 }
4277             case "afterend":
4278                 range.setStartAfter(el);
4279                 frag = range.createContextualFragment(html);
4280                 el.parentNode.insertBefore(frag, el.nextSibling);
4281                 return el.nextSibling;
4282             }
4283             throw 'Illegal insertion point -> "' + where + '"';
4284     },
4285
4286     /**
4287      * Creates new Dom element(s) and inserts them before el
4288      * @param {String/HTMLElement/Element} el The context element
4289      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4290      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4291      * @return {HTMLElement/Roo.Element} The new node
4292      */
4293     insertBefore : function(el, o, returnElement){
4294         return this.doInsert(el, o, returnElement, "beforeBegin");
4295     },
4296
4297     /**
4298      * Creates new Dom element(s) and inserts them after el
4299      * @param {String/HTMLElement/Element} el The context element
4300      * @param {Object} o The Dom object spec (and children)
4301      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4302      * @return {HTMLElement/Roo.Element} The new node
4303      */
4304     insertAfter : function(el, o, returnElement){
4305         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4306     },
4307
4308     /**
4309      * Creates new Dom element(s) and inserts them as the first child of el
4310      * @param {String/HTMLElement/Element} el The context element
4311      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4312      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4313      * @return {HTMLElement/Roo.Element} The new node
4314      */
4315     insertFirst : function(el, o, returnElement){
4316         return this.doInsert(el, o, returnElement, "afterBegin");
4317     },
4318
4319     // private
4320     doInsert : function(el, o, returnElement, pos, sibling){
4321         el = Roo.getDom(el);
4322         var newNode;
4323         if(this.useDom || o.ns){
4324             newNode = createDom(o, null);
4325             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4326         }else{
4327             var html = createHtml(o);
4328             newNode = this.insertHtml(pos, el, html);
4329         }
4330         return returnElement ? Roo.get(newNode, true) : newNode;
4331     },
4332
4333     /**
4334      * Creates new Dom element(s) and appends them to el
4335      * @param {String/HTMLElement/Element} el The context element
4336      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4337      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4338      * @return {HTMLElement/Roo.Element} The new node
4339      */
4340     append : function(el, o, returnElement){
4341         el = Roo.getDom(el);
4342         var newNode;
4343         if(this.useDom || o.ns){
4344             newNode = createDom(o, null);
4345             el.appendChild(newNode);
4346         }else{
4347             var html = createHtml(o);
4348             newNode = this.insertHtml("beforeEnd", el, html);
4349         }
4350         return returnElement ? Roo.get(newNode, true) : newNode;
4351     },
4352
4353     /**
4354      * Creates new Dom element(s) and overwrites the contents of el with them
4355      * @param {String/HTMLElement/Element} el The context element
4356      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4357      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4358      * @return {HTMLElement/Roo.Element} The new node
4359      */
4360     overwrite : function(el, o, returnElement){
4361         el = Roo.getDom(el);
4362         if (o.ns) {
4363           
4364             while (el.childNodes.length) {
4365                 el.removeChild(el.firstChild);
4366             }
4367             createDom(o, el);
4368         } else {
4369             el.innerHTML = createHtml(o);   
4370         }
4371         
4372         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4373     },
4374
4375     /**
4376      * Creates a new Roo.DomHelper.Template from the Dom object spec
4377      * @param {Object} o The Dom object spec (and children)
4378      * @return {Roo.DomHelper.Template} The new template
4379      */
4380     createTemplate : function(o){
4381         var html = createHtml(o);
4382         return new Roo.Template(html);
4383     }
4384     };
4385 }();
4386 /*
4387  * Based on:
4388  * Ext JS Library 1.1.1
4389  * Copyright(c) 2006-2007, Ext JS, LLC.
4390  *
4391  * Originally Released Under LGPL - original licence link has changed is not relivant.
4392  *
4393  * Fork - LGPL
4394  * <script type="text/javascript">
4395  */
4396  
4397 /**
4398 * @class Roo.Template
4399 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4400 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4401 * Usage:
4402 <pre><code>
4403 var t = new Roo.Template(
4404     '&lt;div name="{id}"&gt;',
4405         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4406     '&lt;/div&gt;'
4407 );
4408 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4409 </code></pre>
4410 * 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>. 
4411 * @constructor
4412 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4413 */
4414 Roo.Template = function(html){
4415     if(html instanceof Array){
4416         html = html.join("");
4417     }else if(arguments.length > 1){
4418         html = Array.prototype.join.call(arguments, "");
4419     }
4420     /**@private*/
4421     this.html = html;
4422     
4423 };
4424 Roo.Template.prototype = {
4425     /**
4426      * Returns an HTML fragment of this template with the specified values applied.
4427      * @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'})
4428      * @return {String} The HTML fragment
4429      */
4430     applyTemplate : function(values){
4431         try {
4432             
4433             if(this.compiled){
4434                 return this.compiled(values);
4435             }
4436             var useF = this.disableFormats !== true;
4437             var fm = Roo.util.Format, tpl = this;
4438             var fn = function(m, name, format, args){
4439                 if(format && useF){
4440                     if(format.substr(0, 5) == "this."){
4441                         return tpl.call(format.substr(5), values[name], values);
4442                     }else{
4443                         if(args){
4444                             // quoted values are required for strings in compiled templates, 
4445                             // but for non compiled we need to strip them
4446                             // quoted reversed for jsmin
4447                             var re = /^\s*['"](.*)["']\s*$/;
4448                             args = args.split(',');
4449                             for(var i = 0, len = args.length; i < len; i++){
4450                                 args[i] = args[i].replace(re, "$1");
4451                             }
4452                             args = [values[name]].concat(args);
4453                         }else{
4454                             args = [values[name]];
4455                         }
4456                         return fm[format].apply(fm, args);
4457                     }
4458                 }else{
4459                     return values[name] !== undefined ? values[name] : "";
4460                 }
4461             };
4462             return this.html.replace(this.re, fn);
4463         } catch (e) {
4464             Roo.log(e);
4465             throw e;
4466         }
4467          
4468     },
4469     
4470     /**
4471      * Sets the HTML used as the template and optionally compiles it.
4472      * @param {String} html
4473      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4474      * @return {Roo.Template} this
4475      */
4476     set : function(html, compile){
4477         this.html = html;
4478         this.compiled = null;
4479         if(compile){
4480             this.compile();
4481         }
4482         return this;
4483     },
4484     
4485     /**
4486      * True to disable format functions (defaults to false)
4487      * @type Boolean
4488      */
4489     disableFormats : false,
4490     
4491     /**
4492     * The regular expression used to match template variables 
4493     * @type RegExp
4494     * @property 
4495     */
4496     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4497     
4498     /**
4499      * Compiles the template into an internal function, eliminating the RegEx overhead.
4500      * @return {Roo.Template} this
4501      */
4502     compile : function(){
4503         var fm = Roo.util.Format;
4504         var useF = this.disableFormats !== true;
4505         var sep = Roo.isGecko ? "+" : ",";
4506         var fn = function(m, name, format, args){
4507             if(format && useF){
4508                 args = args ? ',' + args : "";
4509                 if(format.substr(0, 5) != "this."){
4510                     format = "fm." + format + '(';
4511                 }else{
4512                     format = 'this.call("'+ format.substr(5) + '", ';
4513                     args = ", values";
4514                 }
4515             }else{
4516                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4517             }
4518             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4519         };
4520         var body;
4521         // branched to use + in gecko and [].join() in others
4522         if(Roo.isGecko){
4523             body = "this.compiled = function(values){ return '" +
4524                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4525                     "';};";
4526         }else{
4527             body = ["this.compiled = function(values){ return ['"];
4528             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4529             body.push("'].join('');};");
4530             body = body.join('');
4531         }
4532         /**
4533          * eval:var:values
4534          * eval:var:fm
4535          */
4536         eval(body);
4537         return this;
4538     },
4539     
4540     // private function used to call members
4541     call : function(fnName, value, allValues){
4542         return this[fnName](value, allValues);
4543     },
4544     
4545     /**
4546      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4547      * @param {String/HTMLElement/Roo.Element} el The context element
4548      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4549      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4550      * @return {HTMLElement/Roo.Element} The new node or Element
4551      */
4552     insertFirst: function(el, values, returnElement){
4553         return this.doInsert('afterBegin', el, values, returnElement);
4554     },
4555
4556     /**
4557      * Applies the supplied values to the template and inserts the new node(s) before el.
4558      * @param {String/HTMLElement/Roo.Element} el The context element
4559      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4560      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4561      * @return {HTMLElement/Roo.Element} The new node or Element
4562      */
4563     insertBefore: function(el, values, returnElement){
4564         return this.doInsert('beforeBegin', el, values, returnElement);
4565     },
4566
4567     /**
4568      * Applies the supplied values to the template and inserts the new node(s) after el.
4569      * @param {String/HTMLElement/Roo.Element} el The context element
4570      * @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'})
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4572      * @return {HTMLElement/Roo.Element} The new node or Element
4573      */
4574     insertAfter : function(el, values, returnElement){
4575         return this.doInsert('afterEnd', el, values, returnElement);
4576     },
4577     
4578     /**
4579      * Applies the supplied values to the template and appends the new node(s) to el.
4580      * @param {String/HTMLElement/Roo.Element} el The context element
4581      * @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'})
4582      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4583      * @return {HTMLElement/Roo.Element} The new node or Element
4584      */
4585     append : function(el, values, returnElement){
4586         return this.doInsert('beforeEnd', el, values, returnElement);
4587     },
4588
4589     doInsert : function(where, el, values, returnEl){
4590         el = Roo.getDom(el);
4591         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4592         return returnEl ? Roo.get(newNode, true) : newNode;
4593     },
4594
4595     /**
4596      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4597      * @param {String/HTMLElement/Roo.Element} el The context element
4598      * @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'})
4599      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4600      * @return {HTMLElement/Roo.Element} The new node or Element
4601      */
4602     overwrite : function(el, values, returnElement){
4603         el = Roo.getDom(el);
4604         el.innerHTML = this.applyTemplate(values);
4605         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4606     }
4607 };
4608 /**
4609  * Alias for {@link #applyTemplate}
4610  * @method
4611  */
4612 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4613
4614 // backwards compat
4615 Roo.DomHelper.Template = Roo.Template;
4616
4617 /**
4618  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4619  * @param {String/HTMLElement} el A DOM element or its id
4620  * @returns {Roo.Template} The created template
4621  * @static
4622  */
4623 Roo.Template.from = function(el){
4624     el = Roo.getDom(el);
4625     return new Roo.Template(el.value || el.innerHTML);
4626 };/*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637
4638 /*
4639  * This is code is also distributed under MIT license for use
4640  * with jQuery and prototype JavaScript libraries.
4641  */
4642 /**
4643  * @class Roo.DomQuery
4644 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).
4645 <p>
4646 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>
4647
4648 <p>
4649 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.
4650 </p>
4651 <h4>Element Selectors:</h4>
4652 <ul class="list">
4653     <li> <b>*</b> any element</li>
4654     <li> <b>E</b> an element with the tag E</li>
4655     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4656     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4657     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4658     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4659 </ul>
4660 <h4>Attribute Selectors:</h4>
4661 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4662 <ul class="list">
4663     <li> <b>E[foo]</b> has an attribute "foo"</li>
4664     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4665     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4666     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4667     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4668     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4669     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4670 </ul>
4671 <h4>Pseudo Classes:</h4>
4672 <ul class="list">
4673     <li> <b>E:first-child</b> E is the first child of its parent</li>
4674     <li> <b>E:last-child</b> E is the last child of its parent</li>
4675     <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>
4676     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4677     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4678     <li> <b>E:only-child</b> E is the only child of its parent</li>
4679     <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>
4680     <li> <b>E:first</b> the first E in the resultset</li>
4681     <li> <b>E:last</b> the last E in the resultset</li>
4682     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4683     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4684     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4685     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4686     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4687     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4688     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4689     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4690     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4691 </ul>
4692 <h4>CSS Value Selectors:</h4>
4693 <ul class="list">
4694     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4695     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4696     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4697     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4698     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4699     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4700 </ul>
4701  * @singleton
4702  */
4703 Roo.DomQuery = function(){
4704     var cache = {}, simpleCache = {}, valueCache = {};
4705     var nonSpace = /\S/;
4706     var trimRe = /^\s+|\s+$/g;
4707     var tplRe = /\{(\d+)\}/g;
4708     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4709     var tagTokenRe = /^(#)?([\w-\*]+)/;
4710     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4711
4712     function child(p, index){
4713         var i = 0;
4714         var n = p.firstChild;
4715         while(n){
4716             if(n.nodeType == 1){
4717                if(++i == index){
4718                    return n;
4719                }
4720             }
4721             n = n.nextSibling;
4722         }
4723         return null;
4724     };
4725
4726     function next(n){
4727         while((n = n.nextSibling) && n.nodeType != 1);
4728         return n;
4729     };
4730
4731     function prev(n){
4732         while((n = n.previousSibling) && n.nodeType != 1);
4733         return n;
4734     };
4735
4736     function children(d){
4737         var n = d.firstChild, ni = -1;
4738             while(n){
4739                 var nx = n.nextSibling;
4740                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4741                     d.removeChild(n);
4742                 }else{
4743                     n.nodeIndex = ++ni;
4744                 }
4745                 n = nx;
4746             }
4747             return this;
4748         };
4749
4750     function byClassName(c, a, v){
4751         if(!v){
4752             return c;
4753         }
4754         var r = [], ri = -1, cn;
4755         for(var i = 0, ci; ci = c[i]; i++){
4756             if((' '+ci.className+' ').indexOf(v) != -1){
4757                 r[++ri] = ci;
4758             }
4759         }
4760         return r;
4761     };
4762
4763     function attrValue(n, attr){
4764         if(!n.tagName && typeof n.length != "undefined"){
4765             n = n[0];
4766         }
4767         if(!n){
4768             return null;
4769         }
4770         if(attr == "for"){
4771             return n.htmlFor;
4772         }
4773         if(attr == "class" || attr == "className"){
4774             return n.className;
4775         }
4776         return n.getAttribute(attr) || n[attr];
4777
4778     };
4779
4780     function getNodes(ns, mode, tagName){
4781         var result = [], ri = -1, cs;
4782         if(!ns){
4783             return result;
4784         }
4785         tagName = tagName || "*";
4786         if(typeof ns.getElementsByTagName != "undefined"){
4787             ns = [ns];
4788         }
4789         if(!mode){
4790             for(var i = 0, ni; ni = ns[i]; i++){
4791                 cs = ni.getElementsByTagName(tagName);
4792                 for(var j = 0, ci; ci = cs[j]; j++){
4793                     result[++ri] = ci;
4794                 }
4795             }
4796         }else if(mode == "/" || mode == ">"){
4797             var utag = tagName.toUpperCase();
4798             for(var i = 0, ni, cn; ni = ns[i]; i++){
4799                 cn = ni.children || ni.childNodes;
4800                 for(var j = 0, cj; cj = cn[j]; j++){
4801                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4802                         result[++ri] = cj;
4803                     }
4804                 }
4805             }
4806         }else if(mode == "+"){
4807             var utag = tagName.toUpperCase();
4808             for(var i = 0, n; n = ns[i]; i++){
4809                 while((n = n.nextSibling) && n.nodeType != 1);
4810                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4811                     result[++ri] = n;
4812                 }
4813             }
4814         }else if(mode == "~"){
4815             for(var i = 0, n; n = ns[i]; i++){
4816                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4817                 if(n){
4818                     result[++ri] = n;
4819                 }
4820             }
4821         }
4822         return result;
4823     };
4824
4825     function concat(a, b){
4826         if(b.slice){
4827             return a.concat(b);
4828         }
4829         for(var i = 0, l = b.length; i < l; i++){
4830             a[a.length] = b[i];
4831         }
4832         return a;
4833     }
4834
4835     function byTag(cs, tagName){
4836         if(cs.tagName || cs == document){
4837             cs = [cs];
4838         }
4839         if(!tagName){
4840             return cs;
4841         }
4842         var r = [], ri = -1;
4843         tagName = tagName.toLowerCase();
4844         for(var i = 0, ci; ci = cs[i]; i++){
4845             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4846                 r[++ri] = ci;
4847             }
4848         }
4849         return r;
4850     };
4851
4852     function byId(cs, attr, id){
4853         if(cs.tagName || cs == document){
4854             cs = [cs];
4855         }
4856         if(!id){
4857             return cs;
4858         }
4859         var r = [], ri = -1;
4860         for(var i = 0,ci; ci = cs[i]; i++){
4861             if(ci && ci.id == id){
4862                 r[++ri] = ci;
4863                 return r;
4864             }
4865         }
4866         return r;
4867     };
4868
4869     function byAttribute(cs, attr, value, op, custom){
4870         var r = [], ri = -1, st = custom=="{";
4871         var f = Roo.DomQuery.operators[op];
4872         for(var i = 0, ci; ci = cs[i]; i++){
4873             var a;
4874             if(st){
4875                 a = Roo.DomQuery.getStyle(ci, attr);
4876             }
4877             else if(attr == "class" || attr == "className"){
4878                 a = ci.className;
4879             }else if(attr == "for"){
4880                 a = ci.htmlFor;
4881             }else if(attr == "href"){
4882                 a = ci.getAttribute("href", 2);
4883             }else{
4884                 a = ci.getAttribute(attr);
4885             }
4886             if((f && f(a, value)) || (!f && a)){
4887                 r[++ri] = ci;
4888             }
4889         }
4890         return r;
4891     };
4892
4893     function byPseudo(cs, name, value){
4894         return Roo.DomQuery.pseudos[name](cs, value);
4895     };
4896
4897     // This is for IE MSXML which does not support expandos.
4898     // IE runs the same speed using setAttribute, however FF slows way down
4899     // and Safari completely fails so they need to continue to use expandos.
4900     var isIE = window.ActiveXObject ? true : false;
4901
4902     // this eval is stop the compressor from
4903     // renaming the variable to something shorter
4904     
4905     /** eval:var:batch */
4906     var batch = 30803; 
4907
4908     var key = 30803;
4909
4910     function nodupIEXml(cs){
4911         var d = ++key;
4912         cs[0].setAttribute("_nodup", d);
4913         var r = [cs[0]];
4914         for(var i = 1, len = cs.length; i < len; i++){
4915             var c = cs[i];
4916             if(!c.getAttribute("_nodup") != d){
4917                 c.setAttribute("_nodup", d);
4918                 r[r.length] = c;
4919             }
4920         }
4921         for(var i = 0, len = cs.length; i < len; i++){
4922             cs[i].removeAttribute("_nodup");
4923         }
4924         return r;
4925     }
4926
4927     function nodup(cs){
4928         if(!cs){
4929             return [];
4930         }
4931         var len = cs.length, c, i, r = cs, cj, ri = -1;
4932         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4933             return cs;
4934         }
4935         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4936             return nodupIEXml(cs);
4937         }
4938         var d = ++key;
4939         cs[0]._nodup = d;
4940         for(i = 1; c = cs[i]; i++){
4941             if(c._nodup != d){
4942                 c._nodup = d;
4943             }else{
4944                 r = [];
4945                 for(var j = 0; j < i; j++){
4946                     r[++ri] = cs[j];
4947                 }
4948                 for(j = i+1; cj = cs[j]; j++){
4949                     if(cj._nodup != d){
4950                         cj._nodup = d;
4951                         r[++ri] = cj;
4952                     }
4953                 }
4954                 return r;
4955             }
4956         }
4957         return r;
4958     }
4959
4960     function quickDiffIEXml(c1, c2){
4961         var d = ++key;
4962         for(var i = 0, len = c1.length; i < len; i++){
4963             c1[i].setAttribute("_qdiff", d);
4964         }
4965         var r = [];
4966         for(var i = 0, len = c2.length; i < len; i++){
4967             if(c2[i].getAttribute("_qdiff") != d){
4968                 r[r.length] = c2[i];
4969             }
4970         }
4971         for(var i = 0, len = c1.length; i < len; i++){
4972            c1[i].removeAttribute("_qdiff");
4973         }
4974         return r;
4975     }
4976
4977     function quickDiff(c1, c2){
4978         var len1 = c1.length;
4979         if(!len1){
4980             return c2;
4981         }
4982         if(isIE && c1[0].selectSingleNode){
4983             return quickDiffIEXml(c1, c2);
4984         }
4985         var d = ++key;
4986         for(var i = 0; i < len1; i++){
4987             c1[i]._qdiff = d;
4988         }
4989         var r = [];
4990         for(var i = 0, len = c2.length; i < len; i++){
4991             if(c2[i]._qdiff != d){
4992                 r[r.length] = c2[i];
4993             }
4994         }
4995         return r;
4996     }
4997
4998     function quickId(ns, mode, root, id){
4999         if(ns == root){
5000            var d = root.ownerDocument || root;
5001            return d.getElementById(id);
5002         }
5003         ns = getNodes(ns, mode, "*");
5004         return byId(ns, null, id);
5005     }
5006
5007     return {
5008         getStyle : function(el, name){
5009             return Roo.fly(el).getStyle(name);
5010         },
5011         /**
5012          * Compiles a selector/xpath query into a reusable function. The returned function
5013          * takes one parameter "root" (optional), which is the context node from where the query should start.
5014          * @param {String} selector The selector/xpath query
5015          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5016          * @return {Function}
5017          */
5018         compile : function(path, type){
5019             type = type || "select";
5020             
5021             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5022             var q = path, mode, lq;
5023             var tk = Roo.DomQuery.matchers;
5024             var tklen = tk.length;
5025             var mm;
5026
5027             // accept leading mode switch
5028             var lmode = q.match(modeRe);
5029             if(lmode && lmode[1]){
5030                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5031                 q = q.replace(lmode[1], "");
5032             }
5033             // strip leading slashes
5034             while(path.substr(0, 1)=="/"){
5035                 path = path.substr(1);
5036             }
5037
5038             while(q && lq != q){
5039                 lq = q;
5040                 var tm = q.match(tagTokenRe);
5041                 if(type == "select"){
5042                     if(tm){
5043                         if(tm[1] == "#"){
5044                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5045                         }else{
5046                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5047                         }
5048                         q = q.replace(tm[0], "");
5049                     }else if(q.substr(0, 1) != '@'){
5050                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5051                     }
5052                 }else{
5053                     if(tm){
5054                         if(tm[1] == "#"){
5055                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5056                         }else{
5057                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5058                         }
5059                         q = q.replace(tm[0], "");
5060                     }
5061                 }
5062                 while(!(mm = q.match(modeRe))){
5063                     var matched = false;
5064                     for(var j = 0; j < tklen; j++){
5065                         var t = tk[j];
5066                         var m = q.match(t.re);
5067                         if(m){
5068                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5069                                                     return m[i];
5070                                                 });
5071                             q = q.replace(m[0], "");
5072                             matched = true;
5073                             break;
5074                         }
5075                     }
5076                     // prevent infinite loop on bad selector
5077                     if(!matched){
5078                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5079                     }
5080                 }
5081                 if(mm[1]){
5082                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5083                     q = q.replace(mm[1], "");
5084                 }
5085             }
5086             fn[fn.length] = "return nodup(n);\n}";
5087             
5088              /** 
5089               * list of variables that need from compression as they are used by eval.
5090              *  eval:var:batch 
5091              *  eval:var:nodup
5092              *  eval:var:byTag
5093              *  eval:var:ById
5094              *  eval:var:getNodes
5095              *  eval:var:quickId
5096              *  eval:var:mode
5097              *  eval:var:root
5098              *  eval:var:n
5099              *  eval:var:byClassName
5100              *  eval:var:byPseudo
5101              *  eval:var:byAttribute
5102              *  eval:var:attrValue
5103              * 
5104              **/ 
5105             eval(fn.join(""));
5106             return f;
5107         },
5108
5109         /**
5110          * Selects a group of elements.
5111          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5112          * @param {Node} root (optional) The start of the query (defaults to document).
5113          * @return {Array}
5114          */
5115         select : function(path, root, type){
5116             if(!root || root == document){
5117                 root = document;
5118             }
5119             if(typeof root == "string"){
5120                 root = document.getElementById(root);
5121             }
5122             var paths = path.split(",");
5123             var results = [];
5124             for(var i = 0, len = paths.length; i < len; i++){
5125                 var p = paths[i].replace(trimRe, "");
5126                 if(!cache[p]){
5127                     cache[p] = Roo.DomQuery.compile(p);
5128                     if(!cache[p]){
5129                         throw p + " is not a valid selector";
5130                     }
5131                 }
5132                 var result = cache[p](root);
5133                 if(result && result != document){
5134                     results = results.concat(result);
5135                 }
5136             }
5137             if(paths.length > 1){
5138                 return nodup(results);
5139             }
5140             return results;
5141         },
5142
5143         /**
5144          * Selects a single element.
5145          * @param {String} selector The selector/xpath query
5146          * @param {Node} root (optional) The start of the query (defaults to document).
5147          * @return {Element}
5148          */
5149         selectNode : function(path, root){
5150             return Roo.DomQuery.select(path, root)[0];
5151         },
5152
5153         /**
5154          * Selects the value of a node, optionally replacing null with the defaultValue.
5155          * @param {String} selector The selector/xpath query
5156          * @param {Node} root (optional) The start of the query (defaults to document).
5157          * @param {String} defaultValue
5158          */
5159         selectValue : function(path, root, defaultValue){
5160             path = path.replace(trimRe, "");
5161             if(!valueCache[path]){
5162                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5163             }
5164             var n = valueCache[path](root);
5165             n = n[0] ? n[0] : n;
5166             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5167             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5168         },
5169
5170         /**
5171          * Selects the value of a node, parsing integers and floats.
5172          * @param {String} selector The selector/xpath query
5173          * @param {Node} root (optional) The start of the query (defaults to document).
5174          * @param {Number} defaultValue
5175          * @return {Number}
5176          */
5177         selectNumber : function(path, root, defaultValue){
5178             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5179             return parseFloat(v);
5180         },
5181
5182         /**
5183          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5184          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5185          * @param {String} selector The simple selector to test
5186          * @return {Boolean}
5187          */
5188         is : function(el, ss){
5189             if(typeof el == "string"){
5190                 el = document.getElementById(el);
5191             }
5192             var isArray = (el instanceof Array);
5193             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5194             return isArray ? (result.length == el.length) : (result.length > 0);
5195         },
5196
5197         /**
5198          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5199          * @param {Array} el An array of elements to filter
5200          * @param {String} selector The simple selector to test
5201          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5202          * the selector instead of the ones that match
5203          * @return {Array}
5204          */
5205         filter : function(els, ss, nonMatches){
5206             ss = ss.replace(trimRe, "");
5207             if(!simpleCache[ss]){
5208                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5209             }
5210             var result = simpleCache[ss](els);
5211             return nonMatches ? quickDiff(result, els) : result;
5212         },
5213
5214         /**
5215          * Collection of matching regular expressions and code snippets.
5216          */
5217         matchers : [{
5218                 re: /^\.([\w-]+)/,
5219                 select: 'n = byClassName(n, null, " {1} ");'
5220             }, {
5221                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5222                 select: 'n = byPseudo(n, "{1}", "{2}");'
5223             },{
5224                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5225                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5226             }, {
5227                 re: /^#([\w-]+)/,
5228                 select: 'n = byId(n, null, "{1}");'
5229             },{
5230                 re: /^@([\w-]+)/,
5231                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5232             }
5233         ],
5234
5235         /**
5236          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5237          * 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;.
5238          */
5239         operators : {
5240             "=" : function(a, v){
5241                 return a == v;
5242             },
5243             "!=" : function(a, v){
5244                 return a != v;
5245             },
5246             "^=" : function(a, v){
5247                 return a && a.substr(0, v.length) == v;
5248             },
5249             "$=" : function(a, v){
5250                 return a && a.substr(a.length-v.length) == v;
5251             },
5252             "*=" : function(a, v){
5253                 return a && a.indexOf(v) !== -1;
5254             },
5255             "%=" : function(a, v){
5256                 return (a % v) == 0;
5257             },
5258             "|=" : function(a, v){
5259                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5260             },
5261             "~=" : function(a, v){
5262                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5263             }
5264         },
5265
5266         /**
5267          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5268          * and the argument (if any) supplied in the selector.
5269          */
5270         pseudos : {
5271             "first-child" : function(c){
5272                 var r = [], ri = -1, n;
5273                 for(var i = 0, ci; ci = n = c[i]; i++){
5274                     while((n = n.previousSibling) && n.nodeType != 1);
5275                     if(!n){
5276                         r[++ri] = ci;
5277                     }
5278                 }
5279                 return r;
5280             },
5281
5282             "last-child" : function(c){
5283                 var r = [], ri = -1, n;
5284                 for(var i = 0, ci; ci = n = c[i]; i++){
5285                     while((n = n.nextSibling) && n.nodeType != 1);
5286                     if(!n){
5287                         r[++ri] = ci;
5288                     }
5289                 }
5290                 return r;
5291             },
5292
5293             "nth-child" : function(c, a) {
5294                 var r = [], ri = -1;
5295                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5296                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5297                 for(var i = 0, n; n = c[i]; i++){
5298                     var pn = n.parentNode;
5299                     if (batch != pn._batch) {
5300                         var j = 0;
5301                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5302                             if(cn.nodeType == 1){
5303                                cn.nodeIndex = ++j;
5304                             }
5305                         }
5306                         pn._batch = batch;
5307                     }
5308                     if (f == 1) {
5309                         if (l == 0 || n.nodeIndex == l){
5310                             r[++ri] = n;
5311                         }
5312                     } else if ((n.nodeIndex + l) % f == 0){
5313                         r[++ri] = n;
5314                     }
5315                 }
5316
5317                 return r;
5318             },
5319
5320             "only-child" : function(c){
5321                 var r = [], ri = -1;;
5322                 for(var i = 0, ci; ci = c[i]; i++){
5323                     if(!prev(ci) && !next(ci)){
5324                         r[++ri] = ci;
5325                     }
5326                 }
5327                 return r;
5328             },
5329
5330             "empty" : function(c){
5331                 var r = [], ri = -1;
5332                 for(var i = 0, ci; ci = c[i]; i++){
5333                     var cns = ci.childNodes, j = 0, cn, empty = true;
5334                     while(cn = cns[j]){
5335                         ++j;
5336                         if(cn.nodeType == 1 || cn.nodeType == 3){
5337                             empty = false;
5338                             break;
5339                         }
5340                     }
5341                     if(empty){
5342                         r[++ri] = ci;
5343                     }
5344                 }
5345                 return r;
5346             },
5347
5348             "contains" : function(c, v){
5349                 var r = [], ri = -1;
5350                 for(var i = 0, ci; ci = c[i]; i++){
5351                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5352                         r[++ri] = ci;
5353                     }
5354                 }
5355                 return r;
5356             },
5357
5358             "nodeValue" : function(c, v){
5359                 var r = [], ri = -1;
5360                 for(var i = 0, ci; ci = c[i]; i++){
5361                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5362                         r[++ri] = ci;
5363                     }
5364                 }
5365                 return r;
5366             },
5367
5368             "checked" : function(c){
5369                 var r = [], ri = -1;
5370                 for(var i = 0, ci; ci = c[i]; i++){
5371                     if(ci.checked == true){
5372                         r[++ri] = ci;
5373                     }
5374                 }
5375                 return r;
5376             },
5377
5378             "not" : function(c, ss){
5379                 return Roo.DomQuery.filter(c, ss, true);
5380             },
5381
5382             "odd" : function(c){
5383                 return this["nth-child"](c, "odd");
5384             },
5385
5386             "even" : function(c){
5387                 return this["nth-child"](c, "even");
5388             },
5389
5390             "nth" : function(c, a){
5391                 return c[a-1] || [];
5392             },
5393
5394             "first" : function(c){
5395                 return c[0] || [];
5396             },
5397
5398             "last" : function(c){
5399                 return c[c.length-1] || [];
5400             },
5401
5402             "has" : function(c, ss){
5403                 var s = Roo.DomQuery.select;
5404                 var r = [], ri = -1;
5405                 for(var i = 0, ci; ci = c[i]; i++){
5406                     if(s(ss, ci).length > 0){
5407                         r[++ri] = ci;
5408                     }
5409                 }
5410                 return r;
5411             },
5412
5413             "next" : function(c, ss){
5414                 var is = Roo.DomQuery.is;
5415                 var r = [], ri = -1;
5416                 for(var i = 0, ci; ci = c[i]; i++){
5417                     var n = next(ci);
5418                     if(n && is(n, ss)){
5419                         r[++ri] = ci;
5420                     }
5421                 }
5422                 return r;
5423             },
5424
5425             "prev" : function(c, ss){
5426                 var is = Roo.DomQuery.is;
5427                 var r = [], ri = -1;
5428                 for(var i = 0, ci; ci = c[i]; i++){
5429                     var n = prev(ci);
5430                     if(n && is(n, ss)){
5431                         r[++ri] = ci;
5432                     }
5433                 }
5434                 return r;
5435             }
5436         }
5437     };
5438 }();
5439
5440 /**
5441  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5442  * @param {String} path The selector/xpath query
5443  * @param {Node} root (optional) The start of the query (defaults to document).
5444  * @return {Array}
5445  * @member Roo
5446  * @method query
5447  */
5448 Roo.query = Roo.DomQuery.select;
5449 /*
5450  * Based on:
5451  * Ext JS Library 1.1.1
5452  * Copyright(c) 2006-2007, Ext JS, LLC.
5453  *
5454  * Originally Released Under LGPL - original licence link has changed is not relivant.
5455  *
5456  * Fork - LGPL
5457  * <script type="text/javascript">
5458  */
5459
5460 /**
5461  * @class Roo.util.Observable
5462  * Base class that provides a common interface for publishing events. Subclasses are expected to
5463  * to have a property "events" with all the events defined.<br>
5464  * For example:
5465  * <pre><code>
5466  Employee = function(name){
5467     this.name = name;
5468     this.addEvents({
5469         "fired" : true,
5470         "quit" : true
5471     });
5472  }
5473  Roo.extend(Employee, Roo.util.Observable);
5474 </code></pre>
5475  * @param {Object} config properties to use (incuding events / listeners)
5476  */
5477
5478 Roo.util.Observable = function(cfg){
5479     
5480     cfg = cfg|| {};
5481     this.addEvents(cfg.events || {});
5482     if (cfg.events) {
5483         delete cfg.events; // make sure
5484     }
5485      
5486     Roo.apply(this, cfg);
5487     
5488     if(this.listeners){
5489         this.on(this.listeners);
5490         delete this.listeners;
5491     }
5492 };
5493 Roo.util.Observable.prototype = {
5494     /** 
5495  * @cfg {Object} listeners  list of events and functions to call for this object, 
5496  * For example :
5497  * <pre><code>
5498     listeners :  { 
5499        'click' : function(e) {
5500            ..... 
5501         } ,
5502         .... 
5503     } 
5504   </code></pre>
5505  */
5506     
5507     
5508     /**
5509      * Fires the specified event with the passed parameters (minus the event name).
5510      * @param {String} eventName
5511      * @param {Object...} args Variable number of parameters are passed to handlers
5512      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5513      */
5514     fireEvent : function(){
5515         var ce = this.events[arguments[0].toLowerCase()];
5516         if(typeof ce == "object"){
5517             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5518         }else{
5519             return true;
5520         }
5521     },
5522
5523     // private
5524     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5525
5526     /**
5527      * Appends an event handler to this component
5528      * @param {String}   eventName The type of event to listen for
5529      * @param {Function} handler The method the event invokes
5530      * @param {Object}   scope (optional) The scope in which to execute the handler
5531      * function. The handler function's "this" context.
5532      * @param {Object}   options (optional) An object containing handler configuration
5533      * properties. This may contain any of the following properties:<ul>
5534      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5535      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5536      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5537      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5538      * by the specified number of milliseconds. If the event fires again within that time, the original
5539      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5540      * </ul><br>
5541      * <p>
5542      * <b>Combining Options</b><br>
5543      * Using the options argument, it is possible to combine different types of listeners:<br>
5544      * <br>
5545      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5546                 <pre><code>
5547                 el.on('click', this.onClick, this, {
5548                         single: true,
5549                 delay: 100,
5550                 forumId: 4
5551                 });
5552                 </code></pre>
5553      * <p>
5554      * <b>Attaching multiple handlers in 1 call</b><br>
5555      * The method also allows for a single argument to be passed which is a config object containing properties
5556      * which specify multiple handlers.
5557      * <pre><code>
5558                 el.on({
5559                         'click': {
5560                         fn: this.onClick,
5561                         scope: this,
5562                         delay: 100
5563                 }, 
5564                 'mouseover': {
5565                         fn: this.onMouseOver,
5566                         scope: this
5567                 },
5568                 'mouseout': {
5569                         fn: this.onMouseOut,
5570                         scope: this
5571                 }
5572                 });
5573                 </code></pre>
5574      * <p>
5575      * Or a shorthand syntax which passes the same scope object to all handlers:
5576         <pre><code>
5577                 el.on({
5578                         'click': this.onClick,
5579                 'mouseover': this.onMouseOver,
5580                 'mouseout': this.onMouseOut,
5581                 scope: this
5582                 });
5583                 </code></pre>
5584      */
5585     addListener : function(eventName, fn, scope, o){
5586         if(typeof eventName == "object"){
5587             o = eventName;
5588             for(var e in o){
5589                 if(this.filterOptRe.test(e)){
5590                     continue;
5591                 }
5592                 if(typeof o[e] == "function"){
5593                     // shared options
5594                     this.addListener(e, o[e], o.scope,  o);
5595                 }else{
5596                     // individual options
5597                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5598                 }
5599             }
5600             return;
5601         }
5602         o = (!o || typeof o == "boolean") ? {} : o;
5603         eventName = eventName.toLowerCase();
5604         var ce = this.events[eventName] || true;
5605         if(typeof ce == "boolean"){
5606             ce = new Roo.util.Event(this, eventName);
5607             this.events[eventName] = ce;
5608         }
5609         ce.addListener(fn, scope, o);
5610     },
5611
5612     /**
5613      * Removes a listener
5614      * @param {String}   eventName     The type of event to listen for
5615      * @param {Function} handler        The handler to remove
5616      * @param {Object}   scope  (optional) The scope (this object) for the handler
5617      */
5618     removeListener : function(eventName, fn, scope){
5619         var ce = this.events[eventName.toLowerCase()];
5620         if(typeof ce == "object"){
5621             ce.removeListener(fn, scope);
5622         }
5623     },
5624
5625     /**
5626      * Removes all listeners for this object
5627      */
5628     purgeListeners : function(){
5629         for(var evt in this.events){
5630             if(typeof this.events[evt] == "object"){
5631                  this.events[evt].clearListeners();
5632             }
5633         }
5634     },
5635
5636     relayEvents : function(o, events){
5637         var createHandler = function(ename){
5638             return function(){
5639                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5640             };
5641         };
5642         for(var i = 0, len = events.length; i < len; i++){
5643             var ename = events[i];
5644             if(!this.events[ename]){ this.events[ename] = true; };
5645             o.on(ename, createHandler(ename), this);
5646         }
5647     },
5648
5649     /**
5650      * Used to define events on this Observable
5651      * @param {Object} object The object with the events defined
5652      */
5653     addEvents : function(o){
5654         if(!this.events){
5655             this.events = {};
5656         }
5657         Roo.applyIf(this.events, o);
5658     },
5659
5660     /**
5661      * Checks to see if this object has any listeners for a specified event
5662      * @param {String} eventName The name of the event to check for
5663      * @return {Boolean} True if the event is being listened for, else false
5664      */
5665     hasListener : function(eventName){
5666         var e = this.events[eventName];
5667         return typeof e == "object" && e.listeners.length > 0;
5668     }
5669 };
5670 /**
5671  * Appends an event handler to this element (shorthand for addListener)
5672  * @param {String}   eventName     The type of event to listen for
5673  * @param {Function} handler        The method the event invokes
5674  * @param {Object}   scope (optional) The scope in which to execute the handler
5675  * function. The handler function's "this" context.
5676  * @param {Object}   options  (optional)
5677  * @method
5678  */
5679 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5680 /**
5681  * Removes a listener (shorthand for removeListener)
5682  * @param {String}   eventName     The type of event to listen for
5683  * @param {Function} handler        The handler to remove
5684  * @param {Object}   scope  (optional) The scope (this object) for the handler
5685  * @method
5686  */
5687 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5688
5689 /**
5690  * Starts capture on the specified Observable. All events will be passed
5691  * to the supplied function with the event name + standard signature of the event
5692  * <b>before</b> the event is fired. If the supplied function returns false,
5693  * the event will not fire.
5694  * @param {Observable} o The Observable to capture
5695  * @param {Function} fn The function to call
5696  * @param {Object} scope (optional) The scope (this object) for the fn
5697  * @static
5698  */
5699 Roo.util.Observable.capture = function(o, fn, scope){
5700     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5701 };
5702
5703 /**
5704  * Removes <b>all</b> added captures from the Observable.
5705  * @param {Observable} o The Observable to release
5706  * @static
5707  */
5708 Roo.util.Observable.releaseCapture = function(o){
5709     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5710 };
5711
5712 (function(){
5713
5714     var createBuffered = function(h, o, scope){
5715         var task = new Roo.util.DelayedTask();
5716         return function(){
5717             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5718         };
5719     };
5720
5721     var createSingle = function(h, e, fn, scope){
5722         return function(){
5723             e.removeListener(fn, scope);
5724             return h.apply(scope, arguments);
5725         };
5726     };
5727
5728     var createDelayed = function(h, o, scope){
5729         return function(){
5730             var args = Array.prototype.slice.call(arguments, 0);
5731             setTimeout(function(){
5732                 h.apply(scope, args);
5733             }, o.delay || 10);
5734         };
5735     };
5736
5737     Roo.util.Event = function(obj, name){
5738         this.name = name;
5739         this.obj = obj;
5740         this.listeners = [];
5741     };
5742
5743     Roo.util.Event.prototype = {
5744         addListener : function(fn, scope, options){
5745             var o = options || {};
5746             scope = scope || this.obj;
5747             if(!this.isListening(fn, scope)){
5748                 var l = {fn: fn, scope: scope, options: o};
5749                 var h = fn;
5750                 if(o.delay){
5751                     h = createDelayed(h, o, scope);
5752                 }
5753                 if(o.single){
5754                     h = createSingle(h, this, fn, scope);
5755                 }
5756                 if(o.buffer){
5757                     h = createBuffered(h, o, scope);
5758                 }
5759                 l.fireFn = h;
5760                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5761                     this.listeners.push(l);
5762                 }else{
5763                     this.listeners = this.listeners.slice(0);
5764                     this.listeners.push(l);
5765                 }
5766             }
5767         },
5768
5769         findListener : function(fn, scope){
5770             scope = scope || this.obj;
5771             var ls = this.listeners;
5772             for(var i = 0, len = ls.length; i < len; i++){
5773                 var l = ls[i];
5774                 if(l.fn == fn && l.scope == scope){
5775                     return i;
5776                 }
5777             }
5778             return -1;
5779         },
5780
5781         isListening : function(fn, scope){
5782             return this.findListener(fn, scope) != -1;
5783         },
5784
5785         removeListener : function(fn, scope){
5786             var index;
5787             if((index = this.findListener(fn, scope)) != -1){
5788                 if(!this.firing){
5789                     this.listeners.splice(index, 1);
5790                 }else{
5791                     this.listeners = this.listeners.slice(0);
5792                     this.listeners.splice(index, 1);
5793                 }
5794                 return true;
5795             }
5796             return false;
5797         },
5798
5799         clearListeners : function(){
5800             this.listeners = [];
5801         },
5802
5803         fire : function(){
5804             var ls = this.listeners, scope, len = ls.length;
5805             if(len > 0){
5806                 this.firing = true;
5807                 var args = Array.prototype.slice.call(arguments, 0);
5808                 for(var i = 0; i < len; i++){
5809                     var l = ls[i];
5810                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5811                         this.firing = false;
5812                         return false;
5813                     }
5814                 }
5815                 this.firing = false;
5816             }
5817             return true;
5818         }
5819     };
5820 })();/*
5821  * Based on:
5822  * Ext JS Library 1.1.1
5823  * Copyright(c) 2006-2007, Ext JS, LLC.
5824  *
5825  * Originally Released Under LGPL - original licence link has changed is not relivant.
5826  *
5827  * Fork - LGPL
5828  * <script type="text/javascript">
5829  */
5830
5831 /**
5832  * @class Roo.EventManager
5833  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5834  * several useful events directly.
5835  * See {@link Roo.EventObject} for more details on normalized event objects.
5836  * @singleton
5837  */
5838 Roo.EventManager = function(){
5839     var docReadyEvent, docReadyProcId, docReadyState = false;
5840     var resizeEvent, resizeTask, textEvent, textSize;
5841     var E = Roo.lib.Event;
5842     var D = Roo.lib.Dom;
5843
5844
5845     var fireDocReady = function(){
5846         if(!docReadyState){
5847             docReadyState = true;
5848             Roo.isReady = true;
5849             if(docReadyProcId){
5850                 clearInterval(docReadyProcId);
5851             }
5852             if(Roo.isGecko || Roo.isOpera) {
5853                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5854             }
5855             if(Roo.isIE){
5856                 var defer = document.getElementById("ie-deferred-loader");
5857                 if(defer){
5858                     defer.onreadystatechange = null;
5859                     defer.parentNode.removeChild(defer);
5860                 }
5861             }
5862             if(docReadyEvent){
5863                 docReadyEvent.fire();
5864                 docReadyEvent.clearListeners();
5865             }
5866         }
5867     };
5868     
5869     var initDocReady = function(){
5870         docReadyEvent = new Roo.util.Event();
5871         if(Roo.isGecko || Roo.isOpera) {
5872             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5873         }else if(Roo.isIE){
5874             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5875             var defer = document.getElementById("ie-deferred-loader");
5876             defer.onreadystatechange = function(){
5877                 if(this.readyState == "complete"){
5878                     fireDocReady();
5879                 }
5880             };
5881         }else if(Roo.isSafari){ 
5882             docReadyProcId = setInterval(function(){
5883                 var rs = document.readyState;
5884                 if(rs == "complete") {
5885                     fireDocReady();     
5886                  }
5887             }, 10);
5888         }
5889         // no matter what, make sure it fires on load
5890         E.on(window, "load", fireDocReady);
5891     };
5892
5893     var createBuffered = function(h, o){
5894         var task = new Roo.util.DelayedTask(h);
5895         return function(e){
5896             // create new event object impl so new events don't wipe out properties
5897             e = new Roo.EventObjectImpl(e);
5898             task.delay(o.buffer, h, null, [e]);
5899         };
5900     };
5901
5902     var createSingle = function(h, el, ename, fn){
5903         return function(e){
5904             Roo.EventManager.removeListener(el, ename, fn);
5905             h(e);
5906         };
5907     };
5908
5909     var createDelayed = function(h, o){
5910         return function(e){
5911             // create new event object impl so new events don't wipe out properties
5912             e = new Roo.EventObjectImpl(e);
5913             setTimeout(function(){
5914                 h(e);
5915             }, o.delay || 10);
5916         };
5917     };
5918
5919     var listen = function(element, ename, opt, fn, scope){
5920         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5921         fn = fn || o.fn; scope = scope || o.scope;
5922         var el = Roo.getDom(element);
5923         if(!el){
5924             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5925         }
5926         var h = function(e){
5927             e = Roo.EventObject.setEvent(e);
5928             var t;
5929             if(o.delegate){
5930                 t = e.getTarget(o.delegate, el);
5931                 if(!t){
5932                     return;
5933                 }
5934             }else{
5935                 t = e.target;
5936             }
5937             if(o.stopEvent === true){
5938                 e.stopEvent();
5939             }
5940             if(o.preventDefault === true){
5941                e.preventDefault();
5942             }
5943             if(o.stopPropagation === true){
5944                 e.stopPropagation();
5945             }
5946
5947             if(o.normalized === false){
5948                 e = e.browserEvent;
5949             }
5950
5951             fn.call(scope || el, e, t, o);
5952         };
5953         if(o.delay){
5954             h = createDelayed(h, o);
5955         }
5956         if(o.single){
5957             h = createSingle(h, el, ename, fn);
5958         }
5959         if(o.buffer){
5960             h = createBuffered(h, o);
5961         }
5962         fn._handlers = fn._handlers || [];
5963         fn._handlers.push([Roo.id(el), ename, h]);
5964
5965         E.on(el, ename, h);
5966         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5967             el.addEventListener("DOMMouseScroll", h, false);
5968             E.on(window, 'unload', function(){
5969                 el.removeEventListener("DOMMouseScroll", h, false);
5970             });
5971         }
5972         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5973             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5974         }
5975         return h;
5976     };
5977
5978     var stopListening = function(el, ename, fn){
5979         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5980         if(hds){
5981             for(var i = 0, len = hds.length; i < len; i++){
5982                 var h = hds[i];
5983                 if(h[0] == id && h[1] == ename){
5984                     hd = h[2];
5985                     hds.splice(i, 1);
5986                     break;
5987                 }
5988             }
5989         }
5990         E.un(el, ename, hd);
5991         el = Roo.getDom(el);
5992         if(ename == "mousewheel" && el.addEventListener){
5993             el.removeEventListener("DOMMouseScroll", hd, false);
5994         }
5995         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5996             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5997         }
5998     };
5999
6000     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6001     
6002     var pub = {
6003         
6004         
6005         /** 
6006          * Fix for doc tools
6007          * @scope Roo.EventManager
6008          */
6009         
6010         
6011         /** 
6012          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6013          * object with a Roo.EventObject
6014          * @param {Function} fn        The method the event invokes
6015          * @param {Object}   scope    An object that becomes the scope of the handler
6016          * @param {boolean}  override If true, the obj passed in becomes
6017          *                             the execution scope of the listener
6018          * @return {Function} The wrapped function
6019          * @deprecated
6020          */
6021         wrap : function(fn, scope, override){
6022             return function(e){
6023                 Roo.EventObject.setEvent(e);
6024                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6025             };
6026         },
6027         
6028         /**
6029      * Appends an event handler to an element (shorthand for addListener)
6030      * @param {String/HTMLElement}   element        The html element or id to assign the
6031      * @param {String}   eventName The type of event to listen for
6032      * @param {Function} handler The method the event invokes
6033      * @param {Object}   scope (optional) The scope in which to execute the handler
6034      * function. The handler function's "this" context.
6035      * @param {Object}   options (optional) An object containing handler configuration
6036      * properties. This may contain any of the following properties:<ul>
6037      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6038      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6039      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6040      * <li>preventDefault {Boolean} True to prevent the default action</li>
6041      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6042      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6043      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6044      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6045      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6046      * by the specified number of milliseconds. If the event fires again within that time, the original
6047      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6048      * </ul><br>
6049      * <p>
6050      * <b>Combining Options</b><br>
6051      * Using the options argument, it is possible to combine different types of listeners:<br>
6052      * <br>
6053      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6054      * Code:<pre><code>
6055 el.on('click', this.onClick, this, {
6056     single: true,
6057     delay: 100,
6058     stopEvent : true,
6059     forumId: 4
6060 });</code></pre>
6061      * <p>
6062      * <b>Attaching multiple handlers in 1 call</b><br>
6063       * The method also allows for a single argument to be passed which is a config object containing properties
6064      * which specify multiple handlers.
6065      * <p>
6066      * Code:<pre><code>
6067 el.on({
6068     'click' : {
6069         fn: this.onClick
6070         scope: this,
6071         delay: 100
6072     },
6073     'mouseover' : {
6074         fn: this.onMouseOver
6075         scope: this
6076     },
6077     'mouseout' : {
6078         fn: this.onMouseOut
6079         scope: this
6080     }
6081 });</code></pre>
6082      * <p>
6083      * Or a shorthand syntax:<br>
6084      * Code:<pre><code>
6085 el.on({
6086     'click' : this.onClick,
6087     'mouseover' : this.onMouseOver,
6088     'mouseout' : this.onMouseOut
6089     scope: this
6090 });</code></pre>
6091      */
6092         addListener : function(element, eventName, fn, scope, options){
6093             if(typeof eventName == "object"){
6094                 var o = eventName;
6095                 for(var e in o){
6096                     if(propRe.test(e)){
6097                         continue;
6098                     }
6099                     if(typeof o[e] == "function"){
6100                         // shared options
6101                         listen(element, e, o, o[e], o.scope);
6102                     }else{
6103                         // individual options
6104                         listen(element, e, o[e]);
6105                     }
6106                 }
6107                 return;
6108             }
6109             return listen(element, eventName, options, fn, scope);
6110         },
6111         
6112         /**
6113          * Removes an event handler
6114          *
6115          * @param {String/HTMLElement}   element        The id or html element to remove the 
6116          *                             event from
6117          * @param {String}   eventName     The type of event
6118          * @param {Function} fn
6119          * @return {Boolean} True if a listener was actually removed
6120          */
6121         removeListener : function(element, eventName, fn){
6122             return stopListening(element, eventName, fn);
6123         },
6124         
6125         /**
6126          * Fires when the document is ready (before onload and before images are loaded). Can be 
6127          * accessed shorthanded Roo.onReady().
6128          * @param {Function} fn        The method the event invokes
6129          * @param {Object}   scope    An  object that becomes the scope of the handler
6130          * @param {boolean}  options
6131          */
6132         onDocumentReady : function(fn, scope, options){
6133             if(docReadyState){ // if it already fired
6134                 docReadyEvent.addListener(fn, scope, options);
6135                 docReadyEvent.fire();
6136                 docReadyEvent.clearListeners();
6137                 return;
6138             }
6139             if(!docReadyEvent){
6140                 initDocReady();
6141             }
6142             docReadyEvent.addListener(fn, scope, options);
6143         },
6144         
6145         /**
6146          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6147          * @param {Function} fn        The method the event invokes
6148          * @param {Object}   scope    An object that becomes the scope of the handler
6149          * @param {boolean}  options
6150          */
6151         onWindowResize : function(fn, scope, options){
6152             if(!resizeEvent){
6153                 resizeEvent = new Roo.util.Event();
6154                 resizeTask = new Roo.util.DelayedTask(function(){
6155                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6156                 });
6157                 E.on(window, "resize", function(){
6158                     if(Roo.isIE){
6159                         resizeTask.delay(50);
6160                     }else{
6161                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6162                     }
6163                 });
6164             }
6165             resizeEvent.addListener(fn, scope, options);
6166         },
6167
6168         /**
6169          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6170          * @param {Function} fn        The method the event invokes
6171          * @param {Object}   scope    An object that becomes the scope of the handler
6172          * @param {boolean}  options
6173          */
6174         onTextResize : function(fn, scope, options){
6175             if(!textEvent){
6176                 textEvent = new Roo.util.Event();
6177                 var textEl = new Roo.Element(document.createElement('div'));
6178                 textEl.dom.className = 'x-text-resize';
6179                 textEl.dom.innerHTML = 'X';
6180                 textEl.appendTo(document.body);
6181                 textSize = textEl.dom.offsetHeight;
6182                 setInterval(function(){
6183                     if(textEl.dom.offsetHeight != textSize){
6184                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6185                     }
6186                 }, this.textResizeInterval);
6187             }
6188             textEvent.addListener(fn, scope, options);
6189         },
6190
6191         /**
6192          * Removes the passed window resize listener.
6193          * @param {Function} fn        The method the event invokes
6194          * @param {Object}   scope    The scope of handler
6195          */
6196         removeResizeListener : function(fn, scope){
6197             if(resizeEvent){
6198                 resizeEvent.removeListener(fn, scope);
6199             }
6200         },
6201
6202         // private
6203         fireResize : function(){
6204             if(resizeEvent){
6205                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6206             }   
6207         },
6208         /**
6209          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6210          */
6211         ieDeferSrc : false,
6212         /**
6213          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6214          */
6215         textResizeInterval : 50
6216     };
6217     
6218     /**
6219      * Fix for doc tools
6220      * @scopeAlias pub=Roo.EventManager
6221      */
6222     
6223      /**
6224      * Appends an event handler to an element (shorthand for addListener)
6225      * @param {String/HTMLElement}   element        The html element or id to assign the
6226      * @param {String}   eventName The type of event to listen for
6227      * @param {Function} handler The method the event invokes
6228      * @param {Object}   scope (optional) The scope in which to execute the handler
6229      * function. The handler function's "this" context.
6230      * @param {Object}   options (optional) An object containing handler configuration
6231      * properties. This may contain any of the following properties:<ul>
6232      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6233      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6234      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6235      * <li>preventDefault {Boolean} True to prevent the default action</li>
6236      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6237      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6238      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6239      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6240      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6241      * by the specified number of milliseconds. If the event fires again within that time, the original
6242      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6243      * </ul><br>
6244      * <p>
6245      * <b>Combining Options</b><br>
6246      * Using the options argument, it is possible to combine different types of listeners:<br>
6247      * <br>
6248      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6249      * Code:<pre><code>
6250 el.on('click', this.onClick, this, {
6251     single: true,
6252     delay: 100,
6253     stopEvent : true,
6254     forumId: 4
6255 });</code></pre>
6256      * <p>
6257      * <b>Attaching multiple handlers in 1 call</b><br>
6258       * The method also allows for a single argument to be passed which is a config object containing properties
6259      * which specify multiple handlers.
6260      * <p>
6261      * Code:<pre><code>
6262 el.on({
6263     'click' : {
6264         fn: this.onClick
6265         scope: this,
6266         delay: 100
6267     },
6268     'mouseover' : {
6269         fn: this.onMouseOver
6270         scope: this
6271     },
6272     'mouseout' : {
6273         fn: this.onMouseOut
6274         scope: this
6275     }
6276 });</code></pre>
6277      * <p>
6278      * Or a shorthand syntax:<br>
6279      * Code:<pre><code>
6280 el.on({
6281     'click' : this.onClick,
6282     'mouseover' : this.onMouseOver,
6283     'mouseout' : this.onMouseOut
6284     scope: this
6285 });</code></pre>
6286      */
6287     pub.on = pub.addListener;
6288     pub.un = pub.removeListener;
6289
6290     pub.stoppedMouseDownEvent = new Roo.util.Event();
6291     return pub;
6292 }();
6293 /**
6294   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6295   * @param {Function} fn        The method the event invokes
6296   * @param {Object}   scope    An  object that becomes the scope of the handler
6297   * @param {boolean}  override If true, the obj passed in becomes
6298   *                             the execution scope of the listener
6299   * @member Roo
6300   * @method onReady
6301  */
6302 Roo.onReady = Roo.EventManager.onDocumentReady;
6303
6304 Roo.onReady(function(){
6305     var bd = Roo.get(document.body);
6306     if(!bd){ return; }
6307
6308     var cls = [
6309             Roo.isIE ? "roo-ie"
6310             : Roo.isGecko ? "roo-gecko"
6311             : Roo.isOpera ? "roo-opera"
6312             : Roo.isSafari ? "roo-safari" : ""];
6313
6314     if(Roo.isMac){
6315         cls.push("roo-mac");
6316     }
6317     if(Roo.isLinux){
6318         cls.push("roo-linux");
6319     }
6320     if(Roo.isBorderBox){
6321         cls.push('roo-border-box');
6322     }
6323     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6324         var p = bd.dom.parentNode;
6325         if(p){
6326             p.className += ' roo-strict';
6327         }
6328     }
6329     bd.addClass(cls.join(' '));
6330 });
6331
6332 /**
6333  * @class Roo.EventObject
6334  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6335  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6336  * Example:
6337  * <pre><code>
6338  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6339     e.preventDefault();
6340     var target = e.getTarget();
6341     ...
6342  }
6343  var myDiv = Roo.get("myDiv");
6344  myDiv.on("click", handleClick);
6345  //or
6346  Roo.EventManager.on("myDiv", 'click', handleClick);
6347  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6348  </code></pre>
6349  * @singleton
6350  */
6351 Roo.EventObject = function(){
6352     
6353     var E = Roo.lib.Event;
6354     
6355     // safari keypress events for special keys return bad keycodes
6356     var safariKeys = {
6357         63234 : 37, // left
6358         63235 : 39, // right
6359         63232 : 38, // up
6360         63233 : 40, // down
6361         63276 : 33, // page up
6362         63277 : 34, // page down
6363         63272 : 46, // delete
6364         63273 : 36, // home
6365         63275 : 35  // end
6366     };
6367
6368     // normalize button clicks
6369     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6370                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6371
6372     Roo.EventObjectImpl = function(e){
6373         if(e){
6374             this.setEvent(e.browserEvent || e);
6375         }
6376     };
6377     Roo.EventObjectImpl.prototype = {
6378         /**
6379          * Used to fix doc tools.
6380          * @scope Roo.EventObject.prototype
6381          */
6382             
6383
6384         
6385         
6386         /** The normal browser event */
6387         browserEvent : null,
6388         /** The button pressed in a mouse event */
6389         button : -1,
6390         /** True if the shift key was down during the event */
6391         shiftKey : false,
6392         /** True if the control key was down during the event */
6393         ctrlKey : false,
6394         /** True if the alt key was down during the event */
6395         altKey : false,
6396
6397         /** Key constant 
6398         * @type Number */
6399         BACKSPACE : 8,
6400         /** Key constant 
6401         * @type Number */
6402         TAB : 9,
6403         /** Key constant 
6404         * @type Number */
6405         RETURN : 13,
6406         /** Key constant 
6407         * @type Number */
6408         ENTER : 13,
6409         /** Key constant 
6410         * @type Number */
6411         SHIFT : 16,
6412         /** Key constant 
6413         * @type Number */
6414         CONTROL : 17,
6415         /** Key constant 
6416         * @type Number */
6417         ESC : 27,
6418         /** Key constant 
6419         * @type Number */
6420         SPACE : 32,
6421         /** Key constant 
6422         * @type Number */
6423         PAGEUP : 33,
6424         /** Key constant 
6425         * @type Number */
6426         PAGEDOWN : 34,
6427         /** Key constant 
6428         * @type Number */
6429         END : 35,
6430         /** Key constant 
6431         * @type Number */
6432         HOME : 36,
6433         /** Key constant 
6434         * @type Number */
6435         LEFT : 37,
6436         /** Key constant 
6437         * @type Number */
6438         UP : 38,
6439         /** Key constant 
6440         * @type Number */
6441         RIGHT : 39,
6442         /** Key constant 
6443         * @type Number */
6444         DOWN : 40,
6445         /** Key constant 
6446         * @type Number */
6447         DELETE : 46,
6448         /** Key constant 
6449         * @type Number */
6450         F5 : 116,
6451
6452            /** @private */
6453         setEvent : function(e){
6454             if(e == this || (e && e.browserEvent)){ // already wrapped
6455                 return e;
6456             }
6457             this.browserEvent = e;
6458             if(e){
6459                 // normalize buttons
6460                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6461                 if(e.type == 'click' && this.button == -1){
6462                     this.button = 0;
6463                 }
6464                 this.type = e.type;
6465                 this.shiftKey = e.shiftKey;
6466                 // mac metaKey behaves like ctrlKey
6467                 this.ctrlKey = e.ctrlKey || e.metaKey;
6468                 this.altKey = e.altKey;
6469                 // in getKey these will be normalized for the mac
6470                 this.keyCode = e.keyCode;
6471                 // keyup warnings on firefox.
6472                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6473                 // cache the target for the delayed and or buffered events
6474                 this.target = E.getTarget(e);
6475                 // same for XY
6476                 this.xy = E.getXY(e);
6477             }else{
6478                 this.button = -1;
6479                 this.shiftKey = false;
6480                 this.ctrlKey = false;
6481                 this.altKey = false;
6482                 this.keyCode = 0;
6483                 this.charCode =0;
6484                 this.target = null;
6485                 this.xy = [0, 0];
6486             }
6487             return this;
6488         },
6489
6490         /**
6491          * Stop the event (preventDefault and stopPropagation)
6492          */
6493         stopEvent : function(){
6494             if(this.browserEvent){
6495                 if(this.browserEvent.type == 'mousedown'){
6496                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6497                 }
6498                 E.stopEvent(this.browserEvent);
6499             }
6500         },
6501
6502         /**
6503          * Prevents the browsers default handling of the event.
6504          */
6505         preventDefault : function(){
6506             if(this.browserEvent){
6507                 E.preventDefault(this.browserEvent);
6508             }
6509         },
6510
6511         /** @private */
6512         isNavKeyPress : function(){
6513             var k = this.keyCode;
6514             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6515             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6516         },
6517
6518         isSpecialKey : function(){
6519             var k = this.keyCode;
6520             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6521             (k == 16) || (k == 17) ||
6522             (k >= 18 && k <= 20) ||
6523             (k >= 33 && k <= 35) ||
6524             (k >= 36 && k <= 39) ||
6525             (k >= 44 && k <= 45);
6526         },
6527         /**
6528          * Cancels bubbling of the event.
6529          */
6530         stopPropagation : function(){
6531             if(this.browserEvent){
6532                 if(this.type == 'mousedown'){
6533                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6534                 }
6535                 E.stopPropagation(this.browserEvent);
6536             }
6537         },
6538
6539         /**
6540          * Gets the key code for the event.
6541          * @return {Number}
6542          */
6543         getCharCode : function(){
6544             return this.charCode || this.keyCode;
6545         },
6546
6547         /**
6548          * Returns a normalized keyCode for the event.
6549          * @return {Number} The key code
6550          */
6551         getKey : function(){
6552             var k = this.keyCode || this.charCode;
6553             return Roo.isSafari ? (safariKeys[k] || k) : k;
6554         },
6555
6556         /**
6557          * Gets the x coordinate of the event.
6558          * @return {Number}
6559          */
6560         getPageX : function(){
6561             return this.xy[0];
6562         },
6563
6564         /**
6565          * Gets the y coordinate of the event.
6566          * @return {Number}
6567          */
6568         getPageY : function(){
6569             return this.xy[1];
6570         },
6571
6572         /**
6573          * Gets the time of the event.
6574          * @return {Number}
6575          */
6576         getTime : function(){
6577             if(this.browserEvent){
6578                 return E.getTime(this.browserEvent);
6579             }
6580             return null;
6581         },
6582
6583         /**
6584          * Gets the page coordinates of the event.
6585          * @return {Array} The xy values like [x, y]
6586          */
6587         getXY : function(){
6588             return this.xy;
6589         },
6590
6591         /**
6592          * Gets the target for the event.
6593          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6594          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6595                 search as a number or element (defaults to 10 || document.body)
6596          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6597          * @return {HTMLelement}
6598          */
6599         getTarget : function(selector, maxDepth, returnEl){
6600             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6601         },
6602         /**
6603          * Gets the related target.
6604          * @return {HTMLElement}
6605          */
6606         getRelatedTarget : function(){
6607             if(this.browserEvent){
6608                 return E.getRelatedTarget(this.browserEvent);
6609             }
6610             return null;
6611         },
6612
6613         /**
6614          * Normalizes mouse wheel delta across browsers
6615          * @return {Number} The delta
6616          */
6617         getWheelDelta : function(){
6618             var e = this.browserEvent;
6619             var delta = 0;
6620             if(e.wheelDelta){ /* IE/Opera. */
6621                 delta = e.wheelDelta/120;
6622             }else if(e.detail){ /* Mozilla case. */
6623                 delta = -e.detail/3;
6624             }
6625             return delta;
6626         },
6627
6628         /**
6629          * Returns true if the control, meta, shift or alt key was pressed during this event.
6630          * @return {Boolean}
6631          */
6632         hasModifier : function(){
6633             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6634         },
6635
6636         /**
6637          * Returns true if the target of this event equals el or is a child of el
6638          * @param {String/HTMLElement/Element} el
6639          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6640          * @return {Boolean}
6641          */
6642         within : function(el, related){
6643             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6644             return t && Roo.fly(el).contains(t);
6645         },
6646
6647         getPoint : function(){
6648             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6649         }
6650     };
6651
6652     return new Roo.EventObjectImpl();
6653 }();
6654             
6655     /*
6656  * Based on:
6657  * Ext JS Library 1.1.1
6658  * Copyright(c) 2006-2007, Ext JS, LLC.
6659  *
6660  * Originally Released Under LGPL - original licence link has changed is not relivant.
6661  *
6662  * Fork - LGPL
6663  * <script type="text/javascript">
6664  */
6665
6666  
6667 // was in Composite Element!??!?!
6668  
6669 (function(){
6670     var D = Roo.lib.Dom;
6671     var E = Roo.lib.Event;
6672     var A = Roo.lib.Anim;
6673
6674     // local style camelizing for speed
6675     var propCache = {};
6676     var camelRe = /(-[a-z])/gi;
6677     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6678     var view = document.defaultView;
6679
6680 /**
6681  * @class Roo.Element
6682  * Represents an Element in the DOM.<br><br>
6683  * Usage:<br>
6684 <pre><code>
6685 var el = Roo.get("my-div");
6686
6687 // or with getEl
6688 var el = getEl("my-div");
6689
6690 // or with a DOM element
6691 var el = Roo.get(myDivElement);
6692 </code></pre>
6693  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6694  * each call instead of constructing a new one.<br><br>
6695  * <b>Animations</b><br />
6696  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6697  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6698 <pre>
6699 Option    Default   Description
6700 --------- --------  ---------------------------------------------
6701 duration  .35       The duration of the animation in seconds
6702 easing    easeOut   The YUI easing method
6703 callback  none      A function to execute when the anim completes
6704 scope     this      The scope (this) of the callback function
6705 </pre>
6706 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6707 * manipulate the animation. Here's an example:
6708 <pre><code>
6709 var el = Roo.get("my-div");
6710
6711 // no animation
6712 el.setWidth(100);
6713
6714 // default animation
6715 el.setWidth(100, true);
6716
6717 // animation with some options set
6718 el.setWidth(100, {
6719     duration: 1,
6720     callback: this.foo,
6721     scope: this
6722 });
6723
6724 // using the "anim" property to get the Anim object
6725 var opt = {
6726     duration: 1,
6727     callback: this.foo,
6728     scope: this
6729 };
6730 el.setWidth(100, opt);
6731 ...
6732 if(opt.anim.isAnimated()){
6733     opt.anim.stop();
6734 }
6735 </code></pre>
6736 * <b> Composite (Collections of) Elements</b><br />
6737  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6738  * @constructor Create a new Element directly.
6739  * @param {String/HTMLElement} element
6740  * @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).
6741  */
6742     Roo.Element = function(element, forceNew){
6743         var dom = typeof element == "string" ?
6744                 document.getElementById(element) : element;
6745         if(!dom){ // invalid id/element
6746             return null;
6747         }
6748         var id = dom.id;
6749         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6750             return Roo.Element.cache[id];
6751         }
6752
6753         /**
6754          * The DOM element
6755          * @type HTMLElement
6756          */
6757         this.dom = dom;
6758
6759         /**
6760          * The DOM element ID
6761          * @type String
6762          */
6763         this.id = id || Roo.id(dom);
6764     };
6765
6766     var El = Roo.Element;
6767
6768     El.prototype = {
6769         /**
6770          * The element's default display mode  (defaults to "")
6771          * @type String
6772          */
6773         originalDisplay : "",
6774
6775         visibilityMode : 1,
6776         /**
6777          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6778          * @type String
6779          */
6780         defaultUnit : "px",
6781         /**
6782          * Sets the element's visibility mode. When setVisible() is called it
6783          * will use this to determine whether to set the visibility or the display property.
6784          * @param visMode Element.VISIBILITY or Element.DISPLAY
6785          * @return {Roo.Element} this
6786          */
6787         setVisibilityMode : function(visMode){
6788             this.visibilityMode = visMode;
6789             return this;
6790         },
6791         /**
6792          * Convenience method for setVisibilityMode(Element.DISPLAY)
6793          * @param {String} display (optional) What to set display to when visible
6794          * @return {Roo.Element} this
6795          */
6796         enableDisplayMode : function(display){
6797             this.setVisibilityMode(El.DISPLAY);
6798             if(typeof display != "undefined") this.originalDisplay = display;
6799             return this;
6800         },
6801
6802         /**
6803          * 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)
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         findParent : function(simpleSelector, maxDepth, returnEl){
6811             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6812             maxDepth = maxDepth || 50;
6813             if(typeof maxDepth != "number"){
6814                 stopEl = Roo.getDom(maxDepth);
6815                 maxDepth = 10;
6816             }
6817             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6818                 if(dq.is(p, simpleSelector)){
6819                     return returnEl ? Roo.get(p) : p;
6820                 }
6821                 depth++;
6822                 p = p.parentNode;
6823             }
6824             return null;
6825         },
6826
6827
6828         /**
6829          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6830          * @param {String} selector The simple selector to test
6831          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6832                 search as a number or element (defaults to 10 || document.body)
6833          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6834          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6835          */
6836         findParentNode : function(simpleSelector, maxDepth, returnEl){
6837             var p = Roo.fly(this.dom.parentNode, '_internal');
6838             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6839         },
6840
6841         /**
6842          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6843          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6844          * @param {String} selector The simple selector to test
6845          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6846                 search as a number or element (defaults to 10 || document.body)
6847          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6848          */
6849         up : function(simpleSelector, maxDepth){
6850             return this.findParentNode(simpleSelector, maxDepth, true);
6851         },
6852
6853
6854
6855         /**
6856          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6857          * @param {String} selector The simple selector to test
6858          * @return {Boolean} True if this element matches the selector, else false
6859          */
6860         is : function(simpleSelector){
6861             return Roo.DomQuery.is(this.dom, simpleSelector);
6862         },
6863
6864         /**
6865          * Perform animation on this element.
6866          * @param {Object} args The YUI animation control args
6867          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6868          * @param {Function} onComplete (optional) Function to call when animation completes
6869          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6870          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6871          * @return {Roo.Element} this
6872          */
6873         animate : function(args, duration, onComplete, easing, animType){
6874             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6875             return this;
6876         },
6877
6878         /*
6879          * @private Internal animation call
6880          */
6881         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6882             animType = animType || 'run';
6883             opt = opt || {};
6884             var anim = Roo.lib.Anim[animType](
6885                 this.dom, args,
6886                 (opt.duration || defaultDur) || .35,
6887                 (opt.easing || defaultEase) || 'easeOut',
6888                 function(){
6889                     Roo.callback(cb, this);
6890                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6891                 },
6892                 this
6893             );
6894             opt.anim = anim;
6895             return anim;
6896         },
6897
6898         // private legacy anim prep
6899         preanim : function(a, i){
6900             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6901         },
6902
6903         /**
6904          * Removes worthless text nodes
6905          * @param {Boolean} forceReclean (optional) By default the element
6906          * keeps track if it has been cleaned already so
6907          * you can call this over and over. However, if you update the element and
6908          * need to force a reclean, you can pass true.
6909          */
6910         clean : function(forceReclean){
6911             if(this.isCleaned && forceReclean !== true){
6912                 return this;
6913             }
6914             var ns = /\S/;
6915             var d = this.dom, n = d.firstChild, ni = -1;
6916             while(n){
6917                 var nx = n.nextSibling;
6918                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6919                     d.removeChild(n);
6920                 }else{
6921                     n.nodeIndex = ++ni;
6922                 }
6923                 n = nx;
6924             }
6925             this.isCleaned = true;
6926             return this;
6927         },
6928
6929         // private
6930         calcOffsetsTo : function(el){
6931             el = Roo.get(el);
6932             var d = el.dom;
6933             var restorePos = false;
6934             if(el.getStyle('position') == 'static'){
6935                 el.position('relative');
6936                 restorePos = true;
6937             }
6938             var x = 0, y =0;
6939             var op = this.dom;
6940             while(op && op != d && op.tagName != 'HTML'){
6941                 x+= op.offsetLeft;
6942                 y+= op.offsetTop;
6943                 op = op.offsetParent;
6944             }
6945             if(restorePos){
6946                 el.position('static');
6947             }
6948             return [x, y];
6949         },
6950
6951         /**
6952          * Scrolls this element into view within the passed container.
6953          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6954          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6955          * @return {Roo.Element} this
6956          */
6957         scrollIntoView : function(container, hscroll){
6958             var c = Roo.getDom(container) || document.body;
6959             var el = this.dom;
6960
6961             var o = this.calcOffsetsTo(c),
6962                 l = o[0],
6963                 t = o[1],
6964                 b = t+el.offsetHeight,
6965                 r = l+el.offsetWidth;
6966
6967             var ch = c.clientHeight;
6968             var ct = parseInt(c.scrollTop, 10);
6969             var cl = parseInt(c.scrollLeft, 10);
6970             var cb = ct + ch;
6971             var cr = cl + c.clientWidth;
6972
6973             if(t < ct){
6974                 c.scrollTop = t;
6975             }else if(b > cb){
6976                 c.scrollTop = b-ch;
6977             }
6978
6979             if(hscroll !== false){
6980                 if(l < cl){
6981                     c.scrollLeft = l;
6982                 }else if(r > cr){
6983                     c.scrollLeft = r-c.clientWidth;
6984                 }
6985             }
6986             return this;
6987         },
6988
6989         // private
6990         scrollChildIntoView : function(child, hscroll){
6991             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6992         },
6993
6994         /**
6995          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6996          * the new height may not be available immediately.
6997          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6998          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6999          * @param {Function} onComplete (optional) Function to call when animation completes
7000          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7001          * @return {Roo.Element} this
7002          */
7003         autoHeight : function(animate, duration, onComplete, easing){
7004             var oldHeight = this.getHeight();
7005             this.clip();
7006             this.setHeight(1); // force clipping
7007             setTimeout(function(){
7008                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7009                 if(!animate){
7010                     this.setHeight(height);
7011                     this.unclip();
7012                     if(typeof onComplete == "function"){
7013                         onComplete();
7014                     }
7015                 }else{
7016                     this.setHeight(oldHeight); // restore original height
7017                     this.setHeight(height, animate, duration, function(){
7018                         this.unclip();
7019                         if(typeof onComplete == "function") onComplete();
7020                     }.createDelegate(this), easing);
7021                 }
7022             }.createDelegate(this), 0);
7023             return this;
7024         },
7025
7026         /**
7027          * Returns true if this element is an ancestor of the passed element
7028          * @param {HTMLElement/String} el The element to check
7029          * @return {Boolean} True if this element is an ancestor of el, else false
7030          */
7031         contains : function(el){
7032             if(!el){return false;}
7033             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7034         },
7035
7036         /**
7037          * Checks whether the element is currently visible using both visibility and display properties.
7038          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7039          * @return {Boolean} True if the element is currently visible, else false
7040          */
7041         isVisible : function(deep) {
7042             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7043             if(deep !== true || !vis){
7044                 return vis;
7045             }
7046             var p = this.dom.parentNode;
7047             while(p && p.tagName.toLowerCase() != "body"){
7048                 if(!Roo.fly(p, '_isVisible').isVisible()){
7049                     return false;
7050                 }
7051                 p = p.parentNode;
7052             }
7053             return true;
7054         },
7055
7056         /**
7057          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7058          * @param {String} selector The CSS selector
7059          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7060          * @return {CompositeElement/CompositeElementLite} The composite element
7061          */
7062         select : function(selector, unique){
7063             return El.select(selector, unique, this.dom);
7064         },
7065
7066         /**
7067          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7068          * @param {String} selector The CSS selector
7069          * @return {Array} An array of the matched nodes
7070          */
7071         query : function(selector, unique){
7072             return Roo.DomQuery.select(selector, this.dom);
7073         },
7074
7075         /**
7076          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7077          * @param {String} selector The CSS selector
7078          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7079          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7080          */
7081         child : function(selector, returnDom){
7082             var n = Roo.DomQuery.selectNode(selector, this.dom);
7083             return returnDom ? n : Roo.get(n);
7084         },
7085
7086         /**
7087          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7088          * @param {String} selector The CSS selector
7089          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7090          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7091          */
7092         down : function(selector, returnDom){
7093             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7094             return returnDom ? n : Roo.get(n);
7095         },
7096
7097         /**
7098          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7099          * @param {String} group The group the DD object is member of
7100          * @param {Object} config The DD config object
7101          * @param {Object} overrides An object containing methods to override/implement on the DD object
7102          * @return {Roo.dd.DD} The DD object
7103          */
7104         initDD : function(group, config, overrides){
7105             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7106             return Roo.apply(dd, overrides);
7107         },
7108
7109         /**
7110          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7111          * @param {String} group The group the DDProxy object is member of
7112          * @param {Object} config The DDProxy config object
7113          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7114          * @return {Roo.dd.DDProxy} The DDProxy object
7115          */
7116         initDDProxy : function(group, config, overrides){
7117             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7118             return Roo.apply(dd, overrides);
7119         },
7120
7121         /**
7122          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7123          * @param {String} group The group the DDTarget object is member of
7124          * @param {Object} config The DDTarget config object
7125          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7126          * @return {Roo.dd.DDTarget} The DDTarget object
7127          */
7128         initDDTarget : function(group, config, overrides){
7129             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7130             return Roo.apply(dd, overrides);
7131         },
7132
7133         /**
7134          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7135          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7136          * @param {Boolean} visible Whether the element is visible
7137          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7138          * @return {Roo.Element} this
7139          */
7140          setVisible : function(visible, animate){
7141             if(!animate || !A){
7142                 if(this.visibilityMode == El.DISPLAY){
7143                     this.setDisplayed(visible);
7144                 }else{
7145                     this.fixDisplay();
7146                     this.dom.style.visibility = visible ? "visible" : "hidden";
7147                 }
7148             }else{
7149                 // closure for composites
7150                 var dom = this.dom;
7151                 var visMode = this.visibilityMode;
7152                 if(visible){
7153                     this.setOpacity(.01);
7154                     this.setVisible(true);
7155                 }
7156                 this.anim({opacity: { to: (visible?1:0) }},
7157                       this.preanim(arguments, 1),
7158                       null, .35, 'easeIn', function(){
7159                          if(!visible){
7160                              if(visMode == El.DISPLAY){
7161                                  dom.style.display = "none";
7162                              }else{
7163                                  dom.style.visibility = "hidden";
7164                              }
7165                              Roo.get(dom).setOpacity(1);
7166                          }
7167                      });
7168             }
7169             return this;
7170         },
7171
7172         /**
7173          * Returns true if display is not "none"
7174          * @return {Boolean}
7175          */
7176         isDisplayed : function() {
7177             return this.getStyle("display") != "none";
7178         },
7179
7180         /**
7181          * Toggles the element's visibility or display, depending on visibility mode.
7182          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7183          * @return {Roo.Element} this
7184          */
7185         toggle : function(animate){
7186             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7187             return this;
7188         },
7189
7190         /**
7191          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7192          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7193          * @return {Roo.Element} this
7194          */
7195         setDisplayed : function(value) {
7196             if(typeof value == "boolean"){
7197                value = value ? this.originalDisplay : "none";
7198             }
7199             this.setStyle("display", value);
7200             return this;
7201         },
7202
7203         /**
7204          * Tries to focus the element. Any exceptions are caught and ignored.
7205          * @return {Roo.Element} this
7206          */
7207         focus : function() {
7208             try{
7209                 this.dom.focus();
7210             }catch(e){}
7211             return this;
7212         },
7213
7214         /**
7215          * Tries to blur the element. Any exceptions are caught and ignored.
7216          * @return {Roo.Element} this
7217          */
7218         blur : function() {
7219             try{
7220                 this.dom.blur();
7221             }catch(e){}
7222             return this;
7223         },
7224
7225         /**
7226          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7227          * @param {String/Array} className The CSS class to add, or an array of classes
7228          * @return {Roo.Element} this
7229          */
7230         addClass : function(className){
7231             if(className instanceof Array){
7232                 for(var i = 0, len = className.length; i < len; i++) {
7233                     this.addClass(className[i]);
7234                 }
7235             }else{
7236                 if(className && !this.hasClass(className)){
7237                     this.dom.className = this.dom.className + " " + className;
7238                 }
7239             }
7240             return this;
7241         },
7242
7243         /**
7244          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7245          * @param {String/Array} className The CSS class to add, or an array of classes
7246          * @return {Roo.Element} this
7247          */
7248         radioClass : function(className){
7249             var siblings = this.dom.parentNode.childNodes;
7250             for(var i = 0; i < siblings.length; i++) {
7251                 var s = siblings[i];
7252                 if(s.nodeType == 1){
7253                     Roo.get(s).removeClass(className);
7254                 }
7255             }
7256             this.addClass(className);
7257             return this;
7258         },
7259
7260         /**
7261          * Removes one or more CSS classes from the element.
7262          * @param {String/Array} className The CSS class to remove, or an array of classes
7263          * @return {Roo.Element} this
7264          */
7265         removeClass : function(className){
7266             if(!className || !this.dom.className){
7267                 return this;
7268             }
7269             if(className instanceof Array){
7270                 for(var i = 0, len = className.length; i < len; i++) {
7271                     this.removeClass(className[i]);
7272                 }
7273             }else{
7274                 if(this.hasClass(className)){
7275                     var re = this.classReCache[className];
7276                     if (!re) {
7277                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7278                        this.classReCache[className] = re;
7279                     }
7280                     this.dom.className =
7281                         this.dom.className.replace(re, " ");
7282                 }
7283             }
7284             return this;
7285         },
7286
7287         // private
7288         classReCache: {},
7289
7290         /**
7291          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7292          * @param {String} className The CSS class to toggle
7293          * @return {Roo.Element} this
7294          */
7295         toggleClass : function(className){
7296             if(this.hasClass(className)){
7297                 this.removeClass(className);
7298             }else{
7299                 this.addClass(className);
7300             }
7301             return this;
7302         },
7303
7304         /**
7305          * Checks if the specified CSS class exists on this element's DOM node.
7306          * @param {String} className The CSS class to check for
7307          * @return {Boolean} True if the class exists, else false
7308          */
7309         hasClass : function(className){
7310             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7311         },
7312
7313         /**
7314          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7315          * @param {String} oldClassName The CSS class to replace
7316          * @param {String} newClassName The replacement CSS class
7317          * @return {Roo.Element} this
7318          */
7319         replaceClass : function(oldClassName, newClassName){
7320             this.removeClass(oldClassName);
7321             this.addClass(newClassName);
7322             return this;
7323         },
7324
7325         /**
7326          * Returns an object with properties matching the styles requested.
7327          * For example, el.getStyles('color', 'font-size', 'width') might return
7328          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7329          * @param {String} style1 A style name
7330          * @param {String} style2 A style name
7331          * @param {String} etc.
7332          * @return {Object} The style object
7333          */
7334         getStyles : function(){
7335             var a = arguments, len = a.length, r = {};
7336             for(var i = 0; i < len; i++){
7337                 r[a[i]] = this.getStyle(a[i]);
7338             }
7339             return r;
7340         },
7341
7342         /**
7343          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7344          * @param {String} property The style property whose value is returned.
7345          * @return {String} The current value of the style property for this element.
7346          */
7347         getStyle : function(){
7348             return view && view.getComputedStyle ?
7349                 function(prop){
7350                     var el = this.dom, v, cs, camel;
7351                     if(prop == 'float'){
7352                         prop = "cssFloat";
7353                     }
7354                     if(el.style && (v = el.style[prop])){
7355                         return v;
7356                     }
7357                     if(cs = view.getComputedStyle(el, "")){
7358                         if(!(camel = propCache[prop])){
7359                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7360                         }
7361                         return cs[camel];
7362                     }
7363                     return null;
7364                 } :
7365                 function(prop){
7366                     var el = this.dom, v, cs, camel;
7367                     if(prop == 'opacity'){
7368                         if(typeof el.style.filter == 'string'){
7369                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7370                             if(m){
7371                                 var fv = parseFloat(m[1]);
7372                                 if(!isNaN(fv)){
7373                                     return fv ? fv / 100 : 0;
7374                                 }
7375                             }
7376                         }
7377                         return 1;
7378                     }else if(prop == 'float'){
7379                         prop = "styleFloat";
7380                     }
7381                     if(!(camel = propCache[prop])){
7382                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7383                     }
7384                     if(v = el.style[camel]){
7385                         return v;
7386                     }
7387                     if(cs = el.currentStyle){
7388                         return cs[camel];
7389                     }
7390                     return null;
7391                 };
7392         }(),
7393
7394         /**
7395          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7396          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7397          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7398          * @return {Roo.Element} this
7399          */
7400         setStyle : function(prop, value){
7401             if(typeof prop == "string"){
7402                 
7403                 if (prop == 'float') {
7404                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7405                     return this;
7406                 }
7407                 
7408                 var camel;
7409                 if(!(camel = propCache[prop])){
7410                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7411                 }
7412                 
7413                 if(camel == 'opacity') {
7414                     this.setOpacity(value);
7415                 }else{
7416                     this.dom.style[camel] = value;
7417                 }
7418             }else{
7419                 for(var style in prop){
7420                     if(typeof prop[style] != "function"){
7421                        this.setStyle(style, prop[style]);
7422                     }
7423                 }
7424             }
7425             return this;
7426         },
7427
7428         /**
7429          * More flexible version of {@link #setStyle} for setting style properties.
7430          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7431          * a function which returns such a specification.
7432          * @return {Roo.Element} this
7433          */
7434         applyStyles : function(style){
7435             Roo.DomHelper.applyStyles(this.dom, style);
7436             return this;
7437         },
7438
7439         /**
7440           * 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).
7441           * @return {Number} The X position of the element
7442           */
7443         getX : function(){
7444             return D.getX(this.dom);
7445         },
7446
7447         /**
7448           * 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).
7449           * @return {Number} The Y position of the element
7450           */
7451         getY : function(){
7452             return D.getY(this.dom);
7453         },
7454
7455         /**
7456           * 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).
7457           * @return {Array} The XY position of the element
7458           */
7459         getXY : function(){
7460             return D.getXY(this.dom);
7461         },
7462
7463         /**
7464          * 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).
7465          * @param {Number} The X position of the element
7466          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7467          * @return {Roo.Element} this
7468          */
7469         setX : function(x, animate){
7470             if(!animate || !A){
7471                 D.setX(this.dom, x);
7472             }else{
7473                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7474             }
7475             return this;
7476         },
7477
7478         /**
7479          * 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).
7480          * @param {Number} The Y position of the element
7481          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7482          * @return {Roo.Element} this
7483          */
7484         setY : function(y, animate){
7485             if(!animate || !A){
7486                 D.setY(this.dom, y);
7487             }else{
7488                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7489             }
7490             return this;
7491         },
7492
7493         /**
7494          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7495          * @param {String} left The left CSS property value
7496          * @return {Roo.Element} this
7497          */
7498         setLeft : function(left){
7499             this.setStyle("left", this.addUnits(left));
7500             return this;
7501         },
7502
7503         /**
7504          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7505          * @param {String} top The top CSS property value
7506          * @return {Roo.Element} this
7507          */
7508         setTop : function(top){
7509             this.setStyle("top", this.addUnits(top));
7510             return this;
7511         },
7512
7513         /**
7514          * Sets the element's CSS right style.
7515          * @param {String} right The right CSS property value
7516          * @return {Roo.Element} this
7517          */
7518         setRight : function(right){
7519             this.setStyle("right", this.addUnits(right));
7520             return this;
7521         },
7522
7523         /**
7524          * Sets the element's CSS bottom style.
7525          * @param {String} bottom The bottom CSS property value
7526          * @return {Roo.Element} this
7527          */
7528         setBottom : function(bottom){
7529             this.setStyle("bottom", this.addUnits(bottom));
7530             return this;
7531         },
7532
7533         /**
7534          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7535          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7536          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7537          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7538          * @return {Roo.Element} this
7539          */
7540         setXY : function(pos, animate){
7541             if(!animate || !A){
7542                 D.setXY(this.dom, pos);
7543             }else{
7544                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7545             }
7546             return this;
7547         },
7548
7549         /**
7550          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7551          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7552          * @param {Number} x X value for new position (coordinates are page-based)
7553          * @param {Number} y Y value for new position (coordinates are page-based)
7554          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7555          * @return {Roo.Element} this
7556          */
7557         setLocation : function(x, y, animate){
7558             this.setXY([x, y], this.preanim(arguments, 2));
7559             return this;
7560         },
7561
7562         /**
7563          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7564          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7565          * @param {Number} x X value for new position (coordinates are page-based)
7566          * @param {Number} y Y value for new position (coordinates are page-based)
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         moveTo : function(x, y, animate){
7571             this.setXY([x, y], this.preanim(arguments, 2));
7572             return this;
7573         },
7574
7575         /**
7576          * Returns the region of the given element.
7577          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7578          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7579          */
7580         getRegion : function(){
7581             return D.getRegion(this.dom);
7582         },
7583
7584         /**
7585          * Returns the offset height of the element
7586          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7587          * @return {Number} The element's height
7588          */
7589         getHeight : function(contentHeight){
7590             var h = this.dom.offsetHeight || 0;
7591             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7592         },
7593
7594         /**
7595          * Returns the offset width of the element
7596          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7597          * @return {Number} The element's width
7598          */
7599         getWidth : function(contentWidth){
7600             var w = this.dom.offsetWidth || 0;
7601             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7602         },
7603
7604         /**
7605          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7606          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7607          * if a height has not been set using CSS.
7608          * @return {Number}
7609          */
7610         getComputedHeight : function(){
7611             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7612             if(!h){
7613                 h = parseInt(this.getStyle('height'), 10) || 0;
7614                 if(!this.isBorderBox()){
7615                     h += this.getFrameWidth('tb');
7616                 }
7617             }
7618             return h;
7619         },
7620
7621         /**
7622          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7623          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7624          * if a width has not been set using CSS.
7625          * @return {Number}
7626          */
7627         getComputedWidth : function(){
7628             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7629             if(!w){
7630                 w = parseInt(this.getStyle('width'), 10) || 0;
7631                 if(!this.isBorderBox()){
7632                     w += this.getFrameWidth('lr');
7633                 }
7634             }
7635             return w;
7636         },
7637
7638         /**
7639          * Returns the size of the element.
7640          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7641          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7642          */
7643         getSize : function(contentSize){
7644             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7645         },
7646
7647         /**
7648          * Returns the width and height of the viewport.
7649          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7650          */
7651         getViewSize : function(){
7652             var d = this.dom, doc = document, aw = 0, ah = 0;
7653             if(d == doc || d == doc.body){
7654                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7655             }else{
7656                 return {
7657                     width : d.clientWidth,
7658                     height: d.clientHeight
7659                 };
7660             }
7661         },
7662
7663         /**
7664          * Returns the value of the "value" attribute
7665          * @param {Boolean} asNumber true to parse the value as a number
7666          * @return {String/Number}
7667          */
7668         getValue : function(asNumber){
7669             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7670         },
7671
7672         // private
7673         adjustWidth : function(width){
7674             if(typeof width == "number"){
7675                 if(this.autoBoxAdjust && !this.isBorderBox()){
7676                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7677                 }
7678                 if(width < 0){
7679                     width = 0;
7680                 }
7681             }
7682             return width;
7683         },
7684
7685         // private
7686         adjustHeight : function(height){
7687             if(typeof height == "number"){
7688                if(this.autoBoxAdjust && !this.isBorderBox()){
7689                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7690                }
7691                if(height < 0){
7692                    height = 0;
7693                }
7694             }
7695             return height;
7696         },
7697
7698         /**
7699          * Set the width of the element
7700          * @param {Number} width The new width
7701          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         setWidth : function(width, animate){
7705             width = this.adjustWidth(width);
7706             if(!animate || !A){
7707                 this.dom.style.width = this.addUnits(width);
7708             }else{
7709                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * Set the height of the element
7716          * @param {Number} height The new height
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720          setHeight : function(height, animate){
7721             height = this.adjustHeight(height);
7722             if(!animate || !A){
7723                 this.dom.style.height = this.addUnits(height);
7724             }else{
7725                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7726             }
7727             return this;
7728         },
7729
7730         /**
7731          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7732          * @param {Number} width The new width
7733          * @param {Number} height The new height
7734          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7735          * @return {Roo.Element} this
7736          */
7737          setSize : function(width, height, animate){
7738             if(typeof width == "object"){ // in case of object from getSize()
7739                 height = width.height; width = width.width;
7740             }
7741             width = this.adjustWidth(width); height = this.adjustHeight(height);
7742             if(!animate || !A){
7743                 this.dom.style.width = this.addUnits(width);
7744                 this.dom.style.height = this.addUnits(height);
7745             }else{
7746                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7747             }
7748             return this;
7749         },
7750
7751         /**
7752          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7753          * @param {Number} x X value for new position (coordinates are page-based)
7754          * @param {Number} y Y value for new position (coordinates are page-based)
7755          * @param {Number} width The new width
7756          * @param {Number} height The new height
7757          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setBounds : function(x, y, width, height, animate){
7761             if(!animate || !A){
7762                 this.setSize(width, height);
7763                 this.setLocation(x, y);
7764             }else{
7765                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7766                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7767                               this.preanim(arguments, 4), 'motion');
7768             }
7769             return this;
7770         },
7771
7772         /**
7773          * 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.
7774          * @param {Roo.lib.Region} region The region to fill
7775          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7776          * @return {Roo.Element} this
7777          */
7778         setRegion : function(region, animate){
7779             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7780             return this;
7781         },
7782
7783         /**
7784          * Appends an event handler
7785          *
7786          * @param {String}   eventName     The type of event to append
7787          * @param {Function} fn        The method the event invokes
7788          * @param {Object} scope       (optional) The scope (this object) of the fn
7789          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7790          */
7791         addListener : function(eventName, fn, scope, options){
7792             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7793         },
7794
7795         /**
7796          * Removes an event handler from this element
7797          * @param {String} eventName the type of event to remove
7798          * @param {Function} fn the method the event invokes
7799          * @return {Roo.Element} this
7800          */
7801         removeListener : function(eventName, fn){
7802             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7803             return this;
7804         },
7805
7806         /**
7807          * Removes all previous added listeners from this element
7808          * @return {Roo.Element} this
7809          */
7810         removeAllListeners : function(){
7811             E.purgeElement(this.dom);
7812             return this;
7813         },
7814
7815         relayEvent : function(eventName, observable){
7816             this.on(eventName, function(e){
7817                 observable.fireEvent(eventName, e);
7818             });
7819         },
7820
7821         /**
7822          * Set the opacity of the element
7823          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7824          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7825          * @return {Roo.Element} this
7826          */
7827          setOpacity : function(opacity, animate){
7828             if(!animate || !A){
7829                 var s = this.dom.style;
7830                 if(Roo.isIE){
7831                     s.zoom = 1;
7832                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7833                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7834                 }else{
7835                     s.opacity = opacity;
7836                 }
7837             }else{
7838                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7839             }
7840             return this;
7841         },
7842
7843         /**
7844          * Gets the left X coordinate
7845          * @param {Boolean} local True to get the local css position instead of page coordinate
7846          * @return {Number}
7847          */
7848         getLeft : function(local){
7849             if(!local){
7850                 return this.getX();
7851             }else{
7852                 return parseInt(this.getStyle("left"), 10) || 0;
7853             }
7854         },
7855
7856         /**
7857          * Gets the right X coordinate of the element (element X position + element width)
7858          * @param {Boolean} local True to get the local css position instead of page coordinate
7859          * @return {Number}
7860          */
7861         getRight : function(local){
7862             if(!local){
7863                 return this.getX() + this.getWidth();
7864             }else{
7865                 return (this.getLeft(true) + this.getWidth()) || 0;
7866             }
7867         },
7868
7869         /**
7870          * Gets the top Y coordinate
7871          * @param {Boolean} local True to get the local css position instead of page coordinate
7872          * @return {Number}
7873          */
7874         getTop : function(local) {
7875             if(!local){
7876                 return this.getY();
7877             }else{
7878                 return parseInt(this.getStyle("top"), 10) || 0;
7879             }
7880         },
7881
7882         /**
7883          * Gets the bottom Y coordinate of the element (element Y position + element height)
7884          * @param {Boolean} local True to get the local css position instead of page coordinate
7885          * @return {Number}
7886          */
7887         getBottom : function(local){
7888             if(!local){
7889                 return this.getY() + this.getHeight();
7890             }else{
7891                 return (this.getTop(true) + this.getHeight()) || 0;
7892             }
7893         },
7894
7895         /**
7896         * Initializes positioning on this element. If a desired position is not passed, it will make the
7897         * the element positioned relative IF it is not already positioned.
7898         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7899         * @param {Number} zIndex (optional) The zIndex to apply
7900         * @param {Number} x (optional) Set the page X position
7901         * @param {Number} y (optional) Set the page Y position
7902         */
7903         position : function(pos, zIndex, x, y){
7904             if(!pos){
7905                if(this.getStyle('position') == 'static'){
7906                    this.setStyle('position', 'relative');
7907                }
7908             }else{
7909                 this.setStyle("position", pos);
7910             }
7911             if(zIndex){
7912                 this.setStyle("z-index", zIndex);
7913             }
7914             if(x !== undefined && y !== undefined){
7915                 this.setXY([x, y]);
7916             }else if(x !== undefined){
7917                 this.setX(x);
7918             }else if(y !== undefined){
7919                 this.setY(y);
7920             }
7921         },
7922
7923         /**
7924         * Clear positioning back to the default when the document was loaded
7925         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7926         * @return {Roo.Element} this
7927          */
7928         clearPositioning : function(value){
7929             value = value ||'';
7930             this.setStyle({
7931                 "left": value,
7932                 "right": value,
7933                 "top": value,
7934                 "bottom": value,
7935                 "z-index": "",
7936                 "position" : "static"
7937             });
7938             return this;
7939         },
7940
7941         /**
7942         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7943         * snapshot before performing an update and then restoring the element.
7944         * @return {Object}
7945         */
7946         getPositioning : function(){
7947             var l = this.getStyle("left");
7948             var t = this.getStyle("top");
7949             return {
7950                 "position" : this.getStyle("position"),
7951                 "left" : l,
7952                 "right" : l ? "" : this.getStyle("right"),
7953                 "top" : t,
7954                 "bottom" : t ? "" : this.getStyle("bottom"),
7955                 "z-index" : this.getStyle("z-index")
7956             };
7957         },
7958
7959         /**
7960          * Gets the width of the border(s) for the specified side(s)
7961          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7962          * passing lr would get the border (l)eft width + the border (r)ight width.
7963          * @return {Number} The width of the sides passed added together
7964          */
7965         getBorderWidth : function(side){
7966             return this.addStyles(side, El.borders);
7967         },
7968
7969         /**
7970          * Gets the width of the padding(s) for the specified side(s)
7971          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7972          * passing lr would get the padding (l)eft + the padding (r)ight.
7973          * @return {Number} The padding of the sides passed added together
7974          */
7975         getPadding : function(side){
7976             return this.addStyles(side, El.paddings);
7977         },
7978
7979         /**
7980         * Set positioning with an object returned by getPositioning().
7981         * @param {Object} posCfg
7982         * @return {Roo.Element} this
7983          */
7984         setPositioning : function(pc){
7985             this.applyStyles(pc);
7986             if(pc.right == "auto"){
7987                 this.dom.style.right = "";
7988             }
7989             if(pc.bottom == "auto"){
7990                 this.dom.style.bottom = "";
7991             }
7992             return this;
7993         },
7994
7995         // private
7996         fixDisplay : function(){
7997             if(this.getStyle("display") == "none"){
7998                 this.setStyle("visibility", "hidden");
7999                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8000                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8001                     this.setStyle("display", "block");
8002                 }
8003             }
8004         },
8005
8006         /**
8007          * Quick set left and top adding default units
8008          * @param {String} left The left CSS property value
8009          * @param {String} top The top CSS property value
8010          * @return {Roo.Element} this
8011          */
8012          setLeftTop : function(left, top){
8013             this.dom.style.left = this.addUnits(left);
8014             this.dom.style.top = this.addUnits(top);
8015             return this;
8016         },
8017
8018         /**
8019          * Move this element relative to its current position.
8020          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8021          * @param {Number} distance How far to move the element in pixels
8022          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8023          * @return {Roo.Element} this
8024          */
8025          move : function(direction, distance, animate){
8026             var xy = this.getXY();
8027             direction = direction.toLowerCase();
8028             switch(direction){
8029                 case "l":
8030                 case "left":
8031                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8032                     break;
8033                case "r":
8034                case "right":
8035                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8036                     break;
8037                case "t":
8038                case "top":
8039                case "up":
8040                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8041                     break;
8042                case "b":
8043                case "bottom":
8044                case "down":
8045                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8046                     break;
8047             }
8048             return this;
8049         },
8050
8051         /**
8052          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8053          * @return {Roo.Element} this
8054          */
8055         clip : function(){
8056             if(!this.isClipped){
8057                this.isClipped = true;
8058                this.originalClip = {
8059                    "o": this.getStyle("overflow"),
8060                    "x": this.getStyle("overflow-x"),
8061                    "y": this.getStyle("overflow-y")
8062                };
8063                this.setStyle("overflow", "hidden");
8064                this.setStyle("overflow-x", "hidden");
8065                this.setStyle("overflow-y", "hidden");
8066             }
8067             return this;
8068         },
8069
8070         /**
8071          *  Return clipping (overflow) to original clipping before clip() was called
8072          * @return {Roo.Element} this
8073          */
8074         unclip : function(){
8075             if(this.isClipped){
8076                 this.isClipped = false;
8077                 var o = this.originalClip;
8078                 if(o.o){this.setStyle("overflow", o.o);}
8079                 if(o.x){this.setStyle("overflow-x", o.x);}
8080                 if(o.y){this.setStyle("overflow-y", o.y);}
8081             }
8082             return this;
8083         },
8084
8085
8086         /**
8087          * Gets the x,y coordinates specified by the anchor position on the element.
8088          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8089          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8090          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8091          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8092          * @return {Array} [x, y] An array containing the element's x and y coordinates
8093          */
8094         getAnchorXY : function(anchor, local, s){
8095             //Passing a different size is useful for pre-calculating anchors,
8096             //especially for anchored animations that change the el size.
8097
8098             var w, h, vp = false;
8099             if(!s){
8100                 var d = this.dom;
8101                 if(d == document.body || d == document){
8102                     vp = true;
8103                     w = D.getViewWidth(); h = D.getViewHeight();
8104                 }else{
8105                     w = this.getWidth(); h = this.getHeight();
8106                 }
8107             }else{
8108                 w = s.width;  h = s.height;
8109             }
8110             var x = 0, y = 0, r = Math.round;
8111             switch((anchor || "tl").toLowerCase()){
8112                 case "c":
8113                     x = r(w*.5);
8114                     y = r(h*.5);
8115                 break;
8116                 case "t":
8117                     x = r(w*.5);
8118                     y = 0;
8119                 break;
8120                 case "l":
8121                     x = 0;
8122                     y = r(h*.5);
8123                 break;
8124                 case "r":
8125                     x = w;
8126                     y = r(h*.5);
8127                 break;
8128                 case "b":
8129                     x = r(w*.5);
8130                     y = h;
8131                 break;
8132                 case "tl":
8133                     x = 0;
8134                     y = 0;
8135                 break;
8136                 case "bl":
8137                     x = 0;
8138                     y = h;
8139                 break;
8140                 case "br":
8141                     x = w;
8142                     y = h;
8143                 break;
8144                 case "tr":
8145                     x = w;
8146                     y = 0;
8147                 break;
8148             }
8149             if(local === true){
8150                 return [x, y];
8151             }
8152             if(vp){
8153                 var sc = this.getScroll();
8154                 return [x + sc.left, y + sc.top];
8155             }
8156             //Add the element's offset xy
8157             var o = this.getXY();
8158             return [x+o[0], y+o[1]];
8159         },
8160
8161         /**
8162          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8163          * supported position values.
8164          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8165          * @param {String} position The position to align to.
8166          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8167          * @return {Array} [x, y]
8168          */
8169         getAlignToXY : function(el, p, o){
8170             el = Roo.get(el);
8171             var d = this.dom;
8172             if(!el.dom){
8173                 throw "Element.alignTo with an element that doesn't exist";
8174             }
8175             var c = false; //constrain to viewport
8176             var p1 = "", p2 = "";
8177             o = o || [0,0];
8178
8179             if(!p){
8180                 p = "tl-bl";
8181             }else if(p == "?"){
8182                 p = "tl-bl?";
8183             }else if(p.indexOf("-") == -1){
8184                 p = "tl-" + p;
8185             }
8186             p = p.toLowerCase();
8187             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8188             if(!m){
8189                throw "Element.alignTo with an invalid alignment " + p;
8190             }
8191             p1 = m[1]; p2 = m[2]; c = !!m[3];
8192
8193             //Subtract the aligned el's internal xy from the target's offset xy
8194             //plus custom offset to get the aligned el's new offset xy
8195             var a1 = this.getAnchorXY(p1, true);
8196             var a2 = el.getAnchorXY(p2, false);
8197             var x = a2[0] - a1[0] + o[0];
8198             var y = a2[1] - a1[1] + o[1];
8199             if(c){
8200                 //constrain the aligned el to viewport if necessary
8201                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8202                 // 5px of margin for ie
8203                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8204
8205                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8206                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8207                 //otherwise swap the aligned el to the opposite border of the target.
8208                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8209                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8210                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8211                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8212
8213                var doc = document;
8214                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8215                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8216
8217                if((x+w) > dw + scrollX){
8218                     x = swapX ? r.left-w : dw+scrollX-w;
8219                 }
8220                if(x < scrollX){
8221                    x = swapX ? r.right : scrollX;
8222                }
8223                if((y+h) > dh + scrollY){
8224                     y = swapY ? r.top-h : dh+scrollY-h;
8225                 }
8226                if (y < scrollY){
8227                    y = swapY ? r.bottom : scrollY;
8228                }
8229             }
8230             return [x,y];
8231         },
8232
8233         // private
8234         getConstrainToXY : function(){
8235             var os = {top:0, left:0, bottom:0, right: 0};
8236
8237             return function(el, local, offsets, proposedXY){
8238                 el = Roo.get(el);
8239                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8240
8241                 var vw, vh, vx = 0, vy = 0;
8242                 if(el.dom == document.body || el.dom == document){
8243                     vw = Roo.lib.Dom.getViewWidth();
8244                     vh = Roo.lib.Dom.getViewHeight();
8245                 }else{
8246                     vw = el.dom.clientWidth;
8247                     vh = el.dom.clientHeight;
8248                     if(!local){
8249                         var vxy = el.getXY();
8250                         vx = vxy[0];
8251                         vy = vxy[1];
8252                     }
8253                 }
8254
8255                 var s = el.getScroll();
8256
8257                 vx += offsets.left + s.left;
8258                 vy += offsets.top + s.top;
8259
8260                 vw -= offsets.right;
8261                 vh -= offsets.bottom;
8262
8263                 var vr = vx+vw;
8264                 var vb = vy+vh;
8265
8266                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8267                 var x = xy[0], y = xy[1];
8268                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8269
8270                 // only move it if it needs it
8271                 var moved = false;
8272
8273                 // first validate right/bottom
8274                 if((x + w) > vr){
8275                     x = vr - w;
8276                     moved = true;
8277                 }
8278                 if((y + h) > vb){
8279                     y = vb - h;
8280                     moved = true;
8281                 }
8282                 // then make sure top/left isn't negative
8283                 if(x < vx){
8284                     x = vx;
8285                     moved = true;
8286                 }
8287                 if(y < vy){
8288                     y = vy;
8289                     moved = true;
8290                 }
8291                 return moved ? [x, y] : false;
8292             };
8293         }(),
8294
8295         // private
8296         adjustForConstraints : function(xy, parent, offsets){
8297             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8298         },
8299
8300         /**
8301          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8302          * document it aligns it to the viewport.
8303          * The position parameter is optional, and can be specified in any one of the following formats:
8304          * <ul>
8305          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8306          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8307          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8308          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8309          *   <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
8310          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8311          * </ul>
8312          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8313          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8314          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8315          * that specified in order to enforce the viewport constraints.
8316          * Following are all of the supported anchor positions:
8317     <pre>
8318     Value  Description
8319     -----  -----------------------------
8320     tl     The top left corner (default)
8321     t      The center of the top edge
8322     tr     The top right corner
8323     l      The center of the left edge
8324     c      In the center of the element
8325     r      The center of the right edge
8326     bl     The bottom left corner
8327     b      The center of the bottom edge
8328     br     The bottom right corner
8329     </pre>
8330     Example Usage:
8331     <pre><code>
8332     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8333     el.alignTo("other-el");
8334
8335     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8336     el.alignTo("other-el", "tr?");
8337
8338     // align the bottom right corner of el with the center left edge of other-el
8339     el.alignTo("other-el", "br-l?");
8340
8341     // align the center of el with the bottom left corner of other-el and
8342     // adjust the x position by -6 pixels (and the y position by 0)
8343     el.alignTo("other-el", "c-bl", [-6, 0]);
8344     </code></pre>
8345          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8346          * @param {String} position The position to align to.
8347          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8348          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8349          * @return {Roo.Element} this
8350          */
8351         alignTo : function(element, position, offsets, animate){
8352             var xy = this.getAlignToXY(element, position, offsets);
8353             this.setXY(xy, this.preanim(arguments, 3));
8354             return this;
8355         },
8356
8357         /**
8358          * Anchors an element to another element and realigns it when the window is resized.
8359          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8360          * @param {String} position The position to align to.
8361          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8362          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8363          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8364          * is a number, it is used as the buffer delay (defaults to 50ms).
8365          * @param {Function} callback The function to call after the animation finishes
8366          * @return {Roo.Element} this
8367          */
8368         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8369             var action = function(){
8370                 this.alignTo(el, alignment, offsets, animate);
8371                 Roo.callback(callback, this);
8372             };
8373             Roo.EventManager.onWindowResize(action, this);
8374             var tm = typeof monitorScroll;
8375             if(tm != 'undefined'){
8376                 Roo.EventManager.on(window, 'scroll', action, this,
8377                     {buffer: tm == 'number' ? monitorScroll : 50});
8378             }
8379             action.call(this); // align immediately
8380             return this;
8381         },
8382         /**
8383          * Clears any opacity settings from this element. Required in some cases for IE.
8384          * @return {Roo.Element} this
8385          */
8386         clearOpacity : function(){
8387             if (window.ActiveXObject) {
8388                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8389                     this.dom.style.filter = "";
8390                 }
8391             } else {
8392                 this.dom.style.opacity = "";
8393                 this.dom.style["-moz-opacity"] = "";
8394                 this.dom.style["-khtml-opacity"] = "";
8395             }
8396             return this;
8397         },
8398
8399         /**
8400          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8401          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8402          * @return {Roo.Element} this
8403          */
8404         hide : function(animate){
8405             this.setVisible(false, this.preanim(arguments, 0));
8406             return this;
8407         },
8408
8409         /**
8410         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8411         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8412          * @return {Roo.Element} this
8413          */
8414         show : function(animate){
8415             this.setVisible(true, this.preanim(arguments, 0));
8416             return this;
8417         },
8418
8419         /**
8420          * @private Test if size has a unit, otherwise appends the default
8421          */
8422         addUnits : function(size){
8423             return Roo.Element.addUnits(size, this.defaultUnit);
8424         },
8425
8426         /**
8427          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8428          * @return {Roo.Element} this
8429          */
8430         beginMeasure : function(){
8431             var el = this.dom;
8432             if(el.offsetWidth || el.offsetHeight){
8433                 return this; // offsets work already
8434             }
8435             var changed = [];
8436             var p = this.dom, b = document.body; // start with this element
8437             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8438                 var pe = Roo.get(p);
8439                 if(pe.getStyle('display') == 'none'){
8440                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8441                     p.style.visibility = "hidden";
8442                     p.style.display = "block";
8443                 }
8444                 p = p.parentNode;
8445             }
8446             this._measureChanged = changed;
8447             return this;
8448
8449         },
8450
8451         /**
8452          * Restores displays to before beginMeasure was called
8453          * @return {Roo.Element} this
8454          */
8455         endMeasure : function(){
8456             var changed = this._measureChanged;
8457             if(changed){
8458                 for(var i = 0, len = changed.length; i < len; i++) {
8459                     var r = changed[i];
8460                     r.el.style.visibility = r.visibility;
8461                     r.el.style.display = "none";
8462                 }
8463                 this._measureChanged = null;
8464             }
8465             return this;
8466         },
8467
8468         /**
8469         * Update the innerHTML of this element, optionally searching for and processing scripts
8470         * @param {String} html The new HTML
8471         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8472         * @param {Function} callback For async script loading you can be noticed when the update completes
8473         * @return {Roo.Element} this
8474          */
8475         update : function(html, loadScripts, callback){
8476             if(typeof html == "undefined"){
8477                 html = "";
8478             }
8479             if(loadScripts !== true){
8480                 this.dom.innerHTML = html;
8481                 if(typeof callback == "function"){
8482                     callback();
8483                 }
8484                 return this;
8485             }
8486             var id = Roo.id();
8487             var dom = this.dom;
8488
8489             html += '<span id="' + id + '"></span>';
8490
8491             E.onAvailable(id, function(){
8492                 var hd = document.getElementsByTagName("head")[0];
8493                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8494                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8495                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8496
8497                 var match;
8498                 while(match = re.exec(html)){
8499                     var attrs = match[1];
8500                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8501                     if(srcMatch && srcMatch[2]){
8502                        var s = document.createElement("script");
8503                        s.src = srcMatch[2];
8504                        var typeMatch = attrs.match(typeRe);
8505                        if(typeMatch && typeMatch[2]){
8506                            s.type = typeMatch[2];
8507                        }
8508                        hd.appendChild(s);
8509                     }else if(match[2] && match[2].length > 0){
8510                         if(window.execScript) {
8511                            window.execScript(match[2]);
8512                         } else {
8513                             /**
8514                              * eval:var:id
8515                              * eval:var:dom
8516                              * eval:var:html
8517                              * 
8518                              */
8519                            window.eval(match[2]);
8520                         }
8521                     }
8522                 }
8523                 var el = document.getElementById(id);
8524                 if(el){el.parentNode.removeChild(el);}
8525                 if(typeof callback == "function"){
8526                     callback();
8527                 }
8528             });
8529             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8530             return this;
8531         },
8532
8533         /**
8534          * Direct access to the UpdateManager update() method (takes the same parameters).
8535          * @param {String/Function} url The url for this request or a function to call to get the url
8536          * @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}
8537          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8538          * @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.
8539          * @return {Roo.Element} this
8540          */
8541         load : function(){
8542             var um = this.getUpdateManager();
8543             um.update.apply(um, arguments);
8544             return this;
8545         },
8546
8547         /**
8548         * Gets this element's UpdateManager
8549         * @return {Roo.UpdateManager} The UpdateManager
8550         */
8551         getUpdateManager : function(){
8552             if(!this.updateManager){
8553                 this.updateManager = new Roo.UpdateManager(this);
8554             }
8555             return this.updateManager;
8556         },
8557
8558         /**
8559          * Disables text selection for this element (normalized across browsers)
8560          * @return {Roo.Element} this
8561          */
8562         unselectable : function(){
8563             this.dom.unselectable = "on";
8564             this.swallowEvent("selectstart", true);
8565             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8566             this.addClass("x-unselectable");
8567             return this;
8568         },
8569
8570         /**
8571         * Calculates the x, y to center this element on the screen
8572         * @return {Array} The x, y values [x, y]
8573         */
8574         getCenterXY : function(){
8575             return this.getAlignToXY(document, 'c-c');
8576         },
8577
8578         /**
8579         * Centers the Element in either the viewport, or another Element.
8580         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8581         */
8582         center : function(centerIn){
8583             this.alignTo(centerIn || document, 'c-c');
8584             return this;
8585         },
8586
8587         /**
8588          * Tests various css rules/browsers to determine if this element uses a border box
8589          * @return {Boolean}
8590          */
8591         isBorderBox : function(){
8592             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8593         },
8594
8595         /**
8596          * Return a box {x, y, width, height} that can be used to set another elements
8597          * size/location to match this element.
8598          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8599          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8600          * @return {Object} box An object in the format {x, y, width, height}
8601          */
8602         getBox : function(contentBox, local){
8603             var xy;
8604             if(!local){
8605                 xy = this.getXY();
8606             }else{
8607                 var left = parseInt(this.getStyle("left"), 10) || 0;
8608                 var top = parseInt(this.getStyle("top"), 10) || 0;
8609                 xy = [left, top];
8610             }
8611             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8612             if(!contentBox){
8613                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8614             }else{
8615                 var l = this.getBorderWidth("l")+this.getPadding("l");
8616                 var r = this.getBorderWidth("r")+this.getPadding("r");
8617                 var t = this.getBorderWidth("t")+this.getPadding("t");
8618                 var b = this.getBorderWidth("b")+this.getPadding("b");
8619                 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)};
8620             }
8621             bx.right = bx.x + bx.width;
8622             bx.bottom = bx.y + bx.height;
8623             return bx;
8624         },
8625
8626         /**
8627          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8628          for more information about the sides.
8629          * @param {String} sides
8630          * @return {Number}
8631          */
8632         getFrameWidth : function(sides, onlyContentBox){
8633             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8634         },
8635
8636         /**
8637          * 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.
8638          * @param {Object} box The box to fill {x, y, width, height}
8639          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8640          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8641          * @return {Roo.Element} this
8642          */
8643         setBox : function(box, adjust, animate){
8644             var w = box.width, h = box.height;
8645             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8646                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8647                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8648             }
8649             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8650             return this;
8651         },
8652
8653         /**
8654          * Forces the browser to repaint this element
8655          * @return {Roo.Element} this
8656          */
8657          repaint : function(){
8658             var dom = this.dom;
8659             this.addClass("x-repaint");
8660             setTimeout(function(){
8661                 Roo.get(dom).removeClass("x-repaint");
8662             }, 1);
8663             return this;
8664         },
8665
8666         /**
8667          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8668          * then it returns the calculated width of the sides (see getPadding)
8669          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8670          * @return {Object/Number}
8671          */
8672         getMargins : function(side){
8673             if(!side){
8674                 return {
8675                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8676                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8677                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8678                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8679                 };
8680             }else{
8681                 return this.addStyles(side, El.margins);
8682              }
8683         },
8684
8685         // private
8686         addStyles : function(sides, styles){
8687             var val = 0, v, w;
8688             for(var i = 0, len = sides.length; i < len; i++){
8689                 v = this.getStyle(styles[sides.charAt(i)]);
8690                 if(v){
8691                      w = parseInt(v, 10);
8692                      if(w){ val += w; }
8693                 }
8694             }
8695             return val;
8696         },
8697
8698         /**
8699          * Creates a proxy element of this element
8700          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8701          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8702          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8703          * @return {Roo.Element} The new proxy element
8704          */
8705         createProxy : function(config, renderTo, matchBox){
8706             if(renderTo){
8707                 renderTo = Roo.getDom(renderTo);
8708             }else{
8709                 renderTo = document.body;
8710             }
8711             config = typeof config == "object" ?
8712                 config : {tag : "div", cls: config};
8713             var proxy = Roo.DomHelper.append(renderTo, config, true);
8714             if(matchBox){
8715                proxy.setBox(this.getBox());
8716             }
8717             return proxy;
8718         },
8719
8720         /**
8721          * Puts a mask over this element to disable user interaction. Requires core.css.
8722          * This method can only be applied to elements which accept child nodes.
8723          * @param {String} msg (optional) A message to display in the mask
8724          * @param {String} msgCls (optional) A css class to apply to the msg element
8725          * @return {Element} The mask  element
8726          */
8727         mask : function(msg, msgCls){
8728             if(this.getStyle("position") == "static"){
8729                 this.setStyle("position", "relative");
8730             }
8731             if(!this._mask){
8732                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8733             }
8734             this.addClass("x-masked");
8735             this._mask.setDisplayed(true);
8736             if(typeof msg == 'string'){
8737                 if(!this._maskMsg){
8738                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8739                 }
8740                 var mm = this._maskMsg;
8741                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8742                 mm.dom.firstChild.innerHTML = msg;
8743                 mm.setDisplayed(true);
8744                 mm.center(this);
8745             }
8746             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8747                 this._mask.setHeight(this.getHeight());
8748             }
8749             return this._mask;
8750         },
8751
8752         /**
8753          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8754          * it is cached for reuse.
8755          */
8756         unmask : function(removeEl){
8757             if(this._mask){
8758                 if(removeEl === true){
8759                     this._mask.remove();
8760                     delete this._mask;
8761                     if(this._maskMsg){
8762                         this._maskMsg.remove();
8763                         delete this._maskMsg;
8764                     }
8765                 }else{
8766                     this._mask.setDisplayed(false);
8767                     if(this._maskMsg){
8768                         this._maskMsg.setDisplayed(false);
8769                     }
8770                 }
8771             }
8772             this.removeClass("x-masked");
8773         },
8774
8775         /**
8776          * Returns true if this element is masked
8777          * @return {Boolean}
8778          */
8779         isMasked : function(){
8780             return this._mask && this._mask.isVisible();
8781         },
8782
8783         /**
8784          * Creates an iframe shim for this element to keep selects and other windowed objects from
8785          * showing through.
8786          * @return {Roo.Element} The new shim element
8787          */
8788         createShim : function(){
8789             var el = document.createElement('iframe');
8790             el.frameBorder = 'no';
8791             el.className = 'roo-shim';
8792             if(Roo.isIE && Roo.isSecure){
8793                 el.src = Roo.SSL_SECURE_URL;
8794             }
8795             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8796             shim.autoBoxAdjust = false;
8797             return shim;
8798         },
8799
8800         /**
8801          * Removes this element from the DOM and deletes it from the cache
8802          */
8803         remove : function(){
8804             if(this.dom.parentNode){
8805                 this.dom.parentNode.removeChild(this.dom);
8806             }
8807             delete El.cache[this.dom.id];
8808         },
8809
8810         /**
8811          * Sets up event handlers to add and remove a css class when the mouse is over this element
8812          * @param {String} className
8813          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8814          * mouseout events for children elements
8815          * @return {Roo.Element} this
8816          */
8817         addClassOnOver : function(className, preventFlicker){
8818             this.on("mouseover", function(){
8819                 Roo.fly(this, '_internal').addClass(className);
8820             }, this.dom);
8821             var removeFn = function(e){
8822                 if(preventFlicker !== true || !e.within(this, true)){
8823                     Roo.fly(this, '_internal').removeClass(className);
8824                 }
8825             };
8826             this.on("mouseout", removeFn, this.dom);
8827             return this;
8828         },
8829
8830         /**
8831          * Sets up event handlers to add and remove a css class when this element has the focus
8832          * @param {String} className
8833          * @return {Roo.Element} this
8834          */
8835         addClassOnFocus : function(className){
8836             this.on("focus", function(){
8837                 Roo.fly(this, '_internal').addClass(className);
8838             }, this.dom);
8839             this.on("blur", function(){
8840                 Roo.fly(this, '_internal').removeClass(className);
8841             }, this.dom);
8842             return this;
8843         },
8844         /**
8845          * 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)
8846          * @param {String} className
8847          * @return {Roo.Element} this
8848          */
8849         addClassOnClick : function(className){
8850             var dom = this.dom;
8851             this.on("mousedown", function(){
8852                 Roo.fly(dom, '_internal').addClass(className);
8853                 var d = Roo.get(document);
8854                 var fn = function(){
8855                     Roo.fly(dom, '_internal').removeClass(className);
8856                     d.removeListener("mouseup", fn);
8857                 };
8858                 d.on("mouseup", fn);
8859             });
8860             return this;
8861         },
8862
8863         /**
8864          * Stops the specified event from bubbling and optionally prevents the default action
8865          * @param {String} eventName
8866          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8867          * @return {Roo.Element} this
8868          */
8869         swallowEvent : function(eventName, preventDefault){
8870             var fn = function(e){
8871                 e.stopPropagation();
8872                 if(preventDefault){
8873                     e.preventDefault();
8874                 }
8875             };
8876             if(eventName instanceof Array){
8877                 for(var i = 0, len = eventName.length; i < len; i++){
8878                      this.on(eventName[i], fn);
8879                 }
8880                 return this;
8881             }
8882             this.on(eventName, fn);
8883             return this;
8884         },
8885
8886         /**
8887          * @private
8888          */
8889       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8890
8891         /**
8892          * Sizes this element to its parent element's dimensions performing
8893          * neccessary box adjustments.
8894          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8895          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8896          * @return {Roo.Element} this
8897          */
8898         fitToParent : function(monitorResize, targetParent) {
8899           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8900           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8901           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8902             return;
8903           }
8904           var p = Roo.get(targetParent || this.dom.parentNode);
8905           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8906           if (monitorResize === true) {
8907             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8908             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8909           }
8910           return this;
8911         },
8912
8913         /**
8914          * Gets the next sibling, skipping text nodes
8915          * @return {HTMLElement} The next sibling or null
8916          */
8917         getNextSibling : function(){
8918             var n = this.dom.nextSibling;
8919             while(n && n.nodeType != 1){
8920                 n = n.nextSibling;
8921             }
8922             return n;
8923         },
8924
8925         /**
8926          * Gets the previous sibling, skipping text nodes
8927          * @return {HTMLElement} The previous sibling or null
8928          */
8929         getPrevSibling : function(){
8930             var n = this.dom.previousSibling;
8931             while(n && n.nodeType != 1){
8932                 n = n.previousSibling;
8933             }
8934             return n;
8935         },
8936
8937
8938         /**
8939          * Appends the passed element(s) to this element
8940          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8941          * @return {Roo.Element} this
8942          */
8943         appendChild: function(el){
8944             el = Roo.get(el);
8945             el.appendTo(this);
8946             return this;
8947         },
8948
8949         /**
8950          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8951          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8952          * automatically generated with the specified attributes.
8953          * @param {HTMLElement} insertBefore (optional) a child element of this element
8954          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8955          * @return {Roo.Element} The new child element
8956          */
8957         createChild: function(config, insertBefore, returnDom){
8958             config = config || {tag:'div'};
8959             if(insertBefore){
8960                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8961             }
8962             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8963         },
8964
8965         /**
8966          * Appends this element to the passed element
8967          * @param {String/HTMLElement/Element} el The new parent element
8968          * @return {Roo.Element} this
8969          */
8970         appendTo: function(el){
8971             el = Roo.getDom(el);
8972             el.appendChild(this.dom);
8973             return this;
8974         },
8975
8976         /**
8977          * Inserts this element before the passed element in the DOM
8978          * @param {String/HTMLElement/Element} el The element to insert before
8979          * @return {Roo.Element} this
8980          */
8981         insertBefore: function(el){
8982             el = Roo.getDom(el);
8983             el.parentNode.insertBefore(this.dom, el);
8984             return this;
8985         },
8986
8987         /**
8988          * Inserts this element after the passed element in the DOM
8989          * @param {String/HTMLElement/Element} el The element to insert after
8990          * @return {Roo.Element} this
8991          */
8992         insertAfter: function(el){
8993             el = Roo.getDom(el);
8994             el.parentNode.insertBefore(this.dom, el.nextSibling);
8995             return this;
8996         },
8997
8998         /**
8999          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9000          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9001          * @return {Roo.Element} The new child
9002          */
9003         insertFirst: function(el, returnDom){
9004             el = el || {};
9005             if(typeof el == 'object' && !el.nodeType){ // dh config
9006                 return this.createChild(el, this.dom.firstChild, returnDom);
9007             }else{
9008                 el = Roo.getDom(el);
9009                 this.dom.insertBefore(el, this.dom.firstChild);
9010                 return !returnDom ? Roo.get(el) : el;
9011             }
9012         },
9013
9014         /**
9015          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9016          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9017          * @param {String} where (optional) 'before' or 'after' defaults to before
9018          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9019          * @return {Roo.Element} the inserted Element
9020          */
9021         insertSibling: function(el, where, returnDom){
9022             where = where ? where.toLowerCase() : 'before';
9023             el = el || {};
9024             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9025
9026             if(typeof el == 'object' && !el.nodeType){ // dh config
9027                 if(where == 'after' && !this.dom.nextSibling){
9028                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9029                 }else{
9030                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9031                 }
9032
9033             }else{
9034                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9035                             where == 'before' ? this.dom : this.dom.nextSibling);
9036                 if(!returnDom){
9037                     rt = Roo.get(rt);
9038                 }
9039             }
9040             return rt;
9041         },
9042
9043         /**
9044          * Creates and wraps this element with another element
9045          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9046          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9047          * @return {HTMLElement/Element} The newly created wrapper element
9048          */
9049         wrap: function(config, returnDom){
9050             if(!config){
9051                 config = {tag: "div"};
9052             }
9053             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9054             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9055             return newEl;
9056         },
9057
9058         /**
9059          * Replaces the passed element with this element
9060          * @param {String/HTMLElement/Element} el The element to replace
9061          * @return {Roo.Element} this
9062          */
9063         replace: function(el){
9064             el = Roo.get(el);
9065             this.insertBefore(el);
9066             el.remove();
9067             return this;
9068         },
9069
9070         /**
9071          * Inserts an html fragment into this element
9072          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9073          * @param {String} html The HTML fragment
9074          * @param {Boolean} returnEl True to return an Roo.Element
9075          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9076          */
9077         insertHtml : function(where, html, returnEl){
9078             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9079             return returnEl ? Roo.get(el) : el;
9080         },
9081
9082         /**
9083          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9084          * @param {Object} o The object with the attributes
9085          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9086          * @return {Roo.Element} this
9087          */
9088         set : function(o, useSet){
9089             var el = this.dom;
9090             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9091             for(var attr in o){
9092                 if(attr == "style" || typeof o[attr] == "function") continue;
9093                 if(attr=="cls"){
9094                     el.className = o["cls"];
9095                 }else{
9096                     if(useSet) el.setAttribute(attr, o[attr]);
9097                     else el[attr] = o[attr];
9098                 }
9099             }
9100             if(o.style){
9101                 Roo.DomHelper.applyStyles(el, o.style);
9102             }
9103             return this;
9104         },
9105
9106         /**
9107          * Convenience method for constructing a KeyMap
9108          * @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:
9109          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9110          * @param {Function} fn The function to call
9111          * @param {Object} scope (optional) The scope of the function
9112          * @return {Roo.KeyMap} The KeyMap created
9113          */
9114         addKeyListener : function(key, fn, scope){
9115             var config;
9116             if(typeof key != "object" || key instanceof Array){
9117                 config = {
9118                     key: key,
9119                     fn: fn,
9120                     scope: scope
9121                 };
9122             }else{
9123                 config = {
9124                     key : key.key,
9125                     shift : key.shift,
9126                     ctrl : key.ctrl,
9127                     alt : key.alt,
9128                     fn: fn,
9129                     scope: scope
9130                 };
9131             }
9132             return new Roo.KeyMap(this, config);
9133         },
9134
9135         /**
9136          * Creates a KeyMap for this element
9137          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9138          * @return {Roo.KeyMap} The KeyMap created
9139          */
9140         addKeyMap : function(config){
9141             return new Roo.KeyMap(this, config);
9142         },
9143
9144         /**
9145          * Returns true if this element is scrollable.
9146          * @return {Boolean}
9147          */
9148          isScrollable : function(){
9149             var dom = this.dom;
9150             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9151         },
9152
9153         /**
9154          * 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().
9155          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9156          * @param {Number} value The new scroll value
9157          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9158          * @return {Element} this
9159          */
9160
9161         scrollTo : function(side, value, animate){
9162             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9163             if(!animate || !A){
9164                 this.dom[prop] = value;
9165             }else{
9166                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9167                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9168             }
9169             return this;
9170         },
9171
9172         /**
9173          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9174          * within this element's scrollable range.
9175          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9176          * @param {Number} distance How far to scroll the element in pixels
9177          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9178          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9179          * was scrolled as far as it could go.
9180          */
9181          scroll : function(direction, distance, animate){
9182              if(!this.isScrollable()){
9183                  return;
9184              }
9185              var el = this.dom;
9186              var l = el.scrollLeft, t = el.scrollTop;
9187              var w = el.scrollWidth, h = el.scrollHeight;
9188              var cw = el.clientWidth, ch = el.clientHeight;
9189              direction = direction.toLowerCase();
9190              var scrolled = false;
9191              var a = this.preanim(arguments, 2);
9192              switch(direction){
9193                  case "l":
9194                  case "left":
9195                      if(w - l > cw){
9196                          var v = Math.min(l + distance, w-cw);
9197                          this.scrollTo("left", v, a);
9198                          scrolled = true;
9199                      }
9200                      break;
9201                 case "r":
9202                 case "right":
9203                      if(l > 0){
9204                          var v = Math.max(l - distance, 0);
9205                          this.scrollTo("left", v, a);
9206                          scrolled = true;
9207                      }
9208                      break;
9209                 case "t":
9210                 case "top":
9211                 case "up":
9212                      if(t > 0){
9213                          var v = Math.max(t - distance, 0);
9214                          this.scrollTo("top", v, a);
9215                          scrolled = true;
9216                      }
9217                      break;
9218                 case "b":
9219                 case "bottom":
9220                 case "down":
9221                      if(h - t > ch){
9222                          var v = Math.min(t + distance, h-ch);
9223                          this.scrollTo("top", v, a);
9224                          scrolled = true;
9225                      }
9226                      break;
9227              }
9228              return scrolled;
9229         },
9230
9231         /**
9232          * Translates the passed page coordinates into left/top css values for this element
9233          * @param {Number/Array} x The page x or an array containing [x, y]
9234          * @param {Number} y The page y
9235          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9236          */
9237         translatePoints : function(x, y){
9238             if(typeof x == 'object' || x instanceof Array){
9239                 y = x[1]; x = x[0];
9240             }
9241             var p = this.getStyle('position');
9242             var o = this.getXY();
9243
9244             var l = parseInt(this.getStyle('left'), 10);
9245             var t = parseInt(this.getStyle('top'), 10);
9246
9247             if(isNaN(l)){
9248                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9249             }
9250             if(isNaN(t)){
9251                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9252             }
9253
9254             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9255         },
9256
9257         /**
9258          * Returns the current scroll position of the element.
9259          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9260          */
9261         getScroll : function(){
9262             var d = this.dom, doc = document;
9263             if(d == doc || d == doc.body){
9264                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9265                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9266                 return {left: l, top: t};
9267             }else{
9268                 return {left: d.scrollLeft, top: d.scrollTop};
9269             }
9270         },
9271
9272         /**
9273          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9274          * are convert to standard 6 digit hex color.
9275          * @param {String} attr The css attribute
9276          * @param {String} defaultValue The default value to use when a valid color isn't found
9277          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9278          * YUI color anims.
9279          */
9280         getColor : function(attr, defaultValue, prefix){
9281             var v = this.getStyle(attr);
9282             if(!v || v == "transparent" || v == "inherit") {
9283                 return defaultValue;
9284             }
9285             var color = typeof prefix == "undefined" ? "#" : prefix;
9286             if(v.substr(0, 4) == "rgb("){
9287                 var rvs = v.slice(4, v.length -1).split(",");
9288                 for(var i = 0; i < 3; i++){
9289                     var h = parseInt(rvs[i]).toString(16);
9290                     if(h < 16){
9291                         h = "0" + h;
9292                     }
9293                     color += h;
9294                 }
9295             } else {
9296                 if(v.substr(0, 1) == "#"){
9297                     if(v.length == 4) {
9298                         for(var i = 1; i < 4; i++){
9299                             var c = v.charAt(i);
9300                             color +=  c + c;
9301                         }
9302                     }else if(v.length == 7){
9303                         color += v.substr(1);
9304                     }
9305                 }
9306             }
9307             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9308         },
9309
9310         /**
9311          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9312          * gradient background, rounded corners and a 4-way shadow.
9313          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9314          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9315          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9316          * @return {Roo.Element} this
9317          */
9318         boxWrap : function(cls){
9319             cls = cls || 'x-box';
9320             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9321             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9322             return el;
9323         },
9324
9325         /**
9326          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9327          * @param {String} namespace The namespace in which to look for the attribute
9328          * @param {String} name The attribute name
9329          * @return {String} The attribute value
9330          */
9331         getAttributeNS : Roo.isIE ? function(ns, name){
9332             var d = this.dom;
9333             var type = typeof d[ns+":"+name];
9334             if(type != 'undefined' && type != 'unknown'){
9335                 return d[ns+":"+name];
9336             }
9337             return d[name];
9338         } : function(ns, name){
9339             var d = this.dom;
9340             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9341         }
9342     };
9343
9344     var ep = El.prototype;
9345
9346     /**
9347      * Appends an event handler (Shorthand for addListener)
9348      * @param {String}   eventName     The type of event to append
9349      * @param {Function} fn        The method the event invokes
9350      * @param {Object} scope       (optional) The scope (this object) of the fn
9351      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9352      * @method
9353      */
9354     ep.on = ep.addListener;
9355         // backwards compat
9356     ep.mon = ep.addListener;
9357
9358     /**
9359      * Removes an event handler from this element (shorthand for removeListener)
9360      * @param {String} eventName the type of event to remove
9361      * @param {Function} fn the method the event invokes
9362      * @return {Roo.Element} this
9363      * @method
9364      */
9365     ep.un = ep.removeListener;
9366
9367     /**
9368      * true to automatically adjust width and height settings for box-model issues (default to true)
9369      */
9370     ep.autoBoxAdjust = true;
9371
9372     // private
9373     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9374
9375     // private
9376     El.addUnits = function(v, defaultUnit){
9377         if(v === "" || v == "auto"){
9378             return v;
9379         }
9380         if(v === undefined){
9381             return '';
9382         }
9383         if(typeof v == "number" || !El.unitPattern.test(v)){
9384             return v + (defaultUnit || 'px');
9385         }
9386         return v;
9387     };
9388
9389     // special markup used throughout Roo when box wrapping elements
9390     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>';
9391     /**
9392      * Visibility mode constant - Use visibility to hide element
9393      * @static
9394      * @type Number
9395      */
9396     El.VISIBILITY = 1;
9397     /**
9398      * Visibility mode constant - Use display to hide element
9399      * @static
9400      * @type Number
9401      */
9402     El.DISPLAY = 2;
9403
9404     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9405     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9406     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9407
9408
9409
9410     /**
9411      * @private
9412      */
9413     El.cache = {};
9414
9415     var docEl;
9416
9417     /**
9418      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9419      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9420      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9421      * @return {Element} The Element object
9422      * @static
9423      */
9424     El.get = function(el){
9425         var ex, elm, id;
9426         if(!el){ return null; }
9427         if(typeof el == "string"){ // element id
9428             if(!(elm = document.getElementById(el))){
9429                 return null;
9430             }
9431             if(ex = El.cache[el]){
9432                 ex.dom = elm;
9433             }else{
9434                 ex = El.cache[el] = new El(elm);
9435             }
9436             return ex;
9437         }else if(el.tagName){ // dom element
9438             if(!(id = el.id)){
9439                 id = Roo.id(el);
9440             }
9441             if(ex = El.cache[id]){
9442                 ex.dom = el;
9443             }else{
9444                 ex = El.cache[id] = new El(el);
9445             }
9446             return ex;
9447         }else if(el instanceof El){
9448             if(el != docEl){
9449                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9450                                                               // catch case where it hasn't been appended
9451                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9452             }
9453             return el;
9454         }else if(el.isComposite){
9455             return el;
9456         }else if(el instanceof Array){
9457             return El.select(el);
9458         }else if(el == document){
9459             // create a bogus element object representing the document object
9460             if(!docEl){
9461                 var f = function(){};
9462                 f.prototype = El.prototype;
9463                 docEl = new f();
9464                 docEl.dom = document;
9465             }
9466             return docEl;
9467         }
9468         return null;
9469     };
9470
9471     // private
9472     El.uncache = function(el){
9473         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9474             if(a[i]){
9475                 delete El.cache[a[i].id || a[i]];
9476             }
9477         }
9478     };
9479
9480     // private
9481     // Garbage collection - uncache elements/purge listeners on orphaned elements
9482     // so we don't hold a reference and cause the browser to retain them
9483     El.garbageCollect = function(){
9484         if(!Roo.enableGarbageCollector){
9485             clearInterval(El.collectorThread);
9486             return;
9487         }
9488         for(var eid in El.cache){
9489             var el = El.cache[eid], d = el.dom;
9490             // -------------------------------------------------------
9491             // Determining what is garbage:
9492             // -------------------------------------------------------
9493             // !d
9494             // dom node is null, definitely garbage
9495             // -------------------------------------------------------
9496             // !d.parentNode
9497             // no parentNode == direct orphan, definitely garbage
9498             // -------------------------------------------------------
9499             // !d.offsetParent && !document.getElementById(eid)
9500             // display none elements have no offsetParent so we will
9501             // also try to look it up by it's id. However, check
9502             // offsetParent first so we don't do unneeded lookups.
9503             // This enables collection of elements that are not orphans
9504             // directly, but somewhere up the line they have an orphan
9505             // parent.
9506             // -------------------------------------------------------
9507             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9508                 delete El.cache[eid];
9509                 if(d && Roo.enableListenerCollection){
9510                     E.purgeElement(d);
9511                 }
9512             }
9513         }
9514     }
9515     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9516
9517
9518     // dom is optional
9519     El.Flyweight = function(dom){
9520         this.dom = dom;
9521     };
9522     El.Flyweight.prototype = El.prototype;
9523
9524     El._flyweights = {};
9525     /**
9526      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9527      * the dom node can be overwritten by other code.
9528      * @param {String/HTMLElement} el The dom node or id
9529      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9530      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9531      * @static
9532      * @return {Element} The shared Element object
9533      */
9534     El.fly = function(el, named){
9535         named = named || '_global';
9536         el = Roo.getDom(el);
9537         if(!el){
9538             return null;
9539         }
9540         if(!El._flyweights[named]){
9541             El._flyweights[named] = new El.Flyweight();
9542         }
9543         El._flyweights[named].dom = el;
9544         return El._flyweights[named];
9545     };
9546
9547     /**
9548      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9549      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9550      * Shorthand of {@link Roo.Element#get}
9551      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9552      * @return {Element} The Element object
9553      * @member Roo
9554      * @method get
9555      */
9556     Roo.get = El.get;
9557     /**
9558      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9559      * the dom node can be overwritten by other code.
9560      * Shorthand of {@link Roo.Element#fly}
9561      * @param {String/HTMLElement} el The dom node or id
9562      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9563      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9564      * @static
9565      * @return {Element} The shared Element object
9566      * @member Roo
9567      * @method fly
9568      */
9569     Roo.fly = El.fly;
9570
9571     // speedy lookup for elements never to box adjust
9572     var noBoxAdjust = Roo.isStrict ? {
9573         select:1
9574     } : {
9575         input:1, select:1, textarea:1
9576     };
9577     if(Roo.isIE || Roo.isGecko){
9578         noBoxAdjust['button'] = 1;
9579     }
9580
9581
9582     Roo.EventManager.on(window, 'unload', function(){
9583         delete El.cache;
9584         delete El._flyweights;
9585     });
9586 })();
9587
9588
9589
9590
9591 if(Roo.DomQuery){
9592     Roo.Element.selectorFunction = Roo.DomQuery.select;
9593 }
9594
9595 Roo.Element.select = function(selector, unique, root){
9596     var els;
9597     if(typeof selector == "string"){
9598         els = Roo.Element.selectorFunction(selector, root);
9599     }else if(selector.length !== undefined){
9600         els = selector;
9601     }else{
9602         throw "Invalid selector";
9603     }
9604     if(unique === true){
9605         return new Roo.CompositeElement(els);
9606     }else{
9607         return new Roo.CompositeElementLite(els);
9608     }
9609 };
9610 /**
9611  * Selects elements based on the passed CSS selector to enable working on them as 1.
9612  * @param {String/Array} selector The CSS selector or an array of elements
9613  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9614  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9615  * @return {CompositeElementLite/CompositeElement}
9616  * @member Roo
9617  * @method select
9618  */
9619 Roo.select = Roo.Element.select;
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633
9634 /*
9635  * Based on:
9636  * Ext JS Library 1.1.1
9637  * Copyright(c) 2006-2007, Ext JS, LLC.
9638  *
9639  * Originally Released Under LGPL - original licence link has changed is not relivant.
9640  *
9641  * Fork - LGPL
9642  * <script type="text/javascript">
9643  */
9644
9645
9646
9647 //Notifies Element that fx methods are available
9648 Roo.enableFx = true;
9649
9650 /**
9651  * @class Roo.Fx
9652  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9653  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9654  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9655  * Element effects to work.</p><br/>
9656  *
9657  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9658  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9659  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9660  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9661  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9662  * expected results and should be done with care.</p><br/>
9663  *
9664  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9665  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9666 <pre>
9667 Value  Description
9668 -----  -----------------------------
9669 tl     The top left corner
9670 t      The center of the top edge
9671 tr     The top right corner
9672 l      The center of the left edge
9673 r      The center of the right edge
9674 bl     The bottom left corner
9675 b      The center of the bottom edge
9676 br     The bottom right corner
9677 </pre>
9678  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9679  * below are common options that can be passed to any Fx method.</b>
9680  * @cfg {Function} callback A function called when the effect is finished
9681  * @cfg {Object} scope The scope of the effect function
9682  * @cfg {String} easing A valid Easing value for the effect
9683  * @cfg {String} afterCls A css class to apply after the effect
9684  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9685  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9686  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9687  * effects that end with the element being visually hidden, ignored otherwise)
9688  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9689  * a function which returns such a specification that will be applied to the Element after the effect finishes
9690  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9691  * @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
9692  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9693  */
9694 Roo.Fx = {
9695         /**
9696          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9697          * origin for the slide effect.  This function automatically handles wrapping the element with
9698          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9699          * Usage:
9700          *<pre><code>
9701 // default: slide the element in from the top
9702 el.slideIn();
9703
9704 // custom: slide the element in from the right with a 2-second duration
9705 el.slideIn('r', { duration: 2 });
9706
9707 // common config options shown with default values
9708 el.slideIn('t', {
9709     easing: 'easeOut',
9710     duration: .5
9711 });
9712 </code></pre>
9713          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9714          * @param {Object} options (optional) Object literal with any of the Fx config options
9715          * @return {Roo.Element} The Element
9716          */
9717     slideIn : function(anchor, o){
9718         var el = this.getFxEl();
9719         o = o || {};
9720
9721         el.queueFx(o, function(){
9722
9723             anchor = anchor || "t";
9724
9725             // fix display to visibility
9726             this.fixDisplay();
9727
9728             // restore values after effect
9729             var r = this.getFxRestore();
9730             var b = this.getBox();
9731             // fixed size for slide
9732             this.setSize(b);
9733
9734             // wrap if needed
9735             var wrap = this.fxWrap(r.pos, o, "hidden");
9736
9737             var st = this.dom.style;
9738             st.visibility = "visible";
9739             st.position = "absolute";
9740
9741             // clear out temp styles after slide and unwrap
9742             var after = function(){
9743                 el.fxUnwrap(wrap, r.pos, o);
9744                 st.width = r.width;
9745                 st.height = r.height;
9746                 el.afterFx(o);
9747             };
9748             // time to calc the positions
9749             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9750
9751             switch(anchor.toLowerCase()){
9752                 case "t":
9753                     wrap.setSize(b.width, 0);
9754                     st.left = st.bottom = "0";
9755                     a = {height: bh};
9756                 break;
9757                 case "l":
9758                     wrap.setSize(0, b.height);
9759                     st.right = st.top = "0";
9760                     a = {width: bw};
9761                 break;
9762                 case "r":
9763                     wrap.setSize(0, b.height);
9764                     wrap.setX(b.right);
9765                     st.left = st.top = "0";
9766                     a = {width: bw, points: pt};
9767                 break;
9768                 case "b":
9769                     wrap.setSize(b.width, 0);
9770                     wrap.setY(b.bottom);
9771                     st.left = st.top = "0";
9772                     a = {height: bh, points: pt};
9773                 break;
9774                 case "tl":
9775                     wrap.setSize(0, 0);
9776                     st.right = st.bottom = "0";
9777                     a = {width: bw, height: bh};
9778                 break;
9779                 case "bl":
9780                     wrap.setSize(0, 0);
9781                     wrap.setY(b.y+b.height);
9782                     st.right = st.top = "0";
9783                     a = {width: bw, height: bh, points: pt};
9784                 break;
9785                 case "br":
9786                     wrap.setSize(0, 0);
9787                     wrap.setXY([b.right, b.bottom]);
9788                     st.left = st.top = "0";
9789                     a = {width: bw, height: bh, points: pt};
9790                 break;
9791                 case "tr":
9792                     wrap.setSize(0, 0);
9793                     wrap.setX(b.x+b.width);
9794                     st.left = st.bottom = "0";
9795                     a = {width: bw, height: bh, points: pt};
9796                 break;
9797             }
9798             this.dom.style.visibility = "visible";
9799             wrap.show();
9800
9801             arguments.callee.anim = wrap.fxanim(a,
9802                 o,
9803                 'motion',
9804                 .5,
9805                 'easeOut', after);
9806         });
9807         return this;
9808     },
9809     
9810         /**
9811          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9812          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9813          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9814          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9815          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9816          * Usage:
9817          *<pre><code>
9818 // default: slide the element out to the top
9819 el.slideOut();
9820
9821 // custom: slide the element out to the right with a 2-second duration
9822 el.slideOut('r', { duration: 2 });
9823
9824 // common config options shown with default values
9825 el.slideOut('t', {
9826     easing: 'easeOut',
9827     duration: .5,
9828     remove: false,
9829     useDisplay: false
9830 });
9831 </code></pre>
9832          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9833          * @param {Object} options (optional) Object literal with any of the Fx config options
9834          * @return {Roo.Element} The Element
9835          */
9836     slideOut : function(anchor, o){
9837         var el = this.getFxEl();
9838         o = o || {};
9839
9840         el.queueFx(o, function(){
9841
9842             anchor = anchor || "t";
9843
9844             // restore values after effect
9845             var r = this.getFxRestore();
9846             
9847             var b = this.getBox();
9848             // fixed size for slide
9849             this.setSize(b);
9850
9851             // wrap if needed
9852             var wrap = this.fxWrap(r.pos, o, "visible");
9853
9854             var st = this.dom.style;
9855             st.visibility = "visible";
9856             st.position = "absolute";
9857
9858             wrap.setSize(b);
9859
9860             var after = function(){
9861                 if(o.useDisplay){
9862                     el.setDisplayed(false);
9863                 }else{
9864                     el.hide();
9865                 }
9866
9867                 el.fxUnwrap(wrap, r.pos, o);
9868
9869                 st.width = r.width;
9870                 st.height = r.height;
9871
9872                 el.afterFx(o);
9873             };
9874
9875             var a, zero = {to: 0};
9876             switch(anchor.toLowerCase()){
9877                 case "t":
9878                     st.left = st.bottom = "0";
9879                     a = {height: zero};
9880                 break;
9881                 case "l":
9882                     st.right = st.top = "0";
9883                     a = {width: zero};
9884                 break;
9885                 case "r":
9886                     st.left = st.top = "0";
9887                     a = {width: zero, points: {to:[b.right, b.y]}};
9888                 break;
9889                 case "b":
9890                     st.left = st.top = "0";
9891                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9892                 break;
9893                 case "tl":
9894                     st.right = st.bottom = "0";
9895                     a = {width: zero, height: zero};
9896                 break;
9897                 case "bl":
9898                     st.right = st.top = "0";
9899                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9900                 break;
9901                 case "br":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9904                 break;
9905                 case "tr":
9906                     st.left = st.bottom = "0";
9907                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9908                 break;
9909             }
9910
9911             arguments.callee.anim = wrap.fxanim(a,
9912                 o,
9913                 'motion',
9914                 .5,
9915                 "easeOut", after);
9916         });
9917         return this;
9918     },
9919
9920         /**
9921          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9922          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9923          * The element must be removed from the DOM using the 'remove' config option if desired.
9924          * Usage:
9925          *<pre><code>
9926 // default
9927 el.puff();
9928
9929 // common config options shown with default values
9930 el.puff({
9931     easing: 'easeOut',
9932     duration: .5,
9933     remove: false,
9934     useDisplay: false
9935 });
9936 </code></pre>
9937          * @param {Object} options (optional) Object literal with any of the Fx config options
9938          * @return {Roo.Element} The Element
9939          */
9940     puff : function(o){
9941         var el = this.getFxEl();
9942         o = o || {};
9943
9944         el.queueFx(o, function(){
9945             this.clearOpacity();
9946             this.show();
9947
9948             // restore values after effect
9949             var r = this.getFxRestore();
9950             var st = this.dom.style;
9951
9952             var after = function(){
9953                 if(o.useDisplay){
9954                     el.setDisplayed(false);
9955                 }else{
9956                     el.hide();
9957                 }
9958
9959                 el.clearOpacity();
9960
9961                 el.setPositioning(r.pos);
9962                 st.width = r.width;
9963                 st.height = r.height;
9964                 st.fontSize = '';
9965                 el.afterFx(o);
9966             };
9967
9968             var width = this.getWidth();
9969             var height = this.getHeight();
9970
9971             arguments.callee.anim = this.fxanim({
9972                     width : {to: this.adjustWidth(width * 2)},
9973                     height : {to: this.adjustHeight(height * 2)},
9974                     points : {by: [-(width * .5), -(height * .5)]},
9975                     opacity : {to: 0},
9976                     fontSize: {to:200, unit: "%"}
9977                 },
9978                 o,
9979                 'motion',
9980                 .5,
9981                 "easeOut", after);
9982         });
9983         return this;
9984     },
9985
9986         /**
9987          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9988          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9989          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9990          * Usage:
9991          *<pre><code>
9992 // default
9993 el.switchOff();
9994
9995 // all config options shown with default values
9996 el.switchOff({
9997     easing: 'easeIn',
9998     duration: .3,
9999     remove: false,
10000     useDisplay: false
10001 });
10002 </code></pre>
10003          * @param {Object} options (optional) Object literal with any of the Fx config options
10004          * @return {Roo.Element} The Element
10005          */
10006     switchOff : function(o){
10007         var el = this.getFxEl();
10008         o = o || {};
10009
10010         el.queueFx(o, function(){
10011             this.clearOpacity();
10012             this.clip();
10013
10014             // restore values after effect
10015             var r = this.getFxRestore();
10016             var st = this.dom.style;
10017
10018             var after = function(){
10019                 if(o.useDisplay){
10020                     el.setDisplayed(false);
10021                 }else{
10022                     el.hide();
10023                 }
10024
10025                 el.clearOpacity();
10026                 el.setPositioning(r.pos);
10027                 st.width = r.width;
10028                 st.height = r.height;
10029
10030                 el.afterFx(o);
10031             };
10032
10033             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10034                 this.clearOpacity();
10035                 (function(){
10036                     this.fxanim({
10037                         height:{to:1},
10038                         points:{by:[0, this.getHeight() * .5]}
10039                     }, o, 'motion', 0.3, 'easeIn', after);
10040                 }).defer(100, this);
10041             });
10042         });
10043         return this;
10044     },
10045
10046     /**
10047      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10048      * changed using the "attr" config option) and then fading back to the original color. If no original
10049      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10050      * Usage:
10051 <pre><code>
10052 // default: highlight background to yellow
10053 el.highlight();
10054
10055 // custom: highlight foreground text to blue for 2 seconds
10056 el.highlight("0000ff", { attr: 'color', duration: 2 });
10057
10058 // common config options shown with default values
10059 el.highlight("ffff9c", {
10060     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10061     endColor: (current color) or "ffffff",
10062     easing: 'easeIn',
10063     duration: 1
10064 });
10065 </code></pre>
10066      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10067      * @param {Object} options (optional) Object literal with any of the Fx config options
10068      * @return {Roo.Element} The Element
10069      */ 
10070     highlight : function(color, o){
10071         var el = this.getFxEl();
10072         o = o || {};
10073
10074         el.queueFx(o, function(){
10075             color = color || "ffff9c";
10076             attr = o.attr || "backgroundColor";
10077
10078             this.clearOpacity();
10079             this.show();
10080
10081             var origColor = this.getColor(attr);
10082             var restoreColor = this.dom.style[attr];
10083             endColor = (o.endColor || origColor) || "ffffff";
10084
10085             var after = function(){
10086                 el.dom.style[attr] = restoreColor;
10087                 el.afterFx(o);
10088             };
10089
10090             var a = {};
10091             a[attr] = {from: color, to: endColor};
10092             arguments.callee.anim = this.fxanim(a,
10093                 o,
10094                 'color',
10095                 1,
10096                 'easeIn', after);
10097         });
10098         return this;
10099     },
10100
10101    /**
10102     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10103     * Usage:
10104 <pre><code>
10105 // default: a single light blue ripple
10106 el.frame();
10107
10108 // custom: 3 red ripples lasting 3 seconds total
10109 el.frame("ff0000", 3, { duration: 3 });
10110
10111 // common config options shown with default values
10112 el.frame("C3DAF9", 1, {
10113     duration: 1 //duration of entire animation (not each individual ripple)
10114     // Note: Easing is not configurable and will be ignored if included
10115 });
10116 </code></pre>
10117     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10118     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10119     * @param {Object} options (optional) Object literal with any of the Fx config options
10120     * @return {Roo.Element} The Element
10121     */
10122     frame : function(color, count, o){
10123         var el = this.getFxEl();
10124         o = o || {};
10125
10126         el.queueFx(o, function(){
10127             color = color || "#C3DAF9";
10128             if(color.length == 6){
10129                 color = "#" + color;
10130             }
10131             count = count || 1;
10132             duration = o.duration || 1;
10133             this.show();
10134
10135             var b = this.getBox();
10136             var animFn = function(){
10137                 var proxy = this.createProxy({
10138
10139                      style:{
10140                         visbility:"hidden",
10141                         position:"absolute",
10142                         "z-index":"35000", // yee haw
10143                         border:"0px solid " + color
10144                      }
10145                   });
10146                 var scale = Roo.isBorderBox ? 2 : 1;
10147                 proxy.animate({
10148                     top:{from:b.y, to:b.y - 20},
10149                     left:{from:b.x, to:b.x - 20},
10150                     borderWidth:{from:0, to:10},
10151                     opacity:{from:1, to:0},
10152                     height:{from:b.height, to:(b.height + (20*scale))},
10153                     width:{from:b.width, to:(b.width + (20*scale))}
10154                 }, duration, function(){
10155                     proxy.remove();
10156                 });
10157                 if(--count > 0){
10158                      animFn.defer((duration/2)*1000, this);
10159                 }else{
10160                     el.afterFx(o);
10161                 }
10162             };
10163             animFn.call(this);
10164         });
10165         return this;
10166     },
10167
10168    /**
10169     * Creates a pause before any subsequent queued effects begin.  If there are
10170     * no effects queued after the pause it will have no effect.
10171     * Usage:
10172 <pre><code>
10173 el.pause(1);
10174 </code></pre>
10175     * @param {Number} seconds The length of time to pause (in seconds)
10176     * @return {Roo.Element} The Element
10177     */
10178     pause : function(seconds){
10179         var el = this.getFxEl();
10180         var o = {};
10181
10182         el.queueFx(o, function(){
10183             setTimeout(function(){
10184                 el.afterFx(o);
10185             }, seconds * 1000);
10186         });
10187         return this;
10188     },
10189
10190    /**
10191     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10192     * using the "endOpacity" config option.
10193     * Usage:
10194 <pre><code>
10195 // default: fade in from opacity 0 to 100%
10196 el.fadeIn();
10197
10198 // custom: fade in from opacity 0 to 75% over 2 seconds
10199 el.fadeIn({ endOpacity: .75, duration: 2});
10200
10201 // common config options shown with default values
10202 el.fadeIn({
10203     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10204     easing: 'easeOut',
10205     duration: .5
10206 });
10207 </code></pre>
10208     * @param {Object} options (optional) Object literal with any of the Fx config options
10209     * @return {Roo.Element} The Element
10210     */
10211     fadeIn : function(o){
10212         var el = this.getFxEl();
10213         o = o || {};
10214         el.queueFx(o, function(){
10215             this.setOpacity(0);
10216             this.fixDisplay();
10217             this.dom.style.visibility = 'visible';
10218             var to = o.endOpacity || 1;
10219             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10220                 o, null, .5, "easeOut", function(){
10221                 if(to == 1){
10222                     this.clearOpacity();
10223                 }
10224                 el.afterFx(o);
10225             });
10226         });
10227         return this;
10228     },
10229
10230    /**
10231     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10232     * using the "endOpacity" config option.
10233     * Usage:
10234 <pre><code>
10235 // default: fade out from the element's current opacity to 0
10236 el.fadeOut();
10237
10238 // custom: fade out from the element's current opacity to 25% over 2 seconds
10239 el.fadeOut({ endOpacity: .25, duration: 2});
10240
10241 // common config options shown with default values
10242 el.fadeOut({
10243     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10244     easing: 'easeOut',
10245     duration: .5
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250     * @param {Object} options (optional) Object literal with any of the Fx config options
10251     * @return {Roo.Element} The Element
10252     */
10253     fadeOut : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256         el.queueFx(o, function(){
10257             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10258                 o, null, .5, "easeOut", function(){
10259                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10260                      this.dom.style.display = "none";
10261                 }else{
10262                      this.dom.style.visibility = "hidden";
10263                 }
10264                 this.clearOpacity();
10265                 el.afterFx(o);
10266             });
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Animates the transition of an element's dimensions from a starting height/width
10273     * to an ending height/width.
10274     * Usage:
10275 <pre><code>
10276 // change height and width to 100x100 pixels
10277 el.scale(100, 100);
10278
10279 // common config options shown with default values.  The height and width will default to
10280 // the element's existing values if passed as null.
10281 el.scale(
10282     [element's width],
10283     [element's height], {
10284     easing: 'easeOut',
10285     duration: .35
10286 });
10287 </code></pre>
10288     * @param {Number} width  The new width (pass undefined to keep the original width)
10289     * @param {Number} height  The new height (pass undefined to keep the original height)
10290     * @param {Object} options (optional) Object literal with any of the Fx config options
10291     * @return {Roo.Element} The Element
10292     */
10293     scale : function(w, h, o){
10294         this.shift(Roo.apply({}, o, {
10295             width: w,
10296             height: h
10297         }));
10298         return this;
10299     },
10300
10301    /**
10302     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10303     * Any of these properties not specified in the config object will not be changed.  This effect 
10304     * requires that at least one new dimension, position or opacity setting must be passed in on
10305     * the config object in order for the function to have any effect.
10306     * Usage:
10307 <pre><code>
10308 // slide the element horizontally to x position 200 while changing the height and opacity
10309 el.shift({ x: 200, height: 50, opacity: .8 });
10310
10311 // common config options shown with default values.
10312 el.shift({
10313     width: [element's width],
10314     height: [element's height],
10315     x: [element's x position],
10316     y: [element's y position],
10317     opacity: [element's opacity],
10318     easing: 'easeOut',
10319     duration: .35
10320 });
10321 </code></pre>
10322     * @param {Object} options  Object literal with any of the Fx config options
10323     * @return {Roo.Element} The Element
10324     */
10325     shift : function(o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328         el.queueFx(o, function(){
10329             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10330             if(w !== undefined){
10331                 a.width = {to: this.adjustWidth(w)};
10332             }
10333             if(h !== undefined){
10334                 a.height = {to: this.adjustHeight(h)};
10335             }
10336             if(x !== undefined || y !== undefined){
10337                 a.points = {to: [
10338                     x !== undefined ? x : this.getX(),
10339                     y !== undefined ? y : this.getY()
10340                 ]};
10341             }
10342             if(op !== undefined){
10343                 a.opacity = {to: op};
10344             }
10345             if(o.xy !== undefined){
10346                 a.points = {to: o.xy};
10347             }
10348             arguments.callee.anim = this.fxanim(a,
10349                 o, 'motion', .35, "easeOut", function(){
10350                 el.afterFx(o);
10351             });
10352         });
10353         return this;
10354     },
10355
10356         /**
10357          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10358          * ending point of the effect.
10359          * Usage:
10360          *<pre><code>
10361 // default: slide the element downward while fading out
10362 el.ghost();
10363
10364 // custom: slide the element out to the right with a 2-second duration
10365 el.ghost('r', { duration: 2 });
10366
10367 // common config options shown with default values
10368 el.ghost('b', {
10369     easing: 'easeOut',
10370     duration: .5
10371     remove: false,
10372     useDisplay: false
10373 });
10374 </code></pre>
10375          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10376          * @param {Object} options (optional) Object literal with any of the Fx config options
10377          * @return {Roo.Element} The Element
10378          */
10379     ghost : function(anchor, o){
10380         var el = this.getFxEl();
10381         o = o || {};
10382
10383         el.queueFx(o, function(){
10384             anchor = anchor || "b";
10385
10386             // restore values after effect
10387             var r = this.getFxRestore();
10388             var w = this.getWidth(),
10389                 h = this.getHeight();
10390
10391             var st = this.dom.style;
10392
10393             var after = function(){
10394                 if(o.useDisplay){
10395                     el.setDisplayed(false);
10396                 }else{
10397                     el.hide();
10398                 }
10399
10400                 el.clearOpacity();
10401                 el.setPositioning(r.pos);
10402                 st.width = r.width;
10403                 st.height = r.height;
10404
10405                 el.afterFx(o);
10406             };
10407
10408             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10409             switch(anchor.toLowerCase()){
10410                 case "t":
10411                     pt.by = [0, -h];
10412                 break;
10413                 case "l":
10414                     pt.by = [-w, 0];
10415                 break;
10416                 case "r":
10417                     pt.by = [w, 0];
10418                 break;
10419                 case "b":
10420                     pt.by = [0, h];
10421                 break;
10422                 case "tl":
10423                     pt.by = [-w, -h];
10424                 break;
10425                 case "bl":
10426                     pt.by = [-w, h];
10427                 break;
10428                 case "br":
10429                     pt.by = [w, h];
10430                 break;
10431                 case "tr":
10432                     pt.by = [w, -h];
10433                 break;
10434             }
10435
10436             arguments.callee.anim = this.fxanim(a,
10437                 o,
10438                 'motion',
10439                 .5,
10440                 "easeOut", after);
10441         });
10442         return this;
10443     },
10444
10445         /**
10446          * Ensures that all effects queued after syncFx is called on the element are
10447          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10448          * @return {Roo.Element} The Element
10449          */
10450     syncFx : function(){
10451         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10452             block : false,
10453             concurrent : true,
10454             stopFx : false
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Ensures that all effects queued after sequenceFx is called on the element are
10461          * run in sequence.  This is the opposite of {@link #syncFx}.
10462          * @return {Roo.Element} The Element
10463          */
10464     sequenceFx : function(){
10465         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10466             block : false,
10467             concurrent : false,
10468             stopFx : false
10469         });
10470         return this;
10471     },
10472
10473         /* @private */
10474     nextFx : function(){
10475         var ef = this.fxQueue[0];
10476         if(ef){
10477             ef.call(this);
10478         }
10479     },
10480
10481         /**
10482          * Returns true if the element has any effects actively running or queued, else returns false.
10483          * @return {Boolean} True if element has active effects, else false
10484          */
10485     hasActiveFx : function(){
10486         return this.fxQueue && this.fxQueue[0];
10487     },
10488
10489         /**
10490          * Stops any running effects and clears the element's internal effects queue if it contains
10491          * any additional effects that haven't started yet.
10492          * @return {Roo.Element} The Element
10493          */
10494     stopFx : function(){
10495         if(this.hasActiveFx()){
10496             var cur = this.fxQueue[0];
10497             if(cur && cur.anim && cur.anim.isAnimated()){
10498                 this.fxQueue = [cur]; // clear out others
10499                 cur.anim.stop(true);
10500             }
10501         }
10502         return this;
10503     },
10504
10505         /* @private */
10506     beforeFx : function(o){
10507         if(this.hasActiveFx() && !o.concurrent){
10508            if(o.stopFx){
10509                this.stopFx();
10510                return true;
10511            }
10512            return false;
10513         }
10514         return true;
10515     },
10516
10517         /**
10518          * Returns true if the element is currently blocking so that no other effect can be queued
10519          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10520          * used to ensure that an effect initiated by a user action runs to completion prior to the
10521          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10522          * @return {Boolean} True if blocking, else false
10523          */
10524     hasFxBlock : function(){
10525         var q = this.fxQueue;
10526         return q && q[0] && q[0].block;
10527     },
10528
10529         /* @private */
10530     queueFx : function(o, fn){
10531         if(!this.fxQueue){
10532             this.fxQueue = [];
10533         }
10534         if(!this.hasFxBlock()){
10535             Roo.applyIf(o, this.fxDefaults);
10536             if(!o.concurrent){
10537                 var run = this.beforeFx(o);
10538                 fn.block = o.block;
10539                 this.fxQueue.push(fn);
10540                 if(run){
10541                     this.nextFx();
10542                 }
10543             }else{
10544                 fn.call(this);
10545             }
10546         }
10547         return this;
10548     },
10549
10550         /* @private */
10551     fxWrap : function(pos, o, vis){
10552         var wrap;
10553         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10554             var wrapXY;
10555             if(o.fixPosition){
10556                 wrapXY = this.getXY();
10557             }
10558             var div = document.createElement("div");
10559             div.style.visibility = vis;
10560             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10561             wrap.setPositioning(pos);
10562             if(wrap.getStyle("position") == "static"){
10563                 wrap.position("relative");
10564             }
10565             this.clearPositioning('auto');
10566             wrap.clip();
10567             wrap.dom.appendChild(this.dom);
10568             if(wrapXY){
10569                 wrap.setXY(wrapXY);
10570             }
10571         }
10572         return wrap;
10573     },
10574
10575         /* @private */
10576     fxUnwrap : function(wrap, pos, o){
10577         this.clearPositioning();
10578         this.setPositioning(pos);
10579         if(!o.wrap){
10580             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10581             wrap.remove();
10582         }
10583     },
10584
10585         /* @private */
10586     getFxRestore : function(){
10587         var st = this.dom.style;
10588         return {pos: this.getPositioning(), width: st.width, height : st.height};
10589     },
10590
10591         /* @private */
10592     afterFx : function(o){
10593         if(o.afterStyle){
10594             this.applyStyles(o.afterStyle);
10595         }
10596         if(o.afterCls){
10597             this.addClass(o.afterCls);
10598         }
10599         if(o.remove === true){
10600             this.remove();
10601         }
10602         Roo.callback(o.callback, o.scope, [this]);
10603         if(!o.concurrent){
10604             this.fxQueue.shift();
10605             this.nextFx();
10606         }
10607     },
10608
10609         /* @private */
10610     getFxEl : function(){ // support for composite element fx
10611         return Roo.get(this.dom);
10612     },
10613
10614         /* @private */
10615     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10616         animType = animType || 'run';
10617         opt = opt || {};
10618         var anim = Roo.lib.Anim[animType](
10619             this.dom, args,
10620             (opt.duration || defaultDur) || .35,
10621             (opt.easing || defaultEase) || 'easeOut',
10622             function(){
10623                 Roo.callback(cb, this);
10624             },
10625             this
10626         );
10627         opt.anim = anim;
10628         return anim;
10629     }
10630 };
10631
10632 // backwords compat
10633 Roo.Fx.resize = Roo.Fx.scale;
10634
10635 //When included, Roo.Fx is automatically applied to Element so that all basic
10636 //effects are available directly via the Element API
10637 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10638  * Based on:
10639  * Ext JS Library 1.1.1
10640  * Copyright(c) 2006-2007, Ext JS, LLC.
10641  *
10642  * Originally Released Under LGPL - original licence link has changed is not relivant.
10643  *
10644  * Fork - LGPL
10645  * <script type="text/javascript">
10646  */
10647
10648
10649 /**
10650  * @class Roo.CompositeElement
10651  * Standard composite class. Creates a Roo.Element for every element in the collection.
10652  * <br><br>
10653  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10654  * actions will be performed on all the elements in this collection.</b>
10655  * <br><br>
10656  * All methods return <i>this</i> and can be chained.
10657  <pre><code>
10658  var els = Roo.select("#some-el div.some-class", true);
10659  // or select directly from an existing element
10660  var el = Roo.get('some-el');
10661  el.select('div.some-class', true);
10662
10663  els.setWidth(100); // all elements become 100 width
10664  els.hide(true); // all elements fade out and hide
10665  // or
10666  els.setWidth(100).hide(true);
10667  </code></pre>
10668  */
10669 Roo.CompositeElement = function(els){
10670     this.elements = [];
10671     this.addElements(els);
10672 };
10673 Roo.CompositeElement.prototype = {
10674     isComposite: true,
10675     addElements : function(els){
10676         if(!els) return this;
10677         if(typeof els == "string"){
10678             els = Roo.Element.selectorFunction(els);
10679         }
10680         var yels = this.elements;
10681         var index = yels.length-1;
10682         for(var i = 0, len = els.length; i < len; i++) {
10683                 yels[++index] = Roo.get(els[i]);
10684         }
10685         return this;
10686     },
10687
10688     /**
10689     * Clears this composite and adds the elements returned by the passed selector.
10690     * @param {String/Array} els A string CSS selector, an array of elements or an element
10691     * @return {CompositeElement} this
10692     */
10693     fill : function(els){
10694         this.elements = [];
10695         this.add(els);
10696         return this;
10697     },
10698
10699     /**
10700     * Filters this composite to only elements that match the passed selector.
10701     * @param {String} selector A string CSS selector
10702     * @return {CompositeElement} this
10703     */
10704     filter : function(selector){
10705         var els = [];
10706         this.each(function(el){
10707             if(el.is(selector)){
10708                 els[els.length] = el.dom;
10709             }
10710         });
10711         this.fill(els);
10712         return this;
10713     },
10714
10715     invoke : function(fn, args){
10716         var els = this.elements;
10717         for(var i = 0, len = els.length; i < len; i++) {
10718                 Roo.Element.prototype[fn].apply(els[i], args);
10719         }
10720         return this;
10721     },
10722     /**
10723     * Adds elements to this composite.
10724     * @param {String/Array} els A string CSS selector, an array of elements or an element
10725     * @return {CompositeElement} this
10726     */
10727     add : function(els){
10728         if(typeof els == "string"){
10729             this.addElements(Roo.Element.selectorFunction(els));
10730         }else if(els.length !== undefined){
10731             this.addElements(els);
10732         }else{
10733             this.addElements([els]);
10734         }
10735         return this;
10736     },
10737     /**
10738     * Calls the passed function passing (el, this, index) for each element in this composite.
10739     * @param {Function} fn The function to call
10740     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10741     * @return {CompositeElement} this
10742     */
10743     each : function(fn, scope){
10744         var els = this.elements;
10745         for(var i = 0, len = els.length; i < len; i++){
10746             if(fn.call(scope || els[i], els[i], this, i) === false) {
10747                 break;
10748             }
10749         }
10750         return this;
10751     },
10752
10753     /**
10754      * Returns the Element object at the specified index
10755      * @param {Number} index
10756      * @return {Roo.Element}
10757      */
10758     item : function(index){
10759         return this.elements[index] || null;
10760     },
10761
10762     /**
10763      * Returns the first Element
10764      * @return {Roo.Element}
10765      */
10766     first : function(){
10767         return this.item(0);
10768     },
10769
10770     /**
10771      * Returns the last Element
10772      * @return {Roo.Element}
10773      */
10774     last : function(){
10775         return this.item(this.elements.length-1);
10776     },
10777
10778     /**
10779      * Returns the number of elements in this composite
10780      * @return Number
10781      */
10782     getCount : function(){
10783         return this.elements.length;
10784     },
10785
10786     /**
10787      * Returns true if this composite contains the passed element
10788      * @return Boolean
10789      */
10790     contains : function(el){
10791         return this.indexOf(el) !== -1;
10792     },
10793
10794     /**
10795      * Returns true if this composite contains the passed element
10796      * @return Boolean
10797      */
10798     indexOf : function(el){
10799         return this.elements.indexOf(Roo.get(el));
10800     },
10801
10802
10803     /**
10804     * Removes the specified element(s).
10805     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10806     * or an array of any of those.
10807     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10808     * @return {CompositeElement} this
10809     */
10810     removeElement : function(el, removeDom){
10811         if(el instanceof Array){
10812             for(var i = 0, len = el.length; i < len; i++){
10813                 this.removeElement(el[i]);
10814             }
10815             return this;
10816         }
10817         var index = typeof el == 'number' ? el : this.indexOf(el);
10818         if(index !== -1){
10819             if(removeDom){
10820                 var d = this.elements[index];
10821                 if(d.dom){
10822                     d.remove();
10823                 }else{
10824                     d.parentNode.removeChild(d);
10825                 }
10826             }
10827             this.elements.splice(index, 1);
10828         }
10829         return this;
10830     },
10831
10832     /**
10833     * Replaces the specified element with the passed element.
10834     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10835     * to replace.
10836     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10837     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10838     * @return {CompositeElement} this
10839     */
10840     replaceElement : function(el, replacement, domReplace){
10841         var index = typeof el == 'number' ? el : this.indexOf(el);
10842         if(index !== -1){
10843             if(domReplace){
10844                 this.elements[index].replaceWith(replacement);
10845             }else{
10846                 this.elements.splice(index, 1, Roo.get(replacement))
10847             }
10848         }
10849         return this;
10850     },
10851
10852     /**
10853      * Removes all elements.
10854      */
10855     clear : function(){
10856         this.elements = [];
10857     }
10858 };
10859 (function(){
10860     Roo.CompositeElement.createCall = function(proto, fnName){
10861         if(!proto[fnName]){
10862             proto[fnName] = function(){
10863                 return this.invoke(fnName, arguments);
10864             };
10865         }
10866     };
10867     for(var fnName in Roo.Element.prototype){
10868         if(typeof Roo.Element.prototype[fnName] == "function"){
10869             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10870         }
10871     };
10872 })();
10873 /*
10874  * Based on:
10875  * Ext JS Library 1.1.1
10876  * Copyright(c) 2006-2007, Ext JS, LLC.
10877  *
10878  * Originally Released Under LGPL - original licence link has changed is not relivant.
10879  *
10880  * Fork - LGPL
10881  * <script type="text/javascript">
10882  */
10883
10884 /**
10885  * @class Roo.CompositeElementLite
10886  * @extends Roo.CompositeElement
10887  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10888  <pre><code>
10889  var els = Roo.select("#some-el div.some-class");
10890  // or select directly from an existing element
10891  var el = Roo.get('some-el');
10892  el.select('div.some-class');
10893
10894  els.setWidth(100); // all elements become 100 width
10895  els.hide(true); // all elements fade out and hide
10896  // or
10897  els.setWidth(100).hide(true);
10898  </code></pre><br><br>
10899  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10900  * actions will be performed on all the elements in this collection.</b>
10901  */
10902 Roo.CompositeElementLite = function(els){
10903     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10904     this.el = new Roo.Element.Flyweight();
10905 };
10906 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10907     addElements : function(els){
10908         if(els){
10909             if(els instanceof Array){
10910                 this.elements = this.elements.concat(els);
10911             }else{
10912                 var yels = this.elements;
10913                 var index = yels.length-1;
10914                 for(var i = 0, len = els.length; i < len; i++) {
10915                     yels[++index] = els[i];
10916                 }
10917             }
10918         }
10919         return this;
10920     },
10921     invoke : function(fn, args){
10922         var els = this.elements;
10923         var el = this.el;
10924         for(var i = 0, len = els.length; i < len; i++) {
10925             el.dom = els[i];
10926                 Roo.Element.prototype[fn].apply(el, args);
10927         }
10928         return this;
10929     },
10930     /**
10931      * Returns a flyweight Element of the dom element object at the specified index
10932      * @param {Number} index
10933      * @return {Roo.Element}
10934      */
10935     item : function(index){
10936         if(!this.elements[index]){
10937             return null;
10938         }
10939         this.el.dom = this.elements[index];
10940         return this.el;
10941     },
10942
10943     // fixes scope with flyweight
10944     addListener : function(eventName, handler, scope, opt){
10945         var els = this.elements;
10946         for(var i = 0, len = els.length; i < len; i++) {
10947             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10948         }
10949         return this;
10950     },
10951
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10954     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10955     * a reference to the dom node, use el.dom.</b>
10956     * @param {Function} fn The function to call
10957     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10958     * @return {CompositeElement} this
10959     */
10960     each : function(fn, scope){
10961         var els = this.elements;
10962         var el = this.el;
10963         for(var i = 0, len = els.length; i < len; i++){
10964             el.dom = els[i];
10965                 if(fn.call(scope || el, el, this, i) === false){
10966                 break;
10967             }
10968         }
10969         return this;
10970     },
10971
10972     indexOf : function(el){
10973         return this.elements.indexOf(Roo.getDom(el));
10974     },
10975
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             replacement = Roo.getDom(replacement);
10980             if(domReplace){
10981                 var d = this.elements[index];
10982                 d.parentNode.insertBefore(replacement, d);
10983                 d.parentNode.removeChild(d);
10984             }
10985             this.elements.splice(index, 1, replacement);
10986         }
10987         return this;
10988     }
10989 });
10990 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10991
10992 /*
10993  * Based on:
10994  * Ext JS Library 1.1.1
10995  * Copyright(c) 2006-2007, Ext JS, LLC.
10996  *
10997  * Originally Released Under LGPL - original licence link has changed is not relivant.
10998  *
10999  * Fork - LGPL
11000  * <script type="text/javascript">
11001  */
11002
11003  
11004
11005 /**
11006  * @class Roo.data.Connection
11007  * @extends Roo.util.Observable
11008  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11009  * either to a configured URL, or to a URL specified at request time.<br><br>
11010  * <p>
11011  * Requests made by this class are asynchronous, and will return immediately. No data from
11012  * the server will be available to the statement immediately following the {@link #request} call.
11013  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11014  * <p>
11015  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11016  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11017  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11018  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11019  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11020  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11021  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11022  * standard DOM methods.
11023  * @constructor
11024  * @param {Object} config a configuration object.
11025  */
11026 Roo.data.Connection = function(config){
11027     Roo.apply(this, config);
11028     this.addEvents({
11029         /**
11030          * @event beforerequest
11031          * Fires before a network request is made to retrieve a data object.
11032          * @param {Connection} conn This Connection object.
11033          * @param {Object} options The options config object passed to the {@link #request} method.
11034          */
11035         "beforerequest" : true,
11036         /**
11037          * @event requestcomplete
11038          * Fires if the request was successfully completed.
11039          * @param {Connection} conn This Connection object.
11040          * @param {Object} response The XHR object containing the response data.
11041          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11042          * @param {Object} options The options config object passed to the {@link #request} method.
11043          */
11044         "requestcomplete" : true,
11045         /**
11046          * @event requestexception
11047          * Fires if an error HTTP status was returned from the server.
11048          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11049          * @param {Connection} conn This Connection object.
11050          * @param {Object} response The XHR object containing the response data.
11051          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11052          * @param {Object} options The options config object passed to the {@link #request} method.
11053          */
11054         "requestexception" : true
11055     });
11056     Roo.data.Connection.superclass.constructor.call(this);
11057 };
11058
11059 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11060     /**
11061      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11062      */
11063     /**
11064      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11065      * extra parameters to each request made by this object. (defaults to undefined)
11066      */
11067     /**
11068      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11069      *  to each request made by this object. (defaults to undefined)
11070      */
11071     /**
11072      * @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)
11073      */
11074     /**
11075      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11076      */
11077     timeout : 30000,
11078     /**
11079      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11080      * @type Boolean
11081      */
11082     autoAbort:false,
11083
11084     /**
11085      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11086      * @type Boolean
11087      */
11088     disableCaching: true,
11089
11090     /**
11091      * Sends an HTTP request to a remote server.
11092      * @param {Object} options An object which may contain the following properties:<ul>
11093      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11094      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11095      * request, a url encoded string or a function to call to get either.</li>
11096      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11097      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11098      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11099      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11100      * <li>options {Object} The parameter to the request call.</li>
11101      * <li>success {Boolean} True if the request succeeded.</li>
11102      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11103      * </ul></li>
11104      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11105      * The callback is passed the following parameters:<ul>
11106      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11107      * <li>options {Object} The parameter to the request call.</li>
11108      * </ul></li>
11109      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11110      * The callback is passed the following parameters:<ul>
11111      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11112      * <li>options {Object} The parameter to the request call.</li>
11113      * </ul></li>
11114      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11115      * for the callback function. Defaults to the browser window.</li>
11116      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11117      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11118      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11119      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11120      * params for the post data. Any params will be appended to the URL.</li>
11121      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11122      * </ul>
11123      * @return {Number} transactionId
11124      */
11125     request : function(o){
11126         if(this.fireEvent("beforerequest", this, o) !== false){
11127             var p = o.params;
11128
11129             if(typeof p == "function"){
11130                 p = p.call(o.scope||window, o);
11131             }
11132             if(typeof p == "object"){
11133                 p = Roo.urlEncode(o.params);
11134             }
11135             if(this.extraParams){
11136                 var extras = Roo.urlEncode(this.extraParams);
11137                 p = p ? (p + '&' + extras) : extras;
11138             }
11139
11140             var url = o.url || this.url;
11141             if(typeof url == 'function'){
11142                 url = url.call(o.scope||window, o);
11143             }
11144
11145             if(o.form){
11146                 var form = Roo.getDom(o.form);
11147                 url = url || form.action;
11148
11149                 var enctype = form.getAttribute("enctype");
11150                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11151                     return this.doFormUpload(o, p, url);
11152                 }
11153                 var f = Roo.lib.Ajax.serializeForm(form);
11154                 p = p ? (p + '&' + f) : f;
11155             }
11156
11157             var hs = o.headers;
11158             if(this.defaultHeaders){
11159                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11160                 if(!o.headers){
11161                     o.headers = hs;
11162                 }
11163             }
11164
11165             var cb = {
11166                 success: this.handleResponse,
11167                 failure: this.handleFailure,
11168                 scope: this,
11169                 argument: {options: o},
11170                 timeout : this.timeout
11171             };
11172
11173             var method = o.method||this.method||(p ? "POST" : "GET");
11174
11175             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11176                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11177             }
11178
11179             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11180                 if(o.autoAbort){
11181                     this.abort();
11182                 }
11183             }else if(this.autoAbort !== false){
11184                 this.abort();
11185             }
11186
11187             if((method == 'GET' && p) || o.xmlData){
11188                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11189                 p = '';
11190             }
11191             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11192             return this.transId;
11193         }else{
11194             Roo.callback(o.callback, o.scope, [o, null, null]);
11195             return null;
11196         }
11197     },
11198
11199     /**
11200      * Determine whether this object has a request outstanding.
11201      * @param {Number} transactionId (Optional) defaults to the last transaction
11202      * @return {Boolean} True if there is an outstanding request.
11203      */
11204     isLoading : function(transId){
11205         if(transId){
11206             return Roo.lib.Ajax.isCallInProgress(transId);
11207         }else{
11208             return this.transId ? true : false;
11209         }
11210     },
11211
11212     /**
11213      * Aborts any outstanding request.
11214      * @param {Number} transactionId (Optional) defaults to the last transaction
11215      */
11216     abort : function(transId){
11217         if(transId || this.isLoading()){
11218             Roo.lib.Ajax.abort(transId || this.transId);
11219         }
11220     },
11221
11222     // private
11223     handleResponse : function(response){
11224         this.transId = false;
11225         var options = response.argument.options;
11226         response.argument = options ? options.argument : null;
11227         this.fireEvent("requestcomplete", this, response, options);
11228         Roo.callback(options.success, options.scope, [response, options]);
11229         Roo.callback(options.callback, options.scope, [options, true, response]);
11230     },
11231
11232     // private
11233     handleFailure : function(response, e){
11234         this.transId = false;
11235         var options = response.argument.options;
11236         response.argument = options ? options.argument : null;
11237         this.fireEvent("requestexception", this, response, options, e);
11238         Roo.callback(options.failure, options.scope, [response, options]);
11239         Roo.callback(options.callback, options.scope, [options, false, response]);
11240     },
11241
11242     // private
11243     doFormUpload : function(o, ps, url){
11244         var id = Roo.id();
11245         var frame = document.createElement('iframe');
11246         frame.id = id;
11247         frame.name = id;
11248         frame.className = 'x-hidden';
11249         if(Roo.isIE){
11250             frame.src = Roo.SSL_SECURE_URL;
11251         }
11252         document.body.appendChild(frame);
11253
11254         if(Roo.isIE){
11255            document.frames[id].name = id;
11256         }
11257
11258         var form = Roo.getDom(o.form);
11259         form.target = id;
11260         form.method = 'POST';
11261         form.enctype = form.encoding = 'multipart/form-data';
11262         if(url){
11263             form.action = url;
11264         }
11265
11266         var hiddens, hd;
11267         if(ps){ // add dynamic params
11268             hiddens = [];
11269             ps = Roo.urlDecode(ps, false);
11270             for(var k in ps){
11271                 if(ps.hasOwnProperty(k)){
11272                     hd = document.createElement('input');
11273                     hd.type = 'hidden';
11274                     hd.name = k;
11275                     hd.value = ps[k];
11276                     form.appendChild(hd);
11277                     hiddens.push(hd);
11278                 }
11279             }
11280         }
11281
11282         function cb(){
11283             var r = {  // bogus response object
11284                 responseText : '',
11285                 responseXML : null
11286             };
11287
11288             r.argument = o ? o.argument : null;
11289
11290             try { //
11291                 var doc;
11292                 if(Roo.isIE){
11293                     doc = frame.contentWindow.document;
11294                 }else {
11295                     doc = (frame.contentDocument || window.frames[id].document);
11296                 }
11297                 if(doc && doc.body){
11298                     r.responseText = doc.body.innerHTML;
11299                 }
11300                 if(doc && doc.XMLDocument){
11301                     r.responseXML = doc.XMLDocument;
11302                 }else {
11303                     r.responseXML = doc;
11304                 }
11305             }
11306             catch(e) {
11307                 // ignore
11308             }
11309
11310             Roo.EventManager.removeListener(frame, 'load', cb, this);
11311
11312             this.fireEvent("requestcomplete", this, r, o);
11313             Roo.callback(o.success, o.scope, [r, o]);
11314             Roo.callback(o.callback, o.scope, [o, true, r]);
11315
11316             setTimeout(function(){document.body.removeChild(frame);}, 100);
11317         }
11318
11319         Roo.EventManager.on(frame, 'load', cb, this);
11320         form.submit();
11321
11322         if(hiddens){ // remove dynamic params
11323             for(var i = 0, len = hiddens.length; i < len; i++){
11324                 form.removeChild(hiddens[i]);
11325             }
11326         }
11327     }
11328 });
11329
11330 /**
11331  * @class Roo.Ajax
11332  * @extends Roo.data.Connection
11333  * Global Ajax request class.
11334  *
11335  * @singleton
11336  */
11337 Roo.Ajax = new Roo.data.Connection({
11338     // fix up the docs
11339    /**
11340      * @cfg {String} url @hide
11341      */
11342     /**
11343      * @cfg {Object} extraParams @hide
11344      */
11345     /**
11346      * @cfg {Object} defaultHeaders @hide
11347      */
11348     /**
11349      * @cfg {String} method (Optional) @hide
11350      */
11351     /**
11352      * @cfg {Number} timeout (Optional) @hide
11353      */
11354     /**
11355      * @cfg {Boolean} autoAbort (Optional) @hide
11356      */
11357
11358     /**
11359      * @cfg {Boolean} disableCaching (Optional) @hide
11360      */
11361
11362     /**
11363      * @property  disableCaching
11364      * True to add a unique cache-buster param to GET requests. (defaults to true)
11365      * @type Boolean
11366      */
11367     /**
11368      * @property  url
11369      * The default URL to be used for requests to the server. (defaults to undefined)
11370      * @type String
11371      */
11372     /**
11373      * @property  extraParams
11374      * An object containing properties which are used as
11375      * extra parameters to each request made by this object. (defaults to undefined)
11376      * @type Object
11377      */
11378     /**
11379      * @property  defaultHeaders
11380      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11381      * @type Object
11382      */
11383     /**
11384      * @property  method
11385      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11386      * @type String
11387      */
11388     /**
11389      * @property  timeout
11390      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11391      * @type Number
11392      */
11393
11394     /**
11395      * @property  autoAbort
11396      * Whether a new request should abort any pending requests. (defaults to false)
11397      * @type Boolean
11398      */
11399     autoAbort : false,
11400
11401     /**
11402      * Serialize the passed form into a url encoded string
11403      * @param {String/HTMLElement} form
11404      * @return {String}
11405      */
11406     serializeForm : function(form){
11407         return Roo.lib.Ajax.serializeForm(form);
11408     }
11409 });/*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419  
11420 /**
11421  * @class Roo.Ajax
11422  * @extends Roo.data.Connection
11423  * Global Ajax request class.
11424  *
11425  * @instanceOf  Roo.data.Connection
11426  */
11427 Roo.Ajax = new Roo.data.Connection({
11428     // fix up the docs
11429     
11430     /**
11431      * fix up scoping
11432      * @scope Roo.Ajax
11433      */
11434     
11435    /**
11436      * @cfg {String} url @hide
11437      */
11438     /**
11439      * @cfg {Object} extraParams @hide
11440      */
11441     /**
11442      * @cfg {Object} defaultHeaders @hide
11443      */
11444     /**
11445      * @cfg {String} method (Optional) @hide
11446      */
11447     /**
11448      * @cfg {Number} timeout (Optional) @hide
11449      */
11450     /**
11451      * @cfg {Boolean} autoAbort (Optional) @hide
11452      */
11453
11454     /**
11455      * @cfg {Boolean} disableCaching (Optional) @hide
11456      */
11457
11458     /**
11459      * @property  disableCaching
11460      * True to add a unique cache-buster param to GET requests. (defaults to true)
11461      * @type Boolean
11462      */
11463     /**
11464      * @property  url
11465      * The default URL to be used for requests to the server. (defaults to undefined)
11466      * @type String
11467      */
11468     /**
11469      * @property  extraParams
11470      * An object containing properties which are used as
11471      * extra parameters to each request made by this object. (defaults to undefined)
11472      * @type Object
11473      */
11474     /**
11475      * @property  defaultHeaders
11476      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11477      * @type Object
11478      */
11479     /**
11480      * @property  method
11481      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11482      * @type String
11483      */
11484     /**
11485      * @property  timeout
11486      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11487      * @type Number
11488      */
11489
11490     /**
11491      * @property  autoAbort
11492      * Whether a new request should abort any pending requests. (defaults to false)
11493      * @type Boolean
11494      */
11495     autoAbort : false,
11496
11497     /**
11498      * Serialize the passed form into a url encoded string
11499      * @param {String/HTMLElement} form
11500      * @return {String}
11501      */
11502     serializeForm : function(form){
11503         return Roo.lib.Ajax.serializeForm(form);
11504     }
11505 });/*
11506  * Based on:
11507  * Ext JS Library 1.1.1
11508  * Copyright(c) 2006-2007, Ext JS, LLC.
11509  *
11510  * Originally Released Under LGPL - original licence link has changed is not relivant.
11511  *
11512  * Fork - LGPL
11513  * <script type="text/javascript">
11514  */
11515
11516  
11517 /**
11518  * @class Roo.UpdateManager
11519  * @extends Roo.util.Observable
11520  * Provides AJAX-style update for Element object.<br><br>
11521  * Usage:<br>
11522  * <pre><code>
11523  * // Get it from a Roo.Element object
11524  * var el = Roo.get("foo");
11525  * var mgr = el.getUpdateManager();
11526  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11527  * ...
11528  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11529  * <br>
11530  * // or directly (returns the same UpdateManager instance)
11531  * var mgr = new Roo.UpdateManager("myElementId");
11532  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11533  * mgr.on("update", myFcnNeedsToKnow);
11534  * <br>
11535    // short handed call directly from the element object
11536    Roo.get("foo").load({
11537         url: "bar.php",
11538         scripts:true,
11539         params: "for=bar",
11540         text: "Loading Foo..."
11541    });
11542  * </code></pre>
11543  * @constructor
11544  * Create new UpdateManager directly.
11545  * @param {String/HTMLElement/Roo.Element} el The element to update
11546  * @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).
11547  */
11548 Roo.UpdateManager = function(el, forceNew){
11549     el = Roo.get(el);
11550     if(!forceNew && el.updateManager){
11551         return el.updateManager;
11552     }
11553     /**
11554      * The Element object
11555      * @type Roo.Element
11556      */
11557     this.el = el;
11558     /**
11559      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11560      * @type String
11561      */
11562     this.defaultUrl = null;
11563
11564     this.addEvents({
11565         /**
11566          * @event beforeupdate
11567          * Fired before an update is made, return false from your handler and the update is cancelled.
11568          * @param {Roo.Element} el
11569          * @param {String/Object/Function} url
11570          * @param {String/Object} params
11571          */
11572         "beforeupdate": true,
11573         /**
11574          * @event update
11575          * Fired after successful update is made.
11576          * @param {Roo.Element} el
11577          * @param {Object} oResponseObject The response Object
11578          */
11579         "update": true,
11580         /**
11581          * @event failure
11582          * Fired on update failure.
11583          * @param {Roo.Element} el
11584          * @param {Object} oResponseObject The response Object
11585          */
11586         "failure": true
11587     });
11588     var d = Roo.UpdateManager.defaults;
11589     /**
11590      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11591      * @type String
11592      */
11593     this.sslBlankUrl = d.sslBlankUrl;
11594     /**
11595      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11596      * @type Boolean
11597      */
11598     this.disableCaching = d.disableCaching;
11599     /**
11600      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11601      * @type String
11602      */
11603     this.indicatorText = d.indicatorText;
11604     /**
11605      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11606      * @type String
11607      */
11608     this.showLoadIndicator = d.showLoadIndicator;
11609     /**
11610      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11611      * @type Number
11612      */
11613     this.timeout = d.timeout;
11614
11615     /**
11616      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11617      * @type Boolean
11618      */
11619     this.loadScripts = d.loadScripts;
11620
11621     /**
11622      * Transaction object of current executing transaction
11623      */
11624     this.transaction = null;
11625
11626     /**
11627      * @private
11628      */
11629     this.autoRefreshProcId = null;
11630     /**
11631      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11632      * @type Function
11633      */
11634     this.refreshDelegate = this.refresh.createDelegate(this);
11635     /**
11636      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11637      * @type Function
11638      */
11639     this.updateDelegate = this.update.createDelegate(this);
11640     /**
11641      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11642      * @type Function
11643      */
11644     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11645     /**
11646      * @private
11647      */
11648     this.successDelegate = this.processSuccess.createDelegate(this);
11649     /**
11650      * @private
11651      */
11652     this.failureDelegate = this.processFailure.createDelegate(this);
11653
11654     if(!this.renderer){
11655      /**
11656       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11657       */
11658     this.renderer = new Roo.UpdateManager.BasicRenderer();
11659     }
11660     
11661     Roo.UpdateManager.superclass.constructor.call(this);
11662 };
11663
11664 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11665     /**
11666      * Get the Element this UpdateManager is bound to
11667      * @return {Roo.Element} The element
11668      */
11669     getEl : function(){
11670         return this.el;
11671     },
11672     /**
11673      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11674      * @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:
11675 <pre><code>
11676 um.update({<br/>
11677     url: "your-url.php",<br/>
11678     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11679     callback: yourFunction,<br/>
11680     scope: yourObject, //(optional scope)  <br/>
11681     discardUrl: false, <br/>
11682     nocache: false,<br/>
11683     text: "Loading...",<br/>
11684     timeout: 30,<br/>
11685     scripts: false<br/>
11686 });
11687 </code></pre>
11688      * The only required property is url. The optional properties nocache, text and scripts
11689      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11690      * @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}
11691      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11692      * @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.
11693      */
11694     update : function(url, params, callback, discardUrl){
11695         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11696             var method = this.method, cfg;
11697             if(typeof url == "object"){ // must be config object
11698                 cfg = url;
11699                 url = cfg.url;
11700                 params = params || cfg.params;
11701                 callback = callback || cfg.callback;
11702                 discardUrl = discardUrl || cfg.discardUrl;
11703                 if(callback && cfg.scope){
11704                     callback = callback.createDelegate(cfg.scope);
11705                 }
11706                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11707                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11708                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11709                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11710                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11711             }
11712             this.showLoading();
11713             if(!discardUrl){
11714                 this.defaultUrl = url;
11715             }
11716             if(typeof url == "function"){
11717                 url = url.call(this);
11718             }
11719
11720             method = method || (params ? "POST" : "GET");
11721             if(method == "GET"){
11722                 url = this.prepareUrl(url);
11723             }
11724
11725             var o = Roo.apply(cfg ||{}, {
11726                 url : url,
11727                 params: params,
11728                 success: this.successDelegate,
11729                 failure: this.failureDelegate,
11730                 callback: undefined,
11731                 timeout: (this.timeout*1000),
11732                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11733             });
11734
11735             this.transaction = Roo.Ajax.request(o);
11736         }
11737     },
11738
11739     /**
11740      * 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.
11741      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11742      * @param {String/HTMLElement} form The form Id or form element
11743      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11744      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11745      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11746      */
11747     formUpdate : function(form, url, reset, callback){
11748         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11749             if(typeof url == "function"){
11750                 url = url.call(this);
11751             }
11752             form = Roo.getDom(form);
11753             this.transaction = Roo.Ajax.request({
11754                 form: form,
11755                 url:url,
11756                 success: this.successDelegate,
11757                 failure: this.failureDelegate,
11758                 timeout: (this.timeout*1000),
11759                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11760             });
11761             this.showLoading.defer(1, this);
11762         }
11763     },
11764
11765     /**
11766      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11767      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11768      */
11769     refresh : function(callback){
11770         if(this.defaultUrl == null){
11771             return;
11772         }
11773         this.update(this.defaultUrl, null, callback, true);
11774     },
11775
11776     /**
11777      * Set this element to auto refresh.
11778      * @param {Number} interval How often to update (in seconds).
11779      * @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)
11780      * @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}
11781      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11782      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11783      */
11784     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11785         if(refreshNow){
11786             this.update(url || this.defaultUrl, params, callback, true);
11787         }
11788         if(this.autoRefreshProcId){
11789             clearInterval(this.autoRefreshProcId);
11790         }
11791         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11792     },
11793
11794     /**
11795      * Stop auto refresh on this element.
11796      */
11797      stopAutoRefresh : function(){
11798         if(this.autoRefreshProcId){
11799             clearInterval(this.autoRefreshProcId);
11800             delete this.autoRefreshProcId;
11801         }
11802     },
11803
11804     isAutoRefreshing : function(){
11805        return this.autoRefreshProcId ? true : false;
11806     },
11807     /**
11808      * Called to update the element to "Loading" state. Override to perform custom action.
11809      */
11810     showLoading : function(){
11811         if(this.showLoadIndicator){
11812             this.el.update(this.indicatorText);
11813         }
11814     },
11815
11816     /**
11817      * Adds unique parameter to query string if disableCaching = true
11818      * @private
11819      */
11820     prepareUrl : function(url){
11821         if(this.disableCaching){
11822             var append = "_dc=" + (new Date().getTime());
11823             if(url.indexOf("?") !== -1){
11824                 url += "&" + append;
11825             }else{
11826                 url += "?" + append;
11827             }
11828         }
11829         return url;
11830     },
11831
11832     /**
11833      * @private
11834      */
11835     processSuccess : function(response){
11836         this.transaction = null;
11837         if(response.argument.form && response.argument.reset){
11838             try{ // put in try/catch since some older FF releases had problems with this
11839                 response.argument.form.reset();
11840             }catch(e){}
11841         }
11842         if(this.loadScripts){
11843             this.renderer.render(this.el, response, this,
11844                 this.updateComplete.createDelegate(this, [response]));
11845         }else{
11846             this.renderer.render(this.el, response, this);
11847             this.updateComplete(response);
11848         }
11849     },
11850
11851     updateComplete : function(response){
11852         this.fireEvent("update", this.el, response);
11853         if(typeof response.argument.callback == "function"){
11854             response.argument.callback(this.el, true, response);
11855         }
11856     },
11857
11858     /**
11859      * @private
11860      */
11861     processFailure : function(response){
11862         this.transaction = null;
11863         this.fireEvent("failure", this.el, response);
11864         if(typeof response.argument.callback == "function"){
11865             response.argument.callback(this.el, false, response);
11866         }
11867     },
11868
11869     /**
11870      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11871      * @param {Object} renderer The object implementing the render() method
11872      */
11873     setRenderer : function(renderer){
11874         this.renderer = renderer;
11875     },
11876
11877     getRenderer : function(){
11878        return this.renderer;
11879     },
11880
11881     /**
11882      * Set the defaultUrl used for updates
11883      * @param {String/Function} defaultUrl The url or a function to call to get the url
11884      */
11885     setDefaultUrl : function(defaultUrl){
11886         this.defaultUrl = defaultUrl;
11887     },
11888
11889     /**
11890      * Aborts the executing transaction
11891      */
11892     abort : function(){
11893         if(this.transaction){
11894             Roo.Ajax.abort(this.transaction);
11895         }
11896     },
11897
11898     /**
11899      * Returns true if an update is in progress
11900      * @return {Boolean}
11901      */
11902     isUpdating : function(){
11903         if(this.transaction){
11904             return Roo.Ajax.isLoading(this.transaction);
11905         }
11906         return false;
11907     }
11908 });
11909
11910 /**
11911  * @class Roo.UpdateManager.defaults
11912  * @static (not really - but it helps the doc tool)
11913  * The defaults collection enables customizing the default properties of UpdateManager
11914  */
11915    Roo.UpdateManager.defaults = {
11916        /**
11917          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11918          * @type Number
11919          */
11920          timeout : 30,
11921
11922          /**
11923          * True to process scripts by default (Defaults to false).
11924          * @type Boolean
11925          */
11926         loadScripts : false,
11927
11928         /**
11929         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11930         * @type String
11931         */
11932         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11933         /**
11934          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11935          * @type Boolean
11936          */
11937         disableCaching : false,
11938         /**
11939          * Whether to show indicatorText when loading (Defaults to true).
11940          * @type Boolean
11941          */
11942         showLoadIndicator : true,
11943         /**
11944          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11945          * @type String
11946          */
11947         indicatorText : '<div class="loading-indicator">Loading...</div>'
11948    };
11949
11950 /**
11951  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11952  *Usage:
11953  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11954  * @param {String/HTMLElement/Roo.Element} el The element to update
11955  * @param {String} url The url
11956  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11957  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11958  * @static
11959  * @deprecated
11960  * @member Roo.UpdateManager
11961  */
11962 Roo.UpdateManager.updateElement = function(el, url, params, options){
11963     var um = Roo.get(el, true).getUpdateManager();
11964     Roo.apply(um, options);
11965     um.update(url, params, options ? options.callback : null);
11966 };
11967 // alias for backwards compat
11968 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11969 /**
11970  * @class Roo.UpdateManager.BasicRenderer
11971  * Default Content renderer. Updates the elements innerHTML with the responseText.
11972  */
11973 Roo.UpdateManager.BasicRenderer = function(){};
11974
11975 Roo.UpdateManager.BasicRenderer.prototype = {
11976     /**
11977      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11978      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11979      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11980      * @param {Roo.Element} el The element being rendered
11981      * @param {Object} response The YUI Connect response object
11982      * @param {UpdateManager} updateManager The calling update manager
11983      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11984      */
11985      render : function(el, response, updateManager, callback){
11986         el.update(response.responseText, updateManager.loadScripts, callback);
11987     }
11988 };
11989 /*
11990  * Based on:
11991  * Ext JS Library 1.1.1
11992  * Copyright(c) 2006-2007, Ext JS, LLC.
11993  *
11994  * Originally Released Under LGPL - original licence link has changed is not relivant.
11995  *
11996  * Fork - LGPL
11997  * <script type="text/javascript">
11998  */
11999
12000 /**
12001  * @class Roo.util.DelayedTask
12002  * Provides a convenient method of performing setTimeout where a new
12003  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12004  * You can use this class to buffer
12005  * the keypress events for a certain number of milliseconds, and perform only if they stop
12006  * for that amount of time.
12007  * @constructor The parameters to this constructor serve as defaults and are not required.
12008  * @param {Function} fn (optional) The default function to timeout
12009  * @param {Object} scope (optional) The default scope of that timeout
12010  * @param {Array} args (optional) The default Array of arguments
12011  */
12012 Roo.util.DelayedTask = function(fn, scope, args){
12013     var id = null, d, t;
12014
12015     var call = function(){
12016         var now = new Date().getTime();
12017         if(now - t >= d){
12018             clearInterval(id);
12019             id = null;
12020             fn.apply(scope, args || []);
12021         }
12022     };
12023     /**
12024      * Cancels any pending timeout and queues a new one
12025      * @param {Number} delay The milliseconds to delay
12026      * @param {Function} newFn (optional) Overrides function passed to constructor
12027      * @param {Object} newScope (optional) Overrides scope passed to constructor
12028      * @param {Array} newArgs (optional) Overrides args passed to constructor
12029      */
12030     this.delay = function(delay, newFn, newScope, newArgs){
12031         if(id && delay != d){
12032             this.cancel();
12033         }
12034         d = delay;
12035         t = new Date().getTime();
12036         fn = newFn || fn;
12037         scope = newScope || scope;
12038         args = newArgs || args;
12039         if(!id){
12040             id = setInterval(call, d);
12041         }
12042     };
12043
12044     /**
12045      * Cancel the last queued timeout
12046      */
12047     this.cancel = function(){
12048         if(id){
12049             clearInterval(id);
12050             id = null;
12051         }
12052     };
12053 };/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064  
12065 Roo.util.TaskRunner = function(interval){
12066     interval = interval || 10;
12067     var tasks = [], removeQueue = [];
12068     var id = 0;
12069     var running = false;
12070
12071     var stopThread = function(){
12072         running = false;
12073         clearInterval(id);
12074         id = 0;
12075     };
12076
12077     var startThread = function(){
12078         if(!running){
12079             running = true;
12080             id = setInterval(runTasks, interval);
12081         }
12082     };
12083
12084     var removeTask = function(task){
12085         removeQueue.push(task);
12086         if(task.onStop){
12087             task.onStop();
12088         }
12089     };
12090
12091     var runTasks = function(){
12092         if(removeQueue.length > 0){
12093             for(var i = 0, len = removeQueue.length; i < len; i++){
12094                 tasks.remove(removeQueue[i]);
12095             }
12096             removeQueue = [];
12097             if(tasks.length < 1){
12098                 stopThread();
12099                 return;
12100             }
12101         }
12102         var now = new Date().getTime();
12103         for(var i = 0, len = tasks.length; i < len; ++i){
12104             var t = tasks[i];
12105             var itime = now - t.taskRunTime;
12106             if(t.interval <= itime){
12107                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12108                 t.taskRunTime = now;
12109                 if(rt === false || t.taskRunCount === t.repeat){
12110                     removeTask(t);
12111                     return;
12112                 }
12113             }
12114             if(t.duration && t.duration <= (now - t.taskStartTime)){
12115                 removeTask(t);
12116             }
12117         }
12118     };
12119
12120     /**
12121      * Queues a new task.
12122      * @param {Object} task
12123      */
12124     this.start = function(task){
12125         tasks.push(task);
12126         task.taskStartTime = new Date().getTime();
12127         task.taskRunTime = 0;
12128         task.taskRunCount = 0;
12129         startThread();
12130         return task;
12131     };
12132
12133     this.stop = function(task){
12134         removeTask(task);
12135         return task;
12136     };
12137
12138     this.stopAll = function(){
12139         stopThread();
12140         for(var i = 0, len = tasks.length; i < len; i++){
12141             if(tasks[i].onStop){
12142                 tasks[i].onStop();
12143             }
12144         }
12145         tasks = [];
12146         removeQueue = [];
12147     };
12148 };
12149
12150 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12151  * Based on:
12152  * Ext JS Library 1.1.1
12153  * Copyright(c) 2006-2007, Ext JS, LLC.
12154  *
12155  * Originally Released Under LGPL - original licence link has changed is not relivant.
12156  *
12157  * Fork - LGPL
12158  * <script type="text/javascript">
12159  */
12160
12161  
12162 /**
12163  * @class Roo.util.MixedCollection
12164  * @extends Roo.util.Observable
12165  * A Collection class that maintains both numeric indexes and keys and exposes events.
12166  * @constructor
12167  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12168  * collection (defaults to false)
12169  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12170  * and return the key value for that item.  This is used when available to look up the key on items that
12171  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12172  * equivalent to providing an implementation for the {@link #getKey} method.
12173  */
12174 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12175     this.items = [];
12176     this.map = {};
12177     this.keys = [];
12178     this.length = 0;
12179     this.addEvents({
12180         /**
12181          * @event clear
12182          * Fires when the collection is cleared.
12183          */
12184         "clear" : true,
12185         /**
12186          * @event add
12187          * Fires when an item is added to the collection.
12188          * @param {Number} index The index at which the item was added.
12189          * @param {Object} o The item added.
12190          * @param {String} key The key associated with the added item.
12191          */
12192         "add" : true,
12193         /**
12194          * @event replace
12195          * Fires when an item is replaced in the collection.
12196          * @param {String} key he key associated with the new added.
12197          * @param {Object} old The item being replaced.
12198          * @param {Object} new The new item.
12199          */
12200         "replace" : true,
12201         /**
12202          * @event remove
12203          * Fires when an item is removed from the collection.
12204          * @param {Object} o The item being removed.
12205          * @param {String} key (optional) The key associated with the removed item.
12206          */
12207         "remove" : true,
12208         "sort" : true
12209     });
12210     this.allowFunctions = allowFunctions === true;
12211     if(keyFn){
12212         this.getKey = keyFn;
12213     }
12214     Roo.util.MixedCollection.superclass.constructor.call(this);
12215 };
12216
12217 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12218     allowFunctions : false,
12219     
12220 /**
12221  * Adds an item to the collection.
12222  * @param {String} key The key to associate with the item
12223  * @param {Object} o The item to add.
12224  * @return {Object} The item added.
12225  */
12226     add : function(key, o){
12227         if(arguments.length == 1){
12228             o = arguments[0];
12229             key = this.getKey(o);
12230         }
12231         if(typeof key == "undefined" || key === null){
12232             this.length++;
12233             this.items.push(o);
12234             this.keys.push(null);
12235         }else{
12236             var old = this.map[key];
12237             if(old){
12238                 return this.replace(key, o);
12239             }
12240             this.length++;
12241             this.items.push(o);
12242             this.map[key] = o;
12243             this.keys.push(key);
12244         }
12245         this.fireEvent("add", this.length-1, o, key);
12246         return o;
12247     },
12248    
12249 /**
12250   * MixedCollection has a generic way to fetch keys if you implement getKey.
12251 <pre><code>
12252 // normal way
12253 var mc = new Roo.util.MixedCollection();
12254 mc.add(someEl.dom.id, someEl);
12255 mc.add(otherEl.dom.id, otherEl);
12256 //and so on
12257
12258 // using getKey
12259 var mc = new Roo.util.MixedCollection();
12260 mc.getKey = function(el){
12261    return el.dom.id;
12262 };
12263 mc.add(someEl);
12264 mc.add(otherEl);
12265
12266 // or via the constructor
12267 var mc = new Roo.util.MixedCollection(false, function(el){
12268    return el.dom.id;
12269 });
12270 mc.add(someEl);
12271 mc.add(otherEl);
12272 </code></pre>
12273  * @param o {Object} The item for which to find the key.
12274  * @return {Object} The key for the passed item.
12275  */
12276     getKey : function(o){
12277          return o.id; 
12278     },
12279    
12280 /**
12281  * Replaces an item in the collection.
12282  * @param {String} key The key associated with the item to replace, or the item to replace.
12283  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12284  * @return {Object}  The new item.
12285  */
12286     replace : function(key, o){
12287         if(arguments.length == 1){
12288             o = arguments[0];
12289             key = this.getKey(o);
12290         }
12291         var old = this.item(key);
12292         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12293              return this.add(key, o);
12294         }
12295         var index = this.indexOfKey(key);
12296         this.items[index] = o;
12297         this.map[key] = o;
12298         this.fireEvent("replace", key, old, o);
12299         return o;
12300     },
12301    
12302 /**
12303  * Adds all elements of an Array or an Object to the collection.
12304  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12305  * an Array of values, each of which are added to the collection.
12306  */
12307     addAll : function(objs){
12308         if(arguments.length > 1 || objs instanceof Array){
12309             var args = arguments.length > 1 ? arguments : objs;
12310             for(var i = 0, len = args.length; i < len; i++){
12311                 this.add(args[i]);
12312             }
12313         }else{
12314             for(var key in objs){
12315                 if(this.allowFunctions || typeof objs[key] != "function"){
12316                     this.add(key, objs[key]);
12317                 }
12318             }
12319         }
12320     },
12321    
12322 /**
12323  * Executes the specified function once for every item in the collection, passing each
12324  * item as the first and only parameter. returning false from the function will stop the iteration.
12325  * @param {Function} fn The function to execute for each item.
12326  * @param {Object} scope (optional) The scope in which to execute the function.
12327  */
12328     each : function(fn, scope){
12329         var items = [].concat(this.items); // each safe for removal
12330         for(var i = 0, len = items.length; i < len; i++){
12331             if(fn.call(scope || items[i], items[i], i, len) === false){
12332                 break;
12333             }
12334         }
12335     },
12336    
12337 /**
12338  * Executes the specified function once for every key in the collection, passing each
12339  * key, and its associated item as the first two parameters.
12340  * @param {Function} fn The function to execute for each item.
12341  * @param {Object} scope (optional) The scope in which to execute the function.
12342  */
12343     eachKey : function(fn, scope){
12344         for(var i = 0, len = this.keys.length; i < len; i++){
12345             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12346         }
12347     },
12348    
12349 /**
12350  * Returns the first item in the collection which elicits a true return value from the
12351  * passed selection function.
12352  * @param {Function} fn The selection function to execute for each item.
12353  * @param {Object} scope (optional) The scope in which to execute the function.
12354  * @return {Object} The first item in the collection which returned true from the selection function.
12355  */
12356     find : function(fn, scope){
12357         for(var i = 0, len = this.items.length; i < len; i++){
12358             if(fn.call(scope || window, this.items[i], this.keys[i])){
12359                 return this.items[i];
12360             }
12361         }
12362         return null;
12363     },
12364    
12365 /**
12366  * Inserts an item at the specified index in the collection.
12367  * @param {Number} index The index to insert the item at.
12368  * @param {String} key The key to associate with the new item, or the item itself.
12369  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12370  * @return {Object} The item inserted.
12371  */
12372     insert : function(index, key, o){
12373         if(arguments.length == 2){
12374             o = arguments[1];
12375             key = this.getKey(o);
12376         }
12377         if(index >= this.length){
12378             return this.add(key, o);
12379         }
12380         this.length++;
12381         this.items.splice(index, 0, o);
12382         if(typeof key != "undefined" && key != null){
12383             this.map[key] = o;
12384         }
12385         this.keys.splice(index, 0, key);
12386         this.fireEvent("add", index, o, key);
12387         return o;
12388     },
12389    
12390 /**
12391  * Removed an item from the collection.
12392  * @param {Object} o The item to remove.
12393  * @return {Object} The item removed.
12394  */
12395     remove : function(o){
12396         return this.removeAt(this.indexOf(o));
12397     },
12398    
12399 /**
12400  * Remove an item from a specified index in the collection.
12401  * @param {Number} index The index within the collection of the item to remove.
12402  */
12403     removeAt : function(index){
12404         if(index < this.length && index >= 0){
12405             this.length--;
12406             var o = this.items[index];
12407             this.items.splice(index, 1);
12408             var key = this.keys[index];
12409             if(typeof key != "undefined"){
12410                 delete this.map[key];
12411             }
12412             this.keys.splice(index, 1);
12413             this.fireEvent("remove", o, key);
12414         }
12415     },
12416    
12417 /**
12418  * Removed an item associated with the passed key fom the collection.
12419  * @param {String} key The key of the item to remove.
12420  */
12421     removeKey : function(key){
12422         return this.removeAt(this.indexOfKey(key));
12423     },
12424    
12425 /**
12426  * Returns the number of items in the collection.
12427  * @return {Number} the number of items in the collection.
12428  */
12429     getCount : function(){
12430         return this.length; 
12431     },
12432    
12433 /**
12434  * Returns index within the collection of the passed Object.
12435  * @param {Object} o The item to find the index of.
12436  * @return {Number} index of the item.
12437  */
12438     indexOf : function(o){
12439         if(!this.items.indexOf){
12440             for(var i = 0, len = this.items.length; i < len; i++){
12441                 if(this.items[i] == o) return i;
12442             }
12443             return -1;
12444         }else{
12445             return this.items.indexOf(o);
12446         }
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed key.
12451  * @param {String} key The key to find the index of.
12452  * @return {Number} index of the key.
12453  */
12454     indexOfKey : function(key){
12455         if(!this.keys.indexOf){
12456             for(var i = 0, len = this.keys.length; i < len; i++){
12457                 if(this.keys[i] == key) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.keys.indexOf(key);
12462         }
12463     },
12464    
12465 /**
12466  * Returns the item associated with the passed key OR index. Key has priority over index.
12467  * @param {String/Number} key The key or index of the item.
12468  * @return {Object} The item associated with the passed key.
12469  */
12470     item : function(key){
12471         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12472         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12473     },
12474     
12475 /**
12476  * Returns the item at the specified index.
12477  * @param {Number} index The index of the item.
12478  * @return {Object}
12479  */
12480     itemAt : function(index){
12481         return this.items[index];
12482     },
12483     
12484 /**
12485  * Returns the item associated with the passed key.
12486  * @param {String/Number} key The key of the item.
12487  * @return {Object} The item associated with the passed key.
12488  */
12489     key : function(key){
12490         return this.map[key];
12491     },
12492    
12493 /**
12494  * Returns true if the collection contains the passed Object as an item.
12495  * @param {Object} o  The Object to look for in the collection.
12496  * @return {Boolean} True if the collection contains the Object as an item.
12497  */
12498     contains : function(o){
12499         return this.indexOf(o) != -1;
12500     },
12501    
12502 /**
12503  * Returns true if the collection contains the passed Object as a key.
12504  * @param {String} key The key to look for in the collection.
12505  * @return {Boolean} True if the collection contains the Object as a key.
12506  */
12507     containsKey : function(key){
12508         return typeof this.map[key] != "undefined";
12509     },
12510    
12511 /**
12512  * Removes all items from the collection.
12513  */
12514     clear : function(){
12515         this.length = 0;
12516         this.items = [];
12517         this.keys = [];
12518         this.map = {};
12519         this.fireEvent("clear");
12520     },
12521    
12522 /**
12523  * Returns the first item in the collection.
12524  * @return {Object} the first item in the collection..
12525  */
12526     first : function(){
12527         return this.items[0]; 
12528     },
12529    
12530 /**
12531  * Returns the last item in the collection.
12532  * @return {Object} the last item in the collection..
12533  */
12534     last : function(){
12535         return this.items[this.length-1];   
12536     },
12537     
12538     _sort : function(property, dir, fn){
12539         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12540         fn = fn || function(a, b){
12541             return a-b;
12542         };
12543         var c = [], k = this.keys, items = this.items;
12544         for(var i = 0, len = items.length; i < len; i++){
12545             c[c.length] = {key: k[i], value: items[i], index: i};
12546         }
12547         c.sort(function(a, b){
12548             var v = fn(a[property], b[property]) * dsc;
12549             if(v == 0){
12550                 v = (a.index < b.index ? -1 : 1);
12551             }
12552             return v;
12553         });
12554         for(var i = 0, len = c.length; i < len; i++){
12555             items[i] = c[i].value;
12556             k[i] = c[i].key;
12557         }
12558         this.fireEvent("sort", this);
12559     },
12560     
12561     /**
12562      * Sorts this collection with the passed comparison function
12563      * @param {String} direction (optional) "ASC" or "DESC"
12564      * @param {Function} fn (optional) comparison function
12565      */
12566     sort : function(dir, fn){
12567         this._sort("value", dir, fn);
12568     },
12569     
12570     /**
12571      * Sorts this collection by keys
12572      * @param {String} direction (optional) "ASC" or "DESC"
12573      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12574      */
12575     keySort : function(dir, fn){
12576         this._sort("key", dir, fn || function(a, b){
12577             return String(a).toUpperCase()-String(b).toUpperCase();
12578         });
12579     },
12580     
12581     /**
12582      * Returns a range of items in this collection
12583      * @param {Number} startIndex (optional) defaults to 0
12584      * @param {Number} endIndex (optional) default to the last item
12585      * @return {Array} An array of items
12586      */
12587     getRange : function(start, end){
12588         var items = this.items;
12589         if(items.length < 1){
12590             return [];
12591         }
12592         start = start || 0;
12593         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12594         var r = [];
12595         if(start <= end){
12596             for(var i = start; i <= end; i++) {
12597                     r[r.length] = items[i];
12598             }
12599         }else{
12600             for(var i = start; i >= end; i--) {
12601                     r[r.length] = items[i];
12602             }
12603         }
12604         return r;
12605     },
12606         
12607     /**
12608      * Filter the <i>objects</i> in this collection by a specific property. 
12609      * Returns a new collection that has been filtered.
12610      * @param {String} property A property on your objects
12611      * @param {String/RegExp} value Either string that the property values 
12612      * should start with or a RegExp to test against the property
12613      * @return {MixedCollection} The new filtered collection
12614      */
12615     filter : function(property, value){
12616         if(!value.exec){ // not a regex
12617             value = String(value);
12618             if(value.length == 0){
12619                 return this.clone();
12620             }
12621             value = new RegExp("^" + Roo.escapeRe(value), "i");
12622         }
12623         return this.filterBy(function(o){
12624             return o && value.test(o[property]);
12625         });
12626         },
12627     
12628     /**
12629      * Filter by a function. * Returns a new collection that has been filtered.
12630      * The passed function will be called with each 
12631      * object in the collection. If the function returns true, the value is included 
12632      * otherwise it is filtered.
12633      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12634      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12635      * @return {MixedCollection} The new filtered collection
12636      */
12637     filterBy : function(fn, scope){
12638         var r = new Roo.util.MixedCollection();
12639         r.getKey = this.getKey;
12640         var k = this.keys, it = this.items;
12641         for(var i = 0, len = it.length; i < len; i++){
12642             if(fn.call(scope||this, it[i], k[i])){
12643                                 r.add(k[i], it[i]);
12644                         }
12645         }
12646         return r;
12647     },
12648     
12649     /**
12650      * Creates a duplicate of this collection
12651      * @return {MixedCollection}
12652      */
12653     clone : function(){
12654         var r = new Roo.util.MixedCollection();
12655         var k = this.keys, it = this.items;
12656         for(var i = 0, len = it.length; i < len; i++){
12657             r.add(k[i], it[i]);
12658         }
12659         r.getKey = this.getKey;
12660         return r;
12661     }
12662 });
12663 /**
12664  * Returns the item associated with the passed key or index.
12665  * @method
12666  * @param {String/Number} key The key or index of the item.
12667  * @return {Object} The item associated with the passed key.
12668  */
12669 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12670  * Based on:
12671  * Ext JS Library 1.1.1
12672  * Copyright(c) 2006-2007, Ext JS, LLC.
12673  *
12674  * Originally Released Under LGPL - original licence link has changed is not relivant.
12675  *
12676  * Fork - LGPL
12677  * <script type="text/javascript">
12678  */
12679 /**
12680  * @class Roo.util.JSON
12681  * Modified version of Douglas Crockford"s json.js that doesn"t
12682  * mess with the Object prototype 
12683  * http://www.json.org/js.html
12684  * @singleton
12685  */
12686 Roo.util.JSON = new (function(){
12687     var useHasOwn = {}.hasOwnProperty ? true : false;
12688     
12689     // crashes Safari in some instances
12690     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12691     
12692     var pad = function(n) {
12693         return n < 10 ? "0" + n : n;
12694     };
12695     
12696     var m = {
12697         "\b": '\\b',
12698         "\t": '\\t',
12699         "\n": '\\n',
12700         "\f": '\\f',
12701         "\r": '\\r',
12702         '"' : '\\"',
12703         "\\": '\\\\'
12704     };
12705
12706     var encodeString = function(s){
12707         if (/["\\\x00-\x1f]/.test(s)) {
12708             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12709                 var c = m[b];
12710                 if(c){
12711                     return c;
12712                 }
12713                 c = b.charCodeAt();
12714                 return "\\u00" +
12715                     Math.floor(c / 16).toString(16) +
12716                     (c % 16).toString(16);
12717             }) + '"';
12718         }
12719         return '"' + s + '"';
12720     };
12721     
12722     var encodeArray = function(o){
12723         var a = ["["], b, i, l = o.length, v;
12724             for (i = 0; i < l; i += 1) {
12725                 v = o[i];
12726                 switch (typeof v) {
12727                     case "undefined":
12728                     case "function":
12729                     case "unknown":
12730                         break;
12731                     default:
12732                         if (b) {
12733                             a.push(',');
12734                         }
12735                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12736                         b = true;
12737                 }
12738             }
12739             a.push("]");
12740             return a.join("");
12741     };
12742     
12743     var encodeDate = function(o){
12744         return '"' + o.getFullYear() + "-" +
12745                 pad(o.getMonth() + 1) + "-" +
12746                 pad(o.getDate()) + "T" +
12747                 pad(o.getHours()) + ":" +
12748                 pad(o.getMinutes()) + ":" +
12749                 pad(o.getSeconds()) + '"';
12750     };
12751     
12752     /**
12753      * Encodes an Object, Array or other value
12754      * @param {Mixed} o The variable to encode
12755      * @return {String} The JSON string
12756      */
12757     this.encode = function(o){
12758         if(typeof o == "undefined" || o === null){
12759             return "null";
12760         }else if(o instanceof Array){
12761             return encodeArray(o);
12762         }else if(o instanceof Date){
12763             return encodeDate(o);
12764         }else if(typeof o == "string"){
12765             return encodeString(o);
12766         }else if(typeof o == "number"){
12767             return isFinite(o) ? String(o) : "null";
12768         }else if(typeof o == "boolean"){
12769             return String(o);
12770         }else {
12771             var a = ["{"], b, i, v;
12772             for (i in o) {
12773                 if(!useHasOwn || o.hasOwnProperty(i)) {
12774                     v = o[i];
12775                     switch (typeof v) {
12776                     case "undefined":
12777                     case "function":
12778                     case "unknown":
12779                         break;
12780                     default:
12781                         if(b){
12782                             a.push(',');
12783                         }
12784                         a.push(this.encode(i), ":",
12785                                 v === null ? "null" : this.encode(v));
12786                         b = true;
12787                     }
12788                 }
12789             }
12790             a.push("}");
12791             return a.join("");
12792         }
12793     };
12794     
12795     /**
12796      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12797      * @param {String} json The JSON string
12798      * @return {Object} The resulting object
12799      */
12800     this.decode = function(json){
12801         /**
12802          * eval:var:json
12803          */
12804         return eval("(" + json + ')');
12805     };
12806 })();
12807 /** 
12808  * Shorthand for {@link Roo.util.JSON#encode}
12809  * @member Roo encode 
12810  * @method */
12811 Roo.encode = Roo.util.JSON.encode;
12812 /** 
12813  * Shorthand for {@link Roo.util.JSON#decode}
12814  * @member Roo decode 
12815  * @method */
12816 Roo.decode = Roo.util.JSON.decode;
12817 /*
12818  * Based on:
12819  * Ext JS Library 1.1.1
12820  * Copyright(c) 2006-2007, Ext JS, LLC.
12821  *
12822  * Originally Released Under LGPL - original licence link has changed is not relivant.
12823  *
12824  * Fork - LGPL
12825  * <script type="text/javascript">
12826  */
12827  
12828 /**
12829  * @class Roo.util.Format
12830  * Reusable data formatting functions
12831  * @singleton
12832  */
12833 Roo.util.Format = function(){
12834     var trimRe = /^\s+|\s+$/g;
12835     return {
12836         /**
12837          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12838          * @param {String} value The string to truncate
12839          * @param {Number} length The maximum length to allow before truncating
12840          * @return {String} The converted text
12841          */
12842         ellipsis : function(value, len){
12843             if(value && value.length > len){
12844                 return value.substr(0, len-3)+"...";
12845             }
12846             return value;
12847         },
12848
12849         /**
12850          * Checks a reference and converts it to empty string if it is undefined
12851          * @param {Mixed} value Reference to check
12852          * @return {Mixed} Empty string if converted, otherwise the original value
12853          */
12854         undef : function(value){
12855             return typeof value != "undefined" ? value : "";
12856         },
12857
12858         /**
12859          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12860          * @param {String} value The string to encode
12861          * @return {String} The encoded text
12862          */
12863         htmlEncode : function(value){
12864             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12865         },
12866
12867         /**
12868          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12869          * @param {String} value The string to decode
12870          * @return {String} The decoded text
12871          */
12872         htmlDecode : function(value){
12873             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12874         },
12875
12876         /**
12877          * Trims any whitespace from either side of a string
12878          * @param {String} value The text to trim
12879          * @return {String} The trimmed text
12880          */
12881         trim : function(value){
12882             return String(value).replace(trimRe, "");
12883         },
12884
12885         /**
12886          * Returns a substring from within an original string
12887          * @param {String} value The original text
12888          * @param {Number} start The start index of the substring
12889          * @param {Number} length The length of the substring
12890          * @return {String} The substring
12891          */
12892         substr : function(value, start, length){
12893             return String(value).substr(start, length);
12894         },
12895
12896         /**
12897          * Converts a string to all lower case letters
12898          * @param {String} value The text to convert
12899          * @return {String} The converted text
12900          */
12901         lowercase : function(value){
12902             return String(value).toLowerCase();
12903         },
12904
12905         /**
12906          * Converts a string to all upper case letters
12907          * @param {String} value The text to convert
12908          * @return {String} The converted text
12909          */
12910         uppercase : function(value){
12911             return String(value).toUpperCase();
12912         },
12913
12914         /**
12915          * Converts the first character only of a string to upper case
12916          * @param {String} value The text to convert
12917          * @return {String} The converted text
12918          */
12919         capitalize : function(value){
12920             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12921         },
12922
12923         // private
12924         call : function(value, fn){
12925             if(arguments.length > 2){
12926                 var args = Array.prototype.slice.call(arguments, 2);
12927                 args.unshift(value);
12928                  
12929                 return /** eval:var:value */  eval(fn).apply(window, args);
12930             }else{
12931                 /** eval:var:value */
12932                 return /** eval:var:value */ eval(fn).call(window, value);
12933             }
12934         },
12935
12936         /**
12937          * Format a number as US currency
12938          * @param {Number/String} value The numeric value to format
12939          * @return {String} The formatted currency string
12940          */
12941         usMoney : function(v){
12942             v = (Math.round((v-0)*100))/100;
12943             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12944             v = String(v);
12945             var ps = v.split('.');
12946             var whole = ps[0];
12947             var sub = ps[1] ? '.'+ ps[1] : '.00';
12948             var r = /(\d+)(\d{3})/;
12949             while (r.test(whole)) {
12950                 whole = whole.replace(r, '$1' + ',' + '$2');
12951             }
12952             return "$" + whole + sub ;
12953         },
12954
12955         /**
12956          * Parse a value into a formatted date using the specified format pattern.
12957          * @param {Mixed} value The value to format
12958          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12959          * @return {String} The formatted date string
12960          */
12961         date : function(v, format){
12962             if(!v){
12963                 return "";
12964             }
12965             if(!(v instanceof Date)){
12966                 v = new Date(Date.parse(v));
12967             }
12968             return v.dateFormat(format || "m/d/Y");
12969         },
12970
12971         /**
12972          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12973          * @param {String} format Any valid date format string
12974          * @return {Function} The date formatting function
12975          */
12976         dateRenderer : function(format){
12977             return function(v){
12978                 return Roo.util.Format.date(v, format);  
12979             };
12980         },
12981
12982         // private
12983         stripTagsRE : /<\/?[^>]+>/gi,
12984         
12985         /**
12986          * Strips all HTML tags
12987          * @param {Mixed} value The text from which to strip tags
12988          * @return {String} The stripped text
12989          */
12990         stripTags : function(v){
12991             return !v ? v : String(v).replace(this.stripTagsRE, "");
12992         }
12993     };
12994 }();/*
12995  * Based on:
12996  * Ext JS Library 1.1.1
12997  * Copyright(c) 2006-2007, Ext JS, LLC.
12998  *
12999  * Originally Released Under LGPL - original licence link has changed is not relivant.
13000  *
13001  * Fork - LGPL
13002  * <script type="text/javascript">
13003  */
13004
13005
13006  
13007
13008 /**
13009  * @class Roo.MasterTemplate
13010  * @extends Roo.Template
13011  * Provides a template that can have child templates. The syntax is:
13012 <pre><code>
13013 var t = new Roo.MasterTemplate(
13014         '&lt;select name="{name}"&gt;',
13015                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13016         '&lt;/select&gt;'
13017 );
13018 t.add('options', {value: 'foo', text: 'bar'});
13019 // or you can add multiple child elements in one shot
13020 t.addAll('options', [
13021     {value: 'foo', text: 'bar'},
13022     {value: 'foo2', text: 'bar2'},
13023     {value: 'foo3', text: 'bar3'}
13024 ]);
13025 // then append, applying the master template values
13026 t.append('my-form', {name: 'my-select'});
13027 </code></pre>
13028 * A name attribute for the child template is not required if you have only one child
13029 * template or you want to refer to them by index.
13030  */
13031 Roo.MasterTemplate = function(){
13032     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13033     this.originalHtml = this.html;
13034     var st = {};
13035     var m, re = this.subTemplateRe;
13036     re.lastIndex = 0;
13037     var subIndex = 0;
13038     while(m = re.exec(this.html)){
13039         var name = m[1], content = m[2];
13040         st[subIndex] = {
13041             name: name,
13042             index: subIndex,
13043             buffer: [],
13044             tpl : new Roo.Template(content)
13045         };
13046         if(name){
13047             st[name] = st[subIndex];
13048         }
13049         st[subIndex].tpl.compile();
13050         st[subIndex].tpl.call = this.call.createDelegate(this);
13051         subIndex++;
13052     }
13053     this.subCount = subIndex;
13054     this.subs = st;
13055 };
13056 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13057     /**
13058     * The regular expression used to match sub templates
13059     * @type RegExp
13060     * @property
13061     */
13062     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13063
13064     /**
13065      * Applies the passed values to a child template.
13066      * @param {String/Number} name (optional) The name or index of the child template
13067      * @param {Array/Object} values The values to be applied to the template
13068      * @return {MasterTemplate} this
13069      */
13070      add : function(name, values){
13071         if(arguments.length == 1){
13072             values = arguments[0];
13073             name = 0;
13074         }
13075         var s = this.subs[name];
13076         s.buffer[s.buffer.length] = s.tpl.apply(values);
13077         return this;
13078     },
13079
13080     /**
13081      * Applies all the passed values to a child template.
13082      * @param {String/Number} name (optional) The name or index of the child template
13083      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13084      * @param {Boolean} reset (optional) True to reset the template first
13085      * @return {MasterTemplate} this
13086      */
13087     fill : function(name, values, reset){
13088         var a = arguments;
13089         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13090             values = a[0];
13091             name = 0;
13092             reset = a[1];
13093         }
13094         if(reset){
13095             this.reset();
13096         }
13097         for(var i = 0, len = values.length; i < len; i++){
13098             this.add(name, values[i]);
13099         }
13100         return this;
13101     },
13102
13103     /**
13104      * Resets the template for reuse
13105      * @return {MasterTemplate} this
13106      */
13107      reset : function(){
13108         var s = this.subs;
13109         for(var i = 0; i < this.subCount; i++){
13110             s[i].buffer = [];
13111         }
13112         return this;
13113     },
13114
13115     applyTemplate : function(values){
13116         var s = this.subs;
13117         var replaceIndex = -1;
13118         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13119             return s[++replaceIndex].buffer.join("");
13120         });
13121         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13122     },
13123
13124     apply : function(){
13125         return this.applyTemplate.apply(this, arguments);
13126     },
13127
13128     compile : function(){return this;}
13129 });
13130
13131 /**
13132  * Alias for fill().
13133  * @method
13134  */
13135 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13136  /**
13137  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13138  * var tpl = Roo.MasterTemplate.from('element-id');
13139  * @param {String/HTMLElement} el
13140  * @param {Object} config
13141  * @static
13142  */
13143 Roo.MasterTemplate.from = function(el, config){
13144     el = Roo.getDom(el);
13145     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13146 };/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157  
13158 /**
13159  * @class Roo.util.CSS
13160  * Utility class for manipulating CSS rules
13161  * @singleton
13162  */
13163 Roo.util.CSS = function(){
13164         var rules = null;
13165         var doc = document;
13166
13167     var camelRe = /(-[a-z])/gi;
13168     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13169
13170    return {
13171    /**
13172     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13173     * tag and appended to the HEAD of the document.
13174     * @param {String} cssText The text containing the css rules
13175     * @param {String} id An id to add to the stylesheet for later removal
13176     * @return {StyleSheet}
13177     */
13178    createStyleSheet : function(cssText, id){
13179        var ss;
13180        var head = doc.getElementsByTagName("head")[0];
13181        var rules = doc.createElement("style");
13182        rules.setAttribute("type", "text/css");
13183        if(id){
13184            rules.setAttribute("id", id);
13185        }
13186        if(Roo.isIE){
13187            head.appendChild(rules);
13188            ss = rules.styleSheet;
13189            ss.cssText = cssText;
13190        }else{
13191            try{
13192                 rules.appendChild(doc.createTextNode(cssText));
13193            }catch(e){
13194                rules.cssText = cssText; 
13195            }
13196            head.appendChild(rules);
13197            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13198        }
13199        this.cacheStyleSheet(ss);
13200        return ss;
13201    },
13202
13203    /**
13204     * Removes a style or link tag by id
13205     * @param {String} id The id of the tag
13206     */
13207    removeStyleSheet : function(id){
13208        var existing = doc.getElementById(id);
13209        if(existing){
13210            existing.parentNode.removeChild(existing);
13211        }
13212    },
13213
13214    /**
13215     * Dynamically swaps an existing stylesheet reference for a new one
13216     * @param {String} id The id of an existing link tag to remove
13217     * @param {String} url The href of the new stylesheet to include
13218     */
13219    swapStyleSheet : function(id, url){
13220        this.removeStyleSheet(id);
13221        var ss = doc.createElement("link");
13222        ss.setAttribute("rel", "stylesheet");
13223        ss.setAttribute("type", "text/css");
13224        ss.setAttribute("id", id);
13225        ss.setAttribute("href", url);
13226        doc.getElementsByTagName("head")[0].appendChild(ss);
13227    },
13228    
13229    /**
13230     * Refresh the rule cache if you have dynamically added stylesheets
13231     * @return {Object} An object (hash) of rules indexed by selector
13232     */
13233    refreshCache : function(){
13234        return this.getRules(true);
13235    },
13236
13237    // private
13238    cacheStyleSheet : function(ss){
13239        if(!rules){
13240            rules = {};
13241        }
13242        try{// try catch for cross domain access issue
13243            var ssRules = ss.cssRules || ss.rules;
13244            for(var j = ssRules.length-1; j >= 0; --j){
13245                rules[ssRules[j].selectorText] = ssRules[j];
13246            }
13247        }catch(e){}
13248    },
13249    
13250    /**
13251     * Gets all css rules for the document
13252     * @param {Boolean} refreshCache true to refresh the internal cache
13253     * @return {Object} An object (hash) of rules indexed by selector
13254     */
13255    getRules : function(refreshCache){
13256                 if(rules == null || refreshCache){
13257                         rules = {};
13258                         var ds = doc.styleSheets;
13259                         for(var i =0, len = ds.length; i < len; i++){
13260                             try{
13261                         this.cacheStyleSheet(ds[i]);
13262                     }catch(e){} 
13263                 }
13264                 }
13265                 return rules;
13266         },
13267         
13268         /**
13269     * Gets an an individual CSS rule by selector(s)
13270     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13271     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13272     * @return {CSSRule} The CSS rule or null if one is not found
13273     */
13274    getRule : function(selector, refreshCache){
13275                 var rs = this.getRules(refreshCache);
13276                 if(!(selector instanceof Array)){
13277                     return rs[selector];
13278                 }
13279                 for(var i = 0; i < selector.length; i++){
13280                         if(rs[selector[i]]){
13281                                 return rs[selector[i]];
13282                         }
13283                 }
13284                 return null;
13285         },
13286         
13287         
13288         /**
13289     * Updates a rule property
13290     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13291     * @param {String} property The css property
13292     * @param {String} value The new value for the property
13293     * @return {Boolean} true If a rule was found and updated
13294     */
13295    updateRule : function(selector, property, value){
13296                 if(!(selector instanceof Array)){
13297                         var rule = this.getRule(selector);
13298                         if(rule){
13299                                 rule.style[property.replace(camelRe, camelFn)] = value;
13300                                 return true;
13301                         }
13302                 }else{
13303                         for(var i = 0; i < selector.length; i++){
13304                                 if(this.updateRule(selector[i], property, value)){
13305                                         return true;
13306                                 }
13307                         }
13308                 }
13309                 return false;
13310         }
13311    };   
13312 }();/*
13313  * Based on:
13314  * Ext JS Library 1.1.1
13315  * Copyright(c) 2006-2007, Ext JS, LLC.
13316  *
13317  * Originally Released Under LGPL - original licence link has changed is not relivant.
13318  *
13319  * Fork - LGPL
13320  * <script type="text/javascript">
13321  */
13322
13323  
13324
13325 /**
13326  * @class Roo.util.ClickRepeater
13327  * @extends Roo.util.Observable
13328  * 
13329  * A wrapper class which can be applied to any element. Fires a "click" event while the
13330  * mouse is pressed. The interval between firings may be specified in the config but
13331  * defaults to 10 milliseconds.
13332  * 
13333  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13334  * 
13335  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13336  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13337  * Similar to an autorepeat key delay.
13338  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13339  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13340  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13341  *           "interval" and "delay" are ignored. "immediate" is honored.
13342  * @cfg {Boolean} preventDefault True to prevent the default click event
13343  * @cfg {Boolean} stopDefault True to stop the default click event
13344  * 
13345  * @history
13346  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13347  *     2007-02-02 jvs Renamed to ClickRepeater
13348  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13349  *
13350  *  @constructor
13351  * @param {String/HTMLElement/Element} el The element to listen on
13352  * @param {Object} config
13353  **/
13354 Roo.util.ClickRepeater = function(el, config)
13355 {
13356     this.el = Roo.get(el);
13357     this.el.unselectable();
13358
13359     Roo.apply(this, config);
13360
13361     this.addEvents({
13362     /**
13363      * @event mousedown
13364      * Fires when the mouse button is depressed.
13365      * @param {Roo.util.ClickRepeater} this
13366      */
13367         "mousedown" : true,
13368     /**
13369      * @event click
13370      * Fires on a specified interval during the time the element is pressed.
13371      * @param {Roo.util.ClickRepeater} this
13372      */
13373         "click" : true,
13374     /**
13375      * @event mouseup
13376      * Fires when the mouse key is released.
13377      * @param {Roo.util.ClickRepeater} this
13378      */
13379         "mouseup" : true
13380     });
13381
13382     this.el.on("mousedown", this.handleMouseDown, this);
13383     if(this.preventDefault || this.stopDefault){
13384         this.el.on("click", function(e){
13385             if(this.preventDefault){
13386                 e.preventDefault();
13387             }
13388             if(this.stopDefault){
13389                 e.stopEvent();
13390             }
13391         }, this);
13392     }
13393
13394     // allow inline handler
13395     if(this.handler){
13396         this.on("click", this.handler,  this.scope || this);
13397     }
13398
13399     Roo.util.ClickRepeater.superclass.constructor.call(this);
13400 };
13401
13402 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13403     interval : 20,
13404     delay: 250,
13405     preventDefault : true,
13406     stopDefault : false,
13407     timer : 0,
13408
13409     // private
13410     handleMouseDown : function(){
13411         clearTimeout(this.timer);
13412         this.el.blur();
13413         if(this.pressClass){
13414             this.el.addClass(this.pressClass);
13415         }
13416         this.mousedownTime = new Date();
13417
13418         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13419         this.el.on("mouseout", this.handleMouseOut, this);
13420
13421         this.fireEvent("mousedown", this);
13422         this.fireEvent("click", this);
13423         
13424         this.timer = this.click.defer(this.delay || this.interval, this);
13425     },
13426
13427     // private
13428     click : function(){
13429         this.fireEvent("click", this);
13430         this.timer = this.click.defer(this.getInterval(), this);
13431     },
13432
13433     // private
13434     getInterval: function(){
13435         if(!this.accelerate){
13436             return this.interval;
13437         }
13438         var pressTime = this.mousedownTime.getElapsed();
13439         if(pressTime < 500){
13440             return 400;
13441         }else if(pressTime < 1700){
13442             return 320;
13443         }else if(pressTime < 2600){
13444             return 250;
13445         }else if(pressTime < 3500){
13446             return 180;
13447         }else if(pressTime < 4400){
13448             return 140;
13449         }else if(pressTime < 5300){
13450             return 80;
13451         }else if(pressTime < 6200){
13452             return 50;
13453         }else{
13454             return 10;
13455         }
13456     },
13457
13458     // private
13459     handleMouseOut : function(){
13460         clearTimeout(this.timer);
13461         if(this.pressClass){
13462             this.el.removeClass(this.pressClass);
13463         }
13464         this.el.on("mouseover", this.handleMouseReturn, this);
13465     },
13466
13467     // private
13468     handleMouseReturn : function(){
13469         this.el.un("mouseover", this.handleMouseReturn);
13470         if(this.pressClass){
13471             this.el.addClass(this.pressClass);
13472         }
13473         this.click();
13474     },
13475
13476     // private
13477     handleMouseUp : function(){
13478         clearTimeout(this.timer);
13479         this.el.un("mouseover", this.handleMouseReturn);
13480         this.el.un("mouseout", this.handleMouseOut);
13481         Roo.get(document).un("mouseup", this.handleMouseUp);
13482         this.el.removeClass(this.pressClass);
13483         this.fireEvent("mouseup", this);
13484     }
13485 });/*
13486  * Based on:
13487  * Ext JS Library 1.1.1
13488  * Copyright(c) 2006-2007, Ext JS, LLC.
13489  *
13490  * Originally Released Under LGPL - original licence link has changed is not relivant.
13491  *
13492  * Fork - LGPL
13493  * <script type="text/javascript">
13494  */
13495
13496  
13497 /**
13498  * @class Roo.KeyNav
13499  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13500  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13501  * way to implement custom navigation schemes for any UI component.</p>
13502  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13503  * pageUp, pageDown, del, home, end.  Usage:</p>
13504  <pre><code>
13505 var nav = new Roo.KeyNav("my-element", {
13506     "left" : function(e){
13507         this.moveLeft(e.ctrlKey);
13508     },
13509     "right" : function(e){
13510         this.moveRight(e.ctrlKey);
13511     },
13512     "enter" : function(e){
13513         this.save();
13514     },
13515     scope : this
13516 });
13517 </code></pre>
13518  * @constructor
13519  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13520  * @param {Object} config The config
13521  */
13522 Roo.KeyNav = function(el, config){
13523     this.el = Roo.get(el);
13524     Roo.apply(this, config);
13525     if(!this.disabled){
13526         this.disabled = true;
13527         this.enable();
13528     }
13529 };
13530
13531 Roo.KeyNav.prototype = {
13532     /**
13533      * @cfg {Boolean} disabled
13534      * True to disable this KeyNav instance (defaults to false)
13535      */
13536     disabled : false,
13537     /**
13538      * @cfg {String} defaultEventAction
13539      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13540      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13541      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13542      */
13543     defaultEventAction: "stopEvent",
13544     /**
13545      * @cfg {Boolean} forceKeyDown
13546      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13547      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13548      * handle keydown instead of keypress.
13549      */
13550     forceKeyDown : false,
13551
13552     // private
13553     prepareEvent : function(e){
13554         var k = e.getKey();
13555         var h = this.keyToHandler[k];
13556         //if(h && this[h]){
13557         //    e.stopPropagation();
13558         //}
13559         if(Roo.isSafari && h && k >= 37 && k <= 40){
13560             e.stopEvent();
13561         }
13562     },
13563
13564     // private
13565     relay : function(e){
13566         var k = e.getKey();
13567         var h = this.keyToHandler[k];
13568         if(h && this[h]){
13569             if(this.doRelay(e, this[h], h) !== true){
13570                 e[this.defaultEventAction]();
13571             }
13572         }
13573     },
13574
13575     // private
13576     doRelay : function(e, h, hname){
13577         return h.call(this.scope || this, e);
13578     },
13579
13580     // possible handlers
13581     enter : false,
13582     left : false,
13583     right : false,
13584     up : false,
13585     down : false,
13586     tab : false,
13587     esc : false,
13588     pageUp : false,
13589     pageDown : false,
13590     del : false,
13591     home : false,
13592     end : false,
13593
13594     // quick lookup hash
13595     keyToHandler : {
13596         37 : "left",
13597         39 : "right",
13598         38 : "up",
13599         40 : "down",
13600         33 : "pageUp",
13601         34 : "pageDown",
13602         46 : "del",
13603         36 : "home",
13604         35 : "end",
13605         13 : "enter",
13606         27 : "esc",
13607         9  : "tab"
13608     },
13609
13610         /**
13611          * Enable this KeyNav
13612          */
13613         enable: function(){
13614                 if(this.disabled){
13615             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13616             // the EventObject will normalize Safari automatically
13617             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13618                 this.el.on("keydown", this.relay,  this);
13619             }else{
13620                 this.el.on("keydown", this.prepareEvent,  this);
13621                 this.el.on("keypress", this.relay,  this);
13622             }
13623                     this.disabled = false;
13624                 }
13625         },
13626
13627         /**
13628          * Disable this KeyNav
13629          */
13630         disable: function(){
13631                 if(!this.disabled){
13632                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13633                 this.el.un("keydown", this.relay);
13634             }else{
13635                 this.el.un("keydown", this.prepareEvent);
13636                 this.el.un("keypress", this.relay);
13637             }
13638                     this.disabled = true;
13639                 }
13640         }
13641 };/*
13642  * Based on:
13643  * Ext JS Library 1.1.1
13644  * Copyright(c) 2006-2007, Ext JS, LLC.
13645  *
13646  * Originally Released Under LGPL - original licence link has changed is not relivant.
13647  *
13648  * Fork - LGPL
13649  * <script type="text/javascript">
13650  */
13651
13652  
13653 /**
13654  * @class Roo.KeyMap
13655  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13656  * The constructor accepts the same config object as defined by {@link #addBinding}.
13657  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13658  * combination it will call the function with this signature (if the match is a multi-key
13659  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13660  * A KeyMap can also handle a string representation of keys.<br />
13661  * Usage:
13662  <pre><code>
13663 // map one key by key code
13664 var map = new Roo.KeyMap("my-element", {
13665     key: 13, // or Roo.EventObject.ENTER
13666     fn: myHandler,
13667     scope: myObject
13668 });
13669
13670 // map multiple keys to one action by string
13671 var map = new Roo.KeyMap("my-element", {
13672     key: "a\r\n\t",
13673     fn: myHandler,
13674     scope: myObject
13675 });
13676
13677 // map multiple keys to multiple actions by strings and array of codes
13678 var map = new Roo.KeyMap("my-element", [
13679     {
13680         key: [10,13],
13681         fn: function(){ alert("Return was pressed"); }
13682     }, {
13683         key: "abc",
13684         fn: function(){ alert('a, b or c was pressed'); }
13685     }, {
13686         key: "\t",
13687         ctrl:true,
13688         shift:true,
13689         fn: function(){ alert('Control + shift + tab was pressed.'); }
13690     }
13691 ]);
13692 </code></pre>
13693  * <b>Note: A KeyMap starts enabled</b>
13694  * @constructor
13695  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13696  * @param {Object} config The config (see {@link #addBinding})
13697  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13698  */
13699 Roo.KeyMap = function(el, config, eventName){
13700     this.el  = Roo.get(el);
13701     this.eventName = eventName || "keydown";
13702     this.bindings = [];
13703     if(config){
13704         this.addBinding(config);
13705     }
13706     this.enable();
13707 };
13708
13709 Roo.KeyMap.prototype = {
13710     /**
13711      * True to stop the event from bubbling and prevent the default browser action if the
13712      * key was handled by the KeyMap (defaults to false)
13713      * @type Boolean
13714      */
13715     stopEvent : false,
13716
13717     /**
13718      * Add a new binding to this KeyMap. The following config object properties are supported:
13719      * <pre>
13720 Property    Type             Description
13721 ----------  ---------------  ----------------------------------------------------------------------
13722 key         String/Array     A single keycode or an array of keycodes to handle
13723 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13724 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13725 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13726 fn          Function         The function to call when KeyMap finds the expected key combination
13727 scope       Object           The scope of the callback function
13728 </pre>
13729      *
13730      * Usage:
13731      * <pre><code>
13732 // Create a KeyMap
13733 var map = new Roo.KeyMap(document, {
13734     key: Roo.EventObject.ENTER,
13735     fn: handleKey,
13736     scope: this
13737 });
13738
13739 //Add a new binding to the existing KeyMap later
13740 map.addBinding({
13741     key: 'abc',
13742     shift: true,
13743     fn: handleKey,
13744     scope: this
13745 });
13746 </code></pre>
13747      * @param {Object/Array} config A single KeyMap config or an array of configs
13748      */
13749         addBinding : function(config){
13750         if(config instanceof Array){
13751             for(var i = 0, len = config.length; i < len; i++){
13752                 this.addBinding(config[i]);
13753             }
13754             return;
13755         }
13756         var keyCode = config.key,
13757             shift = config.shift, 
13758             ctrl = config.ctrl, 
13759             alt = config.alt,
13760             fn = config.fn,
13761             scope = config.scope;
13762         if(typeof keyCode == "string"){
13763             var ks = [];
13764             var keyString = keyCode.toUpperCase();
13765             for(var j = 0, len = keyString.length; j < len; j++){
13766                 ks.push(keyString.charCodeAt(j));
13767             }
13768             keyCode = ks;
13769         }
13770         var keyArray = keyCode instanceof Array;
13771         var handler = function(e){
13772             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13773                 var k = e.getKey();
13774                 if(keyArray){
13775                     for(var i = 0, len = keyCode.length; i < len; i++){
13776                         if(keyCode[i] == k){
13777                           if(this.stopEvent){
13778                               e.stopEvent();
13779                           }
13780                           fn.call(scope || window, k, e);
13781                           return;
13782                         }
13783                     }
13784                 }else{
13785                     if(k == keyCode){
13786                         if(this.stopEvent){
13787                            e.stopEvent();
13788                         }
13789                         fn.call(scope || window, k, e);
13790                     }
13791                 }
13792             }
13793         };
13794         this.bindings.push(handler);  
13795         },
13796
13797     /**
13798      * Shorthand for adding a single key listener
13799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13800      * following options:
13801      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13802      * @param {Function} fn The function to call
13803      * @param {Object} scope (optional) The scope of the function
13804      */
13805     on : function(key, fn, scope){
13806         var keyCode, shift, ctrl, alt;
13807         if(typeof key == "object" && !(key instanceof Array)){
13808             keyCode = key.key;
13809             shift = key.shift;
13810             ctrl = key.ctrl;
13811             alt = key.alt;
13812         }else{
13813             keyCode = key;
13814         }
13815         this.addBinding({
13816             key: keyCode,
13817             shift: shift,
13818             ctrl: ctrl,
13819             alt: alt,
13820             fn: fn,
13821             scope: scope
13822         })
13823     },
13824
13825     // private
13826     handleKeyDown : function(e){
13827             if(this.enabled){ //just in case
13828             var b = this.bindings;
13829             for(var i = 0, len = b.length; i < len; i++){
13830                 b[i].call(this, e);
13831             }
13832             }
13833         },
13834         
13835         /**
13836          * Returns true if this KeyMap is enabled
13837          * @return {Boolean} 
13838          */
13839         isEnabled : function(){
13840             return this.enabled;  
13841         },
13842         
13843         /**
13844          * Enables this KeyMap
13845          */
13846         enable: function(){
13847                 if(!this.enabled){
13848                     this.el.on(this.eventName, this.handleKeyDown, this);
13849                     this.enabled = true;
13850                 }
13851         },
13852
13853         /**
13854          * Disable this KeyMap
13855          */
13856         disable: function(){
13857                 if(this.enabled){
13858                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13859                     this.enabled = false;
13860                 }
13861         }
13862 };/*
13863  * Based on:
13864  * Ext JS Library 1.1.1
13865  * Copyright(c) 2006-2007, Ext JS, LLC.
13866  *
13867  * Originally Released Under LGPL - original licence link has changed is not relivant.
13868  *
13869  * Fork - LGPL
13870  * <script type="text/javascript">
13871  */
13872
13873  
13874 /**
13875  * @class Roo.util.TextMetrics
13876  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13877  * wide, in pixels, a given block of text will be.
13878  * @singleton
13879  */
13880 Roo.util.TextMetrics = function(){
13881     var shared;
13882     return {
13883         /**
13884          * Measures the size of the specified text
13885          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13886          * that can affect the size of the rendered text
13887          * @param {String} text The text to measure
13888          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13889          * in order to accurately measure the text height
13890          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13891          */
13892         measure : function(el, text, fixedWidth){
13893             if(!shared){
13894                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13895             }
13896             shared.bind(el);
13897             shared.setFixedWidth(fixedWidth || 'auto');
13898             return shared.getSize(text);
13899         },
13900
13901         /**
13902          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13903          * the overhead of multiple calls to initialize the style properties on each measurement.
13904          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13905          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13906          * in order to accurately measure the text height
13907          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13908          */
13909         createInstance : function(el, fixedWidth){
13910             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13911         }
13912     };
13913 }();
13914
13915  
13916
13917 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13918     var ml = new Roo.Element(document.createElement('div'));
13919     document.body.appendChild(ml.dom);
13920     ml.position('absolute');
13921     ml.setLeftTop(-1000, -1000);
13922     ml.hide();
13923
13924     if(fixedWidth){
13925         ml.setWidth(fixedWidth);
13926     }
13927      
13928     var instance = {
13929         /**
13930          * Returns the size of the specified text based on the internal element's style and width properties
13931          * @memberOf Roo.util.TextMetrics.Instance#
13932          * @param {String} text The text to measure
13933          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13934          */
13935         getSize : function(text){
13936             ml.update(text);
13937             var s = ml.getSize();
13938             ml.update('');
13939             return s;
13940         },
13941
13942         /**
13943          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13944          * that can affect the size of the rendered text
13945          * @memberOf Roo.util.TextMetrics.Instance#
13946          * @param {String/HTMLElement} el The element, dom node or id
13947          */
13948         bind : function(el){
13949             ml.setStyle(
13950                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13951             );
13952         },
13953
13954         /**
13955          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13956          * to set a fixed width in order to accurately measure the text height.
13957          * @memberOf Roo.util.TextMetrics.Instance#
13958          * @param {Number} width The width to set on the element
13959          */
13960         setFixedWidth : function(width){
13961             ml.setWidth(width);
13962         },
13963
13964         /**
13965          * Returns the measured width of the specified text
13966          * @memberOf Roo.util.TextMetrics.Instance#
13967          * @param {String} text The text to measure
13968          * @return {Number} width The width in pixels
13969          */
13970         getWidth : function(text){
13971             ml.dom.style.width = 'auto';
13972             return this.getSize(text).width;
13973         },
13974
13975         /**
13976          * Returns the measured height of the specified text.  For multiline text, be sure to call
13977          * {@link #setFixedWidth} if necessary.
13978          * @memberOf Roo.util.TextMetrics.Instance#
13979          * @param {String} text The text to measure
13980          * @return {Number} height The height in pixels
13981          */
13982         getHeight : function(text){
13983             return this.getSize(text).height;
13984         }
13985     };
13986
13987     instance.bind(bindTo);
13988
13989     return instance;
13990 };
13991
13992 // backwards compat
13993 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13994  * Based on:
13995  * Ext JS Library 1.1.1
13996  * Copyright(c) 2006-2007, Ext JS, LLC.
13997  *
13998  * Originally Released Under LGPL - original licence link has changed is not relivant.
13999  *
14000  * Fork - LGPL
14001  * <script type="text/javascript">
14002  */
14003
14004 /**
14005  * @class Roo.state.Provider
14006  * Abstract base class for state provider implementations. This class provides methods
14007  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14008  * Provider interface.
14009  */
14010 Roo.state.Provider = function(){
14011     /**
14012      * @event statechange
14013      * Fires when a state change occurs.
14014      * @param {Provider} this This state provider
14015      * @param {String} key The state key which was changed
14016      * @param {String} value The encoded value for the state
14017      */
14018     this.addEvents({
14019         "statechange": true
14020     });
14021     this.state = {};
14022     Roo.state.Provider.superclass.constructor.call(this);
14023 };
14024 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14025     /**
14026      * Returns the current value for a key
14027      * @param {String} name The key name
14028      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14029      * @return {Mixed} The state data
14030      */
14031     get : function(name, defaultValue){
14032         return typeof this.state[name] == "undefined" ?
14033             defaultValue : this.state[name];
14034     },
14035     
14036     /**
14037      * Clears a value from the state
14038      * @param {String} name The key name
14039      */
14040     clear : function(name){
14041         delete this.state[name];
14042         this.fireEvent("statechange", this, name, null);
14043     },
14044     
14045     /**
14046      * Sets the value for a key
14047      * @param {String} name The key name
14048      * @param {Mixed} value The value to set
14049      */
14050     set : function(name, value){
14051         this.state[name] = value;
14052         this.fireEvent("statechange", this, name, value);
14053     },
14054     
14055     /**
14056      * Decodes a string previously encoded with {@link #encodeValue}.
14057      * @param {String} value The value to decode
14058      * @return {Mixed} The decoded value
14059      */
14060     decodeValue : function(cookie){
14061         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14062         var matches = re.exec(unescape(cookie));
14063         if(!matches || !matches[1]) return; // non state cookie
14064         var type = matches[1];
14065         var v = matches[2];
14066         switch(type){
14067             case "n":
14068                 return parseFloat(v);
14069             case "d":
14070                 return new Date(Date.parse(v));
14071             case "b":
14072                 return (v == "1");
14073             case "a":
14074                 var all = [];
14075                 var values = v.split("^");
14076                 for(var i = 0, len = values.length; i < len; i++){
14077                     all.push(this.decodeValue(values[i]));
14078                 }
14079                 return all;
14080            case "o":
14081                 var all = {};
14082                 var values = v.split("^");
14083                 for(var i = 0, len = values.length; i < len; i++){
14084                     var kv = values[i].split("=");
14085                     all[kv[0]] = this.decodeValue(kv[1]);
14086                 }
14087                 return all;
14088            default:
14089                 return v;
14090         }
14091     },
14092     
14093     /**
14094      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14095      * @param {Mixed} value The value to encode
14096      * @return {String} The encoded value
14097      */
14098     encodeValue : function(v){
14099         var enc;
14100         if(typeof v == "number"){
14101             enc = "n:" + v;
14102         }else if(typeof v == "boolean"){
14103             enc = "b:" + (v ? "1" : "0");
14104         }else if(v instanceof Date){
14105             enc = "d:" + v.toGMTString();
14106         }else if(v instanceof Array){
14107             var flat = "";
14108             for(var i = 0, len = v.length; i < len; i++){
14109                 flat += this.encodeValue(v[i]);
14110                 if(i != len-1) flat += "^";
14111             }
14112             enc = "a:" + flat;
14113         }else if(typeof v == "object"){
14114             var flat = "";
14115             for(var key in v){
14116                 if(typeof v[key] != "function"){
14117                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14118                 }
14119             }
14120             enc = "o:" + flat.substring(0, flat.length-1);
14121         }else{
14122             enc = "s:" + v;
14123         }
14124         return escape(enc);        
14125     }
14126 });
14127
14128 /*
14129  * Based on:
14130  * Ext JS Library 1.1.1
14131  * Copyright(c) 2006-2007, Ext JS, LLC.
14132  *
14133  * Originally Released Under LGPL - original licence link has changed is not relivant.
14134  *
14135  * Fork - LGPL
14136  * <script type="text/javascript">
14137  */
14138 /**
14139  * @class Roo.state.Manager
14140  * This is the global state manager. By default all components that are "state aware" check this class
14141  * for state information if you don't pass them a custom state provider. In order for this class
14142  * to be useful, it must be initialized with a provider when your application initializes.
14143  <pre><code>
14144 // in your initialization function
14145 init : function(){
14146    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14147    ...
14148    // supposed you have a {@link Roo.BorderLayout}
14149    var layout = new Roo.BorderLayout(...);
14150    layout.restoreState();
14151    // or a {Roo.BasicDialog}
14152    var dialog = new Roo.BasicDialog(...);
14153    dialog.restoreState();
14154  </code></pre>
14155  * @singleton
14156  */
14157 Roo.state.Manager = function(){
14158     var provider = new Roo.state.Provider();
14159     
14160     return {
14161         /**
14162          * Configures the default state provider for your application
14163          * @param {Provider} stateProvider The state provider to set
14164          */
14165         setProvider : function(stateProvider){
14166             provider = stateProvider;
14167         },
14168         
14169         /**
14170          * Returns the current value for a key
14171          * @param {String} name The key name
14172          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14173          * @return {Mixed} The state data
14174          */
14175         get : function(key, defaultValue){
14176             return provider.get(key, defaultValue);
14177         },
14178         
14179         /**
14180          * Sets the value for a key
14181          * @param {String} name The key name
14182          * @param {Mixed} value The state data
14183          */
14184          set : function(key, value){
14185             provider.set(key, value);
14186         },
14187         
14188         /**
14189          * Clears a value from the state
14190          * @param {String} name The key name
14191          */
14192         clear : function(key){
14193             provider.clear(key);
14194         },
14195         
14196         /**
14197          * Gets the currently configured state provider
14198          * @return {Provider} The state provider
14199          */
14200         getProvider : function(){
14201             return provider;
14202         }
14203     };
14204 }();
14205 /*
14206  * Based on:
14207  * Ext JS Library 1.1.1
14208  * Copyright(c) 2006-2007, Ext JS, LLC.
14209  *
14210  * Originally Released Under LGPL - original licence link has changed is not relivant.
14211  *
14212  * Fork - LGPL
14213  * <script type="text/javascript">
14214  */
14215 /**
14216  * @class Roo.state.CookieProvider
14217  * @extends Roo.state.Provider
14218  * The default Provider implementation which saves state via cookies.
14219  * <br />Usage:
14220  <pre><code>
14221    var cp = new Roo.state.CookieProvider({
14222        path: "/cgi-bin/",
14223        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14224        domain: "roojs.com"
14225    })
14226    Roo.state.Manager.setProvider(cp);
14227  </code></pre>
14228  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14229  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14230  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14231  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14232  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14233  * domain the page is running on including the 'www' like 'www.roojs.com')
14234  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14235  * @constructor
14236  * Create a new CookieProvider
14237  * @param {Object} config The configuration object
14238  */
14239 Roo.state.CookieProvider = function(config){
14240     Roo.state.CookieProvider.superclass.constructor.call(this);
14241     this.path = "/";
14242     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14243     this.domain = null;
14244     this.secure = false;
14245     Roo.apply(this, config);
14246     this.state = this.readCookies();
14247 };
14248
14249 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14250     // private
14251     set : function(name, value){
14252         if(typeof value == "undefined" || value === null){
14253             this.clear(name);
14254             return;
14255         }
14256         this.setCookie(name, value);
14257         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14258     },
14259
14260     // private
14261     clear : function(name){
14262         this.clearCookie(name);
14263         Roo.state.CookieProvider.superclass.clear.call(this, name);
14264     },
14265
14266     // private
14267     readCookies : function(){
14268         var cookies = {};
14269         var c = document.cookie + ";";
14270         var re = /\s?(.*?)=(.*?);/g;
14271         var matches;
14272         while((matches = re.exec(c)) != null){
14273             var name = matches[1];
14274             var value = matches[2];
14275             if(name && name.substring(0,3) == "ys-"){
14276                 cookies[name.substr(3)] = this.decodeValue(value);
14277             }
14278         }
14279         return cookies;
14280     },
14281
14282     // private
14283     setCookie : function(name, value){
14284         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14285            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14286            ((this.path == null) ? "" : ("; path=" + this.path)) +
14287            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14288            ((this.secure == true) ? "; secure" : "");
14289     },
14290
14291     // private
14292     clearCookie : function(name){
14293         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14294            ((this.path == null) ? "" : ("; path=" + this.path)) +
14295            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14296            ((this.secure == true) ? "; secure" : "");
14297     }
14298 });/*
14299  * Based on:
14300  * Ext JS Library 1.1.1
14301  * Copyright(c) 2006-2007, Ext JS, LLC.
14302  *
14303  * Originally Released Under LGPL - original licence link has changed is not relivant.
14304  *
14305  * Fork - LGPL
14306  * <script type="text/javascript">
14307  */
14308
14309
14310
14311 /*
14312  * These classes are derivatives of the similarly named classes in the YUI Library.
14313  * The original license:
14314  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14315  * Code licensed under the BSD License:
14316  * http://developer.yahoo.net/yui/license.txt
14317  */
14318
14319 (function() {
14320
14321 var Event=Roo.EventManager;
14322 var Dom=Roo.lib.Dom;
14323
14324 /**
14325  * @class Roo.dd.DragDrop
14326  * Defines the interface and base operation of items that that can be
14327  * dragged or can be drop targets.  It was designed to be extended, overriding
14328  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14329  * Up to three html elements can be associated with a DragDrop instance:
14330  * <ul>
14331  * <li>linked element: the element that is passed into the constructor.
14332  * This is the element which defines the boundaries for interaction with
14333  * other DragDrop objects.</li>
14334  * <li>handle element(s): The drag operation only occurs if the element that
14335  * was clicked matches a handle element.  By default this is the linked
14336  * element, but there are times that you will want only a portion of the
14337  * linked element to initiate the drag operation, and the setHandleElId()
14338  * method provides a way to define this.</li>
14339  * <li>drag element: this represents the element that would be moved along
14340  * with the cursor during a drag operation.  By default, this is the linked
14341  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14342  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14343  * </li>
14344  * </ul>
14345  * This class should not be instantiated until the onload event to ensure that
14346  * the associated elements are available.
14347  * The following would define a DragDrop obj that would interact with any
14348  * other DragDrop obj in the "group1" group:
14349  * <pre>
14350  *  dd = new Roo.dd.DragDrop("div1", "group1");
14351  * </pre>
14352  * Since none of the event handlers have been implemented, nothing would
14353  * actually happen if you were to run the code above.  Normally you would
14354  * override this class or one of the default implementations, but you can
14355  * also override the methods you want on an instance of the class...
14356  * <pre>
14357  *  dd.onDragDrop = function(e, id) {
14358  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14359  *  }
14360  * </pre>
14361  * @constructor
14362  * @param {String} id of the element that is linked to this instance
14363  * @param {String} sGroup the group of related DragDrop objects
14364  * @param {object} config an object containing configurable attributes
14365  *                Valid properties for DragDrop:
14366  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14367  */
14368 Roo.dd.DragDrop = function(id, sGroup, config) {
14369     if (id) {
14370         this.init(id, sGroup, config);
14371     }
14372 };
14373
14374 Roo.dd.DragDrop.prototype = {
14375
14376     /**
14377      * The id of the element associated with this object.  This is what we
14378      * refer to as the "linked element" because the size and position of
14379      * this element is used to determine when the drag and drop objects have
14380      * interacted.
14381      * @property id
14382      * @type String
14383      */
14384     id: null,
14385
14386     /**
14387      * Configuration attributes passed into the constructor
14388      * @property config
14389      * @type object
14390      */
14391     config: null,
14392
14393     /**
14394      * The id of the element that will be dragged.  By default this is same
14395      * as the linked element , but could be changed to another element. Ex:
14396      * Roo.dd.DDProxy
14397      * @property dragElId
14398      * @type String
14399      * @private
14400      */
14401     dragElId: null,
14402
14403     /**
14404      * the id of the element that initiates the drag operation.  By default
14405      * this is the linked element, but could be changed to be a child of this
14406      * element.  This lets us do things like only starting the drag when the
14407      * header element within the linked html element is clicked.
14408      * @property handleElId
14409      * @type String
14410      * @private
14411      */
14412     handleElId: null,
14413
14414     /**
14415      * An associative array of HTML tags that will be ignored if clicked.
14416      * @property invalidHandleTypes
14417      * @type {string: string}
14418      */
14419     invalidHandleTypes: null,
14420
14421     /**
14422      * An associative array of ids for elements that will be ignored if clicked
14423      * @property invalidHandleIds
14424      * @type {string: string}
14425      */
14426     invalidHandleIds: null,
14427
14428     /**
14429      * An indexted array of css class names for elements that will be ignored
14430      * if clicked.
14431      * @property invalidHandleClasses
14432      * @type string[]
14433      */
14434     invalidHandleClasses: null,
14435
14436     /**
14437      * The linked element's absolute X position at the time the drag was
14438      * started
14439      * @property startPageX
14440      * @type int
14441      * @private
14442      */
14443     startPageX: 0,
14444
14445     /**
14446      * The linked element's absolute X position at the time the drag was
14447      * started
14448      * @property startPageY
14449      * @type int
14450      * @private
14451      */
14452     startPageY: 0,
14453
14454     /**
14455      * The group defines a logical collection of DragDrop objects that are
14456      * related.  Instances only get events when interacting with other
14457      * DragDrop object in the same group.  This lets us define multiple
14458      * groups using a single DragDrop subclass if we want.
14459      * @property groups
14460      * @type {string: string}
14461      */
14462     groups: null,
14463
14464     /**
14465      * Individual drag/drop instances can be locked.  This will prevent
14466      * onmousedown start drag.
14467      * @property locked
14468      * @type boolean
14469      * @private
14470      */
14471     locked: false,
14472
14473     /**
14474      * Lock this instance
14475      * @method lock
14476      */
14477     lock: function() { this.locked = true; },
14478
14479     /**
14480      * Unlock this instace
14481      * @method unlock
14482      */
14483     unlock: function() { this.locked = false; },
14484
14485     /**
14486      * By default, all insances can be a drop target.  This can be disabled by
14487      * setting isTarget to false.
14488      * @method isTarget
14489      * @type boolean
14490      */
14491     isTarget: true,
14492
14493     /**
14494      * The padding configured for this drag and drop object for calculating
14495      * the drop zone intersection with this object.
14496      * @method padding
14497      * @type int[]
14498      */
14499     padding: null,
14500
14501     /**
14502      * Cached reference to the linked element
14503      * @property _domRef
14504      * @private
14505      */
14506     _domRef: null,
14507
14508     /**
14509      * Internal typeof flag
14510      * @property __ygDragDrop
14511      * @private
14512      */
14513     __ygDragDrop: true,
14514
14515     /**
14516      * Set to true when horizontal contraints are applied
14517      * @property constrainX
14518      * @type boolean
14519      * @private
14520      */
14521     constrainX: false,
14522
14523     /**
14524      * Set to true when vertical contraints are applied
14525      * @property constrainY
14526      * @type boolean
14527      * @private
14528      */
14529     constrainY: false,
14530
14531     /**
14532      * The left constraint
14533      * @property minX
14534      * @type int
14535      * @private
14536      */
14537     minX: 0,
14538
14539     /**
14540      * The right constraint
14541      * @property maxX
14542      * @type int
14543      * @private
14544      */
14545     maxX: 0,
14546
14547     /**
14548      * The up constraint
14549      * @property minY
14550      * @type int
14551      * @type int
14552      * @private
14553      */
14554     minY: 0,
14555
14556     /**
14557      * The down constraint
14558      * @property maxY
14559      * @type int
14560      * @private
14561      */
14562     maxY: 0,
14563
14564     /**
14565      * Maintain offsets when we resetconstraints.  Set to true when you want
14566      * the position of the element relative to its parent to stay the same
14567      * when the page changes
14568      *
14569      * @property maintainOffset
14570      * @type boolean
14571      */
14572     maintainOffset: false,
14573
14574     /**
14575      * Array of pixel locations the element will snap to if we specified a
14576      * horizontal graduation/interval.  This array is generated automatically
14577      * when you define a tick interval.
14578      * @property xTicks
14579      * @type int[]
14580      */
14581     xTicks: null,
14582
14583     /**
14584      * Array of pixel locations the element will snap to if we specified a
14585      * vertical graduation/interval.  This array is generated automatically
14586      * when you define a tick interval.
14587      * @property yTicks
14588      * @type int[]
14589      */
14590     yTicks: null,
14591
14592     /**
14593      * By default the drag and drop instance will only respond to the primary
14594      * button click (left button for a right-handed mouse).  Set to true to
14595      * allow drag and drop to start with any mouse click that is propogated
14596      * by the browser
14597      * @property primaryButtonOnly
14598      * @type boolean
14599      */
14600     primaryButtonOnly: true,
14601
14602     /**
14603      * The availabe property is false until the linked dom element is accessible.
14604      * @property available
14605      * @type boolean
14606      */
14607     available: false,
14608
14609     /**
14610      * By default, drags can only be initiated if the mousedown occurs in the
14611      * region the linked element is.  This is done in part to work around a
14612      * bug in some browsers that mis-report the mousedown if the previous
14613      * mouseup happened outside of the window.  This property is set to true
14614      * if outer handles are defined.
14615      *
14616      * @property hasOuterHandles
14617      * @type boolean
14618      * @default false
14619      */
14620     hasOuterHandles: false,
14621
14622     /**
14623      * Code that executes immediately before the startDrag event
14624      * @method b4StartDrag
14625      * @private
14626      */
14627     b4StartDrag: function(x, y) { },
14628
14629     /**
14630      * Abstract method called after a drag/drop object is clicked
14631      * and the drag or mousedown time thresholds have beeen met.
14632      * @method startDrag
14633      * @param {int} X click location
14634      * @param {int} Y click location
14635      */
14636     startDrag: function(x, y) { /* override this */ },
14637
14638     /**
14639      * Code that executes immediately before the onDrag event
14640      * @method b4Drag
14641      * @private
14642      */
14643     b4Drag: function(e) { },
14644
14645     /**
14646      * Abstract method called during the onMouseMove event while dragging an
14647      * object.
14648      * @method onDrag
14649      * @param {Event} e the mousemove event
14650      */
14651     onDrag: function(e) { /* override this */ },
14652
14653     /**
14654      * Abstract method called when this element fist begins hovering over
14655      * another DragDrop obj
14656      * @method onDragEnter
14657      * @param {Event} e the mousemove event
14658      * @param {String|DragDrop[]} id In POINT mode, the element
14659      * id this is hovering over.  In INTERSECT mode, an array of one or more
14660      * dragdrop items being hovered over.
14661      */
14662     onDragEnter: function(e, id) { /* override this */ },
14663
14664     /**
14665      * Code that executes immediately before the onDragOver event
14666      * @method b4DragOver
14667      * @private
14668      */
14669     b4DragOver: function(e) { },
14670
14671     /**
14672      * Abstract method called when this element is hovering over another
14673      * DragDrop obj
14674      * @method onDragOver
14675      * @param {Event} e the mousemove event
14676      * @param {String|DragDrop[]} id In POINT mode, the element
14677      * id this is hovering over.  In INTERSECT mode, an array of dd items
14678      * being hovered over.
14679      */
14680     onDragOver: function(e, id) { /* override this */ },
14681
14682     /**
14683      * Code that executes immediately before the onDragOut event
14684      * @method b4DragOut
14685      * @private
14686      */
14687     b4DragOut: function(e) { },
14688
14689     /**
14690      * Abstract method called when we are no longer hovering over an element
14691      * @method onDragOut
14692      * @param {Event} e the mousemove event
14693      * @param {String|DragDrop[]} id In POINT mode, the element
14694      * id this was hovering over.  In INTERSECT mode, an array of dd items
14695      * that the mouse is no longer over.
14696      */
14697     onDragOut: function(e, id) { /* override this */ },
14698
14699     /**
14700      * Code that executes immediately before the onDragDrop event
14701      * @method b4DragDrop
14702      * @private
14703      */
14704     b4DragDrop: function(e) { },
14705
14706     /**
14707      * Abstract method called when this item is dropped on another DragDrop
14708      * obj
14709      * @method onDragDrop
14710      * @param {Event} e the mouseup event
14711      * @param {String|DragDrop[]} id In POINT mode, the element
14712      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14713      * was dropped on.
14714      */
14715     onDragDrop: function(e, id) { /* override this */ },
14716
14717     /**
14718      * Abstract method called when this item is dropped on an area with no
14719      * drop target
14720      * @method onInvalidDrop
14721      * @param {Event} e the mouseup event
14722      */
14723     onInvalidDrop: function(e) { /* override this */ },
14724
14725     /**
14726      * Code that executes immediately before the endDrag event
14727      * @method b4EndDrag
14728      * @private
14729      */
14730     b4EndDrag: function(e) { },
14731
14732     /**
14733      * Fired when we are done dragging the object
14734      * @method endDrag
14735      * @param {Event} e the mouseup event
14736      */
14737     endDrag: function(e) { /* override this */ },
14738
14739     /**
14740      * Code executed immediately before the onMouseDown event
14741      * @method b4MouseDown
14742      * @param {Event} e the mousedown event
14743      * @private
14744      */
14745     b4MouseDown: function(e) {  },
14746
14747     /**
14748      * Event handler that fires when a drag/drop obj gets a mousedown
14749      * @method onMouseDown
14750      * @param {Event} e the mousedown event
14751      */
14752     onMouseDown: function(e) { /* override this */ },
14753
14754     /**
14755      * Event handler that fires when a drag/drop obj gets a mouseup
14756      * @method onMouseUp
14757      * @param {Event} e the mouseup event
14758      */
14759     onMouseUp: function(e) { /* override this */ },
14760
14761     /**
14762      * Override the onAvailable method to do what is needed after the initial
14763      * position was determined.
14764      * @method onAvailable
14765      */
14766     onAvailable: function () {
14767     },
14768
14769     /*
14770      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14771      * @type Object
14772      */
14773     defaultPadding : {left:0, right:0, top:0, bottom:0},
14774
14775     /*
14776      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14777  *
14778  * Usage:
14779  <pre><code>
14780  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14781                 { dragElId: "existingProxyDiv" });
14782  dd.startDrag = function(){
14783      this.constrainTo("parent-id");
14784  };
14785  </code></pre>
14786  * Or you can initalize it using the {@link Roo.Element} object:
14787  <pre><code>
14788  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14789      startDrag : function(){
14790          this.constrainTo("parent-id");
14791      }
14792  });
14793  </code></pre>
14794      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14795      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14796      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14797      * an object containing the sides to pad. For example: {right:10, bottom:10}
14798      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14799      */
14800     constrainTo : function(constrainTo, pad, inContent){
14801         if(typeof pad == "number"){
14802             pad = {left: pad, right:pad, top:pad, bottom:pad};
14803         }
14804         pad = pad || this.defaultPadding;
14805         var b = Roo.get(this.getEl()).getBox();
14806         var ce = Roo.get(constrainTo);
14807         var s = ce.getScroll();
14808         var c, cd = ce.dom;
14809         if(cd == document.body){
14810             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14811         }else{
14812             xy = ce.getXY();
14813             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14814         }
14815
14816
14817         var topSpace = b.y - c.y;
14818         var leftSpace = b.x - c.x;
14819
14820         this.resetConstraints();
14821         this.setXConstraint(leftSpace - (pad.left||0), // left
14822                 c.width - leftSpace - b.width - (pad.right||0) //right
14823         );
14824         this.setYConstraint(topSpace - (pad.top||0), //top
14825                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14826         );
14827     },
14828
14829     /**
14830      * Returns a reference to the linked element
14831      * @method getEl
14832      * @return {HTMLElement} the html element
14833      */
14834     getEl: function() {
14835         if (!this._domRef) {
14836             this._domRef = Roo.getDom(this.id);
14837         }
14838
14839         return this._domRef;
14840     },
14841
14842     /**
14843      * Returns a reference to the actual element to drag.  By default this is
14844      * the same as the html element, but it can be assigned to another
14845      * element. An example of this can be found in Roo.dd.DDProxy
14846      * @method getDragEl
14847      * @return {HTMLElement} the html element
14848      */
14849     getDragEl: function() {
14850         return Roo.getDom(this.dragElId);
14851     },
14852
14853     /**
14854      * Sets up the DragDrop object.  Must be called in the constructor of any
14855      * Roo.dd.DragDrop subclass
14856      * @method init
14857      * @param id the id of the linked element
14858      * @param {String} sGroup the group of related items
14859      * @param {object} config configuration attributes
14860      */
14861     init: function(id, sGroup, config) {
14862         this.initTarget(id, sGroup, config);
14863         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14864         // Event.on(this.id, "selectstart", Event.preventDefault);
14865     },
14866
14867     /**
14868      * Initializes Targeting functionality only... the object does not
14869      * get a mousedown handler.
14870      * @method initTarget
14871      * @param id the id of the linked element
14872      * @param {String} sGroup the group of related items
14873      * @param {object} config configuration attributes
14874      */
14875     initTarget: function(id, sGroup, config) {
14876
14877         // configuration attributes
14878         this.config = config || {};
14879
14880         // create a local reference to the drag and drop manager
14881         this.DDM = Roo.dd.DDM;
14882         // initialize the groups array
14883         this.groups = {};
14884
14885         // assume that we have an element reference instead of an id if the
14886         // parameter is not a string
14887         if (typeof id !== "string") {
14888             id = Roo.id(id);
14889         }
14890
14891         // set the id
14892         this.id = id;
14893
14894         // add to an interaction group
14895         this.addToGroup((sGroup) ? sGroup : "default");
14896
14897         // We don't want to register this as the handle with the manager
14898         // so we just set the id rather than calling the setter.
14899         this.handleElId = id;
14900
14901         // the linked element is the element that gets dragged by default
14902         this.setDragElId(id);
14903
14904         // by default, clicked anchors will not start drag operations.
14905         this.invalidHandleTypes = { A: "A" };
14906         this.invalidHandleIds = {};
14907         this.invalidHandleClasses = [];
14908
14909         this.applyConfig();
14910
14911         this.handleOnAvailable();
14912     },
14913
14914     /**
14915      * Applies the configuration parameters that were passed into the constructor.
14916      * This is supposed to happen at each level through the inheritance chain.  So
14917      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14918      * DragDrop in order to get all of the parameters that are available in
14919      * each object.
14920      * @method applyConfig
14921      */
14922     applyConfig: function() {
14923
14924         // configurable properties:
14925         //    padding, isTarget, maintainOffset, primaryButtonOnly
14926         this.padding           = this.config.padding || [0, 0, 0, 0];
14927         this.isTarget          = (this.config.isTarget !== false);
14928         this.maintainOffset    = (this.config.maintainOffset);
14929         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14930
14931     },
14932
14933     /**
14934      * Executed when the linked element is available
14935      * @method handleOnAvailable
14936      * @private
14937      */
14938     handleOnAvailable: function() {
14939         this.available = true;
14940         this.resetConstraints();
14941         this.onAvailable();
14942     },
14943
14944      /**
14945      * Configures the padding for the target zone in px.  Effectively expands
14946      * (or reduces) the virtual object size for targeting calculations.
14947      * Supports css-style shorthand; if only one parameter is passed, all sides
14948      * will have that padding, and if only two are passed, the top and bottom
14949      * will have the first param, the left and right the second.
14950      * @method setPadding
14951      * @param {int} iTop    Top pad
14952      * @param {int} iRight  Right pad
14953      * @param {int} iBot    Bot pad
14954      * @param {int} iLeft   Left pad
14955      */
14956     setPadding: function(iTop, iRight, iBot, iLeft) {
14957         // this.padding = [iLeft, iRight, iTop, iBot];
14958         if (!iRight && 0 !== iRight) {
14959             this.padding = [iTop, iTop, iTop, iTop];
14960         } else if (!iBot && 0 !== iBot) {
14961             this.padding = [iTop, iRight, iTop, iRight];
14962         } else {
14963             this.padding = [iTop, iRight, iBot, iLeft];
14964         }
14965     },
14966
14967     /**
14968      * Stores the initial placement of the linked element.
14969      * @method setInitialPosition
14970      * @param {int} diffX   the X offset, default 0
14971      * @param {int} diffY   the Y offset, default 0
14972      */
14973     setInitPosition: function(diffX, diffY) {
14974         var el = this.getEl();
14975
14976         if (!this.DDM.verifyEl(el)) {
14977             return;
14978         }
14979
14980         var dx = diffX || 0;
14981         var dy = diffY || 0;
14982
14983         var p = Dom.getXY( el );
14984
14985         this.initPageX = p[0] - dx;
14986         this.initPageY = p[1] - dy;
14987
14988         this.lastPageX = p[0];
14989         this.lastPageY = p[1];
14990
14991
14992         this.setStartPosition(p);
14993     },
14994
14995     /**
14996      * Sets the start position of the element.  This is set when the obj
14997      * is initialized, the reset when a drag is started.
14998      * @method setStartPosition
14999      * @param pos current position (from previous lookup)
15000      * @private
15001      */
15002     setStartPosition: function(pos) {
15003         var p = pos || Dom.getXY( this.getEl() );
15004         this.deltaSetXY = null;
15005
15006         this.startPageX = p[0];
15007         this.startPageY = p[1];
15008     },
15009
15010     /**
15011      * Add this instance to a group of related drag/drop objects.  All
15012      * instances belong to at least one group, and can belong to as many
15013      * groups as needed.
15014      * @method addToGroup
15015      * @param sGroup {string} the name of the group
15016      */
15017     addToGroup: function(sGroup) {
15018         this.groups[sGroup] = true;
15019         this.DDM.regDragDrop(this, sGroup);
15020     },
15021
15022     /**
15023      * Remove's this instance from the supplied interaction group
15024      * @method removeFromGroup
15025      * @param {string}  sGroup  The group to drop
15026      */
15027     removeFromGroup: function(sGroup) {
15028         if (this.groups[sGroup]) {
15029             delete this.groups[sGroup];
15030         }
15031
15032         this.DDM.removeDDFromGroup(this, sGroup);
15033     },
15034
15035     /**
15036      * Allows you to specify that an element other than the linked element
15037      * will be moved with the cursor during a drag
15038      * @method setDragElId
15039      * @param id {string} the id of the element that will be used to initiate the drag
15040      */
15041     setDragElId: function(id) {
15042         this.dragElId = id;
15043     },
15044
15045     /**
15046      * Allows you to specify a child of the linked element that should be
15047      * used to initiate the drag operation.  An example of this would be if
15048      * you have a content div with text and links.  Clicking anywhere in the
15049      * content area would normally start the drag operation.  Use this method
15050      * to specify that an element inside of the content div is the element
15051      * that starts the drag operation.
15052      * @method setHandleElId
15053      * @param id {string} the id of the element that will be used to
15054      * initiate the drag.
15055      */
15056     setHandleElId: function(id) {
15057         if (typeof id !== "string") {
15058             id = Roo.id(id);
15059         }
15060         this.handleElId = id;
15061         this.DDM.regHandle(this.id, id);
15062     },
15063
15064     /**
15065      * Allows you to set an element outside of the linked element as a drag
15066      * handle
15067      * @method setOuterHandleElId
15068      * @param id the id of the element that will be used to initiate the drag
15069      */
15070     setOuterHandleElId: function(id) {
15071         if (typeof id !== "string") {
15072             id = Roo.id(id);
15073         }
15074         Event.on(id, "mousedown",
15075                 this.handleMouseDown, this);
15076         this.setHandleElId(id);
15077
15078         this.hasOuterHandles = true;
15079     },
15080
15081     /**
15082      * Remove all drag and drop hooks for this element
15083      * @method unreg
15084      */
15085     unreg: function() {
15086         Event.un(this.id, "mousedown",
15087                 this.handleMouseDown);
15088         this._domRef = null;
15089         this.DDM._remove(this);
15090     },
15091
15092     destroy : function(){
15093         this.unreg();
15094     },
15095
15096     /**
15097      * Returns true if this instance is locked, or the drag drop mgr is locked
15098      * (meaning that all drag/drop is disabled on the page.)
15099      * @method isLocked
15100      * @return {boolean} true if this obj or all drag/drop is locked, else
15101      * false
15102      */
15103     isLocked: function() {
15104         return (this.DDM.isLocked() || this.locked);
15105     },
15106
15107     /**
15108      * Fired when this object is clicked
15109      * @method handleMouseDown
15110      * @param {Event} e
15111      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15112      * @private
15113      */
15114     handleMouseDown: function(e, oDD){
15115         if (this.primaryButtonOnly && e.button != 0) {
15116             return;
15117         }
15118
15119         if (this.isLocked()) {
15120             return;
15121         }
15122
15123         this.DDM.refreshCache(this.groups);
15124
15125         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15126         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15127         } else {
15128             if (this.clickValidator(e)) {
15129
15130                 // set the initial element position
15131                 this.setStartPosition();
15132
15133
15134                 this.b4MouseDown(e);
15135                 this.onMouseDown(e);
15136
15137                 this.DDM.handleMouseDown(e, this);
15138
15139                 this.DDM.stopEvent(e);
15140             } else {
15141
15142
15143             }
15144         }
15145     },
15146
15147     clickValidator: function(e) {
15148         var target = e.getTarget();
15149         return ( this.isValidHandleChild(target) &&
15150                     (this.id == this.handleElId ||
15151                         this.DDM.handleWasClicked(target, this.id)) );
15152     },
15153
15154     /**
15155      * Allows you to specify a tag name that should not start a drag operation
15156      * when clicked.  This is designed to facilitate embedding links within a
15157      * drag handle that do something other than start the drag.
15158      * @method addInvalidHandleType
15159      * @param {string} tagName the type of element to exclude
15160      */
15161     addInvalidHandleType: function(tagName) {
15162         var type = tagName.toUpperCase();
15163         this.invalidHandleTypes[type] = type;
15164     },
15165
15166     /**
15167      * Lets you to specify an element id for a child of a drag handle
15168      * that should not initiate a drag
15169      * @method addInvalidHandleId
15170      * @param {string} id the element id of the element you wish to ignore
15171      */
15172     addInvalidHandleId: function(id) {
15173         if (typeof id !== "string") {
15174             id = Roo.id(id);
15175         }
15176         this.invalidHandleIds[id] = id;
15177     },
15178
15179     /**
15180      * Lets you specify a css class of elements that will not initiate a drag
15181      * @method addInvalidHandleClass
15182      * @param {string} cssClass the class of the elements you wish to ignore
15183      */
15184     addInvalidHandleClass: function(cssClass) {
15185         this.invalidHandleClasses.push(cssClass);
15186     },
15187
15188     /**
15189      * Unsets an excluded tag name set by addInvalidHandleType
15190      * @method removeInvalidHandleType
15191      * @param {string} tagName the type of element to unexclude
15192      */
15193     removeInvalidHandleType: function(tagName) {
15194         var type = tagName.toUpperCase();
15195         // this.invalidHandleTypes[type] = null;
15196         delete this.invalidHandleTypes[type];
15197     },
15198
15199     /**
15200      * Unsets an invalid handle id
15201      * @method removeInvalidHandleId
15202      * @param {string} id the id of the element to re-enable
15203      */
15204     removeInvalidHandleId: function(id) {
15205         if (typeof id !== "string") {
15206             id = Roo.id(id);
15207         }
15208         delete this.invalidHandleIds[id];
15209     },
15210
15211     /**
15212      * Unsets an invalid css class
15213      * @method removeInvalidHandleClass
15214      * @param {string} cssClass the class of the element(s) you wish to
15215      * re-enable
15216      */
15217     removeInvalidHandleClass: function(cssClass) {
15218         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15219             if (this.invalidHandleClasses[i] == cssClass) {
15220                 delete this.invalidHandleClasses[i];
15221             }
15222         }
15223     },
15224
15225     /**
15226      * Checks the tag exclusion list to see if this click should be ignored
15227      * @method isValidHandleChild
15228      * @param {HTMLElement} node the HTMLElement to evaluate
15229      * @return {boolean} true if this is a valid tag type, false if not
15230      */
15231     isValidHandleChild: function(node) {
15232
15233         var valid = true;
15234         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15235         var nodeName;
15236         try {
15237             nodeName = node.nodeName.toUpperCase();
15238         } catch(e) {
15239             nodeName = node.nodeName;
15240         }
15241         valid = valid && !this.invalidHandleTypes[nodeName];
15242         valid = valid && !this.invalidHandleIds[node.id];
15243
15244         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15245             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15246         }
15247
15248
15249         return valid;
15250
15251     },
15252
15253     /**
15254      * Create the array of horizontal tick marks if an interval was specified
15255      * in setXConstraint().
15256      * @method setXTicks
15257      * @private
15258      */
15259     setXTicks: function(iStartX, iTickSize) {
15260         this.xTicks = [];
15261         this.xTickSize = iTickSize;
15262
15263         var tickMap = {};
15264
15265         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15266             if (!tickMap[i]) {
15267                 this.xTicks[this.xTicks.length] = i;
15268                 tickMap[i] = true;
15269             }
15270         }
15271
15272         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15273             if (!tickMap[i]) {
15274                 this.xTicks[this.xTicks.length] = i;
15275                 tickMap[i] = true;
15276             }
15277         }
15278
15279         this.xTicks.sort(this.DDM.numericSort) ;
15280     },
15281
15282     /**
15283      * Create the array of vertical tick marks if an interval was specified in
15284      * setYConstraint().
15285      * @method setYTicks
15286      * @private
15287      */
15288     setYTicks: function(iStartY, iTickSize) {
15289         this.yTicks = [];
15290         this.yTickSize = iTickSize;
15291
15292         var tickMap = {};
15293
15294         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15295             if (!tickMap[i]) {
15296                 this.yTicks[this.yTicks.length] = i;
15297                 tickMap[i] = true;
15298             }
15299         }
15300
15301         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15302             if (!tickMap[i]) {
15303                 this.yTicks[this.yTicks.length] = i;
15304                 tickMap[i] = true;
15305             }
15306         }
15307
15308         this.yTicks.sort(this.DDM.numericSort) ;
15309     },
15310
15311     /**
15312      * By default, the element can be dragged any place on the screen.  Use
15313      * this method to limit the horizontal travel of the element.  Pass in
15314      * 0,0 for the parameters if you want to lock the drag to the y axis.
15315      * @method setXConstraint
15316      * @param {int} iLeft the number of pixels the element can move to the left
15317      * @param {int} iRight the number of pixels the element can move to the
15318      * right
15319      * @param {int} iTickSize optional parameter for specifying that the
15320      * element
15321      * should move iTickSize pixels at a time.
15322      */
15323     setXConstraint: function(iLeft, iRight, iTickSize) {
15324         this.leftConstraint = iLeft;
15325         this.rightConstraint = iRight;
15326
15327         this.minX = this.initPageX - iLeft;
15328         this.maxX = this.initPageX + iRight;
15329         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15330
15331         this.constrainX = true;
15332     },
15333
15334     /**
15335      * Clears any constraints applied to this instance.  Also clears ticks
15336      * since they can't exist independent of a constraint at this time.
15337      * @method clearConstraints
15338      */
15339     clearConstraints: function() {
15340         this.constrainX = false;
15341         this.constrainY = false;
15342         this.clearTicks();
15343     },
15344
15345     /**
15346      * Clears any tick interval defined for this instance
15347      * @method clearTicks
15348      */
15349     clearTicks: function() {
15350         this.xTicks = null;
15351         this.yTicks = null;
15352         this.xTickSize = 0;
15353         this.yTickSize = 0;
15354     },
15355
15356     /**
15357      * By default, the element can be dragged any place on the screen.  Set
15358      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15359      * parameters if you want to lock the drag to the x axis.
15360      * @method setYConstraint
15361      * @param {int} iUp the number of pixels the element can move up
15362      * @param {int} iDown the number of pixels the element can move down
15363      * @param {int} iTickSize optional parameter for specifying that the
15364      * element should move iTickSize pixels at a time.
15365      */
15366     setYConstraint: function(iUp, iDown, iTickSize) {
15367         this.topConstraint = iUp;
15368         this.bottomConstraint = iDown;
15369
15370         this.minY = this.initPageY - iUp;
15371         this.maxY = this.initPageY + iDown;
15372         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15373
15374         this.constrainY = true;
15375
15376     },
15377
15378     /**
15379      * resetConstraints must be called if you manually reposition a dd element.
15380      * @method resetConstraints
15381      * @param {boolean} maintainOffset
15382      */
15383     resetConstraints: function() {
15384
15385
15386         // Maintain offsets if necessary
15387         if (this.initPageX || this.initPageX === 0) {
15388             // figure out how much this thing has moved
15389             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15390             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15391
15392             this.setInitPosition(dx, dy);
15393
15394         // This is the first time we have detected the element's position
15395         } else {
15396             this.setInitPosition();
15397         }
15398
15399         if (this.constrainX) {
15400             this.setXConstraint( this.leftConstraint,
15401                                  this.rightConstraint,
15402                                  this.xTickSize        );
15403         }
15404
15405         if (this.constrainY) {
15406             this.setYConstraint( this.topConstraint,
15407                                  this.bottomConstraint,
15408                                  this.yTickSize         );
15409         }
15410     },
15411
15412     /**
15413      * Normally the drag element is moved pixel by pixel, but we can specify
15414      * that it move a number of pixels at a time.  This method resolves the
15415      * location when we have it set up like this.
15416      * @method getTick
15417      * @param {int} val where we want to place the object
15418      * @param {int[]} tickArray sorted array of valid points
15419      * @return {int} the closest tick
15420      * @private
15421      */
15422     getTick: function(val, tickArray) {
15423
15424         if (!tickArray) {
15425             // If tick interval is not defined, it is effectively 1 pixel,
15426             // so we return the value passed to us.
15427             return val;
15428         } else if (tickArray[0] >= val) {
15429             // The value is lower than the first tick, so we return the first
15430             // tick.
15431             return tickArray[0];
15432         } else {
15433             for (var i=0, len=tickArray.length; i<len; ++i) {
15434                 var next = i + 1;
15435                 if (tickArray[next] && tickArray[next] >= val) {
15436                     var diff1 = val - tickArray[i];
15437                     var diff2 = tickArray[next] - val;
15438                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15439                 }
15440             }
15441
15442             // The value is larger than the last tick, so we return the last
15443             // tick.
15444             return tickArray[tickArray.length - 1];
15445         }
15446     },
15447
15448     /**
15449      * toString method
15450      * @method toString
15451      * @return {string} string representation of the dd obj
15452      */
15453     toString: function() {
15454         return ("DragDrop " + this.id);
15455     }
15456
15457 };
15458
15459 })();
15460 /*
15461  * Based on:
15462  * Ext JS Library 1.1.1
15463  * Copyright(c) 2006-2007, Ext JS, LLC.
15464  *
15465  * Originally Released Under LGPL - original licence link has changed is not relivant.
15466  *
15467  * Fork - LGPL
15468  * <script type="text/javascript">
15469  */
15470
15471
15472 /**
15473  * The drag and drop utility provides a framework for building drag and drop
15474  * applications.  In addition to enabling drag and drop for specific elements,
15475  * the drag and drop elements are tracked by the manager class, and the
15476  * interactions between the various elements are tracked during the drag and
15477  * the implementing code is notified about these important moments.
15478  */
15479
15480 // Only load the library once.  Rewriting the manager class would orphan
15481 // existing drag and drop instances.
15482 if (!Roo.dd.DragDropMgr) {
15483
15484 /**
15485  * @class Roo.dd.DragDropMgr
15486  * DragDropMgr is a singleton that tracks the element interaction for
15487  * all DragDrop items in the window.  Generally, you will not call
15488  * this class directly, but it does have helper methods that could
15489  * be useful in your DragDrop implementations.
15490  * @singleton
15491  */
15492 Roo.dd.DragDropMgr = function() {
15493
15494     var Event = Roo.EventManager;
15495
15496     return {
15497
15498         /**
15499          * Two dimensional Array of registered DragDrop objects.  The first
15500          * dimension is the DragDrop item group, the second the DragDrop
15501          * object.
15502          * @property ids
15503          * @type {string: string}
15504          * @private
15505          * @static
15506          */
15507         ids: {},
15508
15509         /**
15510          * Array of element ids defined as drag handles.  Used to determine
15511          * if the element that generated the mousedown event is actually the
15512          * handle and not the html element itself.
15513          * @property handleIds
15514          * @type {string: string}
15515          * @private
15516          * @static
15517          */
15518         handleIds: {},
15519
15520         /**
15521          * the DragDrop object that is currently being dragged
15522          * @property dragCurrent
15523          * @type DragDrop
15524          * @private
15525          * @static
15526          **/
15527         dragCurrent: null,
15528
15529         /**
15530          * the DragDrop object(s) that are being hovered over
15531          * @property dragOvers
15532          * @type Array
15533          * @private
15534          * @static
15535          */
15536         dragOvers: {},
15537
15538         /**
15539          * the X distance between the cursor and the object being dragged
15540          * @property deltaX
15541          * @type int
15542          * @private
15543          * @static
15544          */
15545         deltaX: 0,
15546
15547         /**
15548          * the Y distance between the cursor and the object being dragged
15549          * @property deltaY
15550          * @type int
15551          * @private
15552          * @static
15553          */
15554         deltaY: 0,
15555
15556         /**
15557          * Flag to determine if we should prevent the default behavior of the
15558          * events we define. By default this is true, but this can be set to
15559          * false if you need the default behavior (not recommended)
15560          * @property preventDefault
15561          * @type boolean
15562          * @static
15563          */
15564         preventDefault: true,
15565
15566         /**
15567          * Flag to determine if we should stop the propagation of the events
15568          * we generate. This is true by default but you may want to set it to
15569          * false if the html element contains other features that require the
15570          * mouse click.
15571          * @property stopPropagation
15572          * @type boolean
15573          * @static
15574          */
15575         stopPropagation: true,
15576
15577         /**
15578          * Internal flag that is set to true when drag and drop has been
15579          * intialized
15580          * @property initialized
15581          * @private
15582          * @static
15583          */
15584         initalized: false,
15585
15586         /**
15587          * All drag and drop can be disabled.
15588          * @property locked
15589          * @private
15590          * @static
15591          */
15592         locked: false,
15593
15594         /**
15595          * Called the first time an element is registered.
15596          * @method init
15597          * @private
15598          * @static
15599          */
15600         init: function() {
15601             this.initialized = true;
15602         },
15603
15604         /**
15605          * In point mode, drag and drop interaction is defined by the
15606          * location of the cursor during the drag/drop
15607          * @property POINT
15608          * @type int
15609          * @static
15610          */
15611         POINT: 0,
15612
15613         /**
15614          * In intersect mode, drag and drop interactio nis defined by the
15615          * overlap of two or more drag and drop objects.
15616          * @property INTERSECT
15617          * @type int
15618          * @static
15619          */
15620         INTERSECT: 1,
15621
15622         /**
15623          * The current drag and drop mode.  Default: POINT
15624          * @property mode
15625          * @type int
15626          * @static
15627          */
15628         mode: 0,
15629
15630         /**
15631          * Runs method on all drag and drop objects
15632          * @method _execOnAll
15633          * @private
15634          * @static
15635          */
15636         _execOnAll: function(sMethod, args) {
15637             for (var i in this.ids) {
15638                 for (var j in this.ids[i]) {
15639                     var oDD = this.ids[i][j];
15640                     if (! this.isTypeOfDD(oDD)) {
15641                         continue;
15642                     }
15643                     oDD[sMethod].apply(oDD, args);
15644                 }
15645             }
15646         },
15647
15648         /**
15649          * Drag and drop initialization.  Sets up the global event handlers
15650          * @method _onLoad
15651          * @private
15652          * @static
15653          */
15654         _onLoad: function() {
15655
15656             this.init();
15657
15658
15659             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15660             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15661             Event.on(window,   "unload",    this._onUnload, this, true);
15662             Event.on(window,   "resize",    this._onResize, this, true);
15663             // Event.on(window,   "mouseout",    this._test);
15664
15665         },
15666
15667         /**
15668          * Reset constraints on all drag and drop objs
15669          * @method _onResize
15670          * @private
15671          * @static
15672          */
15673         _onResize: function(e) {
15674             this._execOnAll("resetConstraints", []);
15675         },
15676
15677         /**
15678          * Lock all drag and drop functionality
15679          * @method lock
15680          * @static
15681          */
15682         lock: function() { this.locked = true; },
15683
15684         /**
15685          * Unlock all drag and drop functionality
15686          * @method unlock
15687          * @static
15688          */
15689         unlock: function() { this.locked = false; },
15690
15691         /**
15692          * Is drag and drop locked?
15693          * @method isLocked
15694          * @return {boolean} True if drag and drop is locked, false otherwise.
15695          * @static
15696          */
15697         isLocked: function() { return this.locked; },
15698
15699         /**
15700          * Location cache that is set for all drag drop objects when a drag is
15701          * initiated, cleared when the drag is finished.
15702          * @property locationCache
15703          * @private
15704          * @static
15705          */
15706         locationCache: {},
15707
15708         /**
15709          * Set useCache to false if you want to force object the lookup of each
15710          * drag and drop linked element constantly during a drag.
15711          * @property useCache
15712          * @type boolean
15713          * @static
15714          */
15715         useCache: true,
15716
15717         /**
15718          * The number of pixels that the mouse needs to move after the
15719          * mousedown before the drag is initiated.  Default=3;
15720          * @property clickPixelThresh
15721          * @type int
15722          * @static
15723          */
15724         clickPixelThresh: 3,
15725
15726         /**
15727          * The number of milliseconds after the mousedown event to initiate the
15728          * drag if we don't get a mouseup event. Default=1000
15729          * @property clickTimeThresh
15730          * @type int
15731          * @static
15732          */
15733         clickTimeThresh: 350,
15734
15735         /**
15736          * Flag that indicates that either the drag pixel threshold or the
15737          * mousdown time threshold has been met
15738          * @property dragThreshMet
15739          * @type boolean
15740          * @private
15741          * @static
15742          */
15743         dragThreshMet: false,
15744
15745         /**
15746          * Timeout used for the click time threshold
15747          * @property clickTimeout
15748          * @type Object
15749          * @private
15750          * @static
15751          */
15752         clickTimeout: null,
15753
15754         /**
15755          * The X position of the mousedown event stored for later use when a
15756          * drag threshold is met.
15757          * @property startX
15758          * @type int
15759          * @private
15760          * @static
15761          */
15762         startX: 0,
15763
15764         /**
15765          * The Y position of the mousedown event stored for later use when a
15766          * drag threshold is met.
15767          * @property startY
15768          * @type int
15769          * @private
15770          * @static
15771          */
15772         startY: 0,
15773
15774         /**
15775          * Each DragDrop instance must be registered with the DragDropMgr.
15776          * This is executed in DragDrop.init()
15777          * @method regDragDrop
15778          * @param {DragDrop} oDD the DragDrop object to register
15779          * @param {String} sGroup the name of the group this element belongs to
15780          * @static
15781          */
15782         regDragDrop: function(oDD, sGroup) {
15783             if (!this.initialized) { this.init(); }
15784
15785             if (!this.ids[sGroup]) {
15786                 this.ids[sGroup] = {};
15787             }
15788             this.ids[sGroup][oDD.id] = oDD;
15789         },
15790
15791         /**
15792          * Removes the supplied dd instance from the supplied group. Executed
15793          * by DragDrop.removeFromGroup, so don't call this function directly.
15794          * @method removeDDFromGroup
15795          * @private
15796          * @static
15797          */
15798         removeDDFromGroup: function(oDD, sGroup) {
15799             if (!this.ids[sGroup]) {
15800                 this.ids[sGroup] = {};
15801             }
15802
15803             var obj = this.ids[sGroup];
15804             if (obj && obj[oDD.id]) {
15805                 delete obj[oDD.id];
15806             }
15807         },
15808
15809         /**
15810          * Unregisters a drag and drop item.  This is executed in
15811          * DragDrop.unreg, use that method instead of calling this directly.
15812          * @method _remove
15813          * @private
15814          * @static
15815          */
15816         _remove: function(oDD) {
15817             for (var g in oDD.groups) {
15818                 if (g && this.ids[g][oDD.id]) {
15819                     delete this.ids[g][oDD.id];
15820                 }
15821             }
15822             delete this.handleIds[oDD.id];
15823         },
15824
15825         /**
15826          * Each DragDrop handle element must be registered.  This is done
15827          * automatically when executing DragDrop.setHandleElId()
15828          * @method regHandle
15829          * @param {String} sDDId the DragDrop id this element is a handle for
15830          * @param {String} sHandleId the id of the element that is the drag
15831          * handle
15832          * @static
15833          */
15834         regHandle: function(sDDId, sHandleId) {
15835             if (!this.handleIds[sDDId]) {
15836                 this.handleIds[sDDId] = {};
15837             }
15838             this.handleIds[sDDId][sHandleId] = sHandleId;
15839         },
15840
15841         /**
15842          * Utility function to determine if a given element has been
15843          * registered as a drag drop item.
15844          * @method isDragDrop
15845          * @param {String} id the element id to check
15846          * @return {boolean} true if this element is a DragDrop item,
15847          * false otherwise
15848          * @static
15849          */
15850         isDragDrop: function(id) {
15851             return ( this.getDDById(id) ) ? true : false;
15852         },
15853
15854         /**
15855          * Returns the drag and drop instances that are in all groups the
15856          * passed in instance belongs to.
15857          * @method getRelated
15858          * @param {DragDrop} p_oDD the obj to get related data for
15859          * @param {boolean} bTargetsOnly if true, only return targetable objs
15860          * @return {DragDrop[]} the related instances
15861          * @static
15862          */
15863         getRelated: function(p_oDD, bTargetsOnly) {
15864             var oDDs = [];
15865             for (var i in p_oDD.groups) {
15866                 for (j in this.ids[i]) {
15867                     var dd = this.ids[i][j];
15868                     if (! this.isTypeOfDD(dd)) {
15869                         continue;
15870                     }
15871                     if (!bTargetsOnly || dd.isTarget) {
15872                         oDDs[oDDs.length] = dd;
15873                     }
15874                 }
15875             }
15876
15877             return oDDs;
15878         },
15879
15880         /**
15881          * Returns true if the specified dd target is a legal target for
15882          * the specifice drag obj
15883          * @method isLegalTarget
15884          * @param {DragDrop} the drag obj
15885          * @param {DragDrop} the target
15886          * @return {boolean} true if the target is a legal target for the
15887          * dd obj
15888          * @static
15889          */
15890         isLegalTarget: function (oDD, oTargetDD) {
15891             var targets = this.getRelated(oDD, true);
15892             for (var i=0, len=targets.length;i<len;++i) {
15893                 if (targets[i].id == oTargetDD.id) {
15894                     return true;
15895                 }
15896             }
15897
15898             return false;
15899         },
15900
15901         /**
15902          * My goal is to be able to transparently determine if an object is
15903          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15904          * returns "object", oDD.constructor.toString() always returns
15905          * "DragDrop" and not the name of the subclass.  So for now it just
15906          * evaluates a well-known variable in DragDrop.
15907          * @method isTypeOfDD
15908          * @param {Object} the object to evaluate
15909          * @return {boolean} true if typeof oDD = DragDrop
15910          * @static
15911          */
15912         isTypeOfDD: function (oDD) {
15913             return (oDD && oDD.__ygDragDrop);
15914         },
15915
15916         /**
15917          * Utility function to determine if a given element has been
15918          * registered as a drag drop handle for the given Drag Drop object.
15919          * @method isHandle
15920          * @param {String} id the element id to check
15921          * @return {boolean} true if this element is a DragDrop handle, false
15922          * otherwise
15923          * @static
15924          */
15925         isHandle: function(sDDId, sHandleId) {
15926             return ( this.handleIds[sDDId] &&
15927                             this.handleIds[sDDId][sHandleId] );
15928         },
15929
15930         /**
15931          * Returns the DragDrop instance for a given id
15932          * @method getDDById
15933          * @param {String} id the id of the DragDrop object
15934          * @return {DragDrop} the drag drop object, null if it is not found
15935          * @static
15936          */
15937         getDDById: function(id) {
15938             for (var i in this.ids) {
15939                 if (this.ids[i][id]) {
15940                     return this.ids[i][id];
15941                 }
15942             }
15943             return null;
15944         },
15945
15946         /**
15947          * Fired after a registered DragDrop object gets the mousedown event.
15948          * Sets up the events required to track the object being dragged
15949          * @method handleMouseDown
15950          * @param {Event} e the event
15951          * @param oDD the DragDrop object being dragged
15952          * @private
15953          * @static
15954          */
15955         handleMouseDown: function(e, oDD) {
15956             if(Roo.QuickTips){
15957                 Roo.QuickTips.disable();
15958             }
15959             this.currentTarget = e.getTarget();
15960
15961             this.dragCurrent = oDD;
15962
15963             var el = oDD.getEl();
15964
15965             // track start position
15966             this.startX = e.getPageX();
15967             this.startY = e.getPageY();
15968
15969             this.deltaX = this.startX - el.offsetLeft;
15970             this.deltaY = this.startY - el.offsetTop;
15971
15972             this.dragThreshMet = false;
15973
15974             this.clickTimeout = setTimeout(
15975                     function() {
15976                         var DDM = Roo.dd.DDM;
15977                         DDM.startDrag(DDM.startX, DDM.startY);
15978                     },
15979                     this.clickTimeThresh );
15980         },
15981
15982         /**
15983          * Fired when either the drag pixel threshol or the mousedown hold
15984          * time threshold has been met.
15985          * @method startDrag
15986          * @param x {int} the X position of the original mousedown
15987          * @param y {int} the Y position of the original mousedown
15988          * @static
15989          */
15990         startDrag: function(x, y) {
15991             clearTimeout(this.clickTimeout);
15992             if (this.dragCurrent) {
15993                 this.dragCurrent.b4StartDrag(x, y);
15994                 this.dragCurrent.startDrag(x, y);
15995             }
15996             this.dragThreshMet = true;
15997         },
15998
15999         /**
16000          * Internal function to handle the mouseup event.  Will be invoked
16001          * from the context of the document.
16002          * @method handleMouseUp
16003          * @param {Event} e the event
16004          * @private
16005          * @static
16006          */
16007         handleMouseUp: function(e) {
16008
16009             if(Roo.QuickTips){
16010                 Roo.QuickTips.enable();
16011             }
16012             if (! this.dragCurrent) {
16013                 return;
16014             }
16015
16016             clearTimeout(this.clickTimeout);
16017
16018             if (this.dragThreshMet) {
16019                 this.fireEvents(e, true);
16020             } else {
16021             }
16022
16023             this.stopDrag(e);
16024
16025             this.stopEvent(e);
16026         },
16027
16028         /**
16029          * Utility to stop event propagation and event default, if these
16030          * features are turned on.
16031          * @method stopEvent
16032          * @param {Event} e the event as returned by this.getEvent()
16033          * @static
16034          */
16035         stopEvent: function(e){
16036             if(this.stopPropagation) {
16037                 e.stopPropagation();
16038             }
16039
16040             if (this.preventDefault) {
16041                 e.preventDefault();
16042             }
16043         },
16044
16045         /**
16046          * Internal function to clean up event handlers after the drag
16047          * operation is complete
16048          * @method stopDrag
16049          * @param {Event} e the event
16050          * @private
16051          * @static
16052          */
16053         stopDrag: function(e) {
16054             // Fire the drag end event for the item that was dragged
16055             if (this.dragCurrent) {
16056                 if (this.dragThreshMet) {
16057                     this.dragCurrent.b4EndDrag(e);
16058                     this.dragCurrent.endDrag(e);
16059                 }
16060
16061                 this.dragCurrent.onMouseUp(e);
16062             }
16063
16064             this.dragCurrent = null;
16065             this.dragOvers = {};
16066         },
16067
16068         /**
16069          * Internal function to handle the mousemove event.  Will be invoked
16070          * from the context of the html element.
16071          *
16072          * @TODO figure out what we can do about mouse events lost when the
16073          * user drags objects beyond the window boundary.  Currently we can
16074          * detect this in internet explorer by verifying that the mouse is
16075          * down during the mousemove event.  Firefox doesn't give us the
16076          * button state on the mousemove event.
16077          * @method handleMouseMove
16078          * @param {Event} e the event
16079          * @private
16080          * @static
16081          */
16082         handleMouseMove: function(e) {
16083             if (! this.dragCurrent) {
16084                 return true;
16085             }
16086
16087             // var button = e.which || e.button;
16088
16089             // check for IE mouseup outside of page boundary
16090             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16091                 this.stopEvent(e);
16092                 return this.handleMouseUp(e);
16093             }
16094
16095             if (!this.dragThreshMet) {
16096                 var diffX = Math.abs(this.startX - e.getPageX());
16097                 var diffY = Math.abs(this.startY - e.getPageY());
16098                 if (diffX > this.clickPixelThresh ||
16099                             diffY > this.clickPixelThresh) {
16100                     this.startDrag(this.startX, this.startY);
16101                 }
16102             }
16103
16104             if (this.dragThreshMet) {
16105                 this.dragCurrent.b4Drag(e);
16106                 this.dragCurrent.onDrag(e);
16107                 if(!this.dragCurrent.moveOnly){
16108                     this.fireEvents(e, false);
16109                 }
16110             }
16111
16112             this.stopEvent(e);
16113
16114             return true;
16115         },
16116
16117         /**
16118          * Iterates over all of the DragDrop elements to find ones we are
16119          * hovering over or dropping on
16120          * @method fireEvents
16121          * @param {Event} e the event
16122          * @param {boolean} isDrop is this a drop op or a mouseover op?
16123          * @private
16124          * @static
16125          */
16126         fireEvents: function(e, isDrop) {
16127             var dc = this.dragCurrent;
16128
16129             // If the user did the mouse up outside of the window, we could
16130             // get here even though we have ended the drag.
16131             if (!dc || dc.isLocked()) {
16132                 return;
16133             }
16134
16135             var pt = e.getPoint();
16136
16137             // cache the previous dragOver array
16138             var oldOvers = [];
16139
16140             var outEvts   = [];
16141             var overEvts  = [];
16142             var dropEvts  = [];
16143             var enterEvts = [];
16144
16145             // Check to see if the object(s) we were hovering over is no longer
16146             // being hovered over so we can fire the onDragOut event
16147             for (var i in this.dragOvers) {
16148
16149                 var ddo = this.dragOvers[i];
16150
16151                 if (! this.isTypeOfDD(ddo)) {
16152                     continue;
16153                 }
16154
16155                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16156                     outEvts.push( ddo );
16157                 }
16158
16159                 oldOvers[i] = true;
16160                 delete this.dragOvers[i];
16161             }
16162
16163             for (var sGroup in dc.groups) {
16164
16165                 if ("string" != typeof sGroup) {
16166                     continue;
16167                 }
16168
16169                 for (i in this.ids[sGroup]) {
16170                     var oDD = this.ids[sGroup][i];
16171                     if (! this.isTypeOfDD(oDD)) {
16172                         continue;
16173                     }
16174
16175                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16176                         if (this.isOverTarget(pt, oDD, this.mode)) {
16177                             // look for drop interactions
16178                             if (isDrop) {
16179                                 dropEvts.push( oDD );
16180                             // look for drag enter and drag over interactions
16181                             } else {
16182
16183                                 // initial drag over: dragEnter fires
16184                                 if (!oldOvers[oDD.id]) {
16185                                     enterEvts.push( oDD );
16186                                 // subsequent drag overs: dragOver fires
16187                                 } else {
16188                                     overEvts.push( oDD );
16189                                 }
16190
16191                                 this.dragOvers[oDD.id] = oDD;
16192                             }
16193                         }
16194                     }
16195                 }
16196             }
16197
16198             if (this.mode) {
16199                 if (outEvts.length) {
16200                     dc.b4DragOut(e, outEvts);
16201                     dc.onDragOut(e, outEvts);
16202                 }
16203
16204                 if (enterEvts.length) {
16205                     dc.onDragEnter(e, enterEvts);
16206                 }
16207
16208                 if (overEvts.length) {
16209                     dc.b4DragOver(e, overEvts);
16210                     dc.onDragOver(e, overEvts);
16211                 }
16212
16213                 if (dropEvts.length) {
16214                     dc.b4DragDrop(e, dropEvts);
16215                     dc.onDragDrop(e, dropEvts);
16216                 }
16217
16218             } else {
16219                 // fire dragout events
16220                 var len = 0;
16221                 for (i=0, len=outEvts.length; i<len; ++i) {
16222                     dc.b4DragOut(e, outEvts[i].id);
16223                     dc.onDragOut(e, outEvts[i].id);
16224                 }
16225
16226                 // fire enter events
16227                 for (i=0,len=enterEvts.length; i<len; ++i) {
16228                     // dc.b4DragEnter(e, oDD.id);
16229                     dc.onDragEnter(e, enterEvts[i].id);
16230                 }
16231
16232                 // fire over events
16233                 for (i=0,len=overEvts.length; i<len; ++i) {
16234                     dc.b4DragOver(e, overEvts[i].id);
16235                     dc.onDragOver(e, overEvts[i].id);
16236                 }
16237
16238                 // fire drop events
16239                 for (i=0, len=dropEvts.length; i<len; ++i) {
16240                     dc.b4DragDrop(e, dropEvts[i].id);
16241                     dc.onDragDrop(e, dropEvts[i].id);
16242                 }
16243
16244             }
16245
16246             // notify about a drop that did not find a target
16247             if (isDrop && !dropEvts.length) {
16248                 dc.onInvalidDrop(e);
16249             }
16250
16251         },
16252
16253         /**
16254          * Helper function for getting the best match from the list of drag
16255          * and drop objects returned by the drag and drop events when we are
16256          * in INTERSECT mode.  It returns either the first object that the
16257          * cursor is over, or the object that has the greatest overlap with
16258          * the dragged element.
16259          * @method getBestMatch
16260          * @param  {DragDrop[]} dds The array of drag and drop objects
16261          * targeted
16262          * @return {DragDrop}       The best single match
16263          * @static
16264          */
16265         getBestMatch: function(dds) {
16266             var winner = null;
16267             // Return null if the input is not what we expect
16268             //if (!dds || !dds.length || dds.length == 0) {
16269                // winner = null;
16270             // If there is only one item, it wins
16271             //} else if (dds.length == 1) {
16272
16273             var len = dds.length;
16274
16275             if (len == 1) {
16276                 winner = dds[0];
16277             } else {
16278                 // Loop through the targeted items
16279                 for (var i=0; i<len; ++i) {
16280                     var dd = dds[i];
16281                     // If the cursor is over the object, it wins.  If the
16282                     // cursor is over multiple matches, the first one we come
16283                     // to wins.
16284                     if (dd.cursorIsOver) {
16285                         winner = dd;
16286                         break;
16287                     // Otherwise the object with the most overlap wins
16288                     } else {
16289                         if (!winner ||
16290                             winner.overlap.getArea() < dd.overlap.getArea()) {
16291                             winner = dd;
16292                         }
16293                     }
16294                 }
16295             }
16296
16297             return winner;
16298         },
16299
16300         /**
16301          * Refreshes the cache of the top-left and bottom-right points of the
16302          * drag and drop objects in the specified group(s).  This is in the
16303          * format that is stored in the drag and drop instance, so typical
16304          * usage is:
16305          * <code>
16306          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16307          * </code>
16308          * Alternatively:
16309          * <code>
16310          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16311          * </code>
16312          * @TODO this really should be an indexed array.  Alternatively this
16313          * method could accept both.
16314          * @method refreshCache
16315          * @param {Object} groups an associative array of groups to refresh
16316          * @static
16317          */
16318         refreshCache: function(groups) {
16319             for (var sGroup in groups) {
16320                 if ("string" != typeof sGroup) {
16321                     continue;
16322                 }
16323                 for (var i in this.ids[sGroup]) {
16324                     var oDD = this.ids[sGroup][i];
16325
16326                     if (this.isTypeOfDD(oDD)) {
16327                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16328                         var loc = this.getLocation(oDD);
16329                         if (loc) {
16330                             this.locationCache[oDD.id] = loc;
16331                         } else {
16332                             delete this.locationCache[oDD.id];
16333                             // this will unregister the drag and drop object if
16334                             // the element is not in a usable state
16335                             // oDD.unreg();
16336                         }
16337                     }
16338                 }
16339             }
16340         },
16341
16342         /**
16343          * This checks to make sure an element exists and is in the DOM.  The
16344          * main purpose is to handle cases where innerHTML is used to remove
16345          * drag and drop objects from the DOM.  IE provides an 'unspecified
16346          * error' when trying to access the offsetParent of such an element
16347          * @method verifyEl
16348          * @param {HTMLElement} el the element to check
16349          * @return {boolean} true if the element looks usable
16350          * @static
16351          */
16352         verifyEl: function(el) {
16353             if (el) {
16354                 var parent;
16355                 if(Roo.isIE){
16356                     try{
16357                         parent = el.offsetParent;
16358                     }catch(e){}
16359                 }else{
16360                     parent = el.offsetParent;
16361                 }
16362                 if (parent) {
16363                     return true;
16364                 }
16365             }
16366
16367             return false;
16368         },
16369
16370         /**
16371          * Returns a Region object containing the drag and drop element's position
16372          * and size, including the padding configured for it
16373          * @method getLocation
16374          * @param {DragDrop} oDD the drag and drop object to get the
16375          *                       location for
16376          * @return {Roo.lib.Region} a Region object representing the total area
16377          *                             the element occupies, including any padding
16378          *                             the instance is configured for.
16379          * @static
16380          */
16381         getLocation: function(oDD) {
16382             if (! this.isTypeOfDD(oDD)) {
16383                 return null;
16384             }
16385
16386             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16387
16388             try {
16389                 pos= Roo.lib.Dom.getXY(el);
16390             } catch (e) { }
16391
16392             if (!pos) {
16393                 return null;
16394             }
16395
16396             x1 = pos[0];
16397             x2 = x1 + el.offsetWidth;
16398             y1 = pos[1];
16399             y2 = y1 + el.offsetHeight;
16400
16401             t = y1 - oDD.padding[0];
16402             r = x2 + oDD.padding[1];
16403             b = y2 + oDD.padding[2];
16404             l = x1 - oDD.padding[3];
16405
16406             return new Roo.lib.Region( t, r, b, l );
16407         },
16408
16409         /**
16410          * Checks the cursor location to see if it over the target
16411          * @method isOverTarget
16412          * @param {Roo.lib.Point} pt The point to evaluate
16413          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16414          * @return {boolean} true if the mouse is over the target
16415          * @private
16416          * @static
16417          */
16418         isOverTarget: function(pt, oTarget, intersect) {
16419             // use cache if available
16420             var loc = this.locationCache[oTarget.id];
16421             if (!loc || !this.useCache) {
16422                 loc = this.getLocation(oTarget);
16423                 this.locationCache[oTarget.id] = loc;
16424
16425             }
16426
16427             if (!loc) {
16428                 return false;
16429             }
16430
16431             oTarget.cursorIsOver = loc.contains( pt );
16432
16433             // DragDrop is using this as a sanity check for the initial mousedown
16434             // in this case we are done.  In POINT mode, if the drag obj has no
16435             // contraints, we are also done. Otherwise we need to evaluate the
16436             // location of the target as related to the actual location of the
16437             // dragged element.
16438             var dc = this.dragCurrent;
16439             if (!dc || !dc.getTargetCoord ||
16440                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16441                 return oTarget.cursorIsOver;
16442             }
16443
16444             oTarget.overlap = null;
16445
16446             // Get the current location of the drag element, this is the
16447             // location of the mouse event less the delta that represents
16448             // where the original mousedown happened on the element.  We
16449             // need to consider constraints and ticks as well.
16450             var pos = dc.getTargetCoord(pt.x, pt.y);
16451
16452             var el = dc.getDragEl();
16453             var curRegion = new Roo.lib.Region( pos.y,
16454                                                    pos.x + el.offsetWidth,
16455                                                    pos.y + el.offsetHeight,
16456                                                    pos.x );
16457
16458             var overlap = curRegion.intersect(loc);
16459
16460             if (overlap) {
16461                 oTarget.overlap = overlap;
16462                 return (intersect) ? true : oTarget.cursorIsOver;
16463             } else {
16464                 return false;
16465             }
16466         },
16467
16468         /**
16469          * unload event handler
16470          * @method _onUnload
16471          * @private
16472          * @static
16473          */
16474         _onUnload: function(e, me) {
16475             Roo.dd.DragDropMgr.unregAll();
16476         },
16477
16478         /**
16479          * Cleans up the drag and drop events and objects.
16480          * @method unregAll
16481          * @private
16482          * @static
16483          */
16484         unregAll: function() {
16485
16486             if (this.dragCurrent) {
16487                 this.stopDrag();
16488                 this.dragCurrent = null;
16489             }
16490
16491             this._execOnAll("unreg", []);
16492
16493             for (i in this.elementCache) {
16494                 delete this.elementCache[i];
16495             }
16496
16497             this.elementCache = {};
16498             this.ids = {};
16499         },
16500
16501         /**
16502          * A cache of DOM elements
16503          * @property elementCache
16504          * @private
16505          * @static
16506          */
16507         elementCache: {},
16508
16509         /**
16510          * Get the wrapper for the DOM element specified
16511          * @method getElWrapper
16512          * @param {String} id the id of the element to get
16513          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16514          * @private
16515          * @deprecated This wrapper isn't that useful
16516          * @static
16517          */
16518         getElWrapper: function(id) {
16519             var oWrapper = this.elementCache[id];
16520             if (!oWrapper || !oWrapper.el) {
16521                 oWrapper = this.elementCache[id] =
16522                     new this.ElementWrapper(Roo.getDom(id));
16523             }
16524             return oWrapper;
16525         },
16526
16527         /**
16528          * Returns the actual DOM element
16529          * @method getElement
16530          * @param {String} id the id of the elment to get
16531          * @return {Object} The element
16532          * @deprecated use Roo.getDom instead
16533          * @static
16534          */
16535         getElement: function(id) {
16536             return Roo.getDom(id);
16537         },
16538
16539         /**
16540          * Returns the style property for the DOM element (i.e.,
16541          * document.getElById(id).style)
16542          * @method getCss
16543          * @param {String} id the id of the elment to get
16544          * @return {Object} The style property of the element
16545          * @deprecated use Roo.getDom instead
16546          * @static
16547          */
16548         getCss: function(id) {
16549             var el = Roo.getDom(id);
16550             return (el) ? el.style : null;
16551         },
16552
16553         /**
16554          * Inner class for cached elements
16555          * @class DragDropMgr.ElementWrapper
16556          * @for DragDropMgr
16557          * @private
16558          * @deprecated
16559          */
16560         ElementWrapper: function(el) {
16561                 /**
16562                  * The element
16563                  * @property el
16564                  */
16565                 this.el = el || null;
16566                 /**
16567                  * The element id
16568                  * @property id
16569                  */
16570                 this.id = this.el && el.id;
16571                 /**
16572                  * A reference to the style property
16573                  * @property css
16574                  */
16575                 this.css = this.el && el.style;
16576             },
16577
16578         /**
16579          * Returns the X position of an html element
16580          * @method getPosX
16581          * @param el the element for which to get the position
16582          * @return {int} the X coordinate
16583          * @for DragDropMgr
16584          * @deprecated use Roo.lib.Dom.getX instead
16585          * @static
16586          */
16587         getPosX: function(el) {
16588             return Roo.lib.Dom.getX(el);
16589         },
16590
16591         /**
16592          * Returns the Y position of an html element
16593          * @method getPosY
16594          * @param el the element for which to get the position
16595          * @return {int} the Y coordinate
16596          * @deprecated use Roo.lib.Dom.getY instead
16597          * @static
16598          */
16599         getPosY: function(el) {
16600             return Roo.lib.Dom.getY(el);
16601         },
16602
16603         /**
16604          * Swap two nodes.  In IE, we use the native method, for others we
16605          * emulate the IE behavior
16606          * @method swapNode
16607          * @param n1 the first node to swap
16608          * @param n2 the other node to swap
16609          * @static
16610          */
16611         swapNode: function(n1, n2) {
16612             if (n1.swapNode) {
16613                 n1.swapNode(n2);
16614             } else {
16615                 var p = n2.parentNode;
16616                 var s = n2.nextSibling;
16617
16618                 if (s == n1) {
16619                     p.insertBefore(n1, n2);
16620                 } else if (n2 == n1.nextSibling) {
16621                     p.insertBefore(n2, n1);
16622                 } else {
16623                     n1.parentNode.replaceChild(n2, n1);
16624                     p.insertBefore(n1, s);
16625                 }
16626             }
16627         },
16628
16629         /**
16630          * Returns the current scroll position
16631          * @method getScroll
16632          * @private
16633          * @static
16634          */
16635         getScroll: function () {
16636             var t, l, dde=document.documentElement, db=document.body;
16637             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16638                 t = dde.scrollTop;
16639                 l = dde.scrollLeft;
16640             } else if (db) {
16641                 t = db.scrollTop;
16642                 l = db.scrollLeft;
16643             } else {
16644
16645             }
16646             return { top: t, left: l };
16647         },
16648
16649         /**
16650          * Returns the specified element style property
16651          * @method getStyle
16652          * @param {HTMLElement} el          the element
16653          * @param {string}      styleProp   the style property
16654          * @return {string} The value of the style property
16655          * @deprecated use Roo.lib.Dom.getStyle
16656          * @static
16657          */
16658         getStyle: function(el, styleProp) {
16659             return Roo.fly(el).getStyle(styleProp);
16660         },
16661
16662         /**
16663          * Gets the scrollTop
16664          * @method getScrollTop
16665          * @return {int} the document's scrollTop
16666          * @static
16667          */
16668         getScrollTop: function () { return this.getScroll().top; },
16669
16670         /**
16671          * Gets the scrollLeft
16672          * @method getScrollLeft
16673          * @return {int} the document's scrollTop
16674          * @static
16675          */
16676         getScrollLeft: function () { return this.getScroll().left; },
16677
16678         /**
16679          * Sets the x/y position of an element to the location of the
16680          * target element.
16681          * @method moveToEl
16682          * @param {HTMLElement} moveEl      The element to move
16683          * @param {HTMLElement} targetEl    The position reference element
16684          * @static
16685          */
16686         moveToEl: function (moveEl, targetEl) {
16687             var aCoord = Roo.lib.Dom.getXY(targetEl);
16688             Roo.lib.Dom.setXY(moveEl, aCoord);
16689         },
16690
16691         /**
16692          * Numeric array sort function
16693          * @method numericSort
16694          * @static
16695          */
16696         numericSort: function(a, b) { return (a - b); },
16697
16698         /**
16699          * Internal counter
16700          * @property _timeoutCount
16701          * @private
16702          * @static
16703          */
16704         _timeoutCount: 0,
16705
16706         /**
16707          * Trying to make the load order less important.  Without this we get
16708          * an error if this file is loaded before the Event Utility.
16709          * @method _addListeners
16710          * @private
16711          * @static
16712          */
16713         _addListeners: function() {
16714             var DDM = Roo.dd.DDM;
16715             if ( Roo.lib.Event && document ) {
16716                 DDM._onLoad();
16717             } else {
16718                 if (DDM._timeoutCount > 2000) {
16719                 } else {
16720                     setTimeout(DDM._addListeners, 10);
16721                     if (document && document.body) {
16722                         DDM._timeoutCount += 1;
16723                     }
16724                 }
16725             }
16726         },
16727
16728         /**
16729          * Recursively searches the immediate parent and all child nodes for
16730          * the handle element in order to determine wheter or not it was
16731          * clicked.
16732          * @method handleWasClicked
16733          * @param node the html element to inspect
16734          * @static
16735          */
16736         handleWasClicked: function(node, id) {
16737             if (this.isHandle(id, node.id)) {
16738                 return true;
16739             } else {
16740                 // check to see if this is a text node child of the one we want
16741                 var p = node.parentNode;
16742
16743                 while (p) {
16744                     if (this.isHandle(id, p.id)) {
16745                         return true;
16746                     } else {
16747                         p = p.parentNode;
16748                     }
16749                 }
16750             }
16751
16752             return false;
16753         }
16754
16755     };
16756
16757 }();
16758
16759 // shorter alias, save a few bytes
16760 Roo.dd.DDM = Roo.dd.DragDropMgr;
16761 Roo.dd.DDM._addListeners();
16762
16763 }/*
16764  * Based on:
16765  * Ext JS Library 1.1.1
16766  * Copyright(c) 2006-2007, Ext JS, LLC.
16767  *
16768  * Originally Released Under LGPL - original licence link has changed is not relivant.
16769  *
16770  * Fork - LGPL
16771  * <script type="text/javascript">
16772  */
16773
16774 /**
16775  * @class Roo.dd.DD
16776  * A DragDrop implementation where the linked element follows the
16777  * mouse cursor during a drag.
16778  * @extends Roo.dd.DragDrop
16779  * @constructor
16780  * @param {String} id the id of the linked element
16781  * @param {String} sGroup the group of related DragDrop items
16782  * @param {object} config an object containing configurable attributes
16783  *                Valid properties for DD:
16784  *                    scroll
16785  */
16786 Roo.dd.DD = function(id, sGroup, config) {
16787     if (id) {
16788         this.init(id, sGroup, config);
16789     }
16790 };
16791
16792 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16793
16794     /**
16795      * When set to true, the utility automatically tries to scroll the browser
16796      * window wehn a drag and drop element is dragged near the viewport boundary.
16797      * Defaults to true.
16798      * @property scroll
16799      * @type boolean
16800      */
16801     scroll: true,
16802
16803     /**
16804      * Sets the pointer offset to the distance between the linked element's top
16805      * left corner and the location the element was clicked
16806      * @method autoOffset
16807      * @param {int} iPageX the X coordinate of the click
16808      * @param {int} iPageY the Y coordinate of the click
16809      */
16810     autoOffset: function(iPageX, iPageY) {
16811         var x = iPageX - this.startPageX;
16812         var y = iPageY - this.startPageY;
16813         this.setDelta(x, y);
16814     },
16815
16816     /**
16817      * Sets the pointer offset.  You can call this directly to force the
16818      * offset to be in a particular location (e.g., pass in 0,0 to set it
16819      * to the center of the object)
16820      * @method setDelta
16821      * @param {int} iDeltaX the distance from the left
16822      * @param {int} iDeltaY the distance from the top
16823      */
16824     setDelta: function(iDeltaX, iDeltaY) {
16825         this.deltaX = iDeltaX;
16826         this.deltaY = iDeltaY;
16827     },
16828
16829     /**
16830      * Sets the drag element to the location of the mousedown or click event,
16831      * maintaining the cursor location relative to the location on the element
16832      * that was clicked.  Override this if you want to place the element in a
16833      * location other than where the cursor is.
16834      * @method setDragElPos
16835      * @param {int} iPageX the X coordinate of the mousedown or drag event
16836      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16837      */
16838     setDragElPos: function(iPageX, iPageY) {
16839         // the first time we do this, we are going to check to make sure
16840         // the element has css positioning
16841
16842         var el = this.getDragEl();
16843         this.alignElWithMouse(el, iPageX, iPageY);
16844     },
16845
16846     /**
16847      * Sets the element to the location of the mousedown or click event,
16848      * maintaining the cursor location relative to the location on the element
16849      * that was clicked.  Override this if you want to place the element in a
16850      * location other than where the cursor is.
16851      * @method alignElWithMouse
16852      * @param {HTMLElement} el the element to move
16853      * @param {int} iPageX the X coordinate of the mousedown or drag event
16854      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16855      */
16856     alignElWithMouse: function(el, iPageX, iPageY) {
16857         var oCoord = this.getTargetCoord(iPageX, iPageY);
16858         var fly = el.dom ? el : Roo.fly(el);
16859         if (!this.deltaSetXY) {
16860             var aCoord = [oCoord.x, oCoord.y];
16861             fly.setXY(aCoord);
16862             var newLeft = fly.getLeft(true);
16863             var newTop  = fly.getTop(true);
16864             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16865         } else {
16866             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16867         }
16868
16869         this.cachePosition(oCoord.x, oCoord.y);
16870         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16871         return oCoord;
16872     },
16873
16874     /**
16875      * Saves the most recent position so that we can reset the constraints and
16876      * tick marks on-demand.  We need to know this so that we can calculate the
16877      * number of pixels the element is offset from its original position.
16878      * @method cachePosition
16879      * @param iPageX the current x position (optional, this just makes it so we
16880      * don't have to look it up again)
16881      * @param iPageY the current y position (optional, this just makes it so we
16882      * don't have to look it up again)
16883      */
16884     cachePosition: function(iPageX, iPageY) {
16885         if (iPageX) {
16886             this.lastPageX = iPageX;
16887             this.lastPageY = iPageY;
16888         } else {
16889             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16890             this.lastPageX = aCoord[0];
16891             this.lastPageY = aCoord[1];
16892         }
16893     },
16894
16895     /**
16896      * Auto-scroll the window if the dragged object has been moved beyond the
16897      * visible window boundary.
16898      * @method autoScroll
16899      * @param {int} x the drag element's x position
16900      * @param {int} y the drag element's y position
16901      * @param {int} h the height of the drag element
16902      * @param {int} w the width of the drag element
16903      * @private
16904      */
16905     autoScroll: function(x, y, h, w) {
16906
16907         if (this.scroll) {
16908             // The client height
16909             var clientH = Roo.lib.Dom.getViewWidth();
16910
16911             // The client width
16912             var clientW = Roo.lib.Dom.getViewHeight();
16913
16914             // The amt scrolled down
16915             var st = this.DDM.getScrollTop();
16916
16917             // The amt scrolled right
16918             var sl = this.DDM.getScrollLeft();
16919
16920             // Location of the bottom of the element
16921             var bot = h + y;
16922
16923             // Location of the right of the element
16924             var right = w + x;
16925
16926             // The distance from the cursor to the bottom of the visible area,
16927             // adjusted so that we don't scroll if the cursor is beyond the
16928             // element drag constraints
16929             var toBot = (clientH + st - y - this.deltaY);
16930
16931             // The distance from the cursor to the right of the visible area
16932             var toRight = (clientW + sl - x - this.deltaX);
16933
16934
16935             // How close to the edge the cursor must be before we scroll
16936             // var thresh = (document.all) ? 100 : 40;
16937             var thresh = 40;
16938
16939             // How many pixels to scroll per autoscroll op.  This helps to reduce
16940             // clunky scrolling. IE is more sensitive about this ... it needs this
16941             // value to be higher.
16942             var scrAmt = (document.all) ? 80 : 30;
16943
16944             // Scroll down if we are near the bottom of the visible page and the
16945             // obj extends below the crease
16946             if ( bot > clientH && toBot < thresh ) {
16947                 window.scrollTo(sl, st + scrAmt);
16948             }
16949
16950             // Scroll up if the window is scrolled down and the top of the object
16951             // goes above the top border
16952             if ( y < st && st > 0 && y - st < thresh ) {
16953                 window.scrollTo(sl, st - scrAmt);
16954             }
16955
16956             // Scroll right if the obj is beyond the right border and the cursor is
16957             // near the border.
16958             if ( right > clientW && toRight < thresh ) {
16959                 window.scrollTo(sl + scrAmt, st);
16960             }
16961
16962             // Scroll left if the window has been scrolled to the right and the obj
16963             // extends past the left border
16964             if ( x < sl && sl > 0 && x - sl < thresh ) {
16965                 window.scrollTo(sl - scrAmt, st);
16966             }
16967         }
16968     },
16969
16970     /**
16971      * Finds the location the element should be placed if we want to move
16972      * it to where the mouse location less the click offset would place us.
16973      * @method getTargetCoord
16974      * @param {int} iPageX the X coordinate of the click
16975      * @param {int} iPageY the Y coordinate of the click
16976      * @return an object that contains the coordinates (Object.x and Object.y)
16977      * @private
16978      */
16979     getTargetCoord: function(iPageX, iPageY) {
16980
16981
16982         var x = iPageX - this.deltaX;
16983         var y = iPageY - this.deltaY;
16984
16985         if (this.constrainX) {
16986             if (x < this.minX) { x = this.minX; }
16987             if (x > this.maxX) { x = this.maxX; }
16988         }
16989
16990         if (this.constrainY) {
16991             if (y < this.minY) { y = this.minY; }
16992             if (y > this.maxY) { y = this.maxY; }
16993         }
16994
16995         x = this.getTick(x, this.xTicks);
16996         y = this.getTick(y, this.yTicks);
16997
16998
16999         return {x:x, y:y};
17000     },
17001
17002     /*
17003      * Sets up config options specific to this class. Overrides
17004      * Roo.dd.DragDrop, but all versions of this method through the
17005      * inheritance chain are called
17006      */
17007     applyConfig: function() {
17008         Roo.dd.DD.superclass.applyConfig.call(this);
17009         this.scroll = (this.config.scroll !== false);
17010     },
17011
17012     /*
17013      * Event that fires prior to the onMouseDown event.  Overrides
17014      * Roo.dd.DragDrop.
17015      */
17016     b4MouseDown: function(e) {
17017         // this.resetConstraints();
17018         this.autoOffset(e.getPageX(),
17019                             e.getPageY());
17020     },
17021
17022     /*
17023      * Event that fires prior to the onDrag event.  Overrides
17024      * Roo.dd.DragDrop.
17025      */
17026     b4Drag: function(e) {
17027         this.setDragElPos(e.getPageX(),
17028                             e.getPageY());
17029     },
17030
17031     toString: function() {
17032         return ("DD " + this.id);
17033     }
17034
17035     //////////////////////////////////////////////////////////////////////////
17036     // Debugging ygDragDrop events that can be overridden
17037     //////////////////////////////////////////////////////////////////////////
17038     /*
17039     startDrag: function(x, y) {
17040     },
17041
17042     onDrag: function(e) {
17043     },
17044
17045     onDragEnter: function(e, id) {
17046     },
17047
17048     onDragOver: function(e, id) {
17049     },
17050
17051     onDragOut: function(e, id) {
17052     },
17053
17054     onDragDrop: function(e, id) {
17055     },
17056
17057     endDrag: function(e) {
17058     }
17059
17060     */
17061
17062 });/*
17063  * Based on:
17064  * Ext JS Library 1.1.1
17065  * Copyright(c) 2006-2007, Ext JS, LLC.
17066  *
17067  * Originally Released Under LGPL - original licence link has changed is not relivant.
17068  *
17069  * Fork - LGPL
17070  * <script type="text/javascript">
17071  */
17072
17073 /**
17074  * @class Roo.dd.DDProxy
17075  * A DragDrop implementation that inserts an empty, bordered div into
17076  * the document that follows the cursor during drag operations.  At the time of
17077  * the click, the frame div is resized to the dimensions of the linked html
17078  * element, and moved to the exact location of the linked element.
17079  *
17080  * References to the "frame" element refer to the single proxy element that
17081  * was created to be dragged in place of all DDProxy elements on the
17082  * page.
17083  *
17084  * @extends Roo.dd.DD
17085  * @constructor
17086  * @param {String} id the id of the linked html element
17087  * @param {String} sGroup the group of related DragDrop objects
17088  * @param {object} config an object containing configurable attributes
17089  *                Valid properties for DDProxy in addition to those in DragDrop:
17090  *                   resizeFrame, centerFrame, dragElId
17091  */
17092 Roo.dd.DDProxy = function(id, sGroup, config) {
17093     if (id) {
17094         this.init(id, sGroup, config);
17095         this.initFrame();
17096     }
17097 };
17098
17099 /**
17100  * The default drag frame div id
17101  * @property Roo.dd.DDProxy.dragElId
17102  * @type String
17103  * @static
17104  */
17105 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17106
17107 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17108
17109     /**
17110      * By default we resize the drag frame to be the same size as the element
17111      * we want to drag (this is to get the frame effect).  We can turn it off
17112      * if we want a different behavior.
17113      * @property resizeFrame
17114      * @type boolean
17115      */
17116     resizeFrame: true,
17117
17118     /**
17119      * By default the frame is positioned exactly where the drag element is, so
17120      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17121      * you do not have constraints on the obj is to have the drag frame centered
17122      * around the cursor.  Set centerFrame to true for this effect.
17123      * @property centerFrame
17124      * @type boolean
17125      */
17126     centerFrame: false,
17127
17128     /**
17129      * Creates the proxy element if it does not yet exist
17130      * @method createFrame
17131      */
17132     createFrame: function() {
17133         var self = this;
17134         var body = document.body;
17135
17136         if (!body || !body.firstChild) {
17137             setTimeout( function() { self.createFrame(); }, 50 );
17138             return;
17139         }
17140
17141         var div = this.getDragEl();
17142
17143         if (!div) {
17144             div    = document.createElement("div");
17145             div.id = this.dragElId;
17146             var s  = div.style;
17147
17148             s.position   = "absolute";
17149             s.visibility = "hidden";
17150             s.cursor     = "move";
17151             s.border     = "2px solid #aaa";
17152             s.zIndex     = 999;
17153
17154             // appendChild can blow up IE if invoked prior to the window load event
17155             // while rendering a table.  It is possible there are other scenarios
17156             // that would cause this to happen as well.
17157             body.insertBefore(div, body.firstChild);
17158         }
17159     },
17160
17161     /**
17162      * Initialization for the drag frame element.  Must be called in the
17163      * constructor of all subclasses
17164      * @method initFrame
17165      */
17166     initFrame: function() {
17167         this.createFrame();
17168     },
17169
17170     applyConfig: function() {
17171         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17172
17173         this.resizeFrame = (this.config.resizeFrame !== false);
17174         this.centerFrame = (this.config.centerFrame);
17175         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17176     },
17177
17178     /**
17179      * Resizes the drag frame to the dimensions of the clicked object, positions
17180      * it over the object, and finally displays it
17181      * @method showFrame
17182      * @param {int} iPageX X click position
17183      * @param {int} iPageY Y click position
17184      * @private
17185      */
17186     showFrame: function(iPageX, iPageY) {
17187         var el = this.getEl();
17188         var dragEl = this.getDragEl();
17189         var s = dragEl.style;
17190
17191         this._resizeProxy();
17192
17193         if (this.centerFrame) {
17194             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17195                            Math.round(parseInt(s.height, 10)/2) );
17196         }
17197
17198         this.setDragElPos(iPageX, iPageY);
17199
17200         Roo.fly(dragEl).show();
17201     },
17202
17203     /**
17204      * The proxy is automatically resized to the dimensions of the linked
17205      * element when a drag is initiated, unless resizeFrame is set to false
17206      * @method _resizeProxy
17207      * @private
17208      */
17209     _resizeProxy: function() {
17210         if (this.resizeFrame) {
17211             var el = this.getEl();
17212             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17213         }
17214     },
17215
17216     // overrides Roo.dd.DragDrop
17217     b4MouseDown: function(e) {
17218         var x = e.getPageX();
17219         var y = e.getPageY();
17220         this.autoOffset(x, y);
17221         this.setDragElPos(x, y);
17222     },
17223
17224     // overrides Roo.dd.DragDrop
17225     b4StartDrag: function(x, y) {
17226         // show the drag frame
17227         this.showFrame(x, y);
17228     },
17229
17230     // overrides Roo.dd.DragDrop
17231     b4EndDrag: function(e) {
17232         Roo.fly(this.getDragEl()).hide();
17233     },
17234
17235     // overrides Roo.dd.DragDrop
17236     // By default we try to move the element to the last location of the frame.
17237     // This is so that the default behavior mirrors that of Roo.dd.DD.
17238     endDrag: function(e) {
17239
17240         var lel = this.getEl();
17241         var del = this.getDragEl();
17242
17243         // Show the drag frame briefly so we can get its position
17244         del.style.visibility = "";
17245
17246         this.beforeMove();
17247         // Hide the linked element before the move to get around a Safari
17248         // rendering bug.
17249         lel.style.visibility = "hidden";
17250         Roo.dd.DDM.moveToEl(lel, del);
17251         del.style.visibility = "hidden";
17252         lel.style.visibility = "";
17253
17254         this.afterDrag();
17255     },
17256
17257     beforeMove : function(){
17258
17259     },
17260
17261     afterDrag : function(){
17262
17263     },
17264
17265     toString: function() {
17266         return ("DDProxy " + this.id);
17267     }
17268
17269 });
17270 /*
17271  * Based on:
17272  * Ext JS Library 1.1.1
17273  * Copyright(c) 2006-2007, Ext JS, LLC.
17274  *
17275  * Originally Released Under LGPL - original licence link has changed is not relivant.
17276  *
17277  * Fork - LGPL
17278  * <script type="text/javascript">
17279  */
17280
17281  /**
17282  * @class Roo.dd.DDTarget
17283  * A DragDrop implementation that does not move, but can be a drop
17284  * target.  You would get the same result by simply omitting implementation
17285  * for the event callbacks, but this way we reduce the processing cost of the
17286  * event listener and the callbacks.
17287  * @extends Roo.dd.DragDrop
17288  * @constructor
17289  * @param {String} id the id of the element that is a drop target
17290  * @param {String} sGroup the group of related DragDrop objects
17291  * @param {object} config an object containing configurable attributes
17292  *                 Valid properties for DDTarget in addition to those in
17293  *                 DragDrop:
17294  *                    none
17295  */
17296 Roo.dd.DDTarget = function(id, sGroup, config) {
17297     if (id) {
17298         this.initTarget(id, sGroup, config);
17299     }
17300 };
17301
17302 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17303 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17304     toString: function() {
17305         return ("DDTarget " + this.id);
17306     }
17307 });
17308 /*
17309  * Based on:
17310  * Ext JS Library 1.1.1
17311  * Copyright(c) 2006-2007, Ext JS, LLC.
17312  *
17313  * Originally Released Under LGPL - original licence link has changed is not relivant.
17314  *
17315  * Fork - LGPL
17316  * <script type="text/javascript">
17317  */
17318  
17319
17320 /**
17321  * @class Roo.dd.ScrollManager
17322  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17323  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17324  * @singleton
17325  */
17326 Roo.dd.ScrollManager = function(){
17327     var ddm = Roo.dd.DragDropMgr;
17328     var els = {};
17329     var dragEl = null;
17330     var proc = {};
17331     
17332     var onStop = function(e){
17333         dragEl = null;
17334         clearProc();
17335     };
17336     
17337     var triggerRefresh = function(){
17338         if(ddm.dragCurrent){
17339              ddm.refreshCache(ddm.dragCurrent.groups);
17340         }
17341     };
17342     
17343     var doScroll = function(){
17344         if(ddm.dragCurrent){
17345             var dds = Roo.dd.ScrollManager;
17346             if(!dds.animate){
17347                 if(proc.el.scroll(proc.dir, dds.increment)){
17348                     triggerRefresh();
17349                 }
17350             }else{
17351                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17352             }
17353         }
17354     };
17355     
17356     var clearProc = function(){
17357         if(proc.id){
17358             clearInterval(proc.id);
17359         }
17360         proc.id = 0;
17361         proc.el = null;
17362         proc.dir = "";
17363     };
17364     
17365     var startProc = function(el, dir){
17366         clearProc();
17367         proc.el = el;
17368         proc.dir = dir;
17369         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17370     };
17371     
17372     var onFire = function(e, isDrop){
17373         if(isDrop || !ddm.dragCurrent){ return; }
17374         var dds = Roo.dd.ScrollManager;
17375         if(!dragEl || dragEl != ddm.dragCurrent){
17376             dragEl = ddm.dragCurrent;
17377             // refresh regions on drag start
17378             dds.refreshCache();
17379         }
17380         
17381         var xy = Roo.lib.Event.getXY(e);
17382         var pt = new Roo.lib.Point(xy[0], xy[1]);
17383         for(var id in els){
17384             var el = els[id], r = el._region;
17385             if(r && r.contains(pt) && el.isScrollable()){
17386                 if(r.bottom - pt.y <= dds.thresh){
17387                     if(proc.el != el){
17388                         startProc(el, "down");
17389                     }
17390                     return;
17391                 }else if(r.right - pt.x <= dds.thresh){
17392                     if(proc.el != el){
17393                         startProc(el, "left");
17394                     }
17395                     return;
17396                 }else if(pt.y - r.top <= dds.thresh){
17397                     if(proc.el != el){
17398                         startProc(el, "up");
17399                     }
17400                     return;
17401                 }else if(pt.x - r.left <= dds.thresh){
17402                     if(proc.el != el){
17403                         startProc(el, "right");
17404                     }
17405                     return;
17406                 }
17407             }
17408         }
17409         clearProc();
17410     };
17411     
17412     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17413     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17414     
17415     return {
17416         /**
17417          * Registers new overflow element(s) to auto scroll
17418          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17419          */
17420         register : function(el){
17421             if(el instanceof Array){
17422                 for(var i = 0, len = el.length; i < len; i++) {
17423                         this.register(el[i]);
17424                 }
17425             }else{
17426                 el = Roo.get(el);
17427                 els[el.id] = el;
17428             }
17429         },
17430         
17431         /**
17432          * Unregisters overflow element(s) so they are no longer scrolled
17433          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17434          */
17435         unregister : function(el){
17436             if(el instanceof Array){
17437                 for(var i = 0, len = el.length; i < len; i++) {
17438                         this.unregister(el[i]);
17439                 }
17440             }else{
17441                 el = Roo.get(el);
17442                 delete els[el.id];
17443             }
17444         },
17445         
17446         /**
17447          * The number of pixels from the edge of a container the pointer needs to be to 
17448          * trigger scrolling (defaults to 25)
17449          * @type Number
17450          */
17451         thresh : 25,
17452         
17453         /**
17454          * The number of pixels to scroll in each scroll increment (defaults to 50)
17455          * @type Number
17456          */
17457         increment : 100,
17458         
17459         /**
17460          * The frequency of scrolls in milliseconds (defaults to 500)
17461          * @type Number
17462          */
17463         frequency : 500,
17464         
17465         /**
17466          * True to animate the scroll (defaults to true)
17467          * @type Boolean
17468          */
17469         animate: true,
17470         
17471         /**
17472          * The animation duration in seconds - 
17473          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17474          * @type Number
17475          */
17476         animDuration: .4,
17477         
17478         /**
17479          * Manually trigger a cache refresh.
17480          */
17481         refreshCache : function(){
17482             for(var id in els){
17483                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17484                     els[id]._region = els[id].getRegion();
17485                 }
17486             }
17487         }
17488     };
17489 }();/*
17490  * Based on:
17491  * Ext JS Library 1.1.1
17492  * Copyright(c) 2006-2007, Ext JS, LLC.
17493  *
17494  * Originally Released Under LGPL - original licence link has changed is not relivant.
17495  *
17496  * Fork - LGPL
17497  * <script type="text/javascript">
17498  */
17499  
17500
17501 /**
17502  * @class Roo.dd.Registry
17503  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17504  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17505  * @singleton
17506  */
17507 Roo.dd.Registry = function(){
17508     var elements = {}; 
17509     var handles = {}; 
17510     var autoIdSeed = 0;
17511
17512     var getId = function(el, autogen){
17513         if(typeof el == "string"){
17514             return el;
17515         }
17516         var id = el.id;
17517         if(!id && autogen !== false){
17518             id = "roodd-" + (++autoIdSeed);
17519             el.id = id;
17520         }
17521         return id;
17522     };
17523     
17524     return {
17525     /**
17526      * Register a drag drop element
17527      * @param {String|HTMLElement} element The id or DOM node to register
17528      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17529      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17530      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17531      * populated in the data object (if applicable):
17532      * <pre>
17533 Value      Description<br />
17534 ---------  ------------------------------------------<br />
17535 handles    Array of DOM nodes that trigger dragging<br />
17536            for the element being registered<br />
17537 isHandle   True if the element passed in triggers<br />
17538            dragging itself, else false
17539 </pre>
17540      */
17541         register : function(el, data){
17542             data = data || {};
17543             if(typeof el == "string"){
17544                 el = document.getElementById(el);
17545             }
17546             data.ddel = el;
17547             elements[getId(el)] = data;
17548             if(data.isHandle !== false){
17549                 handles[data.ddel.id] = data;
17550             }
17551             if(data.handles){
17552                 var hs = data.handles;
17553                 for(var i = 0, len = hs.length; i < len; i++){
17554                         handles[getId(hs[i])] = data;
17555                 }
17556             }
17557         },
17558
17559     /**
17560      * Unregister a drag drop element
17561      * @param {String|HTMLElement}  element The id or DOM node to unregister
17562      */
17563         unregister : function(el){
17564             var id = getId(el, false);
17565             var data = elements[id];
17566             if(data){
17567                 delete elements[id];
17568                 if(data.handles){
17569                     var hs = data.handles;
17570                     for(var i = 0, len = hs.length; i < len; i++){
17571                         delete handles[getId(hs[i], false)];
17572                     }
17573                 }
17574             }
17575         },
17576
17577     /**
17578      * Returns the handle registered for a DOM Node by id
17579      * @param {String|HTMLElement} id The DOM node or id to look up
17580      * @return {Object} handle The custom handle data
17581      */
17582         getHandle : function(id){
17583             if(typeof id != "string"){ // must be element?
17584                 id = id.id;
17585             }
17586             return handles[id];
17587         },
17588
17589     /**
17590      * Returns the handle that is registered for the DOM node that is the target of the event
17591      * @param {Event} e The event
17592      * @return {Object} handle The custom handle data
17593      */
17594         getHandleFromEvent : function(e){
17595             var t = Roo.lib.Event.getTarget(e);
17596             return t ? handles[t.id] : null;
17597         },
17598
17599     /**
17600      * Returns a custom data object that is registered for a DOM node by id
17601      * @param {String|HTMLElement} id The DOM node or id to look up
17602      * @return {Object} data The custom data
17603      */
17604         getTarget : function(id){
17605             if(typeof id != "string"){ // must be element?
17606                 id = id.id;
17607             }
17608             return elements[id];
17609         },
17610
17611     /**
17612      * Returns a custom data object that is registered for the DOM node that is the target of the event
17613      * @param {Event} e The event
17614      * @return {Object} data The custom data
17615      */
17616         getTargetFromEvent : function(e){
17617             var t = Roo.lib.Event.getTarget(e);
17618             return t ? elements[t.id] || handles[t.id] : null;
17619         }
17620     };
17621 }();/*
17622  * Based on:
17623  * Ext JS Library 1.1.1
17624  * Copyright(c) 2006-2007, Ext JS, LLC.
17625  *
17626  * Originally Released Under LGPL - original licence link has changed is not relivant.
17627  *
17628  * Fork - LGPL
17629  * <script type="text/javascript">
17630  */
17631  
17632
17633 /**
17634  * @class Roo.dd.StatusProxy
17635  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17636  * default drag proxy used by all Roo.dd components.
17637  * @constructor
17638  * @param {Object} config
17639  */
17640 Roo.dd.StatusProxy = function(config){
17641     Roo.apply(this, config);
17642     this.id = this.id || Roo.id();
17643     this.el = new Roo.Layer({
17644         dh: {
17645             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17646                 {tag: "div", cls: "x-dd-drop-icon"},
17647                 {tag: "div", cls: "x-dd-drag-ghost"}
17648             ]
17649         }, 
17650         shadow: !config || config.shadow !== false
17651     });
17652     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17653     this.dropStatus = this.dropNotAllowed;
17654 };
17655
17656 Roo.dd.StatusProxy.prototype = {
17657     /**
17658      * @cfg {String} dropAllowed
17659      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17660      */
17661     dropAllowed : "x-dd-drop-ok",
17662     /**
17663      * @cfg {String} dropNotAllowed
17664      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17665      */
17666     dropNotAllowed : "x-dd-drop-nodrop",
17667
17668     /**
17669      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17670      * over the current target element.
17671      * @param {String} cssClass The css class for the new drop status indicator image
17672      */
17673     setStatus : function(cssClass){
17674         cssClass = cssClass || this.dropNotAllowed;
17675         if(this.dropStatus != cssClass){
17676             this.el.replaceClass(this.dropStatus, cssClass);
17677             this.dropStatus = cssClass;
17678         }
17679     },
17680
17681     /**
17682      * Resets the status indicator to the default dropNotAllowed value
17683      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17684      */
17685     reset : function(clearGhost){
17686         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17687         this.dropStatus = this.dropNotAllowed;
17688         if(clearGhost){
17689             this.ghost.update("");
17690         }
17691     },
17692
17693     /**
17694      * Updates the contents of the ghost element
17695      * @param {String} html The html that will replace the current innerHTML of the ghost element
17696      */
17697     update : function(html){
17698         if(typeof html == "string"){
17699             this.ghost.update(html);
17700         }else{
17701             this.ghost.update("");
17702             html.style.margin = "0";
17703             this.ghost.dom.appendChild(html);
17704         }
17705         // ensure float = none set?? cant remember why though.
17706         var el = this.ghost.dom.firstChild;
17707                 if(el){
17708                         Roo.fly(el).setStyle('float', 'none');
17709                 }
17710     },
17711     
17712     /**
17713      * Returns the underlying proxy {@link Roo.Layer}
17714      * @return {Roo.Layer} el
17715     */
17716     getEl : function(){
17717         return this.el;
17718     },
17719
17720     /**
17721      * Returns the ghost element
17722      * @return {Roo.Element} el
17723      */
17724     getGhost : function(){
17725         return this.ghost;
17726     },
17727
17728     /**
17729      * Hides the proxy
17730      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17731      */
17732     hide : function(clear){
17733         this.el.hide();
17734         if(clear){
17735             this.reset(true);
17736         }
17737     },
17738
17739     /**
17740      * Stops the repair animation if it's currently running
17741      */
17742     stop : function(){
17743         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17744             this.anim.stop();
17745         }
17746     },
17747
17748     /**
17749      * Displays this proxy
17750      */
17751     show : function(){
17752         this.el.show();
17753     },
17754
17755     /**
17756      * Force the Layer to sync its shadow and shim positions to the element
17757      */
17758     sync : function(){
17759         this.el.sync();
17760     },
17761
17762     /**
17763      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17764      * invalid drop operation by the item being dragged.
17765      * @param {Array} xy The XY position of the element ([x, y])
17766      * @param {Function} callback The function to call after the repair is complete
17767      * @param {Object} scope The scope in which to execute the callback
17768      */
17769     repair : function(xy, callback, scope){
17770         this.callback = callback;
17771         this.scope = scope;
17772         if(xy && this.animRepair !== false){
17773             this.el.addClass("x-dd-drag-repair");
17774             this.el.hideUnders(true);
17775             this.anim = this.el.shift({
17776                 duration: this.repairDuration || .5,
17777                 easing: 'easeOut',
17778                 xy: xy,
17779                 stopFx: true,
17780                 callback: this.afterRepair,
17781                 scope: this
17782             });
17783         }else{
17784             this.afterRepair();
17785         }
17786     },
17787
17788     // private
17789     afterRepair : function(){
17790         this.hide(true);
17791         if(typeof this.callback == "function"){
17792             this.callback.call(this.scope || this);
17793         }
17794         this.callback = null;
17795         this.scope = null;
17796     }
17797 };/*
17798  * Based on:
17799  * Ext JS Library 1.1.1
17800  * Copyright(c) 2006-2007, Ext JS, LLC.
17801  *
17802  * Originally Released Under LGPL - original licence link has changed is not relivant.
17803  *
17804  * Fork - LGPL
17805  * <script type="text/javascript">
17806  */
17807
17808 /**
17809  * @class Roo.dd.DragSource
17810  * @extends Roo.dd.DDProxy
17811  * A simple class that provides the basic implementation needed to make any element draggable.
17812  * @constructor
17813  * @param {String/HTMLElement/Element} el The container element
17814  * @param {Object} config
17815  */
17816 Roo.dd.DragSource = function(el, config){
17817     this.el = Roo.get(el);
17818     this.dragData = {};
17819     
17820     Roo.apply(this, config);
17821     
17822     if(!this.proxy){
17823         this.proxy = new Roo.dd.StatusProxy();
17824     }
17825
17826     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17827           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17828     
17829     this.dragging = false;
17830 };
17831
17832 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17833     /**
17834      * @cfg {String} dropAllowed
17835      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17836      */
17837     dropAllowed : "x-dd-drop-ok",
17838     /**
17839      * @cfg {String} dropNotAllowed
17840      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17841      */
17842     dropNotAllowed : "x-dd-drop-nodrop",
17843
17844     /**
17845      * Returns the data object associated with this drag source
17846      * @return {Object} data An object containing arbitrary data
17847      */
17848     getDragData : function(e){
17849         return this.dragData;
17850     },
17851
17852     // private
17853     onDragEnter : function(e, id){
17854         var target = Roo.dd.DragDropMgr.getDDById(id);
17855         this.cachedTarget = target;
17856         if(this.beforeDragEnter(target, e, id) !== false){
17857             if(target.isNotifyTarget){
17858                 var status = target.notifyEnter(this, e, this.dragData);
17859                 this.proxy.setStatus(status);
17860             }else{
17861                 this.proxy.setStatus(this.dropAllowed);
17862             }
17863             
17864             if(this.afterDragEnter){
17865                 /**
17866                  * An empty function by default, but provided so that you can perform a custom action
17867                  * when the dragged item enters the drop target by providing an implementation.
17868                  * @param {Roo.dd.DragDrop} target The drop target
17869                  * @param {Event} e The event object
17870                  * @param {String} id The id of the dragged element
17871                  * @method afterDragEnter
17872                  */
17873                 this.afterDragEnter(target, e, id);
17874             }
17875         }
17876     },
17877
17878     /**
17879      * An empty function by default, but provided so that you can perform a custom action
17880      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17881      * @param {Roo.dd.DragDrop} target The drop target
17882      * @param {Event} e The event object
17883      * @param {String} id The id of the dragged element
17884      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17885      */
17886     beforeDragEnter : function(target, e, id){
17887         return true;
17888     },
17889
17890     // private
17891     alignElWithMouse: function() {
17892         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17893         this.proxy.sync();
17894     },
17895
17896     // private
17897     onDragOver : function(e, id){
17898         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17899         if(this.beforeDragOver(target, e, id) !== false){
17900             if(target.isNotifyTarget){
17901                 var status = target.notifyOver(this, e, this.dragData);
17902                 this.proxy.setStatus(status);
17903             }
17904
17905             if(this.afterDragOver){
17906                 /**
17907                  * An empty function by default, but provided so that you can perform a custom action
17908                  * while the dragged item is over the drop target by providing an implementation.
17909                  * @param {Roo.dd.DragDrop} target The drop target
17910                  * @param {Event} e The event object
17911                  * @param {String} id The id of the dragged element
17912                  * @method afterDragOver
17913                  */
17914                 this.afterDragOver(target, e, id);
17915             }
17916         }
17917     },
17918
17919     /**
17920      * An empty function by default, but provided so that you can perform a custom action
17921      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17922      * @param {Roo.dd.DragDrop} target The drop target
17923      * @param {Event} e The event object
17924      * @param {String} id The id of the dragged element
17925      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17926      */
17927     beforeDragOver : function(target, e, id){
17928         return true;
17929     },
17930
17931     // private
17932     onDragOut : function(e, id){
17933         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17934         if(this.beforeDragOut(target, e, id) !== false){
17935             if(target.isNotifyTarget){
17936                 target.notifyOut(this, e, this.dragData);
17937             }
17938             this.proxy.reset();
17939             if(this.afterDragOut){
17940                 /**
17941                  * An empty function by default, but provided so that you can perform a custom action
17942                  * after the dragged item is dragged out of the target without dropping.
17943                  * @param {Roo.dd.DragDrop} target The drop target
17944                  * @param {Event} e The event object
17945                  * @param {String} id The id of the dragged element
17946                  * @method afterDragOut
17947                  */
17948                 this.afterDragOut(target, e, id);
17949             }
17950         }
17951         this.cachedTarget = null;
17952     },
17953
17954     /**
17955      * An empty function by default, but provided so that you can perform a custom action before the dragged
17956      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17957      * @param {Roo.dd.DragDrop} target The drop target
17958      * @param {Event} e The event object
17959      * @param {String} id The id of the dragged element
17960      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17961      */
17962     beforeDragOut : function(target, e, id){
17963         return true;
17964     },
17965     
17966     // private
17967     onDragDrop : function(e, id){
17968         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17969         if(this.beforeDragDrop(target, e, id) !== false){
17970             if(target.isNotifyTarget){
17971                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17972                     this.onValidDrop(target, e, id);
17973                 }else{
17974                     this.onInvalidDrop(target, e, id);
17975                 }
17976             }else{
17977                 this.onValidDrop(target, e, id);
17978             }
17979             
17980             if(this.afterDragDrop){
17981                 /**
17982                  * An empty function by default, but provided so that you can perform a custom action
17983                  * after a valid drag drop has occurred by providing an implementation.
17984                  * @param {Roo.dd.DragDrop} target The drop target
17985                  * @param {Event} e The event object
17986                  * @param {String} id The id of the dropped element
17987                  * @method afterDragDrop
17988                  */
17989                 this.afterDragDrop(target, e, id);
17990             }
17991         }
17992         delete this.cachedTarget;
17993     },
17994
17995     /**
17996      * An empty function by default, but provided so that you can perform a custom action before the dragged
17997      * item is dropped onto the target and optionally cancel the onDragDrop.
17998      * @param {Roo.dd.DragDrop} target The drop target
17999      * @param {Event} e The event object
18000      * @param {String} id The id of the dragged element
18001      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18002      */
18003     beforeDragDrop : function(target, e, id){
18004         return true;
18005     },
18006
18007     // private
18008     onValidDrop : function(target, e, id){
18009         this.hideProxy();
18010         if(this.afterValidDrop){
18011             /**
18012              * An empty function by default, but provided so that you can perform a custom action
18013              * after a valid drop has occurred by providing an implementation.
18014              * @param {Object} target The target DD 
18015              * @param {Event} e The event object
18016              * @param {String} id The id of the dropped element
18017              * @method afterInvalidDrop
18018              */
18019             this.afterValidDrop(target, e, id);
18020         }
18021     },
18022
18023     // private
18024     getRepairXY : function(e, data){
18025         return this.el.getXY();  
18026     },
18027
18028     // private
18029     onInvalidDrop : function(target, e, id){
18030         this.beforeInvalidDrop(target, e, id);
18031         if(this.cachedTarget){
18032             if(this.cachedTarget.isNotifyTarget){
18033                 this.cachedTarget.notifyOut(this, e, this.dragData);
18034             }
18035             this.cacheTarget = null;
18036         }
18037         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18038
18039         if(this.afterInvalidDrop){
18040             /**
18041              * An empty function by default, but provided so that you can perform a custom action
18042              * after an invalid drop has occurred by providing an implementation.
18043              * @param {Event} e The event object
18044              * @param {String} id The id of the dropped element
18045              * @method afterInvalidDrop
18046              */
18047             this.afterInvalidDrop(e, id);
18048         }
18049     },
18050
18051     // private
18052     afterRepair : function(){
18053         if(Roo.enableFx){
18054             this.el.highlight(this.hlColor || "c3daf9");
18055         }
18056         this.dragging = false;
18057     },
18058
18059     /**
18060      * An empty function by default, but provided so that you can perform a custom action after an invalid
18061      * drop has occurred.
18062      * @param {Roo.dd.DragDrop} target The drop target
18063      * @param {Event} e The event object
18064      * @param {String} id The id of the dragged element
18065      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18066      */
18067     beforeInvalidDrop : function(target, e, id){
18068         return true;
18069     },
18070
18071     // private
18072     handleMouseDown : function(e){
18073         if(this.dragging) {
18074             return;
18075         }
18076         var data = this.getDragData(e);
18077         if(data && this.onBeforeDrag(data, e) !== false){
18078             this.dragData = data;
18079             this.proxy.stop();
18080             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18081         } 
18082     },
18083
18084     /**
18085      * An empty function by default, but provided so that you can perform a custom action before the initial
18086      * drag event begins and optionally cancel it.
18087      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18088      * @param {Event} e The event object
18089      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18090      */
18091     onBeforeDrag : function(data, e){
18092         return true;
18093     },
18094
18095     /**
18096      * An empty function by default, but provided so that you can perform a custom action once the initial
18097      * drag event has begun.  The drag cannot be canceled from this function.
18098      * @param {Number} x The x position of the click on the dragged object
18099      * @param {Number} y The y position of the click on the dragged object
18100      */
18101     onStartDrag : Roo.emptyFn,
18102
18103     // private - YUI override
18104     startDrag : function(x, y){
18105         this.proxy.reset();
18106         this.dragging = true;
18107         this.proxy.update("");
18108         this.onInitDrag(x, y);
18109         this.proxy.show();
18110     },
18111
18112     // private
18113     onInitDrag : function(x, y){
18114         var clone = this.el.dom.cloneNode(true);
18115         clone.id = Roo.id(); // prevent duplicate ids
18116         this.proxy.update(clone);
18117         this.onStartDrag(x, y);
18118         return true;
18119     },
18120
18121     /**
18122      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18123      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18124      */
18125     getProxy : function(){
18126         return this.proxy;  
18127     },
18128
18129     /**
18130      * Hides the drag source's {@link Roo.dd.StatusProxy}
18131      */
18132     hideProxy : function(){
18133         this.proxy.hide();  
18134         this.proxy.reset(true);
18135         this.dragging = false;
18136     },
18137
18138     // private
18139     triggerCacheRefresh : function(){
18140         Roo.dd.DDM.refreshCache(this.groups);
18141     },
18142
18143     // private - override to prevent hiding
18144     b4EndDrag: function(e) {
18145     },
18146
18147     // private - override to prevent moving
18148     endDrag : function(e){
18149         this.onEndDrag(this.dragData, e);
18150     },
18151
18152     // private
18153     onEndDrag : function(data, e){
18154     },
18155     
18156     // private - pin to cursor
18157     autoOffset : function(x, y) {
18158         this.setDelta(-12, -20);
18159     }    
18160 });/*
18161  * Based on:
18162  * Ext JS Library 1.1.1
18163  * Copyright(c) 2006-2007, Ext JS, LLC.
18164  *
18165  * Originally Released Under LGPL - original licence link has changed is not relivant.
18166  *
18167  * Fork - LGPL
18168  * <script type="text/javascript">
18169  */
18170
18171
18172 /**
18173  * @class Roo.dd.DropTarget
18174  * @extends Roo.dd.DDTarget
18175  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18176  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18177  * @constructor
18178  * @param {String/HTMLElement/Element} el The container element
18179  * @param {Object} config
18180  */
18181 Roo.dd.DropTarget = function(el, config){
18182     this.el = Roo.get(el);
18183     
18184     Roo.apply(this, config);
18185     
18186     if(this.containerScroll){
18187         Roo.dd.ScrollManager.register(this.el);
18188     }
18189     
18190     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18191           {isTarget: true});
18192
18193 };
18194
18195 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18196     /**
18197      * @cfg {String} overClass
18198      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18199      */
18200     /**
18201      * @cfg {String} dropAllowed
18202      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18203      */
18204     dropAllowed : "x-dd-drop-ok",
18205     /**
18206      * @cfg {String} dropNotAllowed
18207      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18208      */
18209     dropNotAllowed : "x-dd-drop-nodrop",
18210
18211     // private
18212     isTarget : true,
18213
18214     // private
18215     isNotifyTarget : true,
18216
18217     /**
18218      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18219      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18220      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18222      * @param {Event} e The event
18223      * @param {Object} data An object containing arbitrary data supplied by the drag source
18224      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18225      * underlying {@link Roo.dd.StatusProxy} can be updated
18226      */
18227     notifyEnter : function(dd, e, data){
18228         if(this.overClass){
18229             this.el.addClass(this.overClass);
18230         }
18231         return this.dropAllowed;
18232     },
18233
18234     /**
18235      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18236      * This method will be called on every mouse movement while the drag source is over the drop target.
18237      * This default implementation simply returns the dropAllowed config value.
18238      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18239      * @param {Event} e The event
18240      * @param {Object} data An object containing arbitrary data supplied by the drag source
18241      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18242      * underlying {@link Roo.dd.StatusProxy} can be updated
18243      */
18244     notifyOver : function(dd, e, data){
18245         return this.dropAllowed;
18246     },
18247
18248     /**
18249      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18250      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18251      * overClass (if any) from the drop element.
18252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18253      * @param {Event} e The event
18254      * @param {Object} data An object containing arbitrary data supplied by the drag source
18255      */
18256     notifyOut : function(dd, e, data){
18257         if(this.overClass){
18258             this.el.removeClass(this.overClass);
18259         }
18260     },
18261
18262     /**
18263      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18264      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18265      * implementation that does something to process the drop event and returns true so that the drag source's
18266      * repair action does not run.
18267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18268      * @param {Event} e The event
18269      * @param {Object} data An object containing arbitrary data supplied by the drag source
18270      * @return {Boolean} True if the drop was valid, else false
18271      */
18272     notifyDrop : function(dd, e, data){
18273         return false;
18274     }
18275 });/*
18276  * Based on:
18277  * Ext JS Library 1.1.1
18278  * Copyright(c) 2006-2007, Ext JS, LLC.
18279  *
18280  * Originally Released Under LGPL - original licence link has changed is not relivant.
18281  *
18282  * Fork - LGPL
18283  * <script type="text/javascript">
18284  */
18285
18286
18287 /**
18288  * @class Roo.dd.DragZone
18289  * @extends Roo.dd.DragSource
18290  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18291  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18292  * @constructor
18293  * @param {String/HTMLElement/Element} el The container element
18294  * @param {Object} config
18295  */
18296 Roo.dd.DragZone = function(el, config){
18297     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18298     if(this.containerScroll){
18299         Roo.dd.ScrollManager.register(this.el);
18300     }
18301 };
18302
18303 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18304     /**
18305      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18306      * for auto scrolling during drag operations.
18307      */
18308     /**
18309      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18310      * method after a failed drop (defaults to "c3daf9" - light blue)
18311      */
18312
18313     /**
18314      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18315      * for a valid target to drag based on the mouse down. Override this method
18316      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18317      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18318      * @param {EventObject} e The mouse down event
18319      * @return {Object} The dragData
18320      */
18321     getDragData : function(e){
18322         return Roo.dd.Registry.getHandleFromEvent(e);
18323     },
18324     
18325     /**
18326      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18327      * this.dragData.ddel
18328      * @param {Number} x The x position of the click on the dragged object
18329      * @param {Number} y The y position of the click on the dragged object
18330      * @return {Boolean} true to continue the drag, false to cancel
18331      */
18332     onInitDrag : function(x, y){
18333         this.proxy.update(this.dragData.ddel.cloneNode(true));
18334         this.onStartDrag(x, y);
18335         return true;
18336     },
18337     
18338     /**
18339      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18340      */
18341     afterRepair : function(){
18342         if(Roo.enableFx){
18343             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18344         }
18345         this.dragging = false;
18346     },
18347
18348     /**
18349      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18350      * the XY of this.dragData.ddel
18351      * @param {EventObject} e The mouse up event
18352      * @return {Array} The xy location (e.g. [100, 200])
18353      */
18354     getRepairXY : function(e){
18355         return Roo.Element.fly(this.dragData.ddel).getXY();  
18356     }
18357 });/*
18358  * Based on:
18359  * Ext JS Library 1.1.1
18360  * Copyright(c) 2006-2007, Ext JS, LLC.
18361  *
18362  * Originally Released Under LGPL - original licence link has changed is not relivant.
18363  *
18364  * Fork - LGPL
18365  * <script type="text/javascript">
18366  */
18367 /**
18368  * @class Roo.dd.DropZone
18369  * @extends Roo.dd.DropTarget
18370  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18371  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18372  * @constructor
18373  * @param {String/HTMLElement/Element} el The container element
18374  * @param {Object} config
18375  */
18376 Roo.dd.DropZone = function(el, config){
18377     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18378 };
18379
18380 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18381     /**
18382      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18383      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18384      * provide your own custom lookup.
18385      * @param {Event} e The event
18386      * @return {Object} data The custom data
18387      */
18388     getTargetFromEvent : function(e){
18389         return Roo.dd.Registry.getTargetFromEvent(e);
18390     },
18391
18392     /**
18393      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18394      * that it has registered.  This method has no default implementation and should be overridden to provide
18395      * node-specific processing if necessary.
18396      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18397      * {@link #getTargetFromEvent} for this node)
18398      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18399      * @param {Event} e The event
18400      * @param {Object} data An object containing arbitrary data supplied by the drag source
18401      */
18402     onNodeEnter : function(n, dd, e, data){
18403         
18404     },
18405
18406     /**
18407      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18408      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18409      * overridden to provide the proper feedback.
18410      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18411      * {@link #getTargetFromEvent} for this node)
18412      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18413      * @param {Event} e The event
18414      * @param {Object} data An object containing arbitrary data supplied by the drag source
18415      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18416      * underlying {@link Roo.dd.StatusProxy} can be updated
18417      */
18418     onNodeOver : function(n, dd, e, data){
18419         return this.dropAllowed;
18420     },
18421
18422     /**
18423      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18424      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18425      * node-specific processing if necessary.
18426      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18427      * {@link #getTargetFromEvent} for this node)
18428      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18429      * @param {Event} e The event
18430      * @param {Object} data An object containing arbitrary data supplied by the drag source
18431      */
18432     onNodeOut : function(n, dd, e, data){
18433         
18434     },
18435
18436     /**
18437      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18438      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18439      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18440      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18441      * {@link #getTargetFromEvent} for this node)
18442      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18443      * @param {Event} e The event
18444      * @param {Object} data An object containing arbitrary data supplied by the drag source
18445      * @return {Boolean} True if the drop was valid, else false
18446      */
18447     onNodeDrop : function(n, dd, e, data){
18448         return false;
18449     },
18450
18451     /**
18452      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18453      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18454      * it should be overridden to provide the proper feedback if necessary.
18455      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18456      * @param {Event} e The event
18457      * @param {Object} data An object containing arbitrary data supplied by the drag source
18458      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18459      * underlying {@link Roo.dd.StatusProxy} can be updated
18460      */
18461     onContainerOver : function(dd, e, data){
18462         return this.dropNotAllowed;
18463     },
18464
18465     /**
18466      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18467      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18468      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18469      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18470      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18471      * @param {Event} e The event
18472      * @param {Object} data An object containing arbitrary data supplied by the drag source
18473      * @return {Boolean} True if the drop was valid, else false
18474      */
18475     onContainerDrop : function(dd, e, data){
18476         return false;
18477     },
18478
18479     /**
18480      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18481      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18482      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18483      * you should override this method and provide a custom implementation.
18484      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18485      * @param {Event} e The event
18486      * @param {Object} data An object containing arbitrary data supplied by the drag source
18487      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18488      * underlying {@link Roo.dd.StatusProxy} can be updated
18489      */
18490     notifyEnter : function(dd, e, data){
18491         return this.dropNotAllowed;
18492     },
18493
18494     /**
18495      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18496      * This method will be called on every mouse movement while the drag source is over the drop zone.
18497      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18498      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18499      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18500      * registered node, it will call {@link #onContainerOver}.
18501      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18502      * @param {Event} e The event
18503      * @param {Object} data An object containing arbitrary data supplied by the drag source
18504      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18505      * underlying {@link Roo.dd.StatusProxy} can be updated
18506      */
18507     notifyOver : function(dd, e, data){
18508         var n = this.getTargetFromEvent(e);
18509         if(!n){ // not over valid drop target
18510             if(this.lastOverNode){
18511                 this.onNodeOut(this.lastOverNode, dd, e, data);
18512                 this.lastOverNode = null;
18513             }
18514             return this.onContainerOver(dd, e, data);
18515         }
18516         if(this.lastOverNode != n){
18517             if(this.lastOverNode){
18518                 this.onNodeOut(this.lastOverNode, dd, e, data);
18519             }
18520             this.onNodeEnter(n, dd, e, data);
18521             this.lastOverNode = n;
18522         }
18523         return this.onNodeOver(n, dd, e, data);
18524     },
18525
18526     /**
18527      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18528      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18529      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18530      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18531      * @param {Event} e The event
18532      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18533      */
18534     notifyOut : function(dd, e, data){
18535         if(this.lastOverNode){
18536             this.onNodeOut(this.lastOverNode, dd, e, data);
18537             this.lastOverNode = null;
18538         }
18539     },
18540
18541     /**
18542      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18543      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18544      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18545      * otherwise it will call {@link #onContainerDrop}.
18546      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18547      * @param {Event} e The event
18548      * @param {Object} data An object containing arbitrary data supplied by the drag source
18549      * @return {Boolean} True if the drop was valid, else false
18550      */
18551     notifyDrop : function(dd, e, data){
18552         if(this.lastOverNode){
18553             this.onNodeOut(this.lastOverNode, dd, e, data);
18554             this.lastOverNode = null;
18555         }
18556         var n = this.getTargetFromEvent(e);
18557         return n ?
18558             this.onNodeDrop(n, dd, e, data) :
18559             this.onContainerDrop(dd, e, data);
18560     },
18561
18562     // private
18563     triggerCacheRefresh : function(){
18564         Roo.dd.DDM.refreshCache(this.groups);
18565     }  
18566 });/*
18567  * Based on:
18568  * Ext JS Library 1.1.1
18569  * Copyright(c) 2006-2007, Ext JS, LLC.
18570  *
18571  * Originally Released Under LGPL - original licence link has changed is not relivant.
18572  *
18573  * Fork - LGPL
18574  * <script type="text/javascript">
18575  */
18576
18577
18578 /**
18579  * @class Roo.data.SortTypes
18580  * @singleton
18581  * Defines the default sorting (casting?) comparison functions used when sorting data.
18582  */
18583 Roo.data.SortTypes = {
18584     /**
18585      * Default sort that does nothing
18586      * @param {Mixed} s The value being converted
18587      * @return {Mixed} The comparison value
18588      */
18589     none : function(s){
18590         return s;
18591     },
18592     
18593     /**
18594      * The regular expression used to strip tags
18595      * @type {RegExp}
18596      * @property
18597      */
18598     stripTagsRE : /<\/?[^>]+>/gi,
18599     
18600     /**
18601      * Strips all HTML tags to sort on text only
18602      * @param {Mixed} s The value being converted
18603      * @return {String} The comparison value
18604      */
18605     asText : function(s){
18606         return String(s).replace(this.stripTagsRE, "");
18607     },
18608     
18609     /**
18610      * Strips all HTML tags to sort on text only - Case insensitive
18611      * @param {Mixed} s The value being converted
18612      * @return {String} The comparison value
18613      */
18614     asUCText : function(s){
18615         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18616     },
18617     
18618     /**
18619      * Case insensitive string
18620      * @param {Mixed} s The value being converted
18621      * @return {String} The comparison value
18622      */
18623     asUCString : function(s) {
18624         return String(s).toUpperCase();
18625     },
18626     
18627     /**
18628      * Date sorting
18629      * @param {Mixed} s The value being converted
18630      * @return {Number} The comparison value
18631      */
18632     asDate : function(s) {
18633         if(!s){
18634             return 0;
18635         }
18636         if(s instanceof Date){
18637             return s.getTime();
18638         }
18639         return Date.parse(String(s));
18640     },
18641     
18642     /**
18643      * Float sorting
18644      * @param {Mixed} s The value being converted
18645      * @return {Float} The comparison value
18646      */
18647     asFloat : function(s) {
18648         var val = parseFloat(String(s).replace(/,/g, ""));
18649         if(isNaN(val)) val = 0;
18650         return val;
18651     },
18652     
18653     /**
18654      * Integer sorting
18655      * @param {Mixed} s The value being converted
18656      * @return {Number} The comparison value
18657      */
18658     asInt : function(s) {
18659         var val = parseInt(String(s).replace(/,/g, ""));
18660         if(isNaN(val)) val = 0;
18661         return val;
18662     }
18663 };/*
18664  * Based on:
18665  * Ext JS Library 1.1.1
18666  * Copyright(c) 2006-2007, Ext JS, LLC.
18667  *
18668  * Originally Released Under LGPL - original licence link has changed is not relivant.
18669  *
18670  * Fork - LGPL
18671  * <script type="text/javascript">
18672  */
18673
18674 /**
18675 * @class Roo.data.Record
18676  * Instances of this class encapsulate both record <em>definition</em> information, and record
18677  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18678  * to access Records cached in an {@link Roo.data.Store} object.<br>
18679  * <p>
18680  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18681  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18682  * objects.<br>
18683  * <p>
18684  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18685  * @constructor
18686  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18687  * {@link #create}. The parameters are the same.
18688  * @param {Array} data An associative Array of data values keyed by the field name.
18689  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18690  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18691  * not specified an integer id is generated.
18692  */
18693 Roo.data.Record = function(data, id){
18694     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18695     this.data = data;
18696 };
18697
18698 /**
18699  * Generate a constructor for a specific record layout.
18700  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18701  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18702  * Each field definition object may contain the following properties: <ul>
18703  * <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,
18704  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18705  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18706  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18707  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18708  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18709  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18710  * this may be omitted.</p></li>
18711  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18712  * <ul><li>auto (Default, implies no conversion)</li>
18713  * <li>string</li>
18714  * <li>int</li>
18715  * <li>float</li>
18716  * <li>boolean</li>
18717  * <li>date</li></ul></p></li>
18718  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18719  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18720  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18721  * by the Reader into an object that will be stored in the Record. It is passed the
18722  * following parameters:<ul>
18723  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18724  * </ul></p></li>
18725  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18726  * </ul>
18727  * <br>usage:<br><pre><code>
18728 var TopicRecord = Roo.data.Record.create(
18729     {name: 'title', mapping: 'topic_title'},
18730     {name: 'author', mapping: 'username'},
18731     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18732     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18733     {name: 'lastPoster', mapping: 'user2'},
18734     {name: 'excerpt', mapping: 'post_text'}
18735 );
18736
18737 var myNewRecord = new TopicRecord({
18738     title: 'Do my job please',
18739     author: 'noobie',
18740     totalPosts: 1,
18741     lastPost: new Date(),
18742     lastPoster: 'Animal',
18743     excerpt: 'No way dude!'
18744 });
18745 myStore.add(myNewRecord);
18746 </code></pre>
18747  * @method create
18748  * @static
18749  */
18750 Roo.data.Record.create = function(o){
18751     var f = function(){
18752         f.superclass.constructor.apply(this, arguments);
18753     };
18754     Roo.extend(f, Roo.data.Record);
18755     var p = f.prototype;
18756     p.fields = new Roo.util.MixedCollection(false, function(field){
18757         return field.name;
18758     });
18759     for(var i = 0, len = o.length; i < len; i++){
18760         p.fields.add(new Roo.data.Field(o[i]));
18761     }
18762     f.getField = function(name){
18763         return p.fields.get(name);  
18764     };
18765     return f;
18766 };
18767
18768 Roo.data.Record.AUTO_ID = 1000;
18769 Roo.data.Record.EDIT = 'edit';
18770 Roo.data.Record.REJECT = 'reject';
18771 Roo.data.Record.COMMIT = 'commit';
18772
18773 Roo.data.Record.prototype = {
18774     /**
18775      * Readonly flag - true if this record has been modified.
18776      * @type Boolean
18777      */
18778     dirty : false,
18779     editing : false,
18780     error: null,
18781     modified: null,
18782
18783     // private
18784     join : function(store){
18785         this.store = store;
18786     },
18787
18788     /**
18789      * Set the named field to the specified value.
18790      * @param {String} name The name of the field to set.
18791      * @param {Object} value The value to set the field to.
18792      */
18793     set : function(name, value){
18794         if(this.data[name] == value){
18795             return;
18796         }
18797         this.dirty = true;
18798         if(!this.modified){
18799             this.modified = {};
18800         }
18801         if(typeof this.modified[name] == 'undefined'){
18802             this.modified[name] = this.data[name];
18803         }
18804         this.data[name] = value;
18805         if(!this.editing){
18806             this.store.afterEdit(this);
18807         }       
18808     },
18809
18810     /**
18811      * Get the value of the named field.
18812      * @param {String} name The name of the field to get the value of.
18813      * @return {Object} The value of the field.
18814      */
18815     get : function(name){
18816         return this.data[name]; 
18817     },
18818
18819     // private
18820     beginEdit : function(){
18821         this.editing = true;
18822         this.modified = {}; 
18823     },
18824
18825     // private
18826     cancelEdit : function(){
18827         this.editing = false;
18828         delete this.modified;
18829     },
18830
18831     // private
18832     endEdit : function(){
18833         this.editing = false;
18834         if(this.dirty && this.store){
18835             this.store.afterEdit(this);
18836         }
18837     },
18838
18839     /**
18840      * Usually called by the {@link Roo.data.Store} which owns the Record.
18841      * Rejects all changes made to the Record since either creation, or the last commit operation.
18842      * Modified fields are reverted to their original values.
18843      * <p>
18844      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18845      * of reject operations.
18846      */
18847     reject : function(){
18848         var m = this.modified;
18849         for(var n in m){
18850             if(typeof m[n] != "function"){
18851                 this.data[n] = m[n];
18852             }
18853         }
18854         this.dirty = false;
18855         delete this.modified;
18856         this.editing = false;
18857         if(this.store){
18858             this.store.afterReject(this);
18859         }
18860     },
18861
18862     /**
18863      * Usually called by the {@link Roo.data.Store} which owns the Record.
18864      * Commits all changes made to the Record since either creation, or the last commit operation.
18865      * <p>
18866      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18867      * of commit operations.
18868      */
18869     commit : function(){
18870         this.dirty = false;
18871         delete this.modified;
18872         this.editing = false;
18873         if(this.store){
18874             this.store.afterCommit(this);
18875         }
18876     },
18877
18878     // private
18879     hasError : function(){
18880         return this.error != null;
18881     },
18882
18883     // private
18884     clearError : function(){
18885         this.error = null;
18886     },
18887
18888     /**
18889      * Creates a copy of this record.
18890      * @param {String} id (optional) A new record id if you don't want to use this record's id
18891      * @return {Record}
18892      */
18893     copy : function(newId) {
18894         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18895     }
18896 };/*
18897  * Based on:
18898  * Ext JS Library 1.1.1
18899  * Copyright(c) 2006-2007, Ext JS, LLC.
18900  *
18901  * Originally Released Under LGPL - original licence link has changed is not relivant.
18902  *
18903  * Fork - LGPL
18904  * <script type="text/javascript">
18905  */
18906
18907
18908
18909 /**
18910  * @class Roo.data.Store
18911  * @extends Roo.util.Observable
18912  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18913  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18914  * <p>
18915  * 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
18916  * has no knowledge of the format of the data returned by the Proxy.<br>
18917  * <p>
18918  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18919  * instances from the data object. These records are cached and made available through accessor functions.
18920  * @constructor
18921  * Creates a new Store.
18922  * @param {Object} config A config object containing the objects needed for the Store to access data,
18923  * and read the data into Records.
18924  */
18925 Roo.data.Store = function(config){
18926     this.data = new Roo.util.MixedCollection(false);
18927     this.data.getKey = function(o){
18928         return o.id;
18929     };
18930     this.baseParams = {};
18931     // private
18932     this.paramNames = {
18933         "start" : "start",
18934         "limit" : "limit",
18935         "sort" : "sort",
18936         "dir" : "dir"
18937     };
18938
18939     if(config && config.data){
18940         this.inlineData = config.data;
18941         delete config.data;
18942     }
18943
18944     Roo.apply(this, config);
18945     
18946     if(this.reader){ // reader passed
18947         this.reader = Roo.factory(this.reader, Roo.data);
18948         this.reader.xmodule = this.xmodule || false;
18949         if(!this.recordType){
18950             this.recordType = this.reader.recordType;
18951         }
18952         if(this.reader.onMetaChange){
18953             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18954         }
18955     }
18956
18957     if(this.recordType){
18958         this.fields = this.recordType.prototype.fields;
18959     }
18960     this.modified = [];
18961
18962     this.addEvents({
18963         /**
18964          * @event datachanged
18965          * Fires when the data cache has changed, and a widget which is using this Store
18966          * as a Record cache should refresh its view.
18967          * @param {Store} this
18968          */
18969         datachanged : true,
18970         /**
18971          * @event metachange
18972          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18973          * @param {Store} this
18974          * @param {Object} meta The JSON metadata
18975          */
18976         metachange : true,
18977         /**
18978          * @event add
18979          * Fires when Records have been added to the Store
18980          * @param {Store} this
18981          * @param {Roo.data.Record[]} records The array of Records added
18982          * @param {Number} index The index at which the record(s) were added
18983          */
18984         add : true,
18985         /**
18986          * @event remove
18987          * Fires when a Record has been removed from the Store
18988          * @param {Store} this
18989          * @param {Roo.data.Record} record The Record that was removed
18990          * @param {Number} index The index at which the record was removed
18991          */
18992         remove : true,
18993         /**
18994          * @event update
18995          * Fires when a Record has been updated
18996          * @param {Store} this
18997          * @param {Roo.data.Record} record The Record that was updated
18998          * @param {String} operation The update operation being performed.  Value may be one of:
18999          * <pre><code>
19000  Roo.data.Record.EDIT
19001  Roo.data.Record.REJECT
19002  Roo.data.Record.COMMIT
19003          * </code></pre>
19004          */
19005         update : true,
19006         /**
19007          * @event clear
19008          * Fires when the data cache has been cleared.
19009          * @param {Store} this
19010          */
19011         clear : true,
19012         /**
19013          * @event beforeload
19014          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19015          * the load action will be canceled.
19016          * @param {Store} this
19017          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19018          */
19019         beforeload : true,
19020         /**
19021          * @event load
19022          * Fires after a new set of Records has been loaded.
19023          * @param {Store} this
19024          * @param {Roo.data.Record[]} records The Records that were loaded
19025          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19026          */
19027         load : true,
19028         /**
19029          * @event loadexception
19030          * Fires if an exception occurs in the Proxy during loading.
19031          * Called with the signature of the Proxy's "loadexception" event.
19032          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19033          * 
19034          * @param {Proxy} 
19035          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19036          * @param {Object} load options 
19037          * @param {Object} jsonData from your request (normally this contains the Exception)
19038          */
19039         loadexception : true
19040     });
19041     
19042     if(this.proxy){
19043         this.proxy = Roo.factory(this.proxy, Roo.data);
19044         this.proxy.xmodule = this.xmodule || false;
19045         this.relayEvents(this.proxy,  ["loadexception"]);
19046     }
19047     this.sortToggle = {};
19048
19049     Roo.data.Store.superclass.constructor.call(this);
19050
19051     if(this.inlineData){
19052         this.loadData(this.inlineData);
19053         delete this.inlineData;
19054     }
19055 };
19056 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19057      /**
19058     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19059     * without a remote query - used by combo/forms at present.
19060     */
19061     
19062     /**
19063     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19064     */
19065     /**
19066     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19067     */
19068     /**
19069     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19070     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19071     */
19072     /**
19073     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19074     * on any HTTP request
19075     */
19076     /**
19077     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19078     */
19079     /**
19080     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19081     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19082     */
19083     remoteSort : false,
19084
19085     /**
19086     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19087      * loaded or when a record is removed. (defaults to false).
19088     */
19089     pruneModifiedRecords : false,
19090
19091     // private
19092     lastOptions : null,
19093
19094     /**
19095      * Add Records to the Store and fires the add event.
19096      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19097      */
19098     add : function(records){
19099         records = [].concat(records);
19100         for(var i = 0, len = records.length; i < len; i++){
19101             records[i].join(this);
19102         }
19103         var index = this.data.length;
19104         this.data.addAll(records);
19105         this.fireEvent("add", this, records, index);
19106     },
19107
19108     /**
19109      * Remove a Record from the Store and fires the remove event.
19110      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19111      */
19112     remove : function(record){
19113         var index = this.data.indexOf(record);
19114         this.data.removeAt(index);
19115         if(this.pruneModifiedRecords){
19116             this.modified.remove(record);
19117         }
19118         this.fireEvent("remove", this, record, index);
19119     },
19120
19121     /**
19122      * Remove all Records from the Store and fires the clear event.
19123      */
19124     removeAll : function(){
19125         this.data.clear();
19126         if(this.pruneModifiedRecords){
19127             this.modified = [];
19128         }
19129         this.fireEvent("clear", this);
19130     },
19131
19132     /**
19133      * Inserts Records to the Store at the given index and fires the add event.
19134      * @param {Number} index The start index at which to insert the passed Records.
19135      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19136      */
19137     insert : function(index, records){
19138         records = [].concat(records);
19139         for(var i = 0, len = records.length; i < len; i++){
19140             this.data.insert(index, records[i]);
19141             records[i].join(this);
19142         }
19143         this.fireEvent("add", this, records, index);
19144     },
19145
19146     /**
19147      * Get the index within the cache of the passed Record.
19148      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19149      * @return {Number} The index of the passed Record. Returns -1 if not found.
19150      */
19151     indexOf : function(record){
19152         return this.data.indexOf(record);
19153     },
19154
19155     /**
19156      * Get the index within the cache of the Record with the passed id.
19157      * @param {String} id The id of the Record to find.
19158      * @return {Number} The index of the Record. Returns -1 if not found.
19159      */
19160     indexOfId : function(id){
19161         return this.data.indexOfKey(id);
19162     },
19163
19164     /**
19165      * Get the Record with the specified id.
19166      * @param {String} id The id of the Record to find.
19167      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19168      */
19169     getById : function(id){
19170         return this.data.key(id);
19171     },
19172
19173     /**
19174      * Get the Record at the specified index.
19175      * @param {Number} index The index of the Record to find.
19176      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19177      */
19178     getAt : function(index){
19179         return this.data.itemAt(index);
19180     },
19181
19182     /**
19183      * Returns a range of Records between specified indices.
19184      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19185      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19186      * @return {Roo.data.Record[]} An array of Records
19187      */
19188     getRange : function(start, end){
19189         return this.data.getRange(start, end);
19190     },
19191
19192     // private
19193     storeOptions : function(o){
19194         o = Roo.apply({}, o);
19195         delete o.callback;
19196         delete o.scope;
19197         this.lastOptions = o;
19198     },
19199
19200     /**
19201      * Loads the Record cache from the configured Proxy using the configured Reader.
19202      * <p>
19203      * If using remote paging, then the first load call must specify the <em>start</em>
19204      * and <em>limit</em> properties in the options.params property to establish the initial
19205      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19206      * <p>
19207      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19208      * and this call will return before the new data has been loaded. Perform any post-processing
19209      * in a callback function, or in a "load" event handler.</strong>
19210      * <p>
19211      * @param {Object} options An object containing properties which control loading options:<ul>
19212      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19213      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19214      * passed the following arguments:<ul>
19215      * <li>r : Roo.data.Record[]</li>
19216      * <li>options: Options object from the load call</li>
19217      * <li>success: Boolean success indicator</li></ul></li>
19218      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19219      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19220      * </ul>
19221      */
19222     load : function(options){
19223         options = options || {};
19224         if(this.fireEvent("beforeload", this, options) !== false){
19225             this.storeOptions(options);
19226             var p = Roo.apply(options.params || {}, this.baseParams);
19227             if(this.sortInfo && this.remoteSort){
19228                 var pn = this.paramNames;
19229                 p[pn["sort"]] = this.sortInfo.field;
19230                 p[pn["dir"]] = this.sortInfo.direction;
19231             }
19232             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19233         }
19234     },
19235
19236     /**
19237      * Reloads the Record cache from the configured Proxy using the configured Reader and
19238      * the options from the last load operation performed.
19239      * @param {Object} options (optional) An object containing properties which may override the options
19240      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19241      * the most recently used options are reused).
19242      */
19243     reload : function(options){
19244         this.load(Roo.applyIf(options||{}, this.lastOptions));
19245     },
19246
19247     // private
19248     // Called as a callback by the Reader during a load operation.
19249     loadRecords : function(o, options, success){
19250         if(!o || success === false){
19251             if(success !== false){
19252                 this.fireEvent("load", this, [], options);
19253             }
19254             if(options.callback){
19255                 options.callback.call(options.scope || this, [], options, false);
19256             }
19257             return;
19258         }
19259         // if data returned failure - throw an exception.
19260         if (o.success === false) {
19261             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19262             return;
19263         }
19264         var r = o.records, t = o.totalRecords || r.length;
19265         if(!options || options.add !== true){
19266             if(this.pruneModifiedRecords){
19267                 this.modified = [];
19268             }
19269             for(var i = 0, len = r.length; i < len; i++){
19270                 r[i].join(this);
19271             }
19272             if(this.snapshot){
19273                 this.data = this.snapshot;
19274                 delete this.snapshot;
19275             }
19276             this.data.clear();
19277             this.data.addAll(r);
19278             this.totalLength = t;
19279             this.applySort();
19280             this.fireEvent("datachanged", this);
19281         }else{
19282             this.totalLength = Math.max(t, this.data.length+r.length);
19283             this.add(r);
19284         }
19285         this.fireEvent("load", this, r, options);
19286         if(options.callback){
19287             options.callback.call(options.scope || this, r, options, true);
19288         }
19289     },
19290
19291     /**
19292      * Loads data from a passed data block. A Reader which understands the format of the data
19293      * must have been configured in the constructor.
19294      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19295      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19296      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19297      */
19298     loadData : function(o, append){
19299         var r = this.reader.readRecords(o);
19300         this.loadRecords(r, {add: append}, true);
19301     },
19302
19303     /**
19304      * Gets the number of cached records.
19305      * <p>
19306      * <em>If using paging, this may not be the total size of the dataset. If the data object
19307      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19308      * the data set size</em>
19309      */
19310     getCount : function(){
19311         return this.data.length || 0;
19312     },
19313
19314     /**
19315      * Gets the total number of records in the dataset as returned by the server.
19316      * <p>
19317      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19318      * the dataset size</em>
19319      */
19320     getTotalCount : function(){
19321         return this.totalLength || 0;
19322     },
19323
19324     /**
19325      * Returns the sort state of the Store as an object with two properties:
19326      * <pre><code>
19327  field {String} The name of the field by which the Records are sorted
19328  direction {String} The sort order, "ASC" or "DESC"
19329      * </code></pre>
19330      */
19331     getSortState : function(){
19332         return this.sortInfo;
19333     },
19334
19335     // private
19336     applySort : function(){
19337         if(this.sortInfo && !this.remoteSort){
19338             var s = this.sortInfo, f = s.field;
19339             var st = this.fields.get(f).sortType;
19340             var fn = function(r1, r2){
19341                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19342                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19343             };
19344             this.data.sort(s.direction, fn);
19345             if(this.snapshot && this.snapshot != this.data){
19346                 this.snapshot.sort(s.direction, fn);
19347             }
19348         }
19349     },
19350
19351     /**
19352      * Sets the default sort column and order to be used by the next load operation.
19353      * @param {String} fieldName The name of the field to sort by.
19354      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19355      */
19356     setDefaultSort : function(field, dir){
19357         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19358     },
19359
19360     /**
19361      * Sort the Records.
19362      * If remote sorting is used, the sort is performed on the server, and the cache is
19363      * reloaded. If local sorting is used, the cache is sorted internally.
19364      * @param {String} fieldName The name of the field to sort by.
19365      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19366      */
19367     sort : function(fieldName, dir){
19368         var f = this.fields.get(fieldName);
19369         if(!dir){
19370             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19371                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19372             }else{
19373                 dir = f.sortDir;
19374             }
19375         }
19376         this.sortToggle[f.name] = dir;
19377         this.sortInfo = {field: f.name, direction: dir};
19378         if(!this.remoteSort){
19379             this.applySort();
19380             this.fireEvent("datachanged", this);
19381         }else{
19382             this.load(this.lastOptions);
19383         }
19384     },
19385
19386     /**
19387      * Calls the specified function for each of the Records in the cache.
19388      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19389      * Returning <em>false</em> aborts and exits the iteration.
19390      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19391      */
19392     each : function(fn, scope){
19393         this.data.each(fn, scope);
19394     },
19395
19396     /**
19397      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19398      * (e.g., during paging).
19399      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19400      */
19401     getModifiedRecords : function(){
19402         return this.modified;
19403     },
19404
19405     // private
19406     createFilterFn : function(property, value, anyMatch){
19407         if(!value.exec){ // not a regex
19408             value = String(value);
19409             if(value.length == 0){
19410                 return false;
19411             }
19412             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19413         }
19414         return function(r){
19415             return value.test(r.data[property]);
19416         };
19417     },
19418
19419     /**
19420      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19421      * @param {String} property A field on your records
19422      * @param {Number} start The record index to start at (defaults to 0)
19423      * @param {Number} end The last record index to include (defaults to length - 1)
19424      * @return {Number} The sum
19425      */
19426     sum : function(property, start, end){
19427         var rs = this.data.items, v = 0;
19428         start = start || 0;
19429         end = (end || end === 0) ? end : rs.length-1;
19430
19431         for(var i = start; i <= end; i++){
19432             v += (rs[i].data[property] || 0);
19433         }
19434         return v;
19435     },
19436
19437     /**
19438      * Filter the records by a specified property.
19439      * @param {String} field A field on your records
19440      * @param {String/RegExp} value Either a string that the field
19441      * should start with or a RegExp to test against the field
19442      * @param {Boolean} anyMatch True to match any part not just the beginning
19443      */
19444     filter : function(property, value, anyMatch){
19445         var fn = this.createFilterFn(property, value, anyMatch);
19446         return fn ? this.filterBy(fn) : this.clearFilter();
19447     },
19448
19449     /**
19450      * Filter 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      * otherwise it is filtered.
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      */
19456     filterBy : function(fn, scope){
19457         this.snapshot = this.snapshot || this.data;
19458         this.data = this.queryBy(fn, scope||this);
19459         this.fireEvent("datachanged", this);
19460     },
19461
19462     /**
19463      * Query the records by a specified property.
19464      * @param {String} field A field on your records
19465      * @param {String/RegExp} value Either a string that the field
19466      * should start with or a RegExp to test against the field
19467      * @param {Boolean} anyMatch True to match any part not just the beginning
19468      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19469      */
19470     query : function(property, value, anyMatch){
19471         var fn = this.createFilterFn(property, value, anyMatch);
19472         return fn ? this.queryBy(fn) : this.data.clone();
19473     },
19474
19475     /**
19476      * Query by a function. The specified function will be called with each
19477      * record in this data source. If the function returns true the record is included
19478      * in the results.
19479      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19480      * @param {Object} scope (optional) The scope of the function (defaults to this)
19481       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19482      **/
19483     queryBy : function(fn, scope){
19484         var data = this.snapshot || this.data;
19485         return data.filterBy(fn, scope||this);
19486     },
19487
19488     /**
19489      * Collects unique values for a particular dataIndex from this store.
19490      * @param {String} dataIndex The property to collect
19491      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19492      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19493      * @return {Array} An array of the unique values
19494      **/
19495     collect : function(dataIndex, allowNull, bypassFilter){
19496         var d = (bypassFilter === true && this.snapshot) ?
19497                 this.snapshot.items : this.data.items;
19498         var v, sv, r = [], l = {};
19499         for(var i = 0, len = d.length; i < len; i++){
19500             v = d[i].data[dataIndex];
19501             sv = String(v);
19502             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19503                 l[sv] = true;
19504                 r[r.length] = v;
19505             }
19506         }
19507         return r;
19508     },
19509
19510     /**
19511      * Revert to a view of the Record cache with no filtering applied.
19512      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19513      */
19514     clearFilter : function(suppressEvent){
19515         if(this.snapshot && this.snapshot != this.data){
19516             this.data = this.snapshot;
19517             delete this.snapshot;
19518             if(suppressEvent !== true){
19519                 this.fireEvent("datachanged", this);
19520             }
19521         }
19522     },
19523
19524     // private
19525     afterEdit : function(record){
19526         if(this.modified.indexOf(record) == -1){
19527             this.modified.push(record);
19528         }
19529         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19530     },
19531
19532     // private
19533     afterReject : function(record){
19534         this.modified.remove(record);
19535         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19536     },
19537
19538     // private
19539     afterCommit : function(record){
19540         this.modified.remove(record);
19541         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19542     },
19543
19544     /**
19545      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19546      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19547      */
19548     commitChanges : function(){
19549         var m = this.modified.slice(0);
19550         this.modified = [];
19551         for(var i = 0, len = m.length; i < len; i++){
19552             m[i].commit();
19553         }
19554     },
19555
19556     /**
19557      * Cancel outstanding changes on all changed records.
19558      */
19559     rejectChanges : function(){
19560         var m = this.modified.slice(0);
19561         this.modified = [];
19562         for(var i = 0, len = m.length; i < len; i++){
19563             m[i].reject();
19564         }
19565     },
19566
19567     onMetaChange : function(meta, rtype, o){
19568         this.recordType = rtype;
19569         this.fields = rtype.prototype.fields;
19570         delete this.snapshot;
19571         this.sortInfo = meta.sortInfo;
19572         this.modified = [];
19573         this.fireEvent('metachange', this, this.reader.meta);
19574     }
19575 });/*
19576  * Based on:
19577  * Ext JS Library 1.1.1
19578  * Copyright(c) 2006-2007, Ext JS, LLC.
19579  *
19580  * Originally Released Under LGPL - original licence link has changed is not relivant.
19581  *
19582  * Fork - LGPL
19583  * <script type="text/javascript">
19584  */
19585
19586 /**
19587  * @class Roo.data.SimpleStore
19588  * @extends Roo.data.Store
19589  * Small helper class to make creating Stores from Array data easier.
19590  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19591  * @cfg {Array} fields An array of field definition objects, or field name strings.
19592  * @cfg {Array} data The multi-dimensional array of data
19593  * @constructor
19594  * @param {Object} config
19595  */
19596 Roo.data.SimpleStore = function(config){
19597     Roo.data.SimpleStore.superclass.constructor.call(this, {
19598         isLocal : true,
19599         reader: new Roo.data.ArrayReader({
19600                 id: config.id
19601             },
19602             Roo.data.Record.create(config.fields)
19603         ),
19604         proxy : new Roo.data.MemoryProxy(config.data)
19605     });
19606     this.load();
19607 };
19608 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19609  * Based on:
19610  * Ext JS Library 1.1.1
19611  * Copyright(c) 2006-2007, Ext JS, LLC.
19612  *
19613  * Originally Released Under LGPL - original licence link has changed is not relivant.
19614  *
19615  * Fork - LGPL
19616  * <script type="text/javascript">
19617  */
19618
19619 /**
19620 /**
19621  * @extends Roo.data.Store
19622  * @class Roo.data.JsonStore
19623  * Small helper class to make creating Stores for JSON data easier. <br/>
19624 <pre><code>
19625 var store = new Roo.data.JsonStore({
19626     url: 'get-images.php',
19627     root: 'images',
19628     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19629 });
19630 </code></pre>
19631  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19632  * JsonReader and HttpProxy (unless inline data is provided).</b>
19633  * @cfg {Array} fields An array of field definition objects, or field name strings.
19634  * @constructor
19635  * @param {Object} config
19636  */
19637 Roo.data.JsonStore = function(c){
19638     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19639         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19640         reader: new Roo.data.JsonReader(c, c.fields)
19641     }));
19642 };
19643 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19644  * Based on:
19645  * Ext JS Library 1.1.1
19646  * Copyright(c) 2006-2007, Ext JS, LLC.
19647  *
19648  * Originally Released Under LGPL - original licence link has changed is not relivant.
19649  *
19650  * Fork - LGPL
19651  * <script type="text/javascript">
19652  */
19653
19654  
19655 Roo.data.Field = function(config){
19656     if(typeof config == "string"){
19657         config = {name: config};
19658     }
19659     Roo.apply(this, config);
19660     
19661     if(!this.type){
19662         this.type = "auto";
19663     }
19664     
19665     var st = Roo.data.SortTypes;
19666     // named sortTypes are supported, here we look them up
19667     if(typeof this.sortType == "string"){
19668         this.sortType = st[this.sortType];
19669     }
19670     
19671     // set default sortType for strings and dates
19672     if(!this.sortType){
19673         switch(this.type){
19674             case "string":
19675                 this.sortType = st.asUCString;
19676                 break;
19677             case "date":
19678                 this.sortType = st.asDate;
19679                 break;
19680             default:
19681                 this.sortType = st.none;
19682         }
19683     }
19684
19685     // define once
19686     var stripRe = /[\$,%]/g;
19687
19688     // prebuilt conversion function for this field, instead of
19689     // switching every time we're reading a value
19690     if(!this.convert){
19691         var cv, dateFormat = this.dateFormat;
19692         switch(this.type){
19693             case "":
19694             case "auto":
19695             case undefined:
19696                 cv = function(v){ return v; };
19697                 break;
19698             case "string":
19699                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19700                 break;
19701             case "int":
19702                 cv = function(v){
19703                     return v !== undefined && v !== null && v !== '' ?
19704                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19705                     };
19706                 break;
19707             case "float":
19708                 cv = function(v){
19709                     return v !== undefined && v !== null && v !== '' ?
19710                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19711                     };
19712                 break;
19713             case "bool":
19714             case "boolean":
19715                 cv = function(v){ return v === true || v === "true" || v == 1; };
19716                 break;
19717             case "date":
19718                 cv = function(v){
19719                     if(!v){
19720                         return '';
19721                     }
19722                     if(v instanceof Date){
19723                         return v;
19724                     }
19725                     if(dateFormat){
19726                         if(dateFormat == "timestamp"){
19727                             return new Date(v*1000);
19728                         }
19729                         return Date.parseDate(v, dateFormat);
19730                     }
19731                     var parsed = Date.parse(v);
19732                     return parsed ? new Date(parsed) : null;
19733                 };
19734              break;
19735             
19736         }
19737         this.convert = cv;
19738     }
19739 };
19740
19741 Roo.data.Field.prototype = {
19742     dateFormat: null,
19743     defaultValue: "",
19744     mapping: null,
19745     sortType : null,
19746     sortDir : "ASC"
19747 };/*
19748  * Based on:
19749  * Ext JS Library 1.1.1
19750  * Copyright(c) 2006-2007, Ext JS, LLC.
19751  *
19752  * Originally Released Under LGPL - original licence link has changed is not relivant.
19753  *
19754  * Fork - LGPL
19755  * <script type="text/javascript">
19756  */
19757  
19758 // Base class for reading structured data from a data source.  This class is intended to be
19759 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19760
19761 /**
19762  * @class Roo.data.DataReader
19763  * Base class for reading structured data from a data source.  This class is intended to be
19764  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19765  */
19766
19767 Roo.data.DataReader = function(meta, recordType){
19768     
19769     this.meta = meta;
19770     
19771     this.recordType = recordType instanceof Array ? 
19772         Roo.data.Record.create(recordType) : recordType;
19773 };
19774
19775 Roo.data.DataReader.prototype = {
19776      /**
19777      * Create an empty record
19778      * @param {Object} data (optional) - overlay some values
19779      * @return {Roo.data.Record} record created.
19780      */
19781     newRow :  function(d) {
19782         var da =  {};
19783         this.recordType.prototype.fields.each(function(c) {
19784             switch( c.type) {
19785                 case 'int' : da[c.name] = 0; break;
19786                 case 'date' : da[c.name] = new Date(); break;
19787                 case 'float' : da[c.name] = 0.0; break;
19788                 case 'boolean' : da[c.name] = false; break;
19789                 default : da[c.name] = ""; break;
19790             }
19791             
19792         });
19793         return new this.recordType(Roo.apply(da, d));
19794     }
19795     
19796 };/*
19797  * Based on:
19798  * Ext JS Library 1.1.1
19799  * Copyright(c) 2006-2007, Ext JS, LLC.
19800  *
19801  * Originally Released Under LGPL - original licence link has changed is not relivant.
19802  *
19803  * Fork - LGPL
19804  * <script type="text/javascript">
19805  */
19806
19807 /**
19808  * @class Roo.data.DataProxy
19809  * @extends Roo.data.Observable
19810  * This class is an abstract base class for implementations which provide retrieval of
19811  * unformatted data objects.<br>
19812  * <p>
19813  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19814  * (of the appropriate type which knows how to parse the data object) to provide a block of
19815  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19816  * <p>
19817  * Custom implementations must implement the load method as described in
19818  * {@link Roo.data.HttpProxy#load}.
19819  */
19820 Roo.data.DataProxy = function(){
19821     this.addEvents({
19822         /**
19823          * @event beforeload
19824          * Fires before a network request is made to retrieve a data object.
19825          * @param {Object} This DataProxy object.
19826          * @param {Object} params The params parameter to the load function.
19827          */
19828         beforeload : true,
19829         /**
19830          * @event load
19831          * Fires before the load method's callback is called.
19832          * @param {Object} This DataProxy object.
19833          * @param {Object} o The data object.
19834          * @param {Object} arg The callback argument object passed to the load function.
19835          */
19836         load : true,
19837         /**
19838          * @event loadexception
19839          * Fires if an Exception occurs during data retrieval.
19840          * @param {Object} This DataProxy object.
19841          * @param {Object} o The data object.
19842          * @param {Object} arg The callback argument object passed to the load function.
19843          * @param {Object} e The Exception.
19844          */
19845         loadexception : true
19846     });
19847     Roo.data.DataProxy.superclass.constructor.call(this);
19848 };
19849
19850 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19851
19852     /**
19853      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19854      */
19855 /*
19856  * Based on:
19857  * Ext JS Library 1.1.1
19858  * Copyright(c) 2006-2007, Ext JS, LLC.
19859  *
19860  * Originally Released Under LGPL - original licence link has changed is not relivant.
19861  *
19862  * Fork - LGPL
19863  * <script type="text/javascript">
19864  */
19865 /**
19866  * @class Roo.data.MemoryProxy
19867  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19868  * to the Reader when its load method is called.
19869  * @constructor
19870  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19871  */
19872 Roo.data.MemoryProxy = function(data){
19873     if (data.data) {
19874         data = data.data;
19875     }
19876     Roo.data.MemoryProxy.superclass.constructor.call(this);
19877     this.data = data;
19878 };
19879
19880 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19881     /**
19882      * Load data from the requested source (in this case an in-memory
19883      * data object passed to the constructor), read the data object into
19884      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19885      * process that block using the passed callback.
19886      * @param {Object} params This parameter is not used by the MemoryProxy class.
19887      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19888      * object into a block of Roo.data.Records.
19889      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19890      * The function must be passed <ul>
19891      * <li>The Record block object</li>
19892      * <li>The "arg" argument from the load function</li>
19893      * <li>A boolean success indicator</li>
19894      * </ul>
19895      * @param {Object} scope The scope in which to call the callback
19896      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19897      */
19898     load : function(params, reader, callback, scope, arg){
19899         params = params || {};
19900         var result;
19901         try {
19902             result = reader.readRecords(this.data);
19903         }catch(e){
19904             this.fireEvent("loadexception", this, arg, null, e);
19905             callback.call(scope, null, arg, false);
19906             return;
19907         }
19908         callback.call(scope, result, arg, true);
19909     },
19910     
19911     // private
19912     update : function(params, records){
19913         
19914     }
19915 });/*
19916  * Based on:
19917  * Ext JS Library 1.1.1
19918  * Copyright(c) 2006-2007, Ext JS, LLC.
19919  *
19920  * Originally Released Under LGPL - original licence link has changed is not relivant.
19921  *
19922  * Fork - LGPL
19923  * <script type="text/javascript">
19924  */
19925 /**
19926  * @class Roo.data.HttpProxy
19927  * @extends Roo.data.DataProxy
19928  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19929  * configured to reference a certain URL.<br><br>
19930  * <p>
19931  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19932  * from which the running page was served.<br><br>
19933  * <p>
19934  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19935  * <p>
19936  * Be aware that to enable the browser to parse an XML document, the server must set
19937  * the Content-Type header in the HTTP response to "text/xml".
19938  * @constructor
19939  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19940  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19941  * will be used to make the request.
19942  */
19943 Roo.data.HttpProxy = function(conn){
19944     Roo.data.HttpProxy.superclass.constructor.call(this);
19945     // is conn a conn config or a real conn?
19946     this.conn = conn;
19947     this.useAjax = !conn || !conn.events;
19948   
19949 };
19950
19951 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19952     // thse are take from connection...
19953     
19954     /**
19955      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19956      */
19957     /**
19958      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19959      * extra parameters to each request made by this object. (defaults to undefined)
19960      */
19961     /**
19962      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19963      *  to each request made by this object. (defaults to undefined)
19964      */
19965     /**
19966      * @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)
19967      */
19968     /**
19969      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19970      */
19971      /**
19972      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19973      * @type Boolean
19974      */
19975   
19976
19977     /**
19978      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19979      * @type Boolean
19980      */
19981     /**
19982      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19983      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19984      * a finer-grained basis than the DataProxy events.
19985      */
19986     getConnection : function(){
19987         return this.useAjax ? Roo.Ajax : this.conn;
19988     },
19989
19990     /**
19991      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19992      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19993      * process that block using the passed callback.
19994      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19995      * for the request to the remote server.
19996      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19997      * object into a block of Roo.data.Records.
19998      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19999      * The function must be passed <ul>
20000      * <li>The Record block object</li>
20001      * <li>The "arg" argument from the load function</li>
20002      * <li>A boolean success indicator</li>
20003      * </ul>
20004      * @param {Object} scope The scope in which to call the callback
20005      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20006      */
20007     load : function(params, reader, callback, scope, arg){
20008         if(this.fireEvent("beforeload", this, params) !== false){
20009             var  o = {
20010                 params : params || {},
20011                 request: {
20012                     callback : callback,
20013                     scope : scope,
20014                     arg : arg
20015                 },
20016                 reader: reader,
20017                 callback : this.loadResponse,
20018                 scope: this
20019             };
20020             if(this.useAjax){
20021                 Roo.applyIf(o, this.conn);
20022                 if(this.activeRequest){
20023                     Roo.Ajax.abort(this.activeRequest);
20024                 }
20025                 this.activeRequest = Roo.Ajax.request(o);
20026             }else{
20027                 this.conn.request(o);
20028             }
20029         }else{
20030             callback.call(scope||this, null, arg, false);
20031         }
20032     },
20033
20034     // private
20035     loadResponse : function(o, success, response){
20036         delete this.activeRequest;
20037         if(!success){
20038             this.fireEvent("loadexception", this, o, response);
20039             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20040             return;
20041         }
20042         var result;
20043         try {
20044             result = o.reader.read(response);
20045         }catch(e){
20046             this.fireEvent("loadexception", this, o, response, e);
20047             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20048             return;
20049         }
20050         
20051         this.fireEvent("load", this, o, o.request.arg);
20052         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20053     },
20054
20055     // private
20056     update : function(dataSet){
20057
20058     },
20059
20060     // private
20061     updateResponse : function(dataSet){
20062
20063     }
20064 });/*
20065  * Based on:
20066  * Ext JS Library 1.1.1
20067  * Copyright(c) 2006-2007, Ext JS, LLC.
20068  *
20069  * Originally Released Under LGPL - original licence link has changed is not relivant.
20070  *
20071  * Fork - LGPL
20072  * <script type="text/javascript">
20073  */
20074
20075 /**
20076  * @class Roo.data.ScriptTagProxy
20077  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20078  * other than the originating domain of the running page.<br><br>
20079  * <p>
20080  * <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
20081  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20082  * <p>
20083  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20084  * source code that is used as the source inside a &lt;script> tag.<br><br>
20085  * <p>
20086  * In order for the browser to process the returned data, the server must wrap the data object
20087  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20088  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20089  * depending on whether the callback name was passed:
20090  * <p>
20091  * <pre><code>
20092 boolean scriptTag = false;
20093 String cb = request.getParameter("callback");
20094 if (cb != null) {
20095     scriptTag = true;
20096     response.setContentType("text/javascript");
20097 } else {
20098     response.setContentType("application/x-json");
20099 }
20100 Writer out = response.getWriter();
20101 if (scriptTag) {
20102     out.write(cb + "(");
20103 }
20104 out.print(dataBlock.toJsonString());
20105 if (scriptTag) {
20106     out.write(");");
20107 }
20108 </pre></code>
20109  *
20110  * @constructor
20111  * @param {Object} config A configuration object.
20112  */
20113 Roo.data.ScriptTagProxy = function(config){
20114     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20115     Roo.apply(this, config);
20116     this.head = document.getElementsByTagName("head")[0];
20117 };
20118
20119 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20120
20121 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20122     /**
20123      * @cfg {String} url The URL from which to request the data object.
20124      */
20125     /**
20126      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20127      */
20128     timeout : 30000,
20129     /**
20130      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20131      * the server the name of the callback function set up by the load call to process the returned data object.
20132      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20133      * javascript output which calls this named function passing the data object as its only parameter.
20134      */
20135     callbackParam : "callback",
20136     /**
20137      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20138      * name to the request.
20139      */
20140     nocache : true,
20141
20142     /**
20143      * Load data from the configured URL, read the data object into
20144      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20145      * process that block using the passed callback.
20146      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20147      * for the request to the remote server.
20148      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20149      * object into a block of Roo.data.Records.
20150      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20151      * The function must be passed <ul>
20152      * <li>The Record block object</li>
20153      * <li>The "arg" argument from the load function</li>
20154      * <li>A boolean success indicator</li>
20155      * </ul>
20156      * @param {Object} scope The scope in which to call the callback
20157      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20158      */
20159     load : function(params, reader, callback, scope, arg){
20160         if(this.fireEvent("beforeload", this, params) !== false){
20161
20162             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20163
20164             var url = this.url;
20165             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20166             if(this.nocache){
20167                 url += "&_dc=" + (new Date().getTime());
20168             }
20169             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20170             var trans = {
20171                 id : transId,
20172                 cb : "stcCallback"+transId,
20173                 scriptId : "stcScript"+transId,
20174                 params : params,
20175                 arg : arg,
20176                 url : url,
20177                 callback : callback,
20178                 scope : scope,
20179                 reader : reader
20180             };
20181             var conn = this;
20182
20183             window[trans.cb] = function(o){
20184                 conn.handleResponse(o, trans);
20185             };
20186
20187             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20188
20189             if(this.autoAbort !== false){
20190                 this.abort();
20191             }
20192
20193             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20194
20195             var script = document.createElement("script");
20196             script.setAttribute("src", url);
20197             script.setAttribute("type", "text/javascript");
20198             script.setAttribute("id", trans.scriptId);
20199             this.head.appendChild(script);
20200
20201             this.trans = trans;
20202         }else{
20203             callback.call(scope||this, null, arg, false);
20204         }
20205     },
20206
20207     // private
20208     isLoading : function(){
20209         return this.trans ? true : false;
20210     },
20211
20212     /**
20213      * Abort the current server request.
20214      */
20215     abort : function(){
20216         if(this.isLoading()){
20217             this.destroyTrans(this.trans);
20218         }
20219     },
20220
20221     // private
20222     destroyTrans : function(trans, isLoaded){
20223         this.head.removeChild(document.getElementById(trans.scriptId));
20224         clearTimeout(trans.timeoutId);
20225         if(isLoaded){
20226             window[trans.cb] = undefined;
20227             try{
20228                 delete window[trans.cb];
20229             }catch(e){}
20230         }else{
20231             // if hasn't been loaded, wait for load to remove it to prevent script error
20232             window[trans.cb] = function(){
20233                 window[trans.cb] = undefined;
20234                 try{
20235                     delete window[trans.cb];
20236                 }catch(e){}
20237             };
20238         }
20239     },
20240
20241     // private
20242     handleResponse : function(o, trans){
20243         this.trans = false;
20244         this.destroyTrans(trans, true);
20245         var result;
20246         try {
20247             result = trans.reader.readRecords(o);
20248         }catch(e){
20249             this.fireEvent("loadexception", this, o, trans.arg, e);
20250             trans.callback.call(trans.scope||window, null, trans.arg, false);
20251             return;
20252         }
20253         this.fireEvent("load", this, o, trans.arg);
20254         trans.callback.call(trans.scope||window, result, trans.arg, true);
20255     },
20256
20257     // private
20258     handleFailure : function(trans){
20259         this.trans = false;
20260         this.destroyTrans(trans, false);
20261         this.fireEvent("loadexception", this, null, trans.arg);
20262         trans.callback.call(trans.scope||window, null, trans.arg, false);
20263     }
20264 });/*
20265  * Based on:
20266  * Ext JS Library 1.1.1
20267  * Copyright(c) 2006-2007, Ext JS, LLC.
20268  *
20269  * Originally Released Under LGPL - original licence link has changed is not relivant.
20270  *
20271  * Fork - LGPL
20272  * <script type="text/javascript">
20273  */
20274
20275 /**
20276  * @class Roo.data.JsonReader
20277  * @extends Roo.data.DataReader
20278  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20279  * based on mappings in a provided Roo.data.Record constructor.
20280  * <p>
20281  * Example code:
20282  * <pre><code>
20283 var RecordDef = Roo.data.Record.create([
20284     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20285     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20286 ]);
20287 var myReader = new Roo.data.JsonReader({
20288     totalProperty: "results",    // The property which contains the total dataset size (optional)
20289     root: "rows",                // The property which contains an Array of row objects
20290     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20291 }, RecordDef);
20292 </code></pre>
20293  * <p>
20294  * This would consume a JSON file like this:
20295  * <pre><code>
20296 { 'results': 2, 'rows': [
20297     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20298     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20299 }
20300 </code></pre>
20301  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20302  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20303  * paged from the remote server.
20304  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20305  * @cfg {String} root name of the property which contains the Array of row objects.
20306  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20307  * @constructor
20308  * Create a new JsonReader
20309  * @param {Object} meta Metadata configuration options
20310  * @param {Object} recordType Either an Array of field definition objects,
20311  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20312  */
20313 Roo.data.JsonReader = function(meta, recordType){
20314     
20315     meta = meta || {};
20316     // set some defaults:
20317     Roo.applyIf(meta, {
20318         totalProperty: 'total',
20319         successProperty : 'success',
20320         root : 'data',
20321         id : 'id'
20322     });
20323     
20324     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20325 };
20326 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20327     /**
20328      * This method is only used by a DataProxy which has retrieved data from a remote server.
20329      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20330      * @return {Object} data A data block which is used by an Roo.data.Store object as
20331      * a cache of Roo.data.Records.
20332      */
20333     read : function(response){
20334         var json = response.responseText;
20335         /* eval:var:o */
20336         var o = eval("("+json+")");
20337         if(!o) {
20338             throw {message: "JsonReader.read: Json object not found"};
20339         }
20340         
20341         if(o.metaData){
20342             delete this.ef;
20343             this.meta = o.metaData;
20344             this.recordType = Roo.data.Record.create(o.metaData.fields);
20345             this.onMetaChange(this.meta, this.recordType, o);
20346         }
20347         return this.readRecords(o);
20348     },
20349
20350     // private function a store will implement
20351     onMetaChange : function(meta, recordType, o){
20352
20353     },
20354
20355     /**
20356          * @ignore
20357          */
20358     simpleAccess: function(obj, subsc) {
20359         return obj[subsc];
20360     },
20361
20362         /**
20363          * @ignore
20364          */
20365     getJsonAccessor: function(){
20366         var re = /[\[\.]/;
20367         return function(expr) {
20368             try {
20369                 return(re.test(expr))
20370                     ? new Function("obj", "return obj." + expr)
20371                     : function(obj){
20372                         return obj[expr];
20373                     };
20374             } catch(e){}
20375             return Roo.emptyFn;
20376         };
20377     }(),
20378
20379     /**
20380      * Create a data block containing Roo.data.Records from an XML document.
20381      * @param {Object} o An object which contains an Array of row objects in the property specified
20382      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20383      * which contains the total size of the dataset.
20384      * @return {Object} data A data block which is used by an Roo.data.Store object as
20385      * a cache of Roo.data.Records.
20386      */
20387     readRecords : function(o){
20388         /**
20389          * After any data loads, the raw JSON data is available for further custom processing.
20390          * @type Object
20391          */
20392         this.jsonData = o;
20393         var s = this.meta, Record = this.recordType,
20394             f = Record.prototype.fields, fi = f.items, fl = f.length;
20395
20396 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20397         if (!this.ef) {
20398             if(s.totalProperty) {
20399                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20400                 }
20401                 if(s.successProperty) {
20402                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20403                 }
20404                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20405                 if (s.id) {
20406                         var g = this.getJsonAccessor(s.id);
20407                         this.getId = function(rec) {
20408                                 var r = g(rec);
20409                                 return (r === undefined || r === "") ? null : r;
20410                         };
20411                 } else {
20412                         this.getId = function(){return null;};
20413                 }
20414             this.ef = [];
20415             for(var i = 0; i < fl; i++){
20416                 f = fi[i];
20417                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20418                 this.ef[i] = this.getJsonAccessor(map);
20419             }
20420         }
20421
20422         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20423         if(s.totalProperty){
20424             var v = parseInt(this.getTotal(o), 10);
20425             if(!isNaN(v)){
20426                 totalRecords = v;
20427             }
20428         }
20429         if(s.successProperty){
20430             var v = this.getSuccess(o);
20431             if(v === false || v === 'false'){
20432                 success = false;
20433             }
20434         }
20435         var records = [];
20436             for(var i = 0; i < c; i++){
20437                     var n = root[i];
20438                 var values = {};
20439                 var id = this.getId(n);
20440                 for(var j = 0; j < fl; j++){
20441                     f = fi[j];
20442                 var v = this.ef[j](n);
20443                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20444                 }
20445                 var record = new Record(values, id);
20446                 record.json = n;
20447                 records[i] = record;
20448             }
20449             return {
20450                 success : success,
20451                 records : records,
20452                 totalRecords : totalRecords
20453             };
20454     }
20455 });/*
20456  * Based on:
20457  * Ext JS Library 1.1.1
20458  * Copyright(c) 2006-2007, Ext JS, LLC.
20459  *
20460  * Originally Released Under LGPL - original licence link has changed is not relivant.
20461  *
20462  * Fork - LGPL
20463  * <script type="text/javascript">
20464  */
20465
20466 /**
20467  * @class Roo.data.XmlReader
20468  * @extends Roo.data.DataReader
20469  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20470  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20471  * <p>
20472  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20473  * header in the HTTP response must be set to "text/xml".</em>
20474  * <p>
20475  * Example code:
20476  * <pre><code>
20477 var RecordDef = Roo.data.Record.create([
20478    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20479    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20480 ]);
20481 var myReader = new Roo.data.XmlReader({
20482    totalRecords: "results", // The element which contains the total dataset size (optional)
20483    record: "row",           // The repeated element which contains row information
20484    id: "id"                 // The element within the row that provides an ID for the record (optional)
20485 }, RecordDef);
20486 </code></pre>
20487  * <p>
20488  * This would consume an XML file like this:
20489  * <pre><code>
20490 &lt;?xml?>
20491 &lt;dataset>
20492  &lt;results>2&lt;/results>
20493  &lt;row>
20494    &lt;id>1&lt;/id>
20495    &lt;name>Bill&lt;/name>
20496    &lt;occupation>Gardener&lt;/occupation>
20497  &lt;/row>
20498  &lt;row>
20499    &lt;id>2&lt;/id>
20500    &lt;name>Ben&lt;/name>
20501    &lt;occupation>Horticulturalist&lt;/occupation>
20502  &lt;/row>
20503 &lt;/dataset>
20504 </code></pre>
20505  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20506  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20507  * paged from the remote server.
20508  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20509  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20510  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20511  * a record identifier value.
20512  * @constructor
20513  * Create a new XmlReader
20514  * @param {Object} meta Metadata configuration options
20515  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20516  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20517  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20518  */
20519 Roo.data.XmlReader = function(meta, recordType){
20520     meta = meta || {};
20521     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20522 };
20523 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20524     /**
20525      * This method is only used by a DataProxy which has retrieved data from a remote server.
20526          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20527          * to contain a method called 'responseXML' that returns an XML document object.
20528      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20529      * a cache of Roo.data.Records.
20530      */
20531     read : function(response){
20532         var doc = response.responseXML;
20533         if(!doc) {
20534             throw {message: "XmlReader.read: XML Document not available"};
20535         }
20536         return this.readRecords(doc);
20537     },
20538
20539     /**
20540      * Create a data block containing Roo.data.Records from an XML document.
20541          * @param {Object} doc A parsed XML document.
20542      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20543      * a cache of Roo.data.Records.
20544      */
20545     readRecords : function(doc){
20546         /**
20547          * After any data loads/reads, the raw XML Document is available for further custom processing.
20548          * @type XMLDocument
20549          */
20550         this.xmlData = doc;
20551         var root = doc.documentElement || doc;
20552         var q = Roo.DomQuery;
20553         var recordType = this.recordType, fields = recordType.prototype.fields;
20554         var sid = this.meta.id;
20555         var totalRecords = 0, success = true;
20556         if(this.meta.totalRecords){
20557             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20558         }
20559         
20560         if(this.meta.success){
20561             var sv = q.selectValue(this.meta.success, root, true);
20562             success = sv !== false && sv !== 'false';
20563         }
20564         var records = [];
20565         var ns = q.select(this.meta.record, root);
20566         for(var i = 0, len = ns.length; i < len; i++) {
20567                 var n = ns[i];
20568                 var values = {};
20569                 var id = sid ? q.selectValue(sid, n) : undefined;
20570                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20571                     var f = fields.items[j];
20572                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20573                     v = f.convert(v);
20574                     values[f.name] = v;
20575                 }
20576                 var record = new recordType(values, id);
20577                 record.node = n;
20578                 records[records.length] = record;
20579             }
20580
20581             return {
20582                 success : success,
20583                 records : records,
20584                 totalRecords : totalRecords || records.length
20585             };
20586     }
20587 });/*
20588  * Based on:
20589  * Ext JS Library 1.1.1
20590  * Copyright(c) 2006-2007, Ext JS, LLC.
20591  *
20592  * Originally Released Under LGPL - original licence link has changed is not relivant.
20593  *
20594  * Fork - LGPL
20595  * <script type="text/javascript">
20596  */
20597
20598 /**
20599  * @class Roo.data.ArrayReader
20600  * @extends Roo.data.DataReader
20601  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20602  * Each element of that Array represents a row of data fields. The
20603  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20604  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20605  * <p>
20606  * Example code:.
20607  * <pre><code>
20608 var RecordDef = Roo.data.Record.create([
20609     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20610     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20611 ]);
20612 var myReader = new Roo.data.ArrayReader({
20613     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20614 }, RecordDef);
20615 </code></pre>
20616  * <p>
20617  * This would consume an Array like this:
20618  * <pre><code>
20619 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20620   </code></pre>
20621  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20622  * @constructor
20623  * Create a new JsonReader
20624  * @param {Object} meta Metadata configuration options.
20625  * @param {Object} recordType Either an Array of field definition objects
20626  * as specified to {@link Roo.data.Record#create},
20627  * or an {@link Roo.data.Record} object
20628  * created using {@link Roo.data.Record#create}.
20629  */
20630 Roo.data.ArrayReader = function(meta, recordType){
20631     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20632 };
20633
20634 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20635     /**
20636      * Create a data block containing Roo.data.Records from an XML document.
20637      * @param {Object} o An Array of row objects which represents the dataset.
20638      * @return {Object} data A data block which is used by an Roo.data.Store object as
20639      * a cache of Roo.data.Records.
20640      */
20641     readRecords : function(o){
20642         var sid = this.meta ? this.meta.id : null;
20643         var recordType = this.recordType, fields = recordType.prototype.fields;
20644         var records = [];
20645         var root = o;
20646             for(var i = 0; i < root.length; i++){
20647                     var n = root[i];
20648                 var values = {};
20649                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20650                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20651                 var f = fields.items[j];
20652                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20653                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20654                 v = f.convert(v);
20655                 values[f.name] = v;
20656             }
20657                 var record = new recordType(values, id);
20658                 record.json = n;
20659                 records[records.length] = record;
20660             }
20661             return {
20662                 records : records,
20663                 totalRecords : records.length
20664             };
20665     }
20666 });/*
20667  * Based on:
20668  * Ext JS Library 1.1.1
20669  * Copyright(c) 2006-2007, Ext JS, LLC.
20670  *
20671  * Originally Released Under LGPL - original licence link has changed is not relivant.
20672  *
20673  * Fork - LGPL
20674  * <script type="text/javascript">
20675  */
20676
20677
20678 /**
20679  * @class Roo.data.Tree
20680  * @extends Roo.util.Observable
20681  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20682  * in the tree have most standard DOM functionality.
20683  * @constructor
20684  * @param {Node} root (optional) The root node
20685  */
20686 Roo.data.Tree = function(root){
20687    this.nodeHash = {};
20688    /**
20689     * The root node for this tree
20690     * @type Node
20691     */
20692    this.root = null;
20693    if(root){
20694        this.setRootNode(root);
20695    }
20696    this.addEvents({
20697        /**
20698         * @event append
20699         * Fires when a new child node is appended to a node in this tree.
20700         * @param {Tree} tree The owner tree
20701         * @param {Node} parent The parent node
20702         * @param {Node} node The newly appended node
20703         * @param {Number} index The index of the newly appended node
20704         */
20705        "append" : true,
20706        /**
20707         * @event remove
20708         * Fires when a child node is removed from a node in this tree.
20709         * @param {Tree} tree The owner tree
20710         * @param {Node} parent The parent node
20711         * @param {Node} node The child node removed
20712         */
20713        "remove" : true,
20714        /**
20715         * @event move
20716         * Fires when a node is moved to a new location in the tree
20717         * @param {Tree} tree The owner tree
20718         * @param {Node} node The node moved
20719         * @param {Node} oldParent The old parent of this node
20720         * @param {Node} newParent The new parent of this node
20721         * @param {Number} index The index it was moved to
20722         */
20723        "move" : true,
20724        /**
20725         * @event insert
20726         * Fires when a new child node is inserted in a node in this tree.
20727         * @param {Tree} tree The owner tree
20728         * @param {Node} parent The parent node
20729         * @param {Node} node The child node inserted
20730         * @param {Node} refNode The child node the node was inserted before
20731         */
20732        "insert" : true,
20733        /**
20734         * @event beforeappend
20735         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20736         * @param {Tree} tree The owner tree
20737         * @param {Node} parent The parent node
20738         * @param {Node} node The child node to be appended
20739         */
20740        "beforeappend" : true,
20741        /**
20742         * @event beforeremove
20743         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20744         * @param {Tree} tree The owner tree
20745         * @param {Node} parent The parent node
20746         * @param {Node} node The child node to be removed
20747         */
20748        "beforeremove" : true,
20749        /**
20750         * @event beforemove
20751         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20752         * @param {Tree} tree The owner tree
20753         * @param {Node} node The node being moved
20754         * @param {Node} oldParent The parent of the node
20755         * @param {Node} newParent The new parent the node is moving to
20756         * @param {Number} index The index it is being moved to
20757         */
20758        "beforemove" : true,
20759        /**
20760         * @event beforeinsert
20761         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20762         * @param {Tree} tree The owner tree
20763         * @param {Node} parent The parent node
20764         * @param {Node} node The child node to be inserted
20765         * @param {Node} refNode The child node the node is being inserted before
20766         */
20767        "beforeinsert" : true
20768    });
20769
20770     Roo.data.Tree.superclass.constructor.call(this);
20771 };
20772
20773 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20774     pathSeparator: "/",
20775
20776     proxyNodeEvent : function(){
20777         return this.fireEvent.apply(this, arguments);
20778     },
20779
20780     /**
20781      * Returns the root node for this tree.
20782      * @return {Node}
20783      */
20784     getRootNode : function(){
20785         return this.root;
20786     },
20787
20788     /**
20789      * Sets the root node for this tree.
20790      * @param {Node} node
20791      * @return {Node}
20792      */
20793     setRootNode : function(node){
20794         this.root = node;
20795         node.ownerTree = this;
20796         node.isRoot = true;
20797         this.registerNode(node);
20798         return node;
20799     },
20800
20801     /**
20802      * Gets a node in this tree by its id.
20803      * @param {String} id
20804      * @return {Node}
20805      */
20806     getNodeById : function(id){
20807         return this.nodeHash[id];
20808     },
20809
20810     registerNode : function(node){
20811         this.nodeHash[node.id] = node;
20812     },
20813
20814     unregisterNode : function(node){
20815         delete this.nodeHash[node.id];
20816     },
20817
20818     toString : function(){
20819         return "[Tree"+(this.id?" "+this.id:"")+"]";
20820     }
20821 });
20822
20823 /**
20824  * @class Roo.data.Node
20825  * @extends Roo.util.Observable
20826  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20827  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20828  * @constructor
20829  * @param {Object} attributes The attributes/config for the node
20830  */
20831 Roo.data.Node = function(attributes){
20832     /**
20833      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20834      * @type {Object}
20835      */
20836     this.attributes = attributes || {};
20837     this.leaf = this.attributes.leaf;
20838     /**
20839      * The node id. @type String
20840      */
20841     this.id = this.attributes.id;
20842     if(!this.id){
20843         this.id = Roo.id(null, "ynode-");
20844         this.attributes.id = this.id;
20845     }
20846     /**
20847      * All child nodes of this node. @type Array
20848      */
20849     this.childNodes = [];
20850     if(!this.childNodes.indexOf){ // indexOf is a must
20851         this.childNodes.indexOf = function(o){
20852             for(var i = 0, len = this.length; i < len; i++){
20853                 if(this[i] == o) return i;
20854             }
20855             return -1;
20856         };
20857     }
20858     /**
20859      * The parent node for this node. @type Node
20860      */
20861     this.parentNode = null;
20862     /**
20863      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20864      */
20865     this.firstChild = null;
20866     /**
20867      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20868      */
20869     this.lastChild = null;
20870     /**
20871      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20872      */
20873     this.previousSibling = null;
20874     /**
20875      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20876      */
20877     this.nextSibling = null;
20878
20879     this.addEvents({
20880        /**
20881         * @event append
20882         * Fires when a new child node is appended
20883         * @param {Tree} tree The owner tree
20884         * @param {Node} this This node
20885         * @param {Node} node The newly appended node
20886         * @param {Number} index The index of the newly appended node
20887         */
20888        "append" : true,
20889        /**
20890         * @event remove
20891         * Fires when a child node is removed
20892         * @param {Tree} tree The owner tree
20893         * @param {Node} this This node
20894         * @param {Node} node The removed node
20895         */
20896        "remove" : true,
20897        /**
20898         * @event move
20899         * Fires when this node is moved to a new location in the tree
20900         * @param {Tree} tree The owner tree
20901         * @param {Node} this This node
20902         * @param {Node} oldParent The old parent of this node
20903         * @param {Node} newParent The new parent of this node
20904         * @param {Number} index The index it was moved to
20905         */
20906        "move" : true,
20907        /**
20908         * @event insert
20909         * Fires when a new child node is inserted.
20910         * @param {Tree} tree The owner tree
20911         * @param {Node} this This node
20912         * @param {Node} node The child node inserted
20913         * @param {Node} refNode The child node the node was inserted before
20914         */
20915        "insert" : true,
20916        /**
20917         * @event beforeappend
20918         * Fires before a new child is appended, return false to cancel the append.
20919         * @param {Tree} tree The owner tree
20920         * @param {Node} this This node
20921         * @param {Node} node The child node to be appended
20922         */
20923        "beforeappend" : true,
20924        /**
20925         * @event beforeremove
20926         * Fires before a child is removed, return false to cancel the remove.
20927         * @param {Tree} tree The owner tree
20928         * @param {Node} this This node
20929         * @param {Node} node The child node to be removed
20930         */
20931        "beforeremove" : true,
20932        /**
20933         * @event beforemove
20934         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20935         * @param {Tree} tree The owner tree
20936         * @param {Node} this This node
20937         * @param {Node} oldParent The parent of this node
20938         * @param {Node} newParent The new parent this node is moving to
20939         * @param {Number} index The index it is being moved to
20940         */
20941        "beforemove" : true,
20942        /**
20943         * @event beforeinsert
20944         * Fires before a new child is inserted, return false to cancel the insert.
20945         * @param {Tree} tree The owner tree
20946         * @param {Node} this This node
20947         * @param {Node} node The child node to be inserted
20948         * @param {Node} refNode The child node the node is being inserted before
20949         */
20950        "beforeinsert" : true
20951    });
20952     this.listeners = this.attributes.listeners;
20953     Roo.data.Node.superclass.constructor.call(this);
20954 };
20955
20956 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20957     fireEvent : function(evtName){
20958         // first do standard event for this node
20959         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20960             return false;
20961         }
20962         // then bubble it up to the tree if the event wasn't cancelled
20963         var ot = this.getOwnerTree();
20964         if(ot){
20965             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20966                 return false;
20967             }
20968         }
20969         return true;
20970     },
20971
20972     /**
20973      * Returns true if this node is a leaf
20974      * @return {Boolean}
20975      */
20976     isLeaf : function(){
20977         return this.leaf === true;
20978     },
20979
20980     // private
20981     setFirstChild : function(node){
20982         this.firstChild = node;
20983     },
20984
20985     //private
20986     setLastChild : function(node){
20987         this.lastChild = node;
20988     },
20989
20990
20991     /**
20992      * Returns true if this node is the last child of its parent
20993      * @return {Boolean}
20994      */
20995     isLast : function(){
20996        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20997     },
20998
20999     /**
21000      * Returns true if this node is the first child of its parent
21001      * @return {Boolean}
21002      */
21003     isFirst : function(){
21004        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21005     },
21006
21007     hasChildNodes : function(){
21008         return !this.isLeaf() && this.childNodes.length > 0;
21009     },
21010
21011     /**
21012      * Insert node(s) as the last child node of this node.
21013      * @param {Node/Array} node The node or Array of nodes to append
21014      * @return {Node} The appended node if single append, or null if an array was passed
21015      */
21016     appendChild : function(node){
21017         var multi = false;
21018         if(node instanceof Array){
21019             multi = node;
21020         }else if(arguments.length > 1){
21021             multi = arguments;
21022         }
21023         // if passed an array or multiple args do them one by one
21024         if(multi){
21025             for(var i = 0, len = multi.length; i < len; i++) {
21026                 this.appendChild(multi[i]);
21027             }
21028         }else{
21029             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21030                 return false;
21031             }
21032             var index = this.childNodes.length;
21033             var oldParent = node.parentNode;
21034             // it's a move, make sure we move it cleanly
21035             if(oldParent){
21036                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21037                     return false;
21038                 }
21039                 oldParent.removeChild(node);
21040             }
21041             index = this.childNodes.length;
21042             if(index == 0){
21043                 this.setFirstChild(node);
21044             }
21045             this.childNodes.push(node);
21046             node.parentNode = this;
21047             var ps = this.childNodes[index-1];
21048             if(ps){
21049                 node.previousSibling = ps;
21050                 ps.nextSibling = node;
21051             }else{
21052                 node.previousSibling = null;
21053             }
21054             node.nextSibling = null;
21055             this.setLastChild(node);
21056             node.setOwnerTree(this.getOwnerTree());
21057             this.fireEvent("append", this.ownerTree, this, node, index);
21058             if(oldParent){
21059                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21060             }
21061             return node;
21062         }
21063     },
21064
21065     /**
21066      * Removes a child node from this node.
21067      * @param {Node} node The node to remove
21068      * @return {Node} The removed node
21069      */
21070     removeChild : function(node){
21071         var index = this.childNodes.indexOf(node);
21072         if(index == -1){
21073             return false;
21074         }
21075         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21076             return false;
21077         }
21078
21079         // remove it from childNodes collection
21080         this.childNodes.splice(index, 1);
21081
21082         // update siblings
21083         if(node.previousSibling){
21084             node.previousSibling.nextSibling = node.nextSibling;
21085         }
21086         if(node.nextSibling){
21087             node.nextSibling.previousSibling = node.previousSibling;
21088         }
21089
21090         // update child refs
21091         if(this.firstChild == node){
21092             this.setFirstChild(node.nextSibling);
21093         }
21094         if(this.lastChild == node){
21095             this.setLastChild(node.previousSibling);
21096         }
21097
21098         node.setOwnerTree(null);
21099         // clear any references from the node
21100         node.parentNode = null;
21101         node.previousSibling = null;
21102         node.nextSibling = null;
21103         this.fireEvent("remove", this.ownerTree, this, node);
21104         return node;
21105     },
21106
21107     /**
21108      * Inserts the first node before the second node in this nodes childNodes collection.
21109      * @param {Node} node The node to insert
21110      * @param {Node} refNode The node to insert before (if null the node is appended)
21111      * @return {Node} The inserted node
21112      */
21113     insertBefore : function(node, refNode){
21114         if(!refNode){ // like standard Dom, refNode can be null for append
21115             return this.appendChild(node);
21116         }
21117         // nothing to do
21118         if(node == refNode){
21119             return false;
21120         }
21121
21122         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21123             return false;
21124         }
21125         var index = this.childNodes.indexOf(refNode);
21126         var oldParent = node.parentNode;
21127         var refIndex = index;
21128
21129         // when moving internally, indexes will change after remove
21130         if(oldParent == this && this.childNodes.indexOf(node) < index){
21131             refIndex--;
21132         }
21133
21134         // it's a move, make sure we move it cleanly
21135         if(oldParent){
21136             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21137                 return false;
21138             }
21139             oldParent.removeChild(node);
21140         }
21141         if(refIndex == 0){
21142             this.setFirstChild(node);
21143         }
21144         this.childNodes.splice(refIndex, 0, node);
21145         node.parentNode = this;
21146         var ps = this.childNodes[refIndex-1];
21147         if(ps){
21148             node.previousSibling = ps;
21149             ps.nextSibling = node;
21150         }else{
21151             node.previousSibling = null;
21152         }
21153         node.nextSibling = refNode;
21154         refNode.previousSibling = node;
21155         node.setOwnerTree(this.getOwnerTree());
21156         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21157         if(oldParent){
21158             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21159         }
21160         return node;
21161     },
21162
21163     /**
21164      * Returns the child node at the specified index.
21165      * @param {Number} index
21166      * @return {Node}
21167      */
21168     item : function(index){
21169         return this.childNodes[index];
21170     },
21171
21172     /**
21173      * Replaces one child node in this node with another.
21174      * @param {Node} newChild The replacement node
21175      * @param {Node} oldChild The node to replace
21176      * @return {Node} The replaced node
21177      */
21178     replaceChild : function(newChild, oldChild){
21179         this.insertBefore(newChild, oldChild);
21180         this.removeChild(oldChild);
21181         return oldChild;
21182     },
21183
21184     /**
21185      * Returns the index of a child node
21186      * @param {Node} node
21187      * @return {Number} The index of the node or -1 if it was not found
21188      */
21189     indexOf : function(child){
21190         return this.childNodes.indexOf(child);
21191     },
21192
21193     /**
21194      * Returns the tree this node is in.
21195      * @return {Tree}
21196      */
21197     getOwnerTree : function(){
21198         // if it doesn't have one, look for one
21199         if(!this.ownerTree){
21200             var p = this;
21201             while(p){
21202                 if(p.ownerTree){
21203                     this.ownerTree = p.ownerTree;
21204                     break;
21205                 }
21206                 p = p.parentNode;
21207             }
21208         }
21209         return this.ownerTree;
21210     },
21211
21212     /**
21213      * Returns depth of this node (the root node has a depth of 0)
21214      * @return {Number}
21215      */
21216     getDepth : function(){
21217         var depth = 0;
21218         var p = this;
21219         while(p.parentNode){
21220             ++depth;
21221             p = p.parentNode;
21222         }
21223         return depth;
21224     },
21225
21226     // private
21227     setOwnerTree : function(tree){
21228         // if it's move, we need to update everyone
21229         if(tree != this.ownerTree){
21230             if(this.ownerTree){
21231                 this.ownerTree.unregisterNode(this);
21232             }
21233             this.ownerTree = tree;
21234             var cs = this.childNodes;
21235             for(var i = 0, len = cs.length; i < len; i++) {
21236                 cs[i].setOwnerTree(tree);
21237             }
21238             if(tree){
21239                 tree.registerNode(this);
21240             }
21241         }
21242     },
21243
21244     /**
21245      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21246      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21247      * @return {String} The path
21248      */
21249     getPath : function(attr){
21250         attr = attr || "id";
21251         var p = this.parentNode;
21252         var b = [this.attributes[attr]];
21253         while(p){
21254             b.unshift(p.attributes[attr]);
21255             p = p.parentNode;
21256         }
21257         var sep = this.getOwnerTree().pathSeparator;
21258         return sep + b.join(sep);
21259     },
21260
21261     /**
21262      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21263      * function call will be the scope provided or the current node. The arguments to the function
21264      * will be the args provided or the current node. If the function returns false at any point,
21265      * the bubble is stopped.
21266      * @param {Function} fn The function to call
21267      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21268      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21269      */
21270     bubble : function(fn, scope, args){
21271         var p = this;
21272         while(p){
21273             if(fn.call(scope || p, args || p) === false){
21274                 break;
21275             }
21276             p = p.parentNode;
21277         }
21278     },
21279
21280     /**
21281      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21282      * function call will be the scope provided or the current node. The arguments to the function
21283      * will be the args provided or the current node. If the function returns false at any point,
21284      * the cascade is stopped on that branch.
21285      * @param {Function} fn The function to call
21286      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21287      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21288      */
21289     cascade : function(fn, scope, args){
21290         if(fn.call(scope || this, args || this) !== false){
21291             var cs = this.childNodes;
21292             for(var i = 0, len = cs.length; i < len; i++) {
21293                 cs[i].cascade(fn, scope, args);
21294             }
21295         }
21296     },
21297
21298     /**
21299      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21300      * function call will be the scope provided or the current node. The arguments to the function
21301      * will be the args provided or the current node. If the function returns false at any point,
21302      * the iteration stops.
21303      * @param {Function} fn The function to call
21304      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21305      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21306      */
21307     eachChild : function(fn, scope, args){
21308         var cs = this.childNodes;
21309         for(var i = 0, len = cs.length; i < len; i++) {
21310                 if(fn.call(scope || this, args || cs[i]) === false){
21311                     break;
21312                 }
21313         }
21314     },
21315
21316     /**
21317      * Finds the first child that has the attribute with the specified value.
21318      * @param {String} attribute The attribute name
21319      * @param {Mixed} value The value to search for
21320      * @return {Node} The found child or null if none was found
21321      */
21322     findChild : function(attribute, value){
21323         var cs = this.childNodes;
21324         for(var i = 0, len = cs.length; i < len; i++) {
21325                 if(cs[i].attributes[attribute] == value){
21326                     return cs[i];
21327                 }
21328         }
21329         return null;
21330     },
21331
21332     /**
21333      * Finds the first child by a custom function. The child matches if the function passed
21334      * returns true.
21335      * @param {Function} fn
21336      * @param {Object} scope (optional)
21337      * @return {Node} The found child or null if none was found
21338      */
21339     findChildBy : function(fn, scope){
21340         var cs = this.childNodes;
21341         for(var i = 0, len = cs.length; i < len; i++) {
21342                 if(fn.call(scope||cs[i], cs[i]) === true){
21343                     return cs[i];
21344                 }
21345         }
21346         return null;
21347     },
21348
21349     /**
21350      * Sorts this nodes children using the supplied sort function
21351      * @param {Function} fn
21352      * @param {Object} scope (optional)
21353      */
21354     sort : function(fn, scope){
21355         var cs = this.childNodes;
21356         var len = cs.length;
21357         if(len > 0){
21358             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21359             cs.sort(sortFn);
21360             for(var i = 0; i < len; i++){
21361                 var n = cs[i];
21362                 n.previousSibling = cs[i-1];
21363                 n.nextSibling = cs[i+1];
21364                 if(i == 0){
21365                     this.setFirstChild(n);
21366                 }
21367                 if(i == len-1){
21368                     this.setLastChild(n);
21369                 }
21370             }
21371         }
21372     },
21373
21374     /**
21375      * Returns true if this node is an ancestor (at any point) of the passed node.
21376      * @param {Node} node
21377      * @return {Boolean}
21378      */
21379     contains : function(node){
21380         return node.isAncestor(this);
21381     },
21382
21383     /**
21384      * Returns true if the passed node is an ancestor (at any point) of this node.
21385      * @param {Node} node
21386      * @return {Boolean}
21387      */
21388     isAncestor : function(node){
21389         var p = this.parentNode;
21390         while(p){
21391             if(p == node){
21392                 return true;
21393             }
21394             p = p.parentNode;
21395         }
21396         return false;
21397     },
21398
21399     toString : function(){
21400         return "[Node"+(this.id?" "+this.id:"")+"]";
21401     }
21402 });/*
21403  * Based on:
21404  * Ext JS Library 1.1.1
21405  * Copyright(c) 2006-2007, Ext JS, LLC.
21406  *
21407  * Originally Released Under LGPL - original licence link has changed is not relivant.
21408  *
21409  * Fork - LGPL
21410  * <script type="text/javascript">
21411  */
21412  
21413
21414 /**
21415  * @class Roo.ComponentMgr
21416  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21417  * @singleton
21418  */
21419 Roo.ComponentMgr = function(){
21420     var all = new Roo.util.MixedCollection();
21421
21422     return {
21423         /**
21424          * Registers a component.
21425          * @param {Roo.Component} c The component
21426          */
21427         register : function(c){
21428             all.add(c);
21429         },
21430
21431         /**
21432          * Unregisters a component.
21433          * @param {Roo.Component} c The component
21434          */
21435         unregister : function(c){
21436             all.remove(c);
21437         },
21438
21439         /**
21440          * Returns a component by id
21441          * @param {String} id The component id
21442          */
21443         get : function(id){
21444             return all.get(id);
21445         },
21446
21447         /**
21448          * Registers a function that will be called when a specified component is added to ComponentMgr
21449          * @param {String} id The component id
21450          * @param {Funtction} fn The callback function
21451          * @param {Object} scope The scope of the callback
21452          */
21453         onAvailable : function(id, fn, scope){
21454             all.on("add", function(index, o){
21455                 if(o.id == id){
21456                     fn.call(scope || o, o);
21457                     all.un("add", fn, scope);
21458                 }
21459             });
21460         }
21461     };
21462 }();/*
21463  * Based on:
21464  * Ext JS Library 1.1.1
21465  * Copyright(c) 2006-2007, Ext JS, LLC.
21466  *
21467  * Originally Released Under LGPL - original licence link has changed is not relivant.
21468  *
21469  * Fork - LGPL
21470  * <script type="text/javascript">
21471  */
21472  
21473 /**
21474  * @class Roo.Component
21475  * @extends Roo.util.Observable
21476  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21477  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21478  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21479  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21480  * All visual components (widgets) that require rendering into a layout should subclass Component.
21481  * @constructor
21482  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21483  * 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
21484  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21485  */
21486 Roo.Component = function(config){
21487     config = config || {};
21488     if(config.tagName || config.dom || typeof config == "string"){ // element object
21489         config = {el: config, id: config.id || config};
21490     }
21491     this.initialConfig = config;
21492
21493     Roo.apply(this, config);
21494     this.addEvents({
21495         /**
21496          * @event disable
21497          * Fires after the component is disabled.
21498              * @param {Roo.Component} this
21499              */
21500         disable : true,
21501         /**
21502          * @event enable
21503          * Fires after the component is enabled.
21504              * @param {Roo.Component} this
21505              */
21506         enable : true,
21507         /**
21508          * @event beforeshow
21509          * Fires before the component is shown.  Return false to stop the show.
21510              * @param {Roo.Component} this
21511              */
21512         beforeshow : true,
21513         /**
21514          * @event show
21515          * Fires after the component is shown.
21516              * @param {Roo.Component} this
21517              */
21518         show : true,
21519         /**
21520          * @event beforehide
21521          * Fires before the component is hidden. Return false to stop the hide.
21522              * @param {Roo.Component} this
21523              */
21524         beforehide : true,
21525         /**
21526          * @event hide
21527          * Fires after the component is hidden.
21528              * @param {Roo.Component} this
21529              */
21530         hide : true,
21531         /**
21532          * @event beforerender
21533          * Fires before the component is rendered. Return false to stop the render.
21534              * @param {Roo.Component} this
21535              */
21536         beforerender : true,
21537         /**
21538          * @event render
21539          * Fires after the component is rendered.
21540              * @param {Roo.Component} this
21541              */
21542         render : true,
21543         /**
21544          * @event beforedestroy
21545          * Fires before the component is destroyed. Return false to stop the destroy.
21546              * @param {Roo.Component} this
21547              */
21548         beforedestroy : true,
21549         /**
21550          * @event destroy
21551          * Fires after the component is destroyed.
21552              * @param {Roo.Component} this
21553              */
21554         destroy : true
21555     });
21556     if(!this.id){
21557         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21558     }
21559     Roo.ComponentMgr.register(this);
21560     Roo.Component.superclass.constructor.call(this);
21561     this.initComponent();
21562     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21563         this.render(this.renderTo);
21564         delete this.renderTo;
21565     }
21566 };
21567
21568 // private
21569 Roo.Component.AUTO_ID = 1000;
21570
21571 Roo.extend(Roo.Component, Roo.util.Observable, {
21572     /**
21573      * @property {Boolean} hidden
21574      * true if this component is hidden. Read-only.
21575      */
21576     hidden : false,
21577     /**
21578      * true if this component is disabled. Read-only.
21579      */
21580     disabled : false,
21581     /**
21582      * true if this component has been rendered. Read-only.
21583      */
21584     rendered : false,
21585     
21586     /** @cfg {String} disableClass
21587      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21588      */
21589     disabledClass : "x-item-disabled",
21590         /** @cfg {Boolean} allowDomMove
21591          * Whether the component can move the Dom node when rendering (defaults to true).
21592          */
21593     allowDomMove : true,
21594     /** @cfg {String} hideMode
21595      * How this component should hidden. Supported values are
21596      * "visibility" (css visibility), "offsets" (negative offset position) and
21597      * "display" (css display) - defaults to "display".
21598      */
21599     hideMode: 'display',
21600
21601     // private
21602     ctype : "Roo.Component",
21603
21604     /** @cfg {String} actionMode 
21605      * which property holds the element that used for  hide() / show() / disable() / enable()
21606      * default is 'el' 
21607      */
21608     actionMode : "el",
21609
21610     // private
21611     getActionEl : function(){
21612         return this[this.actionMode];
21613     },
21614
21615     initComponent : Roo.emptyFn,
21616     /**
21617      * If this is a lazy rendering component, render it to its container element.
21618      * @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.
21619      */
21620     render : function(container, position){
21621         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21622             if(!container && this.el){
21623                 this.el = Roo.get(this.el);
21624                 container = this.el.dom.parentNode;
21625                 this.allowDomMove = false;
21626             }
21627             this.container = Roo.get(container);
21628             this.rendered = true;
21629             if(position !== undefined){
21630                 if(typeof position == 'number'){
21631                     position = this.container.dom.childNodes[position];
21632                 }else{
21633                     position = Roo.getDom(position);
21634                 }
21635             }
21636             this.onRender(this.container, position || null);
21637             if(this.cls){
21638                 this.el.addClass(this.cls);
21639                 delete this.cls;
21640             }
21641             if(this.style){
21642                 this.el.applyStyles(this.style);
21643                 delete this.style;
21644             }
21645             this.fireEvent("render", this);
21646             this.afterRender(this.container);
21647             if(this.hidden){
21648                 this.hide();
21649             }
21650             if(this.disabled){
21651                 this.disable();
21652             }
21653         }
21654         return this;
21655     },
21656
21657     // private
21658     // default function is not really useful
21659     onRender : function(ct, position){
21660         if(this.el){
21661             this.el = Roo.get(this.el);
21662             if(this.allowDomMove !== false){
21663                 ct.dom.insertBefore(this.el.dom, position);
21664             }
21665         }
21666     },
21667
21668     // private
21669     getAutoCreate : function(){
21670         var cfg = typeof this.autoCreate == "object" ?
21671                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21672         if(this.id && !cfg.id){
21673             cfg.id = this.id;
21674         }
21675         return cfg;
21676     },
21677
21678     // private
21679     afterRender : Roo.emptyFn,
21680
21681     /**
21682      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21683      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21684      */
21685     destroy : function(){
21686         if(this.fireEvent("beforedestroy", this) !== false){
21687             this.purgeListeners();
21688             this.beforeDestroy();
21689             if(this.rendered){
21690                 this.el.removeAllListeners();
21691                 this.el.remove();
21692                 if(this.actionMode == "container"){
21693                     this.container.remove();
21694                 }
21695             }
21696             this.onDestroy();
21697             Roo.ComponentMgr.unregister(this);
21698             this.fireEvent("destroy", this);
21699         }
21700     },
21701
21702         // private
21703     beforeDestroy : function(){
21704
21705     },
21706
21707         // private
21708         onDestroy : function(){
21709
21710     },
21711
21712     /**
21713      * Returns the underlying {@link Roo.Element}.
21714      * @return {Roo.Element} The element
21715      */
21716     getEl : function(){
21717         return this.el;
21718     },
21719
21720     /**
21721      * Returns the id of this component.
21722      * @return {String}
21723      */
21724     getId : function(){
21725         return this.id;
21726     },
21727
21728     /**
21729      * Try to focus this component.
21730      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21731      * @return {Roo.Component} this
21732      */
21733     focus : function(selectText){
21734         if(this.rendered){
21735             this.el.focus();
21736             if(selectText === true){
21737                 this.el.dom.select();
21738             }
21739         }
21740         return this;
21741     },
21742
21743     // private
21744     blur : function(){
21745         if(this.rendered){
21746             this.el.blur();
21747         }
21748         return this;
21749     },
21750
21751     /**
21752      * Disable this component.
21753      * @return {Roo.Component} this
21754      */
21755     disable : function(){
21756         if(this.rendered){
21757             this.onDisable();
21758         }
21759         this.disabled = true;
21760         this.fireEvent("disable", this);
21761         return this;
21762     },
21763
21764         // private
21765     onDisable : function(){
21766         this.getActionEl().addClass(this.disabledClass);
21767         this.el.dom.disabled = true;
21768     },
21769
21770     /**
21771      * Enable this component.
21772      * @return {Roo.Component} this
21773      */
21774     enable : function(){
21775         if(this.rendered){
21776             this.onEnable();
21777         }
21778         this.disabled = false;
21779         this.fireEvent("enable", this);
21780         return this;
21781     },
21782
21783         // private
21784     onEnable : function(){
21785         this.getActionEl().removeClass(this.disabledClass);
21786         this.el.dom.disabled = false;
21787     },
21788
21789     /**
21790      * Convenience function for setting disabled/enabled by boolean.
21791      * @param {Boolean} disabled
21792      */
21793     setDisabled : function(disabled){
21794         this[disabled ? "disable" : "enable"]();
21795     },
21796
21797     /**
21798      * Show this component.
21799      * @return {Roo.Component} this
21800      */
21801     show: function(){
21802         if(this.fireEvent("beforeshow", this) !== false){
21803             this.hidden = false;
21804             if(this.rendered){
21805                 this.onShow();
21806             }
21807             this.fireEvent("show", this);
21808         }
21809         return this;
21810     },
21811
21812     // private
21813     onShow : function(){
21814         var ae = this.getActionEl();
21815         if(this.hideMode == 'visibility'){
21816             ae.dom.style.visibility = "visible";
21817         }else if(this.hideMode == 'offsets'){
21818             ae.removeClass('x-hidden');
21819         }else{
21820             ae.dom.style.display = "";
21821         }
21822     },
21823
21824     /**
21825      * Hide this component.
21826      * @return {Roo.Component} this
21827      */
21828     hide: function(){
21829         if(this.fireEvent("beforehide", this) !== false){
21830             this.hidden = true;
21831             if(this.rendered){
21832                 this.onHide();
21833             }
21834             this.fireEvent("hide", this);
21835         }
21836         return this;
21837     },
21838
21839     // private
21840     onHide : function(){
21841         var ae = this.getActionEl();
21842         if(this.hideMode == 'visibility'){
21843             ae.dom.style.visibility = "hidden";
21844         }else if(this.hideMode == 'offsets'){
21845             ae.addClass('x-hidden');
21846         }else{
21847             ae.dom.style.display = "none";
21848         }
21849     },
21850
21851     /**
21852      * Convenience function to hide or show this component by boolean.
21853      * @param {Boolean} visible True to show, false to hide
21854      * @return {Roo.Component} this
21855      */
21856     setVisible: function(visible){
21857         if(visible) {
21858             this.show();
21859         }else{
21860             this.hide();
21861         }
21862         return this;
21863     },
21864
21865     /**
21866      * Returns true if this component is visible.
21867      */
21868     isVisible : function(){
21869         return this.getActionEl().isVisible();
21870     },
21871
21872     cloneConfig : function(overrides){
21873         overrides = overrides || {};
21874         var id = overrides.id || Roo.id();
21875         var cfg = Roo.applyIf(overrides, this.initialConfig);
21876         cfg.id = id; // prevent dup id
21877         return new this.constructor(cfg);
21878     }
21879 });/*
21880  * Based on:
21881  * Ext JS Library 1.1.1
21882  * Copyright(c) 2006-2007, Ext JS, LLC.
21883  *
21884  * Originally Released Under LGPL - original licence link has changed is not relivant.
21885  *
21886  * Fork - LGPL
21887  * <script type="text/javascript">
21888  */
21889  (function(){ 
21890 /**
21891  * @class Roo.Layer
21892  * @extends Roo.Element
21893  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21894  * automatic maintaining of shadow/shim positions.
21895  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21896  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21897  * you can pass a string with a CSS class name. False turns off the shadow.
21898  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21899  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21900  * @cfg {String} cls CSS class to add to the element
21901  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21902  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21903  * @constructor
21904  * @param {Object} config An object with config options.
21905  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21906  */
21907
21908 Roo.Layer = function(config, existingEl){
21909     config = config || {};
21910     var dh = Roo.DomHelper;
21911     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21912     if(existingEl){
21913         this.dom = Roo.getDom(existingEl);
21914     }
21915     if(!this.dom){
21916         var o = config.dh || {tag: "div", cls: "x-layer"};
21917         this.dom = dh.append(pel, o);
21918     }
21919     if(config.cls){
21920         this.addClass(config.cls);
21921     }
21922     this.constrain = config.constrain !== false;
21923     this.visibilityMode = Roo.Element.VISIBILITY;
21924     if(config.id){
21925         this.id = this.dom.id = config.id;
21926     }else{
21927         this.id = Roo.id(this.dom);
21928     }
21929     this.zindex = config.zindex || this.getZIndex();
21930     this.position("absolute", this.zindex);
21931     if(config.shadow){
21932         this.shadowOffset = config.shadowOffset || 4;
21933         this.shadow = new Roo.Shadow({
21934             offset : this.shadowOffset,
21935             mode : config.shadow
21936         });
21937     }else{
21938         this.shadowOffset = 0;
21939     }
21940     this.useShim = config.shim !== false && Roo.useShims;
21941     this.useDisplay = config.useDisplay;
21942     this.hide();
21943 };
21944
21945 var supr = Roo.Element.prototype;
21946
21947 // shims are shared among layer to keep from having 100 iframes
21948 var shims = [];
21949
21950 Roo.extend(Roo.Layer, Roo.Element, {
21951
21952     getZIndex : function(){
21953         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21954     },
21955
21956     getShim : function(){
21957         if(!this.useShim){
21958             return null;
21959         }
21960         if(this.shim){
21961             return this.shim;
21962         }
21963         var shim = shims.shift();
21964         if(!shim){
21965             shim = this.createShim();
21966             shim.enableDisplayMode('block');
21967             shim.dom.style.display = 'none';
21968             shim.dom.style.visibility = 'visible';
21969         }
21970         var pn = this.dom.parentNode;
21971         if(shim.dom.parentNode != pn){
21972             pn.insertBefore(shim.dom, this.dom);
21973         }
21974         shim.setStyle('z-index', this.getZIndex()-2);
21975         this.shim = shim;
21976         return shim;
21977     },
21978
21979     hideShim : function(){
21980         if(this.shim){
21981             this.shim.setDisplayed(false);
21982             shims.push(this.shim);
21983             delete this.shim;
21984         }
21985     },
21986
21987     disableShadow : function(){
21988         if(this.shadow){
21989             this.shadowDisabled = true;
21990             this.shadow.hide();
21991             this.lastShadowOffset = this.shadowOffset;
21992             this.shadowOffset = 0;
21993         }
21994     },
21995
21996     enableShadow : function(show){
21997         if(this.shadow){
21998             this.shadowDisabled = false;
21999             this.shadowOffset = this.lastShadowOffset;
22000             delete this.lastShadowOffset;
22001             if(show){
22002                 this.sync(true);
22003             }
22004         }
22005     },
22006
22007     // private
22008     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22009     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22010     sync : function(doShow){
22011         var sw = this.shadow;
22012         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22013             var sh = this.getShim();
22014
22015             var w = this.getWidth(),
22016                 h = this.getHeight();
22017
22018             var l = this.getLeft(true),
22019                 t = this.getTop(true);
22020
22021             if(sw && !this.shadowDisabled){
22022                 if(doShow && !sw.isVisible()){
22023                     sw.show(this);
22024                 }else{
22025                     sw.realign(l, t, w, h);
22026                 }
22027                 if(sh){
22028                     if(doShow){
22029                        sh.show();
22030                     }
22031                     // fit the shim behind the shadow, so it is shimmed too
22032                     var a = sw.adjusts, s = sh.dom.style;
22033                     s.left = (Math.min(l, l+a.l))+"px";
22034                     s.top = (Math.min(t, t+a.t))+"px";
22035                     s.width = (w+a.w)+"px";
22036                     s.height = (h+a.h)+"px";
22037                 }
22038             }else if(sh){
22039                 if(doShow){
22040                    sh.show();
22041                 }
22042                 sh.setSize(w, h);
22043                 sh.setLeftTop(l, t);
22044             }
22045             
22046         }
22047     },
22048
22049     // private
22050     destroy : function(){
22051         this.hideShim();
22052         if(this.shadow){
22053             this.shadow.hide();
22054         }
22055         this.removeAllListeners();
22056         var pn = this.dom.parentNode;
22057         if(pn){
22058             pn.removeChild(this.dom);
22059         }
22060         Roo.Element.uncache(this.id);
22061     },
22062
22063     remove : function(){
22064         this.destroy();
22065     },
22066
22067     // private
22068     beginUpdate : function(){
22069         this.updating = true;
22070     },
22071
22072     // private
22073     endUpdate : function(){
22074         this.updating = false;
22075         this.sync(true);
22076     },
22077
22078     // private
22079     hideUnders : function(negOffset){
22080         if(this.shadow){
22081             this.shadow.hide();
22082         }
22083         this.hideShim();
22084     },
22085
22086     // private
22087     constrainXY : function(){
22088         if(this.constrain){
22089             var vw = Roo.lib.Dom.getViewWidth(),
22090                 vh = Roo.lib.Dom.getViewHeight();
22091             var s = Roo.get(document).getScroll();
22092
22093             var xy = this.getXY();
22094             var x = xy[0], y = xy[1];   
22095             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22096             // only move it if it needs it
22097             var moved = false;
22098             // first validate right/bottom
22099             if((x + w) > vw+s.left){
22100                 x = vw - w - this.shadowOffset;
22101                 moved = true;
22102             }
22103             if((y + h) > vh+s.top){
22104                 y = vh - h - this.shadowOffset;
22105                 moved = true;
22106             }
22107             // then make sure top/left isn't negative
22108             if(x < s.left){
22109                 x = s.left;
22110                 moved = true;
22111             }
22112             if(y < s.top){
22113                 y = s.top;
22114                 moved = true;
22115             }
22116             if(moved){
22117                 if(this.avoidY){
22118                     var ay = this.avoidY;
22119                     if(y <= ay && (y+h) >= ay){
22120                         y = ay-h-5;   
22121                     }
22122                 }
22123                 xy = [x, y];
22124                 this.storeXY(xy);
22125                 supr.setXY.call(this, xy);
22126                 this.sync();
22127             }
22128         }
22129     },
22130
22131     isVisible : function(){
22132         return this.visible;    
22133     },
22134
22135     // private
22136     showAction : function(){
22137         this.visible = true; // track visibility to prevent getStyle calls
22138         if(this.useDisplay === true){
22139             this.setDisplayed("");
22140         }else if(this.lastXY){
22141             supr.setXY.call(this, this.lastXY);
22142         }else if(this.lastLT){
22143             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22144         }
22145     },
22146
22147     // private
22148     hideAction : function(){
22149         this.visible = false;
22150         if(this.useDisplay === true){
22151             this.setDisplayed(false);
22152         }else{
22153             this.setLeftTop(-10000,-10000);
22154         }
22155     },
22156
22157     // overridden Element method
22158     setVisible : function(v, a, d, c, e){
22159         if(v){
22160             this.showAction();
22161         }
22162         if(a && v){
22163             var cb = function(){
22164                 this.sync(true);
22165                 if(c){
22166                     c();
22167                 }
22168             }.createDelegate(this);
22169             supr.setVisible.call(this, true, true, d, cb, e);
22170         }else{
22171             if(!v){
22172                 this.hideUnders(true);
22173             }
22174             var cb = c;
22175             if(a){
22176                 cb = function(){
22177                     this.hideAction();
22178                     if(c){
22179                         c();
22180                     }
22181                 }.createDelegate(this);
22182             }
22183             supr.setVisible.call(this, v, a, d, cb, e);
22184             if(v){
22185                 this.sync(true);
22186             }else if(!a){
22187                 this.hideAction();
22188             }
22189         }
22190     },
22191
22192     storeXY : function(xy){
22193         delete this.lastLT;
22194         this.lastXY = xy;
22195     },
22196
22197     storeLeftTop : function(left, top){
22198         delete this.lastXY;
22199         this.lastLT = [left, top];
22200     },
22201
22202     // private
22203     beforeFx : function(){
22204         this.beforeAction();
22205         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22206     },
22207
22208     // private
22209     afterFx : function(){
22210         Roo.Layer.superclass.afterFx.apply(this, arguments);
22211         this.sync(this.isVisible());
22212     },
22213
22214     // private
22215     beforeAction : function(){
22216         if(!this.updating && this.shadow){
22217             this.shadow.hide();
22218         }
22219     },
22220
22221     // overridden Element method
22222     setLeft : function(left){
22223         this.storeLeftTop(left, this.getTop(true));
22224         supr.setLeft.apply(this, arguments);
22225         this.sync();
22226     },
22227
22228     setTop : function(top){
22229         this.storeLeftTop(this.getLeft(true), top);
22230         supr.setTop.apply(this, arguments);
22231         this.sync();
22232     },
22233
22234     setLeftTop : function(left, top){
22235         this.storeLeftTop(left, top);
22236         supr.setLeftTop.apply(this, arguments);
22237         this.sync();
22238     },
22239
22240     setXY : function(xy, a, d, c, e){
22241         this.fixDisplay();
22242         this.beforeAction();
22243         this.storeXY(xy);
22244         var cb = this.createCB(c);
22245         supr.setXY.call(this, xy, a, d, cb, e);
22246         if(!a){
22247             cb();
22248         }
22249     },
22250
22251     // private
22252     createCB : function(c){
22253         var el = this;
22254         return function(){
22255             el.constrainXY();
22256             el.sync(true);
22257             if(c){
22258                 c();
22259             }
22260         };
22261     },
22262
22263     // overridden Element method
22264     setX : function(x, a, d, c, e){
22265         this.setXY([x, this.getY()], a, d, c, e);
22266     },
22267
22268     // overridden Element method
22269     setY : function(y, a, d, c, e){
22270         this.setXY([this.getX(), y], a, d, c, e);
22271     },
22272
22273     // overridden Element method
22274     setSize : function(w, h, a, d, c, e){
22275         this.beforeAction();
22276         var cb = this.createCB(c);
22277         supr.setSize.call(this, w, h, a, d, cb, e);
22278         if(!a){
22279             cb();
22280         }
22281     },
22282
22283     // overridden Element method
22284     setWidth : function(w, a, d, c, e){
22285         this.beforeAction();
22286         var cb = this.createCB(c);
22287         supr.setWidth.call(this, w, a, d, cb, e);
22288         if(!a){
22289             cb();
22290         }
22291     },
22292
22293     // overridden Element method
22294     setHeight : function(h, a, d, c, e){
22295         this.beforeAction();
22296         var cb = this.createCB(c);
22297         supr.setHeight.call(this, h, a, d, cb, e);
22298         if(!a){
22299             cb();
22300         }
22301     },
22302
22303     // overridden Element method
22304     setBounds : function(x, y, w, h, a, d, c, e){
22305         this.beforeAction();
22306         var cb = this.createCB(c);
22307         if(!a){
22308             this.storeXY([x, y]);
22309             supr.setXY.call(this, [x, y]);
22310             supr.setSize.call(this, w, h, a, d, cb, e);
22311             cb();
22312         }else{
22313             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22314         }
22315         return this;
22316     },
22317     
22318     /**
22319      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22320      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22321      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22322      * @param {Number} zindex The new z-index to set
22323      * @return {this} The Layer
22324      */
22325     setZIndex : function(zindex){
22326         this.zindex = zindex;
22327         this.setStyle("z-index", zindex + 2);
22328         if(this.shadow){
22329             this.shadow.setZIndex(zindex + 1);
22330         }
22331         if(this.shim){
22332             this.shim.setStyle("z-index", zindex);
22333         }
22334     }
22335 });
22336 })();/*
22337  * Based on:
22338  * Ext JS Library 1.1.1
22339  * Copyright(c) 2006-2007, Ext JS, LLC.
22340  *
22341  * Originally Released Under LGPL - original licence link has changed is not relivant.
22342  *
22343  * Fork - LGPL
22344  * <script type="text/javascript">
22345  */
22346
22347
22348 /**
22349  * @class Roo.Shadow
22350  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22351  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22352  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22353  * @constructor
22354  * Create a new Shadow
22355  * @param {Object} config The config object
22356  */
22357 Roo.Shadow = function(config){
22358     Roo.apply(this, config);
22359     if(typeof this.mode != "string"){
22360         this.mode = this.defaultMode;
22361     }
22362     var o = this.offset, a = {h: 0};
22363     var rad = Math.floor(this.offset/2);
22364     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22365         case "drop":
22366             a.w = 0;
22367             a.l = a.t = o;
22368             a.t -= 1;
22369             if(Roo.isIE){
22370                 a.l -= this.offset + rad;
22371                 a.t -= this.offset + rad;
22372                 a.w -= rad;
22373                 a.h -= rad;
22374                 a.t += 1;
22375             }
22376         break;
22377         case "sides":
22378             a.w = (o*2);
22379             a.l = -o;
22380             a.t = o-1;
22381             if(Roo.isIE){
22382                 a.l -= (this.offset - rad);
22383                 a.t -= this.offset + rad;
22384                 a.l += 1;
22385                 a.w -= (this.offset - rad)*2;
22386                 a.w -= rad + 1;
22387                 a.h -= 1;
22388             }
22389         break;
22390         case "frame":
22391             a.w = a.h = (o*2);
22392             a.l = a.t = -o;
22393             a.t += 1;
22394             a.h -= 2;
22395             if(Roo.isIE){
22396                 a.l -= (this.offset - rad);
22397                 a.t -= (this.offset - rad);
22398                 a.l += 1;
22399                 a.w -= (this.offset + rad + 1);
22400                 a.h -= (this.offset + rad);
22401                 a.h += 1;
22402             }
22403         break;
22404     };
22405
22406     this.adjusts = a;
22407 };
22408
22409 Roo.Shadow.prototype = {
22410     /**
22411      * @cfg {String} mode
22412      * The shadow display mode.  Supports the following options:<br />
22413      * sides: Shadow displays on both sides and bottom only<br />
22414      * frame: Shadow displays equally on all four sides<br />
22415      * drop: Traditional bottom-right drop shadow (default)
22416      */
22417     /**
22418      * @cfg {String} offset
22419      * The number of pixels to offset the shadow from the element (defaults to 4)
22420      */
22421     offset: 4,
22422
22423     // private
22424     defaultMode: "drop",
22425
22426     /**
22427      * Displays the shadow under the target element
22428      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22429      */
22430     show : function(target){
22431         target = Roo.get(target);
22432         if(!this.el){
22433             this.el = Roo.Shadow.Pool.pull();
22434             if(this.el.dom.nextSibling != target.dom){
22435                 this.el.insertBefore(target);
22436             }
22437         }
22438         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22439         if(Roo.isIE){
22440             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22441         }
22442         this.realign(
22443             target.getLeft(true),
22444             target.getTop(true),
22445             target.getWidth(),
22446             target.getHeight()
22447         );
22448         this.el.dom.style.display = "block";
22449     },
22450
22451     /**
22452      * Returns true if the shadow is visible, else false
22453      */
22454     isVisible : function(){
22455         return this.el ? true : false;  
22456     },
22457
22458     /**
22459      * Direct alignment when values are already available. Show must be called at least once before
22460      * calling this method to ensure it is initialized.
22461      * @param {Number} left The target element left position
22462      * @param {Number} top The target element top position
22463      * @param {Number} width The target element width
22464      * @param {Number} height The target element height
22465      */
22466     realign : function(l, t, w, h){
22467         if(!this.el){
22468             return;
22469         }
22470         var a = this.adjusts, d = this.el.dom, s = d.style;
22471         var iea = 0;
22472         s.left = (l+a.l)+"px";
22473         s.top = (t+a.t)+"px";
22474         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22475         if(s.width != sws || s.height != shs){
22476             s.width = sws;
22477             s.height = shs;
22478             if(!Roo.isIE){
22479                 var cn = d.childNodes;
22480                 var sww = Math.max(0, (sw-12))+"px";
22481                 cn[0].childNodes[1].style.width = sww;
22482                 cn[1].childNodes[1].style.width = sww;
22483                 cn[2].childNodes[1].style.width = sww;
22484                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22485             }
22486         }
22487     },
22488
22489     /**
22490      * Hides this shadow
22491      */
22492     hide : function(){
22493         if(this.el){
22494             this.el.dom.style.display = "none";
22495             Roo.Shadow.Pool.push(this.el);
22496             delete this.el;
22497         }
22498     },
22499
22500     /**
22501      * Adjust the z-index of this shadow
22502      * @param {Number} zindex The new z-index
22503      */
22504     setZIndex : function(z){
22505         this.zIndex = z;
22506         if(this.el){
22507             this.el.setStyle("z-index", z);
22508         }
22509     }
22510 };
22511
22512 // Private utility class that manages the internal Shadow cache
22513 Roo.Shadow.Pool = function(){
22514     var p = [];
22515     var markup = Roo.isIE ?
22516                  '<div class="x-ie-shadow"></div>' :
22517                  '<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>';
22518     return {
22519         pull : function(){
22520             var sh = p.shift();
22521             if(!sh){
22522                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22523                 sh.autoBoxAdjust = false;
22524             }
22525             return sh;
22526         },
22527
22528         push : function(sh){
22529             p.push(sh);
22530         }
22531     };
22532 }();/*
22533  * Based on:
22534  * Ext JS Library 1.1.1
22535  * Copyright(c) 2006-2007, Ext JS, LLC.
22536  *
22537  * Originally Released Under LGPL - original licence link has changed is not relivant.
22538  *
22539  * Fork - LGPL
22540  * <script type="text/javascript">
22541  */
22542
22543 /**
22544  * @class Roo.BoxComponent
22545  * @extends Roo.Component
22546  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22547  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22548  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22549  * layout containers.
22550  * @constructor
22551  * @param {Roo.Element/String/Object} config The configuration options.
22552  */
22553 Roo.BoxComponent = function(config){
22554     Roo.Component.call(this, config);
22555     this.addEvents({
22556         /**
22557          * @event resize
22558          * Fires after the component is resized.
22559              * @param {Roo.Component} this
22560              * @param {Number} adjWidth The box-adjusted width that was set
22561              * @param {Number} adjHeight The box-adjusted height that was set
22562              * @param {Number} rawWidth The width that was originally specified
22563              * @param {Number} rawHeight The height that was originally specified
22564              */
22565         resize : true,
22566         /**
22567          * @event move
22568          * Fires after the component is moved.
22569              * @param {Roo.Component} this
22570              * @param {Number} x The new x position
22571              * @param {Number} y The new y position
22572              */
22573         move : true
22574     });
22575 };
22576
22577 Roo.extend(Roo.BoxComponent, Roo.Component, {
22578     // private, set in afterRender to signify that the component has been rendered
22579     boxReady : false,
22580     // private, used to defer height settings to subclasses
22581     deferHeight: false,
22582     /** @cfg {Number} width
22583      * width (optional) size of component
22584      */
22585      /** @cfg {Number} height
22586      * height (optional) size of component
22587      */
22588      
22589     /**
22590      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22591      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22592      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22593      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22594      * @return {Roo.BoxComponent} this
22595      */
22596     setSize : function(w, h){
22597         // support for standard size objects
22598         if(typeof w == 'object'){
22599             h = w.height;
22600             w = w.width;
22601         }
22602         // not rendered
22603         if(!this.boxReady){
22604             this.width = w;
22605             this.height = h;
22606             return this;
22607         }
22608
22609         // prevent recalcs when not needed
22610         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22611             return this;
22612         }
22613         this.lastSize = {width: w, height: h};
22614
22615         var adj = this.adjustSize(w, h);
22616         var aw = adj.width, ah = adj.height;
22617         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22618             var rz = this.getResizeEl();
22619             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22620                 rz.setSize(aw, ah);
22621             }else if(!this.deferHeight && ah !== undefined){
22622                 rz.setHeight(ah);
22623             }else if(aw !== undefined){
22624                 rz.setWidth(aw);
22625             }
22626             this.onResize(aw, ah, w, h);
22627             this.fireEvent('resize', this, aw, ah, w, h);
22628         }
22629         return this;
22630     },
22631
22632     /**
22633      * Gets the current size of the component's underlying element.
22634      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22635      */
22636     getSize : function(){
22637         return this.el.getSize();
22638     },
22639
22640     /**
22641      * Gets the current XY position of the component's underlying element.
22642      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22643      * @return {Array} The XY position of the element (e.g., [100, 200])
22644      */
22645     getPosition : function(local){
22646         if(local === true){
22647             return [this.el.getLeft(true), this.el.getTop(true)];
22648         }
22649         return this.xy || this.el.getXY();
22650     },
22651
22652     /**
22653      * Gets the current box measurements of the component's underlying element.
22654      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22655      * @returns {Object} box An object in the format {x, y, width, height}
22656      */
22657     getBox : function(local){
22658         var s = this.el.getSize();
22659         if(local){
22660             s.x = this.el.getLeft(true);
22661             s.y = this.el.getTop(true);
22662         }else{
22663             var xy = this.xy || this.el.getXY();
22664             s.x = xy[0];
22665             s.y = xy[1];
22666         }
22667         return s;
22668     },
22669
22670     /**
22671      * Sets the current box measurements of the component's underlying element.
22672      * @param {Object} box An object in the format {x, y, width, height}
22673      * @returns {Roo.BoxComponent} this
22674      */
22675     updateBox : function(box){
22676         this.setSize(box.width, box.height);
22677         this.setPagePosition(box.x, box.y);
22678         return this;
22679     },
22680
22681     // protected
22682     getResizeEl : function(){
22683         return this.resizeEl || this.el;
22684     },
22685
22686     // protected
22687     getPositionEl : function(){
22688         return this.positionEl || this.el;
22689     },
22690
22691     /**
22692      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22693      * This method fires the move event.
22694      * @param {Number} left The new left
22695      * @param {Number} top The new top
22696      * @returns {Roo.BoxComponent} this
22697      */
22698     setPosition : function(x, y){
22699         this.x = x;
22700         this.y = y;
22701         if(!this.boxReady){
22702             return this;
22703         }
22704         var adj = this.adjustPosition(x, y);
22705         var ax = adj.x, ay = adj.y;
22706
22707         var el = this.getPositionEl();
22708         if(ax !== undefined || ay !== undefined){
22709             if(ax !== undefined && ay !== undefined){
22710                 el.setLeftTop(ax, ay);
22711             }else if(ax !== undefined){
22712                 el.setLeft(ax);
22713             }else if(ay !== undefined){
22714                 el.setTop(ay);
22715             }
22716             this.onPosition(ax, ay);
22717             this.fireEvent('move', this, ax, ay);
22718         }
22719         return this;
22720     },
22721
22722     /**
22723      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22724      * This method fires the move event.
22725      * @param {Number} x The new x position
22726      * @param {Number} y The new y position
22727      * @returns {Roo.BoxComponent} this
22728      */
22729     setPagePosition : function(x, y){
22730         this.pageX = x;
22731         this.pageY = y;
22732         if(!this.boxReady){
22733             return;
22734         }
22735         if(x === undefined || y === undefined){ // cannot translate undefined points
22736             return;
22737         }
22738         var p = this.el.translatePoints(x, y);
22739         this.setPosition(p.left, p.top);
22740         return this;
22741     },
22742
22743     // private
22744     onRender : function(ct, position){
22745         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22746         if(this.resizeEl){
22747             this.resizeEl = Roo.get(this.resizeEl);
22748         }
22749         if(this.positionEl){
22750             this.positionEl = Roo.get(this.positionEl);
22751         }
22752     },
22753
22754     // private
22755     afterRender : function(){
22756         Roo.BoxComponent.superclass.afterRender.call(this);
22757         this.boxReady = true;
22758         this.setSize(this.width, this.height);
22759         if(this.x || this.y){
22760             this.setPosition(this.x, this.y);
22761         }
22762         if(this.pageX || this.pageY){
22763             this.setPagePosition(this.pageX, this.pageY);
22764         }
22765     },
22766
22767     /**
22768      * Force the component's size to recalculate based on the underlying element's current height and width.
22769      * @returns {Roo.BoxComponent} this
22770      */
22771     syncSize : function(){
22772         delete this.lastSize;
22773         this.setSize(this.el.getWidth(), this.el.getHeight());
22774         return this;
22775     },
22776
22777     /**
22778      * Called after the component is resized, this method is empty by default but can be implemented by any
22779      * subclass that needs to perform custom logic after a resize occurs.
22780      * @param {Number} adjWidth The box-adjusted width that was set
22781      * @param {Number} adjHeight The box-adjusted height that was set
22782      * @param {Number} rawWidth The width that was originally specified
22783      * @param {Number} rawHeight The height that was originally specified
22784      */
22785     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22786
22787     },
22788
22789     /**
22790      * Called after the component is moved, this method is empty by default but can be implemented by any
22791      * subclass that needs to perform custom logic after a move occurs.
22792      * @param {Number} x The new x position
22793      * @param {Number} y The new y position
22794      */
22795     onPosition : function(x, y){
22796
22797     },
22798
22799     // private
22800     adjustSize : function(w, h){
22801         if(this.autoWidth){
22802             w = 'auto';
22803         }
22804         if(this.autoHeight){
22805             h = 'auto';
22806         }
22807         return {width : w, height: h};
22808     },
22809
22810     // private
22811     adjustPosition : function(x, y){
22812         return {x : x, y: y};
22813     }
22814 });/*
22815  * Based on:
22816  * Ext JS Library 1.1.1
22817  * Copyright(c) 2006-2007, Ext JS, LLC.
22818  *
22819  * Originally Released Under LGPL - original licence link has changed is not relivant.
22820  *
22821  * Fork - LGPL
22822  * <script type="text/javascript">
22823  */
22824
22825
22826 /**
22827  * @class Roo.SplitBar
22828  * @extends Roo.util.Observable
22829  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22830  * <br><br>
22831  * Usage:
22832  * <pre><code>
22833 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22834                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22835 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22836 split.minSize = 100;
22837 split.maxSize = 600;
22838 split.animate = true;
22839 split.on('moved', splitterMoved);
22840 </code></pre>
22841  * @constructor
22842  * Create a new SplitBar
22843  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22844  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22845  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22846  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22847                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22848                         position of the SplitBar).
22849  */
22850 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22851     
22852     /** @private */
22853     this.el = Roo.get(dragElement, true);
22854     this.el.dom.unselectable = "on";
22855     /** @private */
22856     this.resizingEl = Roo.get(resizingElement, true);
22857
22858     /**
22859      * @private
22860      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22861      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22862      * @type Number
22863      */
22864     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22865     
22866     /**
22867      * The minimum size of the resizing element. (Defaults to 0)
22868      * @type Number
22869      */
22870     this.minSize = 0;
22871     
22872     /**
22873      * The maximum size of the resizing element. (Defaults to 2000)
22874      * @type Number
22875      */
22876     this.maxSize = 2000;
22877     
22878     /**
22879      * Whether to animate the transition to the new size
22880      * @type Boolean
22881      */
22882     this.animate = false;
22883     
22884     /**
22885      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22886      * @type Boolean
22887      */
22888     this.useShim = false;
22889     
22890     /** @private */
22891     this.shim = null;
22892     
22893     if(!existingProxy){
22894         /** @private */
22895         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22896     }else{
22897         this.proxy = Roo.get(existingProxy).dom;
22898     }
22899     /** @private */
22900     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22901     
22902     /** @private */
22903     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22904     
22905     /** @private */
22906     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22907     
22908     /** @private */
22909     this.dragSpecs = {};
22910     
22911     /**
22912      * @private The adapter to use to positon and resize elements
22913      */
22914     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22915     this.adapter.init(this);
22916     
22917     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22918         /** @private */
22919         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22920         this.el.addClass("x-splitbar-h");
22921     }else{
22922         /** @private */
22923         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22924         this.el.addClass("x-splitbar-v");
22925     }
22926     
22927     this.addEvents({
22928         /**
22929          * @event resize
22930          * Fires when the splitter is moved (alias for {@link #event-moved})
22931          * @param {Roo.SplitBar} this
22932          * @param {Number} newSize the new width or height
22933          */
22934         "resize" : true,
22935         /**
22936          * @event moved
22937          * Fires when the splitter is moved
22938          * @param {Roo.SplitBar} this
22939          * @param {Number} newSize the new width or height
22940          */
22941         "moved" : true,
22942         /**
22943          * @event beforeresize
22944          * Fires before the splitter is dragged
22945          * @param {Roo.SplitBar} this
22946          */
22947         "beforeresize" : true,
22948
22949         "beforeapply" : true
22950     });
22951
22952     Roo.util.Observable.call(this);
22953 };
22954
22955 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22956     onStartProxyDrag : function(x, y){
22957         this.fireEvent("beforeresize", this);
22958         if(!this.overlay){
22959             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22960             o.unselectable();
22961             o.enableDisplayMode("block");
22962             // all splitbars share the same overlay
22963             Roo.SplitBar.prototype.overlay = o;
22964         }
22965         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22966         this.overlay.show();
22967         Roo.get(this.proxy).setDisplayed("block");
22968         var size = this.adapter.getElementSize(this);
22969         this.activeMinSize = this.getMinimumSize();;
22970         this.activeMaxSize = this.getMaximumSize();;
22971         var c1 = size - this.activeMinSize;
22972         var c2 = Math.max(this.activeMaxSize - size, 0);
22973         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22974             this.dd.resetConstraints();
22975             this.dd.setXConstraint(
22976                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22977                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22978             );
22979             this.dd.setYConstraint(0, 0);
22980         }else{
22981             this.dd.resetConstraints();
22982             this.dd.setXConstraint(0, 0);
22983             this.dd.setYConstraint(
22984                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22985                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22986             );
22987          }
22988         this.dragSpecs.startSize = size;
22989         this.dragSpecs.startPoint = [x, y];
22990         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22991     },
22992     
22993     /** 
22994      * @private Called after the drag operation by the DDProxy
22995      */
22996     onEndProxyDrag : function(e){
22997         Roo.get(this.proxy).setDisplayed(false);
22998         var endPoint = Roo.lib.Event.getXY(e);
22999         if(this.overlay){
23000             this.overlay.hide();
23001         }
23002         var newSize;
23003         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23004             newSize = this.dragSpecs.startSize + 
23005                 (this.placement == Roo.SplitBar.LEFT ?
23006                     endPoint[0] - this.dragSpecs.startPoint[0] :
23007                     this.dragSpecs.startPoint[0] - endPoint[0]
23008                 );
23009         }else{
23010             newSize = this.dragSpecs.startSize + 
23011                 (this.placement == Roo.SplitBar.TOP ?
23012                     endPoint[1] - this.dragSpecs.startPoint[1] :
23013                     this.dragSpecs.startPoint[1] - endPoint[1]
23014                 );
23015         }
23016         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23017         if(newSize != this.dragSpecs.startSize){
23018             if(this.fireEvent('beforeapply', this, newSize) !== false){
23019                 this.adapter.setElementSize(this, newSize);
23020                 this.fireEvent("moved", this, newSize);
23021                 this.fireEvent("resize", this, newSize);
23022             }
23023         }
23024     },
23025     
23026     /**
23027      * Get the adapter this SplitBar uses
23028      * @return The adapter object
23029      */
23030     getAdapter : function(){
23031         return this.adapter;
23032     },
23033     
23034     /**
23035      * Set the adapter this SplitBar uses
23036      * @param {Object} adapter A SplitBar adapter object
23037      */
23038     setAdapter : function(adapter){
23039         this.adapter = adapter;
23040         this.adapter.init(this);
23041     },
23042     
23043     /**
23044      * Gets the minimum size for the resizing element
23045      * @return {Number} The minimum size
23046      */
23047     getMinimumSize : function(){
23048         return this.minSize;
23049     },
23050     
23051     /**
23052      * Sets the minimum size for the resizing element
23053      * @param {Number} minSize The minimum size
23054      */
23055     setMinimumSize : function(minSize){
23056         this.minSize = minSize;
23057     },
23058     
23059     /**
23060      * Gets the maximum size for the resizing element
23061      * @return {Number} The maximum size
23062      */
23063     getMaximumSize : function(){
23064         return this.maxSize;
23065     },
23066     
23067     /**
23068      * Sets the maximum size for the resizing element
23069      * @param {Number} maxSize The maximum size
23070      */
23071     setMaximumSize : function(maxSize){
23072         this.maxSize = maxSize;
23073     },
23074     
23075     /**
23076      * Sets the initialize size for the resizing element
23077      * @param {Number} size The initial size
23078      */
23079     setCurrentSize : function(size){
23080         var oldAnimate = this.animate;
23081         this.animate = false;
23082         this.adapter.setElementSize(this, size);
23083         this.animate = oldAnimate;
23084     },
23085     
23086     /**
23087      * Destroy this splitbar. 
23088      * @param {Boolean} removeEl True to remove the element
23089      */
23090     destroy : function(removeEl){
23091         if(this.shim){
23092             this.shim.remove();
23093         }
23094         this.dd.unreg();
23095         this.proxy.parentNode.removeChild(this.proxy);
23096         if(removeEl){
23097             this.el.remove();
23098         }
23099     }
23100 });
23101
23102 /**
23103  * @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.
23104  */
23105 Roo.SplitBar.createProxy = function(dir){
23106     var proxy = new Roo.Element(document.createElement("div"));
23107     proxy.unselectable();
23108     var cls = 'x-splitbar-proxy';
23109     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23110     document.body.appendChild(proxy.dom);
23111     return proxy.dom;
23112 };
23113
23114 /** 
23115  * @class Roo.SplitBar.BasicLayoutAdapter
23116  * Default Adapter. It assumes the splitter and resizing element are not positioned
23117  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23118  */
23119 Roo.SplitBar.BasicLayoutAdapter = function(){
23120 };
23121
23122 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23123     // do nothing for now
23124     init : function(s){
23125     
23126     },
23127     /**
23128      * Called before drag operations to get the current size of the resizing element. 
23129      * @param {Roo.SplitBar} s The SplitBar using this adapter
23130      */
23131      getElementSize : function(s){
23132         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23133             return s.resizingEl.getWidth();
23134         }else{
23135             return s.resizingEl.getHeight();
23136         }
23137     },
23138     
23139     /**
23140      * Called after drag operations to set the size of the resizing element.
23141      * @param {Roo.SplitBar} s The SplitBar using this adapter
23142      * @param {Number} newSize The new size to set
23143      * @param {Function} onComplete A function to be invoked when resizing is complete
23144      */
23145     setElementSize : function(s, newSize, onComplete){
23146         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23147             if(!s.animate){
23148                 s.resizingEl.setWidth(newSize);
23149                 if(onComplete){
23150                     onComplete(s, newSize);
23151                 }
23152             }else{
23153                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23154             }
23155         }else{
23156             
23157             if(!s.animate){
23158                 s.resizingEl.setHeight(newSize);
23159                 if(onComplete){
23160                     onComplete(s, newSize);
23161                 }
23162             }else{
23163                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23164             }
23165         }
23166     }
23167 };
23168
23169 /** 
23170  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23171  * @extends Roo.SplitBar.BasicLayoutAdapter
23172  * Adapter that  moves the splitter element to align with the resized sizing element. 
23173  * Used with an absolute positioned SplitBar.
23174  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23175  * document.body, make sure you assign an id to the body element.
23176  */
23177 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23178     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23179     this.container = Roo.get(container);
23180 };
23181
23182 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23183     init : function(s){
23184         this.basic.init(s);
23185     },
23186     
23187     getElementSize : function(s){
23188         return this.basic.getElementSize(s);
23189     },
23190     
23191     setElementSize : function(s, newSize, onComplete){
23192         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23193     },
23194     
23195     moveSplitter : function(s){
23196         var yes = Roo.SplitBar;
23197         switch(s.placement){
23198             case yes.LEFT:
23199                 s.el.setX(s.resizingEl.getRight());
23200                 break;
23201             case yes.RIGHT:
23202                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23203                 break;
23204             case yes.TOP:
23205                 s.el.setY(s.resizingEl.getBottom());
23206                 break;
23207             case yes.BOTTOM:
23208                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23209                 break;
23210         }
23211     }
23212 };
23213
23214 /**
23215  * Orientation constant - Create a vertical SplitBar
23216  * @static
23217  * @type Number
23218  */
23219 Roo.SplitBar.VERTICAL = 1;
23220
23221 /**
23222  * Orientation constant - Create a horizontal SplitBar
23223  * @static
23224  * @type Number
23225  */
23226 Roo.SplitBar.HORIZONTAL = 2;
23227
23228 /**
23229  * Placement constant - The resizing element is to the left of the splitter element
23230  * @static
23231  * @type Number
23232  */
23233 Roo.SplitBar.LEFT = 1;
23234
23235 /**
23236  * Placement constant - The resizing element is to the right of the splitter element
23237  * @static
23238  * @type Number
23239  */
23240 Roo.SplitBar.RIGHT = 2;
23241
23242 /**
23243  * Placement constant - The resizing element is positioned above the splitter element
23244  * @static
23245  * @type Number
23246  */
23247 Roo.SplitBar.TOP = 3;
23248
23249 /**
23250  * Placement constant - The resizing element is positioned under splitter element
23251  * @static
23252  * @type Number
23253  */
23254 Roo.SplitBar.BOTTOM = 4;
23255 /*
23256  * Based on:
23257  * Ext JS Library 1.1.1
23258  * Copyright(c) 2006-2007, Ext JS, LLC.
23259  *
23260  * Originally Released Under LGPL - original licence link has changed is not relivant.
23261  *
23262  * Fork - LGPL
23263  * <script type="text/javascript">
23264  */
23265
23266 /**
23267  * @class Roo.View
23268  * @extends Roo.util.Observable
23269  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23270  * This class also supports single and multi selection modes. <br>
23271  * Create a data model bound view:
23272  <pre><code>
23273  var store = new Roo.data.Store(...);
23274
23275  var view = new Roo.View({
23276     el : "my-element",
23277     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23278  
23279     singleSelect: true,
23280     selectedClass: "ydataview-selected",
23281     store: store
23282  });
23283
23284  // listen for node click?
23285  view.on("click", function(vw, index, node, e){
23286  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23287  });
23288
23289  // load XML data
23290  dataModel.load("foobar.xml");
23291  </code></pre>
23292  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23293  * <br><br>
23294  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23295  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23296  * 
23297  * Note: old style constructor is still suported (container, template, config)
23298  * 
23299  * @constructor
23300  * Create a new View
23301  * @param {Object} config The config object
23302  * 
23303  */
23304 Roo.View = function(config, depreciated_tpl, depreciated_config){
23305     
23306     if (typeof(depreciated_tpl) == 'undefined') {
23307         // new way.. - universal constructor.
23308         Roo.apply(this, config);
23309         this.el  = Roo.get(this.el);
23310     } else {
23311         // old format..
23312         this.el  = Roo.get(config);
23313         this.tpl = depreciated_tpl;
23314         Roo.apply(this, depreciated_config);
23315     }
23316      
23317     
23318     if(typeof(this.tpl) == "string"){
23319         this.tpl = new Roo.Template(this.tpl);
23320     } 
23321     
23322     
23323     this.tpl.compile();
23324    
23325
23326      
23327     /** @private */
23328     this.addEvents({
23329     /**
23330      * @event beforeclick
23331      * Fires before a click is processed. Returns false to cancel the default action.
23332      * @param {Roo.View} this
23333      * @param {Number} index The index of the target node
23334      * @param {HTMLElement} node The target node
23335      * @param {Roo.EventObject} e The raw event object
23336      */
23337         "beforeclick" : true,
23338     /**
23339      * @event click
23340      * Fires when a template node is clicked.
23341      * @param {Roo.View} this
23342      * @param {Number} index The index of the target node
23343      * @param {HTMLElement} node The target node
23344      * @param {Roo.EventObject} e The raw event object
23345      */
23346         "click" : true,
23347     /**
23348      * @event dblclick
23349      * Fires when a template node is double clicked.
23350      * @param {Roo.View} this
23351      * @param {Number} index The index of the target node
23352      * @param {HTMLElement} node The target node
23353      * @param {Roo.EventObject} e The raw event object
23354      */
23355         "dblclick" : true,
23356     /**
23357      * @event contextmenu
23358      * Fires when a template node is right clicked.
23359      * @param {Roo.View} this
23360      * @param {Number} index The index of the target node
23361      * @param {HTMLElement} node The target node
23362      * @param {Roo.EventObject} e The raw event object
23363      */
23364         "contextmenu" : true,
23365     /**
23366      * @event selectionchange
23367      * Fires when the selected nodes change.
23368      * @param {Roo.View} this
23369      * @param {Array} selections Array of the selected nodes
23370      */
23371         "selectionchange" : true,
23372
23373     /**
23374      * @event beforeselect
23375      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23376      * @param {Roo.View} this
23377      * @param {HTMLElement} node The node to be selected
23378      * @param {Array} selections Array of currently selected nodes
23379      */
23380         "beforeselect" : true
23381     });
23382
23383     this.el.on({
23384         "click": this.onClick,
23385         "dblclick": this.onDblClick,
23386         "contextmenu": this.onContextMenu,
23387         scope:this
23388     });
23389
23390     this.selections = [];
23391     this.nodes = [];
23392     this.cmp = new Roo.CompositeElementLite([]);
23393     if(this.store){
23394         this.store = Roo.factory(this.store, Roo.data);
23395         this.setStore(this.store, true);
23396     }
23397     Roo.View.superclass.constructor.call(this);
23398 };
23399
23400 Roo.extend(Roo.View, Roo.util.Observable, {
23401     
23402      /**
23403      * @cfg {Roo.data.Store} store Data store to load data from.
23404      */
23405     store : false,
23406     
23407     /**
23408      * @cfg {String|Roo.Element} el The container element.
23409      */
23410     el : '',
23411     
23412     /**
23413      * @cfg {String|Roo.Template} tpl The template used by this View 
23414      */
23415     tpl : false,
23416     
23417     /**
23418      * @cfg {String} selectedClass The css class to add to selected nodes
23419      */
23420     selectedClass : "x-view-selected",
23421      /**
23422      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23423      */
23424     emptyText : "",
23425     /**
23426      * Returns the element this view is bound to.
23427      * @return {Roo.Element}
23428      */
23429     getEl : function(){
23430         return this.el;
23431     },
23432
23433     /**
23434      * Refreshes the view.
23435      */
23436     refresh : function(){
23437         var t = this.tpl;
23438         this.clearSelections();
23439         this.el.update("");
23440         var html = [];
23441         var records = this.store.getRange();
23442         if(records.length < 1){
23443             this.el.update(this.emptyText);
23444             return;
23445         }
23446         for(var i = 0, len = records.length; i < len; i++){
23447             var data = this.prepareData(records[i].data, i, records[i]);
23448             html[html.length] = t.apply(data);
23449         }
23450         this.el.update(html.join(""));
23451         this.nodes = this.el.dom.childNodes;
23452         this.updateIndexes(0);
23453     },
23454
23455     /**
23456      * Function to override to reformat the data that is sent to
23457      * the template for each node.
23458      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23459      * a JSON object for an UpdateManager bound view).
23460      */
23461     prepareData : function(data){
23462         return data;
23463     },
23464
23465     onUpdate : function(ds, record){
23466         this.clearSelections();
23467         var index = this.store.indexOf(record);
23468         var n = this.nodes[index];
23469         this.tpl.insertBefore(n, this.prepareData(record.data));
23470         n.parentNode.removeChild(n);
23471         this.updateIndexes(index, index);
23472     },
23473
23474     onAdd : function(ds, records, index){
23475         this.clearSelections();
23476         if(this.nodes.length == 0){
23477             this.refresh();
23478             return;
23479         }
23480         var n = this.nodes[index];
23481         for(var i = 0, len = records.length; i < len; i++){
23482             var d = this.prepareData(records[i].data);
23483             if(n){
23484                 this.tpl.insertBefore(n, d);
23485             }else{
23486                 this.tpl.append(this.el, d);
23487             }
23488         }
23489         this.updateIndexes(index);
23490     },
23491
23492     onRemove : function(ds, record, index){
23493         this.clearSelections();
23494         this.el.dom.removeChild(this.nodes[index]);
23495         this.updateIndexes(index);
23496     },
23497
23498     /**
23499      * Refresh an individual node.
23500      * @param {Number} index
23501      */
23502     refreshNode : function(index){
23503         this.onUpdate(this.store, this.store.getAt(index));
23504     },
23505
23506     updateIndexes : function(startIndex, endIndex){
23507         var ns = this.nodes;
23508         startIndex = startIndex || 0;
23509         endIndex = endIndex || ns.length - 1;
23510         for(var i = startIndex; i <= endIndex; i++){
23511             ns[i].nodeIndex = i;
23512         }
23513     },
23514
23515     /**
23516      * Changes the data store this view uses and refresh the view.
23517      * @param {Store} store
23518      */
23519     setStore : function(store, initial){
23520         if(!initial && this.store){
23521             this.store.un("datachanged", this.refresh);
23522             this.store.un("add", this.onAdd);
23523             this.store.un("remove", this.onRemove);
23524             this.store.un("update", this.onUpdate);
23525             this.store.un("clear", this.refresh);
23526         }
23527         if(store){
23528           
23529             store.on("datachanged", this.refresh, this);
23530             store.on("add", this.onAdd, this);
23531             store.on("remove", this.onRemove, this);
23532             store.on("update", this.onUpdate, this);
23533             store.on("clear", this.refresh, this);
23534         }
23535         
23536         if(store){
23537             this.refresh();
23538         }
23539     },
23540
23541     /**
23542      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23543      * @param {HTMLElement} node
23544      * @return {HTMLElement} The template node
23545      */
23546     findItemFromChild : function(node){
23547         var el = this.el.dom;
23548         if(!node || node.parentNode == el){
23549                     return node;
23550             }
23551             var p = node.parentNode;
23552             while(p && p != el){
23553             if(p.parentNode == el){
23554                 return p;
23555             }
23556             p = p.parentNode;
23557         }
23558             return null;
23559     },
23560
23561     /** @ignore */
23562     onClick : function(e){
23563         var item = this.findItemFromChild(e.getTarget());
23564         if(item){
23565             var index = this.indexOf(item);
23566             if(this.onItemClick(item, index, e) !== false){
23567                 this.fireEvent("click", this, index, item, e);
23568             }
23569         }else{
23570             this.clearSelections();
23571         }
23572     },
23573
23574     /** @ignore */
23575     onContextMenu : function(e){
23576         var item = this.findItemFromChild(e.getTarget());
23577         if(item){
23578             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23579         }
23580     },
23581
23582     /** @ignore */
23583     onDblClick : function(e){
23584         var item = this.findItemFromChild(e.getTarget());
23585         if(item){
23586             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23587         }
23588     },
23589
23590     onItemClick : function(item, index, e){
23591         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23592             return false;
23593         }
23594         if(this.multiSelect || this.singleSelect){
23595             if(this.multiSelect && e.shiftKey && this.lastSelection){
23596                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23597             }else{
23598                 this.select(item, this.multiSelect && e.ctrlKey);
23599                 this.lastSelection = item;
23600             }
23601             e.preventDefault();
23602         }
23603         return true;
23604     },
23605
23606     /**
23607      * Get the number of selected nodes.
23608      * @return {Number}
23609      */
23610     getSelectionCount : function(){
23611         return this.selections.length;
23612     },
23613
23614     /**
23615      * Get the currently selected nodes.
23616      * @return {Array} An array of HTMLElements
23617      */
23618     getSelectedNodes : function(){
23619         return this.selections;
23620     },
23621
23622     /**
23623      * Get the indexes of the selected nodes.
23624      * @return {Array}
23625      */
23626     getSelectedIndexes : function(){
23627         var indexes = [], s = this.selections;
23628         for(var i = 0, len = s.length; i < len; i++){
23629             indexes.push(s[i].nodeIndex);
23630         }
23631         return indexes;
23632     },
23633
23634     /**
23635      * Clear all selections
23636      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23637      */
23638     clearSelections : function(suppressEvent){
23639         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23640             this.cmp.elements = this.selections;
23641             this.cmp.removeClass(this.selectedClass);
23642             this.selections = [];
23643             if(!suppressEvent){
23644                 this.fireEvent("selectionchange", this, this.selections);
23645             }
23646         }
23647     },
23648
23649     /**
23650      * Returns true if the passed node is selected
23651      * @param {HTMLElement/Number} node The node or node index
23652      * @return {Boolean}
23653      */
23654     isSelected : function(node){
23655         var s = this.selections;
23656         if(s.length < 1){
23657             return false;
23658         }
23659         node = this.getNode(node);
23660         return s.indexOf(node) !== -1;
23661     },
23662
23663     /**
23664      * Selects nodes.
23665      * @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
23666      * @param {Boolean} keepExisting (optional) true to keep existing selections
23667      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23668      */
23669     select : function(nodeInfo, keepExisting, suppressEvent){
23670         if(nodeInfo instanceof Array){
23671             if(!keepExisting){
23672                 this.clearSelections(true);
23673             }
23674             for(var i = 0, len = nodeInfo.length; i < len; i++){
23675                 this.select(nodeInfo[i], true, true);
23676             }
23677         } else{
23678             var node = this.getNode(nodeInfo);
23679             if(node && !this.isSelected(node)){
23680                 if(!keepExisting){
23681                     this.clearSelections(true);
23682                 }
23683                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23684                     Roo.fly(node).addClass(this.selectedClass);
23685                     this.selections.push(node);
23686                     if(!suppressEvent){
23687                         this.fireEvent("selectionchange", this, this.selections);
23688                     }
23689                 }
23690             }
23691         }
23692     },
23693
23694     /**
23695      * Gets a template node.
23696      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23697      * @return {HTMLElement} The node or null if it wasn't found
23698      */
23699     getNode : function(nodeInfo){
23700         if(typeof nodeInfo == "string"){
23701             return document.getElementById(nodeInfo);
23702         }else if(typeof nodeInfo == "number"){
23703             return this.nodes[nodeInfo];
23704         }
23705         return nodeInfo;
23706     },
23707
23708     /**
23709      * Gets a range template nodes.
23710      * @param {Number} startIndex
23711      * @param {Number} endIndex
23712      * @return {Array} An array of nodes
23713      */
23714     getNodes : function(start, end){
23715         var ns = this.nodes;
23716         start = start || 0;
23717         end = typeof end == "undefined" ? ns.length - 1 : end;
23718         var nodes = [];
23719         if(start <= end){
23720             for(var i = start; i <= end; i++){
23721                 nodes.push(ns[i]);
23722             }
23723         } else{
23724             for(var i = start; i >= end; i--){
23725                 nodes.push(ns[i]);
23726             }
23727         }
23728         return nodes;
23729     },
23730
23731     /**
23732      * Finds the index of the passed node
23733      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23734      * @return {Number} The index of the node or -1
23735      */
23736     indexOf : function(node){
23737         node = this.getNode(node);
23738         if(typeof node.nodeIndex == "number"){
23739             return node.nodeIndex;
23740         }
23741         var ns = this.nodes;
23742         for(var i = 0, len = ns.length; i < len; i++){
23743             if(ns[i] == node){
23744                 return i;
23745             }
23746         }
23747         return -1;
23748     }
23749 });
23750 /*
23751  * Based on:
23752  * Ext JS Library 1.1.1
23753  * Copyright(c) 2006-2007, Ext JS, LLC.
23754  *
23755  * Originally Released Under LGPL - original licence link has changed is not relivant.
23756  *
23757  * Fork - LGPL
23758  * <script type="text/javascript">
23759  */
23760
23761 /**
23762  * @class Roo.JsonView
23763  * @extends Roo.View
23764  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23765 <pre><code>
23766 var view = new Roo.JsonView({
23767     container: "my-element",
23768     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23769     multiSelect: true, 
23770     jsonRoot: "data" 
23771 });
23772
23773 // listen for node click?
23774 view.on("click", function(vw, index, node, e){
23775     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23776 });
23777
23778 // direct load of JSON data
23779 view.load("foobar.php");
23780
23781 // Example from my blog list
23782 var tpl = new Roo.Template(
23783     '&lt;div class="entry"&gt;' +
23784     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23785     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23786     "&lt;/div&gt;&lt;hr /&gt;"
23787 );
23788
23789 var moreView = new Roo.JsonView({
23790     container :  "entry-list", 
23791     template : tpl,
23792     jsonRoot: "posts"
23793 });
23794 moreView.on("beforerender", this.sortEntries, this);
23795 moreView.load({
23796     url: "/blog/get-posts.php",
23797     params: "allposts=true",
23798     text: "Loading Blog Entries..."
23799 });
23800 </code></pre>
23801
23802 * Note: old code is supported with arguments : (container, template, config)
23803
23804
23805  * @constructor
23806  * Create a new JsonView
23807  * 
23808  * @param {Object} config The config object
23809  * 
23810  */
23811 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23812     
23813     
23814     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23815
23816     var um = this.el.getUpdateManager();
23817     um.setRenderer(this);
23818     um.on("update", this.onLoad, this);
23819     um.on("failure", this.onLoadException, this);
23820
23821     /**
23822      * @event beforerender
23823      * Fires before rendering of the downloaded JSON data.
23824      * @param {Roo.JsonView} this
23825      * @param {Object} data The JSON data loaded
23826      */
23827     /**
23828      * @event load
23829      * Fires when data is loaded.
23830      * @param {Roo.JsonView} this
23831      * @param {Object} data The JSON data loaded
23832      * @param {Object} response The raw Connect response object
23833      */
23834     /**
23835      * @event loadexception
23836      * Fires when loading fails.
23837      * @param {Roo.JsonView} this
23838      * @param {Object} response The raw Connect response object
23839      */
23840     this.addEvents({
23841         'beforerender' : true,
23842         'load' : true,
23843         'loadexception' : true
23844     });
23845 };
23846 Roo.extend(Roo.JsonView, Roo.View, {
23847     /**
23848      * @type {String} The root property in the loaded JSON object that contains the data
23849      */
23850     jsonRoot : "",
23851
23852     /**
23853      * Refreshes the view.
23854      */
23855     refresh : function(){
23856         this.clearSelections();
23857         this.el.update("");
23858         var html = [];
23859         var o = this.jsonData;
23860         if(o && o.length > 0){
23861             for(var i = 0, len = o.length; i < len; i++){
23862                 var data = this.prepareData(o[i], i, o);
23863                 html[html.length] = this.tpl.apply(data);
23864             }
23865         }else{
23866             html.push(this.emptyText);
23867         }
23868         this.el.update(html.join(""));
23869         this.nodes = this.el.dom.childNodes;
23870         this.updateIndexes(0);
23871     },
23872
23873     /**
23874      * 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.
23875      * @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:
23876      <pre><code>
23877      view.load({
23878          url: "your-url.php",
23879          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23880          callback: yourFunction,
23881          scope: yourObject, //(optional scope)
23882          discardUrl: false,
23883          nocache: false,
23884          text: "Loading...",
23885          timeout: 30,
23886          scripts: false
23887      });
23888      </code></pre>
23889      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23890      * 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.
23891      * @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}
23892      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23893      * @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.
23894      */
23895     load : function(){
23896         var um = this.el.getUpdateManager();
23897         um.update.apply(um, arguments);
23898     },
23899
23900     render : function(el, response){
23901         this.clearSelections();
23902         this.el.update("");
23903         var o;
23904         try{
23905             o = Roo.util.JSON.decode(response.responseText);
23906             if(this.jsonRoot){
23907                 
23908                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23909             }
23910         } catch(e){
23911         }
23912         /**
23913          * The current JSON data or null
23914          */
23915         this.jsonData = o;
23916         this.beforeRender();
23917         this.refresh();
23918     },
23919
23920 /**
23921  * Get the number of records in the current JSON dataset
23922  * @return {Number}
23923  */
23924     getCount : function(){
23925         return this.jsonData ? this.jsonData.length : 0;
23926     },
23927
23928 /**
23929  * Returns the JSON object for the specified node(s)
23930  * @param {HTMLElement/Array} node The node or an array of nodes
23931  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23932  * you get the JSON object for the node
23933  */
23934     getNodeData : function(node){
23935         if(node instanceof Array){
23936             var data = [];
23937             for(var i = 0, len = node.length; i < len; i++){
23938                 data.push(this.getNodeData(node[i]));
23939             }
23940             return data;
23941         }
23942         return this.jsonData[this.indexOf(node)] || null;
23943     },
23944
23945     beforeRender : function(){
23946         this.snapshot = this.jsonData;
23947         if(this.sortInfo){
23948             this.sort.apply(this, this.sortInfo);
23949         }
23950         this.fireEvent("beforerender", this, this.jsonData);
23951     },
23952
23953     onLoad : function(el, o){
23954         this.fireEvent("load", this, this.jsonData, o);
23955     },
23956
23957     onLoadException : function(el, o){
23958         this.fireEvent("loadexception", this, o);
23959     },
23960
23961 /**
23962  * Filter the data by a specific property.
23963  * @param {String} property A property on your JSON objects
23964  * @param {String/RegExp} value Either string that the property values
23965  * should start with, or a RegExp to test against the property
23966  */
23967     filter : function(property, value){
23968         if(this.jsonData){
23969             var data = [];
23970             var ss = this.snapshot;
23971             if(typeof value == "string"){
23972                 var vlen = value.length;
23973                 if(vlen == 0){
23974                     this.clearFilter();
23975                     return;
23976                 }
23977                 value = value.toLowerCase();
23978                 for(var i = 0, len = ss.length; i < len; i++){
23979                     var o = ss[i];
23980                     if(o[property].substr(0, vlen).toLowerCase() == value){
23981                         data.push(o);
23982                     }
23983                 }
23984             } else if(value.exec){ // regex?
23985                 for(var i = 0, len = ss.length; i < len; i++){
23986                     var o = ss[i];
23987                     if(value.test(o[property])){
23988                         data.push(o);
23989                     }
23990                 }
23991             } else{
23992                 return;
23993             }
23994             this.jsonData = data;
23995             this.refresh();
23996         }
23997     },
23998
23999 /**
24000  * Filter by a function. The passed function will be called with each
24001  * object in the current dataset. If the function returns true the value is kept,
24002  * otherwise it is filtered.
24003  * @param {Function} fn
24004  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24005  */
24006     filterBy : function(fn, scope){
24007         if(this.jsonData){
24008             var data = [];
24009             var ss = this.snapshot;
24010             for(var i = 0, len = ss.length; i < len; i++){
24011                 var o = ss[i];
24012                 if(fn.call(scope || this, o)){
24013                     data.push(o);
24014                 }
24015             }
24016             this.jsonData = data;
24017             this.refresh();
24018         }
24019     },
24020
24021 /**
24022  * Clears the current filter.
24023  */
24024     clearFilter : function(){
24025         if(this.snapshot && this.jsonData != this.snapshot){
24026             this.jsonData = this.snapshot;
24027             this.refresh();
24028         }
24029     },
24030
24031
24032 /**
24033  * Sorts the data for this view and refreshes it.
24034  * @param {String} property A property on your JSON objects to sort on
24035  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24036  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24037  */
24038     sort : function(property, dir, sortType){
24039         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24040         if(this.jsonData){
24041             var p = property;
24042             var dsc = dir && dir.toLowerCase() == "desc";
24043             var f = function(o1, o2){
24044                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24045                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24046                 ;
24047                 if(v1 < v2){
24048                     return dsc ? +1 : -1;
24049                 } else if(v1 > v2){
24050                     return dsc ? -1 : +1;
24051                 } else{
24052                     return 0;
24053                 }
24054             };
24055             this.jsonData.sort(f);
24056             this.refresh();
24057             if(this.jsonData != this.snapshot){
24058                 this.snapshot.sort(f);
24059             }
24060         }
24061     }
24062 });/*
24063  * Based on:
24064  * Ext JS Library 1.1.1
24065  * Copyright(c) 2006-2007, Ext JS, LLC.
24066  *
24067  * Originally Released Under LGPL - original licence link has changed is not relivant.
24068  *
24069  * Fork - LGPL
24070  * <script type="text/javascript">
24071  */
24072  
24073
24074 /**
24075  * @class Roo.ColorPalette
24076  * @extends Roo.Component
24077  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24078  * Here's an example of typical usage:
24079  * <pre><code>
24080 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24081 cp.render('my-div');
24082
24083 cp.on('select', function(palette, selColor){
24084     // do something with selColor
24085 });
24086 </code></pre>
24087  * @constructor
24088  * Create a new ColorPalette
24089  * @param {Object} config The config object
24090  */
24091 Roo.ColorPalette = function(config){
24092     Roo.ColorPalette.superclass.constructor.call(this, config);
24093     this.addEvents({
24094         /**
24095              * @event select
24096              * Fires when a color is selected
24097              * @param {ColorPalette} this
24098              * @param {String} color The 6-digit color hex code (without the # symbol)
24099              */
24100         select: true
24101     });
24102
24103     if(this.handler){
24104         this.on("select", this.handler, this.scope, true);
24105     }
24106 };
24107 Roo.extend(Roo.ColorPalette, Roo.Component, {
24108     /**
24109      * @cfg {String} itemCls
24110      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24111      */
24112     itemCls : "x-color-palette",
24113     /**
24114      * @cfg {String} value
24115      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24116      * the hex codes are case-sensitive.
24117      */
24118     value : null,
24119     clickEvent:'click',
24120     // private
24121     ctype: "Roo.ColorPalette",
24122
24123     /**
24124      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24125      */
24126     allowReselect : false,
24127
24128     /**
24129      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24130      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24131      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24132      * of colors with the width setting until the box is symmetrical.</p>
24133      * <p>You can override individual colors if needed:</p>
24134      * <pre><code>
24135 var cp = new Roo.ColorPalette();
24136 cp.colors[0] = "FF0000";  // change the first box to red
24137 </code></pre>
24138
24139 Or you can provide a custom array of your own for complete control:
24140 <pre><code>
24141 var cp = new Roo.ColorPalette();
24142 cp.colors = ["000000", "993300", "333300"];
24143 </code></pre>
24144      * @type Array
24145      */
24146     colors : [
24147         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24148         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24149         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24150         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24151         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24152     ],
24153
24154     // private
24155     onRender : function(container, position){
24156         var t = new Roo.MasterTemplate(
24157             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24158         );
24159         var c = this.colors;
24160         for(var i = 0, len = c.length; i < len; i++){
24161             t.add([c[i]]);
24162         }
24163         var el = document.createElement("div");
24164         el.className = this.itemCls;
24165         t.overwrite(el);
24166         container.dom.insertBefore(el, position);
24167         this.el = Roo.get(el);
24168         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24169         if(this.clickEvent != 'click'){
24170             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24171         }
24172     },
24173
24174     // private
24175     afterRender : function(){
24176         Roo.ColorPalette.superclass.afterRender.call(this);
24177         if(this.value){
24178             var s = this.value;
24179             this.value = null;
24180             this.select(s);
24181         }
24182     },
24183
24184     // private
24185     handleClick : function(e, t){
24186         e.preventDefault();
24187         if(!this.disabled){
24188             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24189             this.select(c.toUpperCase());
24190         }
24191     },
24192
24193     /**
24194      * Selects the specified color in the palette (fires the select event)
24195      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24196      */
24197     select : function(color){
24198         color = color.replace("#", "");
24199         if(color != this.value || this.allowReselect){
24200             var el = this.el;
24201             if(this.value){
24202                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24203             }
24204             el.child("a.color-"+color).addClass("x-color-palette-sel");
24205             this.value = color;
24206             this.fireEvent("select", this, color);
24207         }
24208     }
24209 });/*
24210  * Based on:
24211  * Ext JS Library 1.1.1
24212  * Copyright(c) 2006-2007, Ext JS, LLC.
24213  *
24214  * Originally Released Under LGPL - original licence link has changed is not relivant.
24215  *
24216  * Fork - LGPL
24217  * <script type="text/javascript">
24218  */
24219  
24220 /**
24221  * @class Roo.DatePicker
24222  * @extends Roo.Component
24223  * Simple date picker class.
24224  * @constructor
24225  * Create a new DatePicker
24226  * @param {Object} config The config object
24227  */
24228 Roo.DatePicker = function(config){
24229     Roo.DatePicker.superclass.constructor.call(this, config);
24230
24231     this.value = config && config.value ?
24232                  config.value.clearTime() : new Date().clearTime();
24233
24234     this.addEvents({
24235         /**
24236              * @event select
24237              * Fires when a date is selected
24238              * @param {DatePicker} this
24239              * @param {Date} date The selected date
24240              */
24241         select: true
24242     });
24243
24244     if(this.handler){
24245         this.on("select", this.handler,  this.scope || this);
24246     }
24247     // build the disabledDatesRE
24248     if(!this.disabledDatesRE && this.disabledDates){
24249         var dd = this.disabledDates;
24250         var re = "(?:";
24251         for(var i = 0; i < dd.length; i++){
24252             re += dd[i];
24253             if(i != dd.length-1) re += "|";
24254         }
24255         this.disabledDatesRE = new RegExp(re + ")");
24256     }
24257 };
24258
24259 Roo.extend(Roo.DatePicker, Roo.Component, {
24260     /**
24261      * @cfg {String} todayText
24262      * The text to display on the button that selects the current date (defaults to "Today")
24263      */
24264     todayText : "Today",
24265     /**
24266      * @cfg {String} okText
24267      * The text to display on the ok button
24268      */
24269     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24270     /**
24271      * @cfg {String} cancelText
24272      * The text to display on the cancel button
24273      */
24274     cancelText : "Cancel",
24275     /**
24276      * @cfg {String} todayTip
24277      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24278      */
24279     todayTip : "{0} (Spacebar)",
24280     /**
24281      * @cfg {Date} minDate
24282      * Minimum allowable date (JavaScript date object, defaults to null)
24283      */
24284     minDate : null,
24285     /**
24286      * @cfg {Date} maxDate
24287      * Maximum allowable date (JavaScript date object, defaults to null)
24288      */
24289     maxDate : null,
24290     /**
24291      * @cfg {String} minText
24292      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24293      */
24294     minText : "This date is before the minimum date",
24295     /**
24296      * @cfg {String} maxText
24297      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24298      */
24299     maxText : "This date is after the maximum date",
24300     /**
24301      * @cfg {String} format
24302      * The default date format string which can be overriden for localization support.  The format must be
24303      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24304      */
24305     format : "m/d/y",
24306     /**
24307      * @cfg {Array} disabledDays
24308      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24309      */
24310     disabledDays : null,
24311     /**
24312      * @cfg {String} disabledDaysText
24313      * The tooltip to display when the date falls on a disabled day (defaults to "")
24314      */
24315     disabledDaysText : "",
24316     /**
24317      * @cfg {RegExp} disabledDatesRE
24318      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24319      */
24320     disabledDatesRE : null,
24321     /**
24322      * @cfg {String} disabledDatesText
24323      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24324      */
24325     disabledDatesText : "",
24326     /**
24327      * @cfg {Boolean} constrainToViewport
24328      * True to constrain the date picker to the viewport (defaults to true)
24329      */
24330     constrainToViewport : true,
24331     /**
24332      * @cfg {Array} monthNames
24333      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24334      */
24335     monthNames : Date.monthNames,
24336     /**
24337      * @cfg {Array} dayNames
24338      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24339      */
24340     dayNames : Date.dayNames,
24341     /**
24342      * @cfg {String} nextText
24343      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24344      */
24345     nextText: 'Next Month (Control+Right)',
24346     /**
24347      * @cfg {String} prevText
24348      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24349      */
24350     prevText: 'Previous Month (Control+Left)',
24351     /**
24352      * @cfg {String} monthYearText
24353      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24354      */
24355     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24356     /**
24357      * @cfg {Number} startDay
24358      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24359      */
24360     startDay : 0,
24361     /**
24362      * @cfg {Bool} showClear
24363      * Show a clear button (usefull for date form elements that can be blank.)
24364      */
24365     
24366     showClear: false,
24367     
24368     /**
24369      * Sets the value of the date field
24370      * @param {Date} value The date to set
24371      */
24372     setValue : function(value){
24373         var old = this.value;
24374         this.value = value.clearTime(true);
24375         if(this.el){
24376             this.update(this.value);
24377         }
24378     },
24379
24380     /**
24381      * Gets the current selected value of the date field
24382      * @return {Date} The selected date
24383      */
24384     getValue : function(){
24385         return this.value;
24386     },
24387
24388     // private
24389     focus : function(){
24390         if(this.el){
24391             this.update(this.activeDate);
24392         }
24393     },
24394
24395     // private
24396     onRender : function(container, position){
24397         var m = [
24398              '<table cellspacing="0">',
24399                 '<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>',
24400                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24401         var dn = this.dayNames;
24402         for(var i = 0; i < 7; i++){
24403             var d = this.startDay+i;
24404             if(d > 6){
24405                 d = d-7;
24406             }
24407             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24408         }
24409         m[m.length] = "</tr></thead><tbody><tr>";
24410         for(var i = 0; i < 42; i++) {
24411             if(i % 7 == 0 && i != 0){
24412                 m[m.length] = "</tr><tr>";
24413             }
24414             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24415         }
24416         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24417             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24418
24419         var el = document.createElement("div");
24420         el.className = "x-date-picker";
24421         el.innerHTML = m.join("");
24422
24423         container.dom.insertBefore(el, position);
24424
24425         this.el = Roo.get(el);
24426         this.eventEl = Roo.get(el.firstChild);
24427
24428         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24429             handler: this.showPrevMonth,
24430             scope: this,
24431             preventDefault:true,
24432             stopDefault:true
24433         });
24434
24435         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24436             handler: this.showNextMonth,
24437             scope: this,
24438             preventDefault:true,
24439             stopDefault:true
24440         });
24441
24442         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24443
24444         this.monthPicker = this.el.down('div.x-date-mp');
24445         this.monthPicker.enableDisplayMode('block');
24446         
24447         var kn = new Roo.KeyNav(this.eventEl, {
24448             "left" : function(e){
24449                 e.ctrlKey ?
24450                     this.showPrevMonth() :
24451                     this.update(this.activeDate.add("d", -1));
24452             },
24453
24454             "right" : function(e){
24455                 e.ctrlKey ?
24456                     this.showNextMonth() :
24457                     this.update(this.activeDate.add("d", 1));
24458             },
24459
24460             "up" : function(e){
24461                 e.ctrlKey ?
24462                     this.showNextYear() :
24463                     this.update(this.activeDate.add("d", -7));
24464             },
24465
24466             "down" : function(e){
24467                 e.ctrlKey ?
24468                     this.showPrevYear() :
24469                     this.update(this.activeDate.add("d", 7));
24470             },
24471
24472             "pageUp" : function(e){
24473                 this.showNextMonth();
24474             },
24475
24476             "pageDown" : function(e){
24477                 this.showPrevMonth();
24478             },
24479
24480             "enter" : function(e){
24481                 e.stopPropagation();
24482                 return true;
24483             },
24484
24485             scope : this
24486         });
24487
24488         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24489
24490         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24491
24492         this.el.unselectable();
24493         
24494         this.cells = this.el.select("table.x-date-inner tbody td");
24495         this.textNodes = this.el.query("table.x-date-inner tbody span");
24496
24497         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24498             text: "&#160;",
24499             tooltip: this.monthYearText
24500         });
24501
24502         this.mbtn.on('click', this.showMonthPicker, this);
24503         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24504
24505
24506         var today = (new Date()).dateFormat(this.format);
24507         
24508         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24509         baseTb.add({
24510             text: String.format(this.todayText, today),
24511             tooltip: String.format(this.todayTip, today),
24512             handler: this.selectToday,
24513             scope: this
24514         });
24515         
24516         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24517             
24518         //});
24519         if (this.showClear) {
24520             
24521             baseTb.add( new Roo.Toolbar.Fill());
24522             baseTb.add({
24523                 text: '&#160;',
24524                 cls: 'x-btn-icon x-btn-clear',
24525                 handler: function() {
24526                     //this.value = '';
24527                     this.fireEvent("select", this, '');
24528                 },
24529                 scope: this
24530             });
24531         }
24532         
24533         
24534         if(Roo.isIE){
24535             this.el.repaint();
24536         }
24537         this.update(this.value);
24538     },
24539
24540     createMonthPicker : function(){
24541         if(!this.monthPicker.dom.firstChild){
24542             var buf = ['<table border="0" cellspacing="0">'];
24543             for(var i = 0; i < 6; i++){
24544                 buf.push(
24545                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24546                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24547                     i == 0 ?
24548                     '<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>' :
24549                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24550                 );
24551             }
24552             buf.push(
24553                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24554                     this.okText,
24555                     '</button><button type="button" class="x-date-mp-cancel">',
24556                     this.cancelText,
24557                     '</button></td></tr>',
24558                 '</table>'
24559             );
24560             this.monthPicker.update(buf.join(''));
24561             this.monthPicker.on('click', this.onMonthClick, this);
24562             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24563
24564             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24565             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24566
24567             this.mpMonths.each(function(m, a, i){
24568                 i += 1;
24569                 if((i%2) == 0){
24570                     m.dom.xmonth = 5 + Math.round(i * .5);
24571                 }else{
24572                     m.dom.xmonth = Math.round((i-1) * .5);
24573                 }
24574             });
24575         }
24576     },
24577
24578     showMonthPicker : function(){
24579         this.createMonthPicker();
24580         var size = this.el.getSize();
24581         this.monthPicker.setSize(size);
24582         this.monthPicker.child('table').setSize(size);
24583
24584         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24585         this.updateMPMonth(this.mpSelMonth);
24586         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24587         this.updateMPYear(this.mpSelYear);
24588
24589         this.monthPicker.slideIn('t', {duration:.2});
24590     },
24591
24592     updateMPYear : function(y){
24593         this.mpyear = y;
24594         var ys = this.mpYears.elements;
24595         for(var i = 1; i <= 10; i++){
24596             var td = ys[i-1], y2;
24597             if((i%2) == 0){
24598                 y2 = y + Math.round(i * .5);
24599                 td.firstChild.innerHTML = y2;
24600                 td.xyear = y2;
24601             }else{
24602                 y2 = y - (5-Math.round(i * .5));
24603                 td.firstChild.innerHTML = y2;
24604                 td.xyear = y2;
24605             }
24606             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24607         }
24608     },
24609
24610     updateMPMonth : function(sm){
24611         this.mpMonths.each(function(m, a, i){
24612             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24613         });
24614     },
24615
24616     selectMPMonth: function(m){
24617         
24618     },
24619
24620     onMonthClick : function(e, t){
24621         e.stopEvent();
24622         var el = new Roo.Element(t), pn;
24623         if(el.is('button.x-date-mp-cancel')){
24624             this.hideMonthPicker();
24625         }
24626         else if(el.is('button.x-date-mp-ok')){
24627             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24628             this.hideMonthPicker();
24629         }
24630         else if(pn = el.up('td.x-date-mp-month', 2)){
24631             this.mpMonths.removeClass('x-date-mp-sel');
24632             pn.addClass('x-date-mp-sel');
24633             this.mpSelMonth = pn.dom.xmonth;
24634         }
24635         else if(pn = el.up('td.x-date-mp-year', 2)){
24636             this.mpYears.removeClass('x-date-mp-sel');
24637             pn.addClass('x-date-mp-sel');
24638             this.mpSelYear = pn.dom.xyear;
24639         }
24640         else if(el.is('a.x-date-mp-prev')){
24641             this.updateMPYear(this.mpyear-10);
24642         }
24643         else if(el.is('a.x-date-mp-next')){
24644             this.updateMPYear(this.mpyear+10);
24645         }
24646     },
24647
24648     onMonthDblClick : function(e, t){
24649         e.stopEvent();
24650         var el = new Roo.Element(t), pn;
24651         if(pn = el.up('td.x-date-mp-month', 2)){
24652             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24653             this.hideMonthPicker();
24654         }
24655         else if(pn = el.up('td.x-date-mp-year', 2)){
24656             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24657             this.hideMonthPicker();
24658         }
24659     },
24660
24661     hideMonthPicker : function(disableAnim){
24662         if(this.monthPicker){
24663             if(disableAnim === true){
24664                 this.monthPicker.hide();
24665             }else{
24666                 this.monthPicker.slideOut('t', {duration:.2});
24667             }
24668         }
24669     },
24670
24671     // private
24672     showPrevMonth : function(e){
24673         this.update(this.activeDate.add("mo", -1));
24674     },
24675
24676     // private
24677     showNextMonth : function(e){
24678         this.update(this.activeDate.add("mo", 1));
24679     },
24680
24681     // private
24682     showPrevYear : function(){
24683         this.update(this.activeDate.add("y", -1));
24684     },
24685
24686     // private
24687     showNextYear : function(){
24688         this.update(this.activeDate.add("y", 1));
24689     },
24690
24691     // private
24692     handleMouseWheel : function(e){
24693         var delta = e.getWheelDelta();
24694         if(delta > 0){
24695             this.showPrevMonth();
24696             e.stopEvent();
24697         } else if(delta < 0){
24698             this.showNextMonth();
24699             e.stopEvent();
24700         }
24701     },
24702
24703     // private
24704     handleDateClick : function(e, t){
24705         e.stopEvent();
24706         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24707             this.setValue(new Date(t.dateValue));
24708             this.fireEvent("select", this, this.value);
24709         }
24710     },
24711
24712     // private
24713     selectToday : function(){
24714         this.setValue(new Date().clearTime());
24715         this.fireEvent("select", this, this.value);
24716     },
24717
24718     // private
24719     update : function(date){
24720         var vd = this.activeDate;
24721         this.activeDate = date;
24722         if(vd && this.el){
24723             var t = date.getTime();
24724             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24725                 this.cells.removeClass("x-date-selected");
24726                 this.cells.each(function(c){
24727                    if(c.dom.firstChild.dateValue == t){
24728                        c.addClass("x-date-selected");
24729                        setTimeout(function(){
24730                             try{c.dom.firstChild.focus();}catch(e){}
24731                        }, 50);
24732                        return false;
24733                    }
24734                 });
24735                 return;
24736             }
24737         }
24738         var days = date.getDaysInMonth();
24739         var firstOfMonth = date.getFirstDateOfMonth();
24740         var startingPos = firstOfMonth.getDay()-this.startDay;
24741
24742         if(startingPos <= this.startDay){
24743             startingPos += 7;
24744         }
24745
24746         var pm = date.add("mo", -1);
24747         var prevStart = pm.getDaysInMonth()-startingPos;
24748
24749         var cells = this.cells.elements;
24750         var textEls = this.textNodes;
24751         days += startingPos;
24752
24753         // convert everything to numbers so it's fast
24754         var day = 86400000;
24755         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24756         var today = new Date().clearTime().getTime();
24757         var sel = date.clearTime().getTime();
24758         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24759         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24760         var ddMatch = this.disabledDatesRE;
24761         var ddText = this.disabledDatesText;
24762         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24763         var ddaysText = this.disabledDaysText;
24764         var format = this.format;
24765
24766         var setCellClass = function(cal, cell){
24767             cell.title = "";
24768             var t = d.getTime();
24769             cell.firstChild.dateValue = t;
24770             if(t == today){
24771                 cell.className += " x-date-today";
24772                 cell.title = cal.todayText;
24773             }
24774             if(t == sel){
24775                 cell.className += " x-date-selected";
24776                 setTimeout(function(){
24777                     try{cell.firstChild.focus();}catch(e){}
24778                 }, 50);
24779             }
24780             // disabling
24781             if(t < min) {
24782                 cell.className = " x-date-disabled";
24783                 cell.title = cal.minText;
24784                 return;
24785             }
24786             if(t > max) {
24787                 cell.className = " x-date-disabled";
24788                 cell.title = cal.maxText;
24789                 return;
24790             }
24791             if(ddays){
24792                 if(ddays.indexOf(d.getDay()) != -1){
24793                     cell.title = ddaysText;
24794                     cell.className = " x-date-disabled";
24795                 }
24796             }
24797             if(ddMatch && format){
24798                 var fvalue = d.dateFormat(format);
24799                 if(ddMatch.test(fvalue)){
24800                     cell.title = ddText.replace("%0", fvalue);
24801                     cell.className = " x-date-disabled";
24802                 }
24803             }
24804         };
24805
24806         var i = 0;
24807         for(; i < startingPos; i++) {
24808             textEls[i].innerHTML = (++prevStart);
24809             d.setDate(d.getDate()+1);
24810             cells[i].className = "x-date-prevday";
24811             setCellClass(this, cells[i]);
24812         }
24813         for(; i < days; i++){
24814             intDay = i - startingPos + 1;
24815             textEls[i].innerHTML = (intDay);
24816             d.setDate(d.getDate()+1);
24817             cells[i].className = "x-date-active";
24818             setCellClass(this, cells[i]);
24819         }
24820         var extraDays = 0;
24821         for(; i < 42; i++) {
24822              textEls[i].innerHTML = (++extraDays);
24823              d.setDate(d.getDate()+1);
24824              cells[i].className = "x-date-nextday";
24825              setCellClass(this, cells[i]);
24826         }
24827
24828         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24829
24830         if(!this.internalRender){
24831             var main = this.el.dom.firstChild;
24832             var w = main.offsetWidth;
24833             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24834             Roo.fly(main).setWidth(w);
24835             this.internalRender = true;
24836             // opera does not respect the auto grow header center column
24837             // then, after it gets a width opera refuses to recalculate
24838             // without a second pass
24839             if(Roo.isOpera && !this.secondPass){
24840                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24841                 this.secondPass = true;
24842                 this.update.defer(10, this, [date]);
24843             }
24844         }
24845     }
24846 });/*
24847  * Based on:
24848  * Ext JS Library 1.1.1
24849  * Copyright(c) 2006-2007, Ext JS, LLC.
24850  *
24851  * Originally Released Under LGPL - original licence link has changed is not relivant.
24852  *
24853  * Fork - LGPL
24854  * <script type="text/javascript">
24855  */
24856 /**
24857  * @class Roo.TabPanel
24858  * @extends Roo.util.Observable
24859  * A lightweight tab container.
24860  * <br><br>
24861  * Usage:
24862  * <pre><code>
24863 // basic tabs 1, built from existing content
24864 var tabs = new Roo.TabPanel("tabs1");
24865 tabs.addTab("script", "View Script");
24866 tabs.addTab("markup", "View Markup");
24867 tabs.activate("script");
24868
24869 // more advanced tabs, built from javascript
24870 var jtabs = new Roo.TabPanel("jtabs");
24871 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24872
24873 // set up the UpdateManager
24874 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24875 var updater = tab2.getUpdateManager();
24876 updater.setDefaultUrl("ajax1.htm");
24877 tab2.on('activate', updater.refresh, updater, true);
24878
24879 // Use setUrl for Ajax loading
24880 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24881 tab3.setUrl("ajax2.htm", null, true);
24882
24883 // Disabled tab
24884 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24885 tab4.disable();
24886
24887 jtabs.activate("jtabs-1");
24888  * </code></pre>
24889  * @constructor
24890  * Create a new TabPanel.
24891  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24892  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24893  */
24894 Roo.TabPanel = function(container, config){
24895     /**
24896     * The container element for this TabPanel.
24897     * @type Roo.Element
24898     */
24899     this.el = Roo.get(container, true);
24900     if(config){
24901         if(typeof config == "boolean"){
24902             this.tabPosition = config ? "bottom" : "top";
24903         }else{
24904             Roo.apply(this, config);
24905         }
24906     }
24907     if(this.tabPosition == "bottom"){
24908         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24909         this.el.addClass("x-tabs-bottom");
24910     }
24911     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24912     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24913     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24914     if(Roo.isIE){
24915         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24916     }
24917     if(this.tabPosition != "bottom"){
24918     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24919      * @type Roo.Element
24920      */
24921       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24922       this.el.addClass("x-tabs-top");
24923     }
24924     this.items = [];
24925
24926     this.bodyEl.setStyle("position", "relative");
24927
24928     this.active = null;
24929     this.activateDelegate = this.activate.createDelegate(this);
24930
24931     this.addEvents({
24932         /**
24933          * @event tabchange
24934          * Fires when the active tab changes
24935          * @param {Roo.TabPanel} this
24936          * @param {Roo.TabPanelItem} activePanel The new active tab
24937          */
24938         "tabchange": true,
24939         /**
24940          * @event beforetabchange
24941          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24942          * @param {Roo.TabPanel} this
24943          * @param {Object} e Set cancel to true on this object to cancel the tab change
24944          * @param {Roo.TabPanelItem} tab The tab being changed to
24945          */
24946         "beforetabchange" : true
24947     });
24948
24949     Roo.EventManager.onWindowResize(this.onResize, this);
24950     this.cpad = this.el.getPadding("lr");
24951     this.hiddenCount = 0;
24952
24953     Roo.TabPanel.superclass.constructor.call(this);
24954 };
24955
24956 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24957         /*
24958          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24959          */
24960     tabPosition : "top",
24961         /*
24962          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24963          */
24964     currentTabWidth : 0,
24965         /*
24966          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24967          */
24968     minTabWidth : 40,
24969         /*
24970          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24971          */
24972     maxTabWidth : 250,
24973         /*
24974          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24975          */
24976     preferredTabWidth : 175,
24977         /*
24978          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24979          */
24980     resizeTabs : false,
24981         /*
24982          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24983          */
24984     monitorResize : true,
24985
24986     /**
24987      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24988      * @param {String} id The id of the div to use <b>or create</b>
24989      * @param {String} text The text for the tab
24990      * @param {String} content (optional) Content to put in the TabPanelItem body
24991      * @param {Boolean} closable (optional) True to create a close icon on the tab
24992      * @return {Roo.TabPanelItem} The created TabPanelItem
24993      */
24994     addTab : function(id, text, content, closable){
24995         var item = new Roo.TabPanelItem(this, id, text, closable);
24996         this.addTabItem(item);
24997         if(content){
24998             item.setContent(content);
24999         }
25000         return item;
25001     },
25002
25003     /**
25004      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25005      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25006      * @return {Roo.TabPanelItem}
25007      */
25008     getTab : function(id){
25009         return this.items[id];
25010     },
25011
25012     /**
25013      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25014      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25015      */
25016     hideTab : function(id){
25017         var t = this.items[id];
25018         if(!t.isHidden()){
25019            t.setHidden(true);
25020            this.hiddenCount++;
25021            this.autoSizeTabs();
25022         }
25023     },
25024
25025     /**
25026      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25027      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25028      */
25029     unhideTab : function(id){
25030         var t = this.items[id];
25031         if(t.isHidden()){
25032            t.setHidden(false);
25033            this.hiddenCount--;
25034            this.autoSizeTabs();
25035         }
25036     },
25037
25038     /**
25039      * Adds an existing {@link Roo.TabPanelItem}.
25040      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25041      */
25042     addTabItem : function(item){
25043         this.items[item.id] = item;
25044         this.items.push(item);
25045         if(this.resizeTabs){
25046            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25047            this.autoSizeTabs();
25048         }else{
25049             item.autoSize();
25050         }
25051     },
25052
25053     /**
25054      * Removes a {@link Roo.TabPanelItem}.
25055      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25056      */
25057     removeTab : function(id){
25058         var items = this.items;
25059         var tab = items[id];
25060         if(!tab) return;
25061         var index = items.indexOf(tab);
25062         if(this.active == tab && items.length > 1){
25063             var newTab = this.getNextAvailable(index);
25064             if(newTab)newTab.activate();
25065         }
25066         this.stripEl.dom.removeChild(tab.pnode.dom);
25067         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25068             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25069         }
25070         items.splice(index, 1);
25071         delete this.items[tab.id];
25072         tab.fireEvent("close", tab);
25073         tab.purgeListeners();
25074         this.autoSizeTabs();
25075     },
25076
25077     getNextAvailable : function(start){
25078         var items = this.items;
25079         var index = start;
25080         // look for a next tab that will slide over to
25081         // replace the one being removed
25082         while(index < items.length){
25083             var item = items[++index];
25084             if(item && !item.isHidden()){
25085                 return item;
25086             }
25087         }
25088         // if one isn't found select the previous tab (on the left)
25089         index = start;
25090         while(index >= 0){
25091             var item = items[--index];
25092             if(item && !item.isHidden()){
25093                 return item;
25094             }
25095         }
25096         return null;
25097     },
25098
25099     /**
25100      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25101      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25102      */
25103     disableTab : function(id){
25104         var tab = this.items[id];
25105         if(tab && this.active != tab){
25106             tab.disable();
25107         }
25108     },
25109
25110     /**
25111      * Enables a {@link Roo.TabPanelItem} that is disabled.
25112      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25113      */
25114     enableTab : function(id){
25115         var tab = this.items[id];
25116         tab.enable();
25117     },
25118
25119     /**
25120      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25121      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25122      * @return {Roo.TabPanelItem} The TabPanelItem.
25123      */
25124     activate : function(id){
25125         var tab = this.items[id];
25126         if(!tab){
25127             return null;
25128         }
25129         if(tab == this.active || tab.disabled){
25130             return tab;
25131         }
25132         var e = {};
25133         this.fireEvent("beforetabchange", this, e, tab);
25134         if(e.cancel !== true && !tab.disabled){
25135             if(this.active){
25136                 this.active.hide();
25137             }
25138             this.active = this.items[id];
25139             this.active.show();
25140             this.fireEvent("tabchange", this, this.active);
25141         }
25142         return tab;
25143     },
25144
25145     /**
25146      * Gets the active {@link Roo.TabPanelItem}.
25147      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25148      */
25149     getActiveTab : function(){
25150         return this.active;
25151     },
25152
25153     /**
25154      * Updates the tab body element to fit the height of the container element
25155      * for overflow scrolling
25156      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25157      */
25158     syncHeight : function(targetHeight){
25159         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25160         var bm = this.bodyEl.getMargins();
25161         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25162         this.bodyEl.setHeight(newHeight);
25163         return newHeight;
25164     },
25165
25166     onResize : function(){
25167         if(this.monitorResize){
25168             this.autoSizeTabs();
25169         }
25170     },
25171
25172     /**
25173      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25174      */
25175     beginUpdate : function(){
25176         this.updating = true;
25177     },
25178
25179     /**
25180      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25181      */
25182     endUpdate : function(){
25183         this.updating = false;
25184         this.autoSizeTabs();
25185     },
25186
25187     /**
25188      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25189      */
25190     autoSizeTabs : function(){
25191         var count = this.items.length;
25192         var vcount = count - this.hiddenCount;
25193         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25194         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25195         var availWidth = Math.floor(w / vcount);
25196         var b = this.stripBody;
25197         if(b.getWidth() > w){
25198             var tabs = this.items;
25199             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25200             if(availWidth < this.minTabWidth){
25201                 /*if(!this.sleft){    // incomplete scrolling code
25202                     this.createScrollButtons();
25203                 }
25204                 this.showScroll();
25205                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25206             }
25207         }else{
25208             if(this.currentTabWidth < this.preferredTabWidth){
25209                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25210             }
25211         }
25212     },
25213
25214     /**
25215      * Returns the number of tabs in this TabPanel.
25216      * @return {Number}
25217      */
25218      getCount : function(){
25219          return this.items.length;
25220      },
25221
25222     /**
25223      * Resizes all the tabs to the passed width
25224      * @param {Number} The new width
25225      */
25226     setTabWidth : function(width){
25227         this.currentTabWidth = width;
25228         for(var i = 0, len = this.items.length; i < len; i++) {
25229                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25230         }
25231     },
25232
25233     /**
25234      * Destroys this TabPanel
25235      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25236      */
25237     destroy : function(removeEl){
25238         Roo.EventManager.removeResizeListener(this.onResize, this);
25239         for(var i = 0, len = this.items.length; i < len; i++){
25240             this.items[i].purgeListeners();
25241         }
25242         if(removeEl === true){
25243             this.el.update("");
25244             this.el.remove();
25245         }
25246     }
25247 });
25248
25249 /**
25250  * @class Roo.TabPanelItem
25251  * @extends Roo.util.Observable
25252  * Represents an individual item (tab plus body) in a TabPanel.
25253  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25254  * @param {String} id The id of this TabPanelItem
25255  * @param {String} text The text for the tab of this TabPanelItem
25256  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25257  */
25258 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25259     /**
25260      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25261      * @type Roo.TabPanel
25262      */
25263     this.tabPanel = tabPanel;
25264     /**
25265      * The id for this TabPanelItem
25266      * @type String
25267      */
25268     this.id = id;
25269     /** @private */
25270     this.disabled = false;
25271     /** @private */
25272     this.text = text;
25273     /** @private */
25274     this.loaded = false;
25275     this.closable = closable;
25276
25277     /**
25278      * The body element for this TabPanelItem.
25279      * @type Roo.Element
25280      */
25281     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25282     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25283     this.bodyEl.setStyle("display", "block");
25284     this.bodyEl.setStyle("zoom", "1");
25285     this.hideAction();
25286
25287     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25288     /** @private */
25289     this.el = Roo.get(els.el, true);
25290     this.inner = Roo.get(els.inner, true);
25291     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25292     this.pnode = Roo.get(els.el.parentNode, true);
25293     this.el.on("mousedown", this.onTabMouseDown, this);
25294     this.el.on("click", this.onTabClick, this);
25295     /** @private */
25296     if(closable){
25297         var c = Roo.get(els.close, true);
25298         c.dom.title = this.closeText;
25299         c.addClassOnOver("close-over");
25300         c.on("click", this.closeClick, this);
25301      }
25302
25303     this.addEvents({
25304          /**
25305          * @event activate
25306          * Fires when this tab becomes the active tab.
25307          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25308          * @param {Roo.TabPanelItem} this
25309          */
25310         "activate": true,
25311         /**
25312          * @event beforeclose
25313          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25314          * @param {Roo.TabPanelItem} this
25315          * @param {Object} e Set cancel to true on this object to cancel the close.
25316          */
25317         "beforeclose": true,
25318         /**
25319          * @event close
25320          * Fires when this tab is closed.
25321          * @param {Roo.TabPanelItem} this
25322          */
25323          "close": true,
25324         /**
25325          * @event deactivate
25326          * Fires when this tab is no longer the active tab.
25327          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25328          * @param {Roo.TabPanelItem} this
25329          */
25330          "deactivate" : true
25331     });
25332     this.hidden = false;
25333
25334     Roo.TabPanelItem.superclass.constructor.call(this);
25335 };
25336
25337 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25338     purgeListeners : function(){
25339        Roo.util.Observable.prototype.purgeListeners.call(this);
25340        this.el.removeAllListeners();
25341     },
25342     /**
25343      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25344      */
25345     show : function(){
25346         this.pnode.addClass("on");
25347         this.showAction();
25348         if(Roo.isOpera){
25349             this.tabPanel.stripWrap.repaint();
25350         }
25351         this.fireEvent("activate", this.tabPanel, this);
25352     },
25353
25354     /**
25355      * Returns true if this tab is the active tab.
25356      * @return {Boolean}
25357      */
25358     isActive : function(){
25359         return this.tabPanel.getActiveTab() == this;
25360     },
25361
25362     /**
25363      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25364      */
25365     hide : function(){
25366         this.pnode.removeClass("on");
25367         this.hideAction();
25368         this.fireEvent("deactivate", this.tabPanel, this);
25369     },
25370
25371     hideAction : function(){
25372         this.bodyEl.hide();
25373         this.bodyEl.setStyle("position", "absolute");
25374         this.bodyEl.setLeft("-20000px");
25375         this.bodyEl.setTop("-20000px");
25376     },
25377
25378     showAction : function(){
25379         this.bodyEl.setStyle("position", "relative");
25380         this.bodyEl.setTop("");
25381         this.bodyEl.setLeft("");
25382         this.bodyEl.show();
25383     },
25384
25385     /**
25386      * Set the tooltip for the tab.
25387      * @param {String} tooltip The tab's tooltip
25388      */
25389     setTooltip : function(text){
25390         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25391             this.textEl.dom.qtip = text;
25392             this.textEl.dom.removeAttribute('title');
25393         }else{
25394             this.textEl.dom.title = text;
25395         }
25396     },
25397
25398     onTabClick : function(e){
25399         e.preventDefault();
25400         this.tabPanel.activate(this.id);
25401     },
25402
25403     onTabMouseDown : function(e){
25404         e.preventDefault();
25405         this.tabPanel.activate(this.id);
25406     },
25407
25408     getWidth : function(){
25409         return this.inner.getWidth();
25410     },
25411
25412     setWidth : function(width){
25413         var iwidth = width - this.pnode.getPadding("lr");
25414         this.inner.setWidth(iwidth);
25415         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25416         this.pnode.setWidth(width);
25417     },
25418
25419     /**
25420      * Show or hide the tab
25421      * @param {Boolean} hidden True to hide or false to show.
25422      */
25423     setHidden : function(hidden){
25424         this.hidden = hidden;
25425         this.pnode.setStyle("display", hidden ? "none" : "");
25426     },
25427
25428     /**
25429      * Returns true if this tab is "hidden"
25430      * @return {Boolean}
25431      */
25432     isHidden : function(){
25433         return this.hidden;
25434     },
25435
25436     /**
25437      * Returns the text for this tab
25438      * @return {String}
25439      */
25440     getText : function(){
25441         return this.text;
25442     },
25443
25444     autoSize : function(){
25445         //this.el.beginMeasure();
25446         this.textEl.setWidth(1);
25447         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25448         //this.el.endMeasure();
25449     },
25450
25451     /**
25452      * Sets the text for the tab (Note: this also sets the tooltip text)
25453      * @param {String} text The tab's text and tooltip
25454      */
25455     setText : function(text){
25456         this.text = text;
25457         this.textEl.update(text);
25458         this.setTooltip(text);
25459         if(!this.tabPanel.resizeTabs){
25460             this.autoSize();
25461         }
25462     },
25463     /**
25464      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25465      */
25466     activate : function(){
25467         this.tabPanel.activate(this.id);
25468     },
25469
25470     /**
25471      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25472      */
25473     disable : function(){
25474         if(this.tabPanel.active != this){
25475             this.disabled = true;
25476             this.pnode.addClass("disabled");
25477         }
25478     },
25479
25480     /**
25481      * Enables this TabPanelItem if it was previously disabled.
25482      */
25483     enable : function(){
25484         this.disabled = false;
25485         this.pnode.removeClass("disabled");
25486     },
25487
25488     /**
25489      * Sets the content for this TabPanelItem.
25490      * @param {String} content The content
25491      * @param {Boolean} loadScripts true to look for and load scripts
25492      */
25493     setContent : function(content, loadScripts){
25494         this.bodyEl.update(content, loadScripts);
25495     },
25496
25497     /**
25498      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25499      * @return {Roo.UpdateManager} The UpdateManager
25500      */
25501     getUpdateManager : function(){
25502         return this.bodyEl.getUpdateManager();
25503     },
25504
25505     /**
25506      * Set a URL to be used to load the content for this TabPanelItem.
25507      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25508      * @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)
25509      * @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)
25510      * @return {Roo.UpdateManager} The UpdateManager
25511      */
25512     setUrl : function(url, params, loadOnce){
25513         if(this.refreshDelegate){
25514             this.un('activate', this.refreshDelegate);
25515         }
25516         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25517         this.on("activate", this.refreshDelegate);
25518         return this.bodyEl.getUpdateManager();
25519     },
25520
25521     /** @private */
25522     _handleRefresh : function(url, params, loadOnce){
25523         if(!loadOnce || !this.loaded){
25524             var updater = this.bodyEl.getUpdateManager();
25525             updater.update(url, params, this._setLoaded.createDelegate(this));
25526         }
25527     },
25528
25529     /**
25530      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25531      *   Will fail silently if the setUrl method has not been called.
25532      *   This does not activate the panel, just updates its content.
25533      */
25534     refresh : function(){
25535         if(this.refreshDelegate){
25536            this.loaded = false;
25537            this.refreshDelegate();
25538         }
25539     },
25540
25541     /** @private */
25542     _setLoaded : function(){
25543         this.loaded = true;
25544     },
25545
25546     /** @private */
25547     closeClick : function(e){
25548         var o = {};
25549         e.stopEvent();
25550         this.fireEvent("beforeclose", this, o);
25551         if(o.cancel !== true){
25552             this.tabPanel.removeTab(this.id);
25553         }
25554     },
25555     /**
25556      * The text displayed in the tooltip for the close icon.
25557      * @type String
25558      */
25559     closeText : "Close this tab"
25560 });
25561
25562 /** @private */
25563 Roo.TabPanel.prototype.createStrip = function(container){
25564     var strip = document.createElement("div");
25565     strip.className = "x-tabs-wrap";
25566     container.appendChild(strip);
25567     return strip;
25568 };
25569 /** @private */
25570 Roo.TabPanel.prototype.createStripList = function(strip){
25571     // div wrapper for retard IE
25572     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>';
25573     return strip.firstChild.firstChild.firstChild.firstChild;
25574 };
25575 /** @private */
25576 Roo.TabPanel.prototype.createBody = function(container){
25577     var body = document.createElement("div");
25578     Roo.id(body, "tab-body");
25579     Roo.fly(body).addClass("x-tabs-body");
25580     container.appendChild(body);
25581     return body;
25582 };
25583 /** @private */
25584 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25585     var body = Roo.getDom(id);
25586     if(!body){
25587         body = document.createElement("div");
25588         body.id = id;
25589     }
25590     Roo.fly(body).addClass("x-tabs-item-body");
25591     bodyEl.insertBefore(body, bodyEl.firstChild);
25592     return body;
25593 };
25594 /** @private */
25595 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25596     var td = document.createElement("td");
25597     stripEl.appendChild(td);
25598     if(closable){
25599         td.className = "x-tabs-closable";
25600         if(!this.closeTpl){
25601             this.closeTpl = new Roo.Template(
25602                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25603                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25604                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25605             );
25606         }
25607         var el = this.closeTpl.overwrite(td, {"text": text});
25608         var close = el.getElementsByTagName("div")[0];
25609         var inner = el.getElementsByTagName("em")[0];
25610         return {"el": el, "close": close, "inner": inner};
25611     } else {
25612         if(!this.tabTpl){
25613             this.tabTpl = new Roo.Template(
25614                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25615                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25616             );
25617         }
25618         var el = this.tabTpl.overwrite(td, {"text": text});
25619         var inner = el.getElementsByTagName("em")[0];
25620         return {"el": el, "inner": inner};
25621     }
25622 };/*
25623  * Based on:
25624  * Ext JS Library 1.1.1
25625  * Copyright(c) 2006-2007, Ext JS, LLC.
25626  *
25627  * Originally Released Under LGPL - original licence link has changed is not relivant.
25628  *
25629  * Fork - LGPL
25630  * <script type="text/javascript">
25631  */
25632
25633 /**
25634  * @class Roo.Button
25635  * @extends Roo.util.Observable
25636  * Simple Button class
25637  * @cfg {String} text The button text
25638  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25639  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25640  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25641  * @cfg {Object} scope The scope of the handler
25642  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25643  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25644  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25645  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25646  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25647  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25648    applies if enableToggle = true)
25649  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25650  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25651   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25652  * @constructor
25653  * Create a new button
25654  * @param {Object} config The config object
25655  */
25656 Roo.Button = function(renderTo, config)
25657 {
25658     if (!config) {
25659         config = renderTo;
25660         renderTo = config.renderTo || false;
25661     }
25662     
25663     Roo.apply(this, config);
25664     this.addEvents({
25665         /**
25666              * @event click
25667              * Fires when this button is clicked
25668              * @param {Button} this
25669              * @param {EventObject} e The click event
25670              */
25671             "click" : true,
25672         /**
25673              * @event toggle
25674              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25675              * @param {Button} this
25676              * @param {Boolean} pressed
25677              */
25678             "toggle" : true,
25679         /**
25680              * @event mouseover
25681              * Fires when the mouse hovers over the button
25682              * @param {Button} this
25683              * @param {Event} e The event object
25684              */
25685         'mouseover' : true,
25686         /**
25687              * @event mouseout
25688              * Fires when the mouse exits the button
25689              * @param {Button} this
25690              * @param {Event} e The event object
25691              */
25692         'mouseout': true,
25693          /**
25694              * @event render
25695              * Fires when the button is rendered
25696              * @param {Button} this
25697              */
25698         'render': true
25699     });
25700     if(this.menu){
25701         this.menu = Roo.menu.MenuMgr.get(this.menu);
25702     }
25703     if(renderTo){
25704         this.render(renderTo);
25705     }
25706     
25707     Roo.util.Observable.call(this);
25708 };
25709
25710 Roo.extend(Roo.Button, Roo.util.Observable, {
25711     /**
25712      * 
25713      */
25714     
25715     /**
25716      * Read-only. True if this button is hidden
25717      * @type Boolean
25718      */
25719     hidden : false,
25720     /**
25721      * Read-only. True if this button is disabled
25722      * @type Boolean
25723      */
25724     disabled : false,
25725     /**
25726      * Read-only. True if this button is pressed (only if enableToggle = true)
25727      * @type Boolean
25728      */
25729     pressed : false,
25730
25731     /**
25732      * @cfg {Number} tabIndex 
25733      * The DOM tabIndex for this button (defaults to undefined)
25734      */
25735     tabIndex : undefined,
25736
25737     /**
25738      * @cfg {Boolean} enableToggle
25739      * True to enable pressed/not pressed toggling (defaults to false)
25740      */
25741     enableToggle: false,
25742     /**
25743      * @cfg {Mixed} menu
25744      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25745      */
25746     menu : undefined,
25747     /**
25748      * @cfg {String} menuAlign
25749      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25750      */
25751     menuAlign : "tl-bl?",
25752
25753     /**
25754      * @cfg {String} iconCls
25755      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25756      */
25757     iconCls : undefined,
25758     /**
25759      * @cfg {String} type
25760      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25761      */
25762     type : 'button',
25763
25764     // private
25765     menuClassTarget: 'tr',
25766
25767     /**
25768      * @cfg {String} clickEvent
25769      * The type of event to map to the button's event handler (defaults to 'click')
25770      */
25771     clickEvent : 'click',
25772
25773     /**
25774      * @cfg {Boolean} handleMouseEvents
25775      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25776      */
25777     handleMouseEvents : true,
25778
25779     /**
25780      * @cfg {String} tooltipType
25781      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25782      */
25783     tooltipType : 'qtip',
25784
25785     /**
25786      * @cfg {String} cls
25787      * A CSS class to apply to the button's main element.
25788      */
25789     
25790     /**
25791      * @cfg {Roo.Template} template (Optional)
25792      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25793      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25794      * require code modifications if required elements (e.g. a button) aren't present.
25795      */
25796
25797     // private
25798     render : function(renderTo){
25799         var btn;
25800         if(this.hideParent){
25801             this.parentEl = Roo.get(renderTo);
25802         }
25803         if(!this.dhconfig){
25804             if(!this.template){
25805                 if(!Roo.Button.buttonTemplate){
25806                     // hideous table template
25807                     Roo.Button.buttonTemplate = new Roo.Template(
25808                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25809                         '<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>',
25810                         "</tr></tbody></table>");
25811                 }
25812                 this.template = Roo.Button.buttonTemplate;
25813             }
25814             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25815             var btnEl = btn.child("button:first");
25816             btnEl.on('focus', this.onFocus, this);
25817             btnEl.on('blur', this.onBlur, this);
25818             if(this.cls){
25819                 btn.addClass(this.cls);
25820             }
25821             if(this.icon){
25822                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25823             }
25824             if(this.iconCls){
25825                 btnEl.addClass(this.iconCls);
25826                 if(!this.cls){
25827                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25828                 }
25829             }
25830             if(this.tabIndex !== undefined){
25831                 btnEl.dom.tabIndex = this.tabIndex;
25832             }
25833             if(this.tooltip){
25834                 if(typeof this.tooltip == 'object'){
25835                     Roo.QuickTips.tips(Roo.apply({
25836                           target: btnEl.id
25837                     }, this.tooltip));
25838                 } else {
25839                     btnEl.dom[this.tooltipType] = this.tooltip;
25840                 }
25841             }
25842         }else{
25843             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25844         }
25845         this.el = btn;
25846         if(this.id){
25847             this.el.dom.id = this.el.id = this.id;
25848         }
25849         if(this.menu){
25850             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25851             this.menu.on("show", this.onMenuShow, this);
25852             this.menu.on("hide", this.onMenuHide, this);
25853         }
25854         btn.addClass("x-btn");
25855         if(Roo.isIE && !Roo.isIE7){
25856             this.autoWidth.defer(1, this);
25857         }else{
25858             this.autoWidth();
25859         }
25860         if(this.handleMouseEvents){
25861             btn.on("mouseover", this.onMouseOver, this);
25862             btn.on("mouseout", this.onMouseOut, this);
25863             btn.on("mousedown", this.onMouseDown, this);
25864         }
25865         btn.on(this.clickEvent, this.onClick, this);
25866         //btn.on("mouseup", this.onMouseUp, this);
25867         if(this.hidden){
25868             this.hide();
25869         }
25870         if(this.disabled){
25871             this.disable();
25872         }
25873         Roo.ButtonToggleMgr.register(this);
25874         if(this.pressed){
25875             this.el.addClass("x-btn-pressed");
25876         }
25877         if(this.repeat){
25878             var repeater = new Roo.util.ClickRepeater(btn,
25879                 typeof this.repeat == "object" ? this.repeat : {}
25880             );
25881             repeater.on("click", this.onClick,  this);
25882         }
25883         this.fireEvent('render', this);
25884         
25885     },
25886     /**
25887      * Returns the button's underlying element
25888      * @return {Roo.Element} The element
25889      */
25890     getEl : function(){
25891         return this.el;  
25892     },
25893     
25894     /**
25895      * Destroys this Button and removes any listeners.
25896      */
25897     destroy : function(){
25898         Roo.ButtonToggleMgr.unregister(this);
25899         this.el.removeAllListeners();
25900         this.purgeListeners();
25901         this.el.remove();
25902     },
25903
25904     // private
25905     autoWidth : function(){
25906         if(this.el){
25907             this.el.setWidth("auto");
25908             if(Roo.isIE7 && Roo.isStrict){
25909                 var ib = this.el.child('button');
25910                 if(ib && ib.getWidth() > 20){
25911                     ib.clip();
25912                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25913                 }
25914             }
25915             if(this.minWidth){
25916                 if(this.hidden){
25917                     this.el.beginMeasure();
25918                 }
25919                 if(this.el.getWidth() < this.minWidth){
25920                     this.el.setWidth(this.minWidth);
25921                 }
25922                 if(this.hidden){
25923                     this.el.endMeasure();
25924                 }
25925             }
25926         }
25927     },
25928
25929     /**
25930      * Assigns this button's click handler
25931      * @param {Function} handler The function to call when the button is clicked
25932      * @param {Object} scope (optional) Scope for the function passed in
25933      */
25934     setHandler : function(handler, scope){
25935         this.handler = handler;
25936         this.scope = scope;  
25937     },
25938     
25939     /**
25940      * Sets this button's text
25941      * @param {String} text The button text
25942      */
25943     setText : function(text){
25944         this.text = text;
25945         if(this.el){
25946             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25947         }
25948         this.autoWidth();
25949     },
25950     
25951     /**
25952      * Gets the text for this button
25953      * @return {String} The button text
25954      */
25955     getText : function(){
25956         return this.text;  
25957     },
25958     
25959     /**
25960      * Show this button
25961      */
25962     show: function(){
25963         this.hidden = false;
25964         if(this.el){
25965             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25966         }
25967     },
25968     
25969     /**
25970      * Hide this button
25971      */
25972     hide: function(){
25973         this.hidden = true;
25974         if(this.el){
25975             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25976         }
25977     },
25978     
25979     /**
25980      * Convenience function for boolean show/hide
25981      * @param {Boolean} visible True to show, false to hide
25982      */
25983     setVisible: function(visible){
25984         if(visible) {
25985             this.show();
25986         }else{
25987             this.hide();
25988         }
25989     },
25990     
25991     /**
25992      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25993      * @param {Boolean} state (optional) Force a particular state
25994      */
25995     toggle : function(state){
25996         state = state === undefined ? !this.pressed : state;
25997         if(state != this.pressed){
25998             if(state){
25999                 this.el.addClass("x-btn-pressed");
26000                 this.pressed = true;
26001                 this.fireEvent("toggle", this, true);
26002             }else{
26003                 this.el.removeClass("x-btn-pressed");
26004                 this.pressed = false;
26005                 this.fireEvent("toggle", this, false);
26006             }
26007             if(this.toggleHandler){
26008                 this.toggleHandler.call(this.scope || this, this, state);
26009             }
26010         }
26011     },
26012     
26013     /**
26014      * Focus the button
26015      */
26016     focus : function(){
26017         this.el.child('button:first').focus();
26018     },
26019     
26020     /**
26021      * Disable this button
26022      */
26023     disable : function(){
26024         if(this.el){
26025             this.el.addClass("x-btn-disabled");
26026         }
26027         this.disabled = true;
26028     },
26029     
26030     /**
26031      * Enable this button
26032      */
26033     enable : function(){
26034         if(this.el){
26035             this.el.removeClass("x-btn-disabled");
26036         }
26037         this.disabled = false;
26038     },
26039
26040     /**
26041      * Convenience function for boolean enable/disable
26042      * @param {Boolean} enabled True to enable, false to disable
26043      */
26044     setDisabled : function(v){
26045         this[v !== true ? "enable" : "disable"]();
26046     },
26047
26048     // private
26049     onClick : function(e){
26050         if(e){
26051             e.preventDefault();
26052         }
26053         if(e.button != 0){
26054             return;
26055         }
26056         if(!this.disabled){
26057             if(this.enableToggle){
26058                 this.toggle();
26059             }
26060             if(this.menu && !this.menu.isVisible()){
26061                 this.menu.show(this.el, this.menuAlign);
26062             }
26063             this.fireEvent("click", this, e);
26064             if(this.handler){
26065                 this.el.removeClass("x-btn-over");
26066                 this.handler.call(this.scope || this, this, e);
26067             }
26068         }
26069     },
26070     // private
26071     onMouseOver : function(e){
26072         if(!this.disabled){
26073             this.el.addClass("x-btn-over");
26074             this.fireEvent('mouseover', this, e);
26075         }
26076     },
26077     // private
26078     onMouseOut : function(e){
26079         if(!e.within(this.el,  true)){
26080             this.el.removeClass("x-btn-over");
26081             this.fireEvent('mouseout', this, e);
26082         }
26083     },
26084     // private
26085     onFocus : function(e){
26086         if(!this.disabled){
26087             this.el.addClass("x-btn-focus");
26088         }
26089     },
26090     // private
26091     onBlur : function(e){
26092         this.el.removeClass("x-btn-focus");
26093     },
26094     // private
26095     onMouseDown : function(e){
26096         if(!this.disabled && e.button == 0){
26097             this.el.addClass("x-btn-click");
26098             Roo.get(document).on('mouseup', this.onMouseUp, this);
26099         }
26100     },
26101     // private
26102     onMouseUp : function(e){
26103         if(e.button == 0){
26104             this.el.removeClass("x-btn-click");
26105             Roo.get(document).un('mouseup', this.onMouseUp, this);
26106         }
26107     },
26108     // private
26109     onMenuShow : function(e){
26110         this.el.addClass("x-btn-menu-active");
26111     },
26112     // private
26113     onMenuHide : function(e){
26114         this.el.removeClass("x-btn-menu-active");
26115     }   
26116 });
26117
26118 // Private utility class used by Button
26119 Roo.ButtonToggleMgr = function(){
26120    var groups = {};
26121    
26122    function toggleGroup(btn, state){
26123        if(state){
26124            var g = groups[btn.toggleGroup];
26125            for(var i = 0, l = g.length; i < l; i++){
26126                if(g[i] != btn){
26127                    g[i].toggle(false);
26128                }
26129            }
26130        }
26131    }
26132    
26133    return {
26134        register : function(btn){
26135            if(!btn.toggleGroup){
26136                return;
26137            }
26138            var g = groups[btn.toggleGroup];
26139            if(!g){
26140                g = groups[btn.toggleGroup] = [];
26141            }
26142            g.push(btn);
26143            btn.on("toggle", toggleGroup);
26144        },
26145        
26146        unregister : function(btn){
26147            if(!btn.toggleGroup){
26148                return;
26149            }
26150            var g = groups[btn.toggleGroup];
26151            if(g){
26152                g.remove(btn);
26153                btn.un("toggle", toggleGroup);
26154            }
26155        }
26156    };
26157 }();/*
26158  * Based on:
26159  * Ext JS Library 1.1.1
26160  * Copyright(c) 2006-2007, Ext JS, LLC.
26161  *
26162  * Originally Released Under LGPL - original licence link has changed is not relivant.
26163  *
26164  * Fork - LGPL
26165  * <script type="text/javascript">
26166  */
26167  
26168 /**
26169  * @class Roo.SplitButton
26170  * @extends Roo.Button
26171  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26172  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26173  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26174  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26175  * @cfg {String} arrowTooltip The title attribute of the arrow
26176  * @constructor
26177  * Create a new menu button
26178  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26179  * @param {Object} config The config object
26180  */
26181 Roo.SplitButton = function(renderTo, config){
26182     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26183     /**
26184      * @event arrowclick
26185      * Fires when this button's arrow is clicked
26186      * @param {SplitButton} this
26187      * @param {EventObject} e The click event
26188      */
26189     this.addEvents({"arrowclick":true});
26190 };
26191
26192 Roo.extend(Roo.SplitButton, Roo.Button, {
26193     render : function(renderTo){
26194         // this is one sweet looking template!
26195         var tpl = new Roo.Template(
26196             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26197             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26198             '<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>',
26199             "</tbody></table></td><td>",
26200             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26201             '<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>',
26202             "</tbody></table></td></tr></table>"
26203         );
26204         var btn = tpl.append(renderTo, [this.text, this.type], true);
26205         var btnEl = btn.child("button");
26206         if(this.cls){
26207             btn.addClass(this.cls);
26208         }
26209         if(this.icon){
26210             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26211         }
26212         if(this.iconCls){
26213             btnEl.addClass(this.iconCls);
26214             if(!this.cls){
26215                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26216             }
26217         }
26218         this.el = btn;
26219         if(this.handleMouseEvents){
26220             btn.on("mouseover", this.onMouseOver, this);
26221             btn.on("mouseout", this.onMouseOut, this);
26222             btn.on("mousedown", this.onMouseDown, this);
26223             btn.on("mouseup", this.onMouseUp, this);
26224         }
26225         btn.on(this.clickEvent, this.onClick, this);
26226         if(this.tooltip){
26227             if(typeof this.tooltip == 'object'){
26228                 Roo.QuickTips.tips(Roo.apply({
26229                       target: btnEl.id
26230                 }, this.tooltip));
26231             } else {
26232                 btnEl.dom[this.tooltipType] = this.tooltip;
26233             }
26234         }
26235         if(this.arrowTooltip){
26236             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26237         }
26238         if(this.hidden){
26239             this.hide();
26240         }
26241         if(this.disabled){
26242             this.disable();
26243         }
26244         if(this.pressed){
26245             this.el.addClass("x-btn-pressed");
26246         }
26247         if(Roo.isIE && !Roo.isIE7){
26248             this.autoWidth.defer(1, this);
26249         }else{
26250             this.autoWidth();
26251         }
26252         if(this.menu){
26253             this.menu.on("show", this.onMenuShow, this);
26254             this.menu.on("hide", this.onMenuHide, this);
26255         }
26256         this.fireEvent('render', this);
26257     },
26258
26259     // private
26260     autoWidth : function(){
26261         if(this.el){
26262             var tbl = this.el.child("table:first");
26263             var tbl2 = this.el.child("table:last");
26264             this.el.setWidth("auto");
26265             tbl.setWidth("auto");
26266             if(Roo.isIE7 && Roo.isStrict){
26267                 var ib = this.el.child('button:first');
26268                 if(ib && ib.getWidth() > 20){
26269                     ib.clip();
26270                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26271                 }
26272             }
26273             if(this.minWidth){
26274                 if(this.hidden){
26275                     this.el.beginMeasure();
26276                 }
26277                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26278                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26279                 }
26280                 if(this.hidden){
26281                     this.el.endMeasure();
26282                 }
26283             }
26284             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26285         } 
26286     },
26287     /**
26288      * Sets this button's click handler
26289      * @param {Function} handler The function to call when the button is clicked
26290      * @param {Object} scope (optional) Scope for the function passed above
26291      */
26292     setHandler : function(handler, scope){
26293         this.handler = handler;
26294         this.scope = scope;  
26295     },
26296     
26297     /**
26298      * Sets this button's arrow click handler
26299      * @param {Function} handler The function to call when the arrow is clicked
26300      * @param {Object} scope (optional) Scope for the function passed above
26301      */
26302     setArrowHandler : function(handler, scope){
26303         this.arrowHandler = handler;
26304         this.scope = scope;  
26305     },
26306     
26307     /**
26308      * Focus the button
26309      */
26310     focus : function(){
26311         if(this.el){
26312             this.el.child("button:first").focus();
26313         }
26314     },
26315
26316     // private
26317     onClick : function(e){
26318         e.preventDefault();
26319         if(!this.disabled){
26320             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26321                 if(this.menu && !this.menu.isVisible()){
26322                     this.menu.show(this.el, this.menuAlign);
26323                 }
26324                 this.fireEvent("arrowclick", this, e);
26325                 if(this.arrowHandler){
26326                     this.arrowHandler.call(this.scope || this, this, e);
26327                 }
26328             }else{
26329                 this.fireEvent("click", this, e);
26330                 if(this.handler){
26331                     this.handler.call(this.scope || this, this, e);
26332                 }
26333             }
26334         }
26335     },
26336     // private
26337     onMouseDown : function(e){
26338         if(!this.disabled){
26339             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26340         }
26341     },
26342     // private
26343     onMouseUp : function(e){
26344         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26345     }   
26346 });
26347
26348
26349 // backwards compat
26350 Roo.MenuButton = Roo.SplitButton;/*
26351  * Based on:
26352  * Ext JS Library 1.1.1
26353  * Copyright(c) 2006-2007, Ext JS, LLC.
26354  *
26355  * Originally Released Under LGPL - original licence link has changed is not relivant.
26356  *
26357  * Fork - LGPL
26358  * <script type="text/javascript">
26359  */
26360
26361 /**
26362  * @class Roo.Toolbar
26363  * Basic Toolbar class.
26364  * @constructor
26365  * Creates a new Toolbar
26366  * @param {Object} config The config object
26367  */ 
26368 Roo.Toolbar = function(container, buttons, config)
26369 {
26370     /// old consturctor format still supported..
26371     if(container instanceof Array){ // omit the container for later rendering
26372         buttons = container;
26373         config = buttons;
26374         container = null;
26375     }
26376     if (typeof(container) == 'object' && container.xtype) {
26377         config = container;
26378         container = config.container;
26379         buttons = config.buttons; // not really - use items!!
26380     }
26381     var xitems = [];
26382     if (config && config.items) {
26383         xitems = config.items;
26384         delete config.items;
26385     }
26386     Roo.apply(this, config);
26387     this.buttons = buttons;
26388     
26389     if(container){
26390         this.render(container);
26391     }
26392     Roo.each(xitems, function(b) {
26393         this.add(b);
26394     }, this);
26395     
26396 };
26397
26398 Roo.Toolbar.prototype = {
26399     /**
26400      * @cfg {Roo.data.Store} items
26401      * array of button configs or elements to add
26402      */
26403     
26404     /**
26405      * @cfg {String/HTMLElement/Element} container
26406      * The id or element that will contain the toolbar
26407      */
26408     // private
26409     render : function(ct){
26410         this.el = Roo.get(ct);
26411         if(this.cls){
26412             this.el.addClass(this.cls);
26413         }
26414         // using a table allows for vertical alignment
26415         // 100% width is needed by Safari...
26416         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26417         this.tr = this.el.child("tr", true);
26418         var autoId = 0;
26419         this.items = new Roo.util.MixedCollection(false, function(o){
26420             return o.id || ("item" + (++autoId));
26421         });
26422         if(this.buttons){
26423             this.add.apply(this, this.buttons);
26424             delete this.buttons;
26425         }
26426     },
26427
26428     /**
26429      * Adds element(s) to the toolbar -- this function takes a variable number of 
26430      * arguments of mixed type and adds them to the toolbar.
26431      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26432      * <ul>
26433      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26434      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26435      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26436      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26437      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26438      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26439      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26440      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26441      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26442      * </ul>
26443      * @param {Mixed} arg2
26444      * @param {Mixed} etc.
26445      */
26446     add : function(){
26447         var a = arguments, l = a.length;
26448         for(var i = 0; i < l; i++){
26449             this._add(a[i]);
26450         }
26451     },
26452     // private..
26453     _add : function(el) {
26454         
26455         if (el.xtype) {
26456             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26457         }
26458         
26459         if (el.applyTo){ // some kind of form field
26460             return this.addField(el);
26461         } 
26462         if (el.render){ // some kind of Toolbar.Item
26463             return this.addItem(el);
26464         }
26465         if (typeof el == "string"){ // string
26466             if(el == "separator" || el == "-"){
26467                 return this.addSeparator();
26468             }
26469             if (el == " "){
26470                 return this.addSpacer();
26471             }
26472             if(el == "->"){
26473                 return this.addFill();
26474             }
26475             return this.addText(el);
26476             
26477         }
26478         if(el.tagName){ // element
26479             return this.addElement(el);
26480         }
26481         if(typeof el == "object"){ // must be button config?
26482             return this.addButton(el);
26483         }
26484         // and now what?!?!
26485         return false;
26486         
26487     },
26488     
26489     /**
26490      * Add an Xtype element
26491      * @param {Object} xtype Xtype Object
26492      * @return {Object} created Object
26493      */
26494     addxtype : function(e){
26495         return this.add(e);  
26496     },
26497     
26498     /**
26499      * Returns the Element for this toolbar.
26500      * @return {Roo.Element}
26501      */
26502     getEl : function(){
26503         return this.el;  
26504     },
26505     
26506     /**
26507      * Adds a separator
26508      * @return {Roo.Toolbar.Item} The separator item
26509      */
26510     addSeparator : function(){
26511         return this.addItem(new Roo.Toolbar.Separator());
26512     },
26513
26514     /**
26515      * Adds a spacer element
26516      * @return {Roo.Toolbar.Spacer} The spacer item
26517      */
26518     addSpacer : function(){
26519         return this.addItem(new Roo.Toolbar.Spacer());
26520     },
26521
26522     /**
26523      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26524      * @return {Roo.Toolbar.Fill} The fill item
26525      */
26526     addFill : function(){
26527         return this.addItem(new Roo.Toolbar.Fill());
26528     },
26529
26530     /**
26531      * Adds any standard HTML element to the toolbar
26532      * @param {String/HTMLElement/Element} el The element or id of the element to add
26533      * @return {Roo.Toolbar.Item} The element's item
26534      */
26535     addElement : function(el){
26536         return this.addItem(new Roo.Toolbar.Item(el));
26537     },
26538     /**
26539      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26540      * @type Roo.util.MixedCollection  
26541      */
26542     items : false,
26543      
26544     /**
26545      * Adds any Toolbar.Item or subclass
26546      * @param {Roo.Toolbar.Item} item
26547      * @return {Roo.Toolbar.Item} The item
26548      */
26549     addItem : function(item){
26550         var td = this.nextBlock();
26551         item.render(td);
26552         this.items.add(item);
26553         return item;
26554     },
26555     
26556     /**
26557      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26558      * @param {Object/Array} config A button config or array of configs
26559      * @return {Roo.Toolbar.Button/Array}
26560      */
26561     addButton : function(config){
26562         if(config instanceof Array){
26563             var buttons = [];
26564             for(var i = 0, len = config.length; i < len; i++) {
26565                 buttons.push(this.addButton(config[i]));
26566             }
26567             return buttons;
26568         }
26569         var b = config;
26570         if(!(config instanceof Roo.Toolbar.Button)){
26571             b = config.split ?
26572                 new Roo.Toolbar.SplitButton(config) :
26573                 new Roo.Toolbar.Button(config);
26574         }
26575         var td = this.nextBlock();
26576         b.render(td);
26577         this.items.add(b);
26578         return b;
26579     },
26580     
26581     /**
26582      * Adds text to the toolbar
26583      * @param {String} text The text to add
26584      * @return {Roo.Toolbar.Item} The element's item
26585      */
26586     addText : function(text){
26587         return this.addItem(new Roo.Toolbar.TextItem(text));
26588     },
26589     
26590     /**
26591      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26592      * @param {Number} index The index where the item is to be inserted
26593      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26594      * @return {Roo.Toolbar.Button/Item}
26595      */
26596     insertButton : function(index, item){
26597         if(item instanceof Array){
26598             var buttons = [];
26599             for(var i = 0, len = item.length; i < len; i++) {
26600                buttons.push(this.insertButton(index + i, item[i]));
26601             }
26602             return buttons;
26603         }
26604         if (!(item instanceof Roo.Toolbar.Button)){
26605            item = new Roo.Toolbar.Button(item);
26606         }
26607         var td = document.createElement("td");
26608         this.tr.insertBefore(td, this.tr.childNodes[index]);
26609         item.render(td);
26610         this.items.insert(index, item);
26611         return item;
26612     },
26613     
26614     /**
26615      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26616      * @param {Object} config
26617      * @return {Roo.Toolbar.Item} The element's item
26618      */
26619     addDom : function(config, returnEl){
26620         var td = this.nextBlock();
26621         Roo.DomHelper.overwrite(td, config);
26622         var ti = new Roo.Toolbar.Item(td.firstChild);
26623         ti.render(td);
26624         this.items.add(ti);
26625         return ti;
26626     },
26627
26628     /**
26629      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26630      * @type Roo.util.MixedCollection  
26631      */
26632     fields : false,
26633     
26634     /**
26635      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26636      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26637      * @param {Roo.form.Field} field
26638      * @return {Roo.ToolbarItem}
26639      */
26640      
26641       
26642     addField : function(field) {
26643         if (!this.fields) {
26644             var autoId = 0;
26645             this.fields = new Roo.util.MixedCollection(false, function(o){
26646                 return o.id || ("item" + (++autoId));
26647             });
26648
26649         }
26650         
26651         var td = this.nextBlock();
26652         field.render(td);
26653         var ti = new Roo.Toolbar.Item(td.firstChild);
26654         ti.render(td);
26655         this.items.add(ti);
26656         this.fields.add(field);
26657         return ti;
26658     },
26659     /**
26660      * Hide the toolbar
26661      * @method hide
26662      */
26663      
26664       
26665     hide : function()
26666     {
26667         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26668         this.el.child('div').hide();
26669     },
26670     /**
26671      * Show the toolbar
26672      * @method show
26673      */
26674     show : function()
26675     {
26676         this.el.child('div').show();
26677     },
26678       
26679     // private
26680     nextBlock : function(){
26681         var td = document.createElement("td");
26682         this.tr.appendChild(td);
26683         return td;
26684     },
26685
26686     // private
26687     destroy : function(){
26688         if(this.items){ // rendered?
26689             Roo.destroy.apply(Roo, this.items.items);
26690         }
26691         if(this.fields){ // rendered?
26692             Roo.destroy.apply(Roo, this.fields.items);
26693         }
26694         Roo.Element.uncache(this.el, this.tr);
26695     }
26696 };
26697
26698 /**
26699  * @class Roo.Toolbar.Item
26700  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26701  * @constructor
26702  * Creates a new Item
26703  * @param {HTMLElement} el 
26704  */
26705 Roo.Toolbar.Item = function(el){
26706     this.el = Roo.getDom(el);
26707     this.id = Roo.id(this.el);
26708     this.hidden = false;
26709 };
26710
26711 Roo.Toolbar.Item.prototype = {
26712     
26713     /**
26714      * Get this item's HTML Element
26715      * @return {HTMLElement}
26716      */
26717     getEl : function(){
26718        return this.el;  
26719     },
26720
26721     // private
26722     render : function(td){
26723         this.td = td;
26724         td.appendChild(this.el);
26725     },
26726     
26727     /**
26728      * Removes and destroys this item.
26729      */
26730     destroy : function(){
26731         this.td.parentNode.removeChild(this.td);
26732     },
26733     
26734     /**
26735      * Shows this item.
26736      */
26737     show: function(){
26738         this.hidden = false;
26739         this.td.style.display = "";
26740     },
26741     
26742     /**
26743      * Hides this item.
26744      */
26745     hide: function(){
26746         this.hidden = true;
26747         this.td.style.display = "none";
26748     },
26749     
26750     /**
26751      * Convenience function for boolean show/hide.
26752      * @param {Boolean} visible true to show/false to hide
26753      */
26754     setVisible: function(visible){
26755         if(visible) {
26756             this.show();
26757         }else{
26758             this.hide();
26759         }
26760     },
26761     
26762     /**
26763      * Try to focus this item.
26764      */
26765     focus : function(){
26766         Roo.fly(this.el).focus();
26767     },
26768     
26769     /**
26770      * Disables this item.
26771      */
26772     disable : function(){
26773         Roo.fly(this.td).addClass("x-item-disabled");
26774         this.disabled = true;
26775         this.el.disabled = true;
26776     },
26777     
26778     /**
26779      * Enables this item.
26780      */
26781     enable : function(){
26782         Roo.fly(this.td).removeClass("x-item-disabled");
26783         this.disabled = false;
26784         this.el.disabled = false;
26785     }
26786 };
26787
26788
26789 /**
26790  * @class Roo.Toolbar.Separator
26791  * @extends Roo.Toolbar.Item
26792  * A simple toolbar separator class
26793  * @constructor
26794  * Creates a new Separator
26795  */
26796 Roo.Toolbar.Separator = function(){
26797     var s = document.createElement("span");
26798     s.className = "ytb-sep";
26799     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26800 };
26801 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26802     enable:Roo.emptyFn,
26803     disable:Roo.emptyFn,
26804     focus:Roo.emptyFn
26805 });
26806
26807 /**
26808  * @class Roo.Toolbar.Spacer
26809  * @extends Roo.Toolbar.Item
26810  * A simple element that adds extra horizontal space to a toolbar.
26811  * @constructor
26812  * Creates a new Spacer
26813  */
26814 Roo.Toolbar.Spacer = function(){
26815     var s = document.createElement("div");
26816     s.className = "ytb-spacer";
26817     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26818 };
26819 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26820     enable:Roo.emptyFn,
26821     disable:Roo.emptyFn,
26822     focus:Roo.emptyFn
26823 });
26824
26825 /**
26826  * @class Roo.Toolbar.Fill
26827  * @extends Roo.Toolbar.Spacer
26828  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26829  * @constructor
26830  * Creates a new Spacer
26831  */
26832 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26833     // private
26834     render : function(td){
26835         td.style.width = '100%';
26836         Roo.Toolbar.Fill.superclass.render.call(this, td);
26837     }
26838 });
26839
26840 /**
26841  * @class Roo.Toolbar.TextItem
26842  * @extends Roo.Toolbar.Item
26843  * A simple class that renders text directly into a toolbar.
26844  * @constructor
26845  * Creates a new TextItem
26846  * @param {String} text
26847  */
26848 Roo.Toolbar.TextItem = function(text){
26849     if (typeof(text) == 'object') {
26850         text = text.text;
26851     }
26852     var s = document.createElement("span");
26853     s.className = "ytb-text";
26854     s.innerHTML = text;
26855     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26856 };
26857 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26858     enable:Roo.emptyFn,
26859     disable:Roo.emptyFn,
26860     focus:Roo.emptyFn
26861 });
26862
26863 /**
26864  * @class Roo.Toolbar.Button
26865  * @extends Roo.Button
26866  * A button that renders into a toolbar.
26867  * @constructor
26868  * Creates a new Button
26869  * @param {Object} config A standard {@link Roo.Button} config object
26870  */
26871 Roo.Toolbar.Button = function(config){
26872     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26873 };
26874 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26875     render : function(td){
26876         this.td = td;
26877         Roo.Toolbar.Button.superclass.render.call(this, td);
26878     },
26879     
26880     /**
26881      * Removes and destroys this button
26882      */
26883     destroy : function(){
26884         Roo.Toolbar.Button.superclass.destroy.call(this);
26885         this.td.parentNode.removeChild(this.td);
26886     },
26887     
26888     /**
26889      * Shows this button
26890      */
26891     show: function(){
26892         this.hidden = false;
26893         this.td.style.display = "";
26894     },
26895     
26896     /**
26897      * Hides this button
26898      */
26899     hide: function(){
26900         this.hidden = true;
26901         this.td.style.display = "none";
26902     },
26903
26904     /**
26905      * Disables this item
26906      */
26907     disable : function(){
26908         Roo.fly(this.td).addClass("x-item-disabled");
26909         this.disabled = true;
26910     },
26911
26912     /**
26913      * Enables this item
26914      */
26915     enable : function(){
26916         Roo.fly(this.td).removeClass("x-item-disabled");
26917         this.disabled = false;
26918     }
26919 });
26920 // backwards compat
26921 Roo.ToolbarButton = Roo.Toolbar.Button;
26922
26923 /**
26924  * @class Roo.Toolbar.SplitButton
26925  * @extends Roo.SplitButton
26926  * A menu button that renders into a toolbar.
26927  * @constructor
26928  * Creates a new SplitButton
26929  * @param {Object} config A standard {@link Roo.SplitButton} config object
26930  */
26931 Roo.Toolbar.SplitButton = function(config){
26932     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26933 };
26934 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26935     render : function(td){
26936         this.td = td;
26937         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26938     },
26939     
26940     /**
26941      * Removes and destroys this button
26942      */
26943     destroy : function(){
26944         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26945         this.td.parentNode.removeChild(this.td);
26946     },
26947     
26948     /**
26949      * Shows this button
26950      */
26951     show: function(){
26952         this.hidden = false;
26953         this.td.style.display = "";
26954     },
26955     
26956     /**
26957      * Hides this button
26958      */
26959     hide: function(){
26960         this.hidden = true;
26961         this.td.style.display = "none";
26962     }
26963 });
26964
26965 // backwards compat
26966 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26967  * Based on:
26968  * Ext JS Library 1.1.1
26969  * Copyright(c) 2006-2007, Ext JS, LLC.
26970  *
26971  * Originally Released Under LGPL - original licence link has changed is not relivant.
26972  *
26973  * Fork - LGPL
26974  * <script type="text/javascript">
26975  */
26976  
26977 /**
26978  * @class Roo.PagingToolbar
26979  * @extends Roo.Toolbar
26980  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26981  * @constructor
26982  * Create a new PagingToolbar
26983  * @param {Object} config The config object
26984  */
26985 Roo.PagingToolbar = function(el, ds, config)
26986 {
26987     // old args format still supported... - xtype is prefered..
26988     if (typeof(el) == 'object' && el.xtype) {
26989         // created from xtype...
26990         config = el;
26991         ds = el.dataSource;
26992         el = config.container;
26993     }
26994     
26995     
26996     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26997     this.ds = ds;
26998     this.cursor = 0;
26999     this.renderButtons(this.el);
27000     this.bind(ds);
27001 };
27002
27003 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27004     /**
27005      * @cfg {Roo.data.Store} dataSource
27006      * The underlying data store providing the paged data
27007      */
27008     /**
27009      * @cfg {String/HTMLElement/Element} container
27010      * container The id or element that will contain the toolbar
27011      */
27012     /**
27013      * @cfg {Boolean} displayInfo
27014      * True to display the displayMsg (defaults to false)
27015      */
27016     /**
27017      * @cfg {Number} pageSize
27018      * The number of records to display per page (defaults to 20)
27019      */
27020     pageSize: 20,
27021     /**
27022      * @cfg {String} displayMsg
27023      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27024      */
27025     displayMsg : 'Displaying {0} - {1} of {2}',
27026     /**
27027      * @cfg {String} emptyMsg
27028      * The message to display when no records are found (defaults to "No data to display")
27029      */
27030     emptyMsg : 'No data to display',
27031     /**
27032      * Customizable piece of the default paging text (defaults to "Page")
27033      * @type String
27034      */
27035     beforePageText : "Page",
27036     /**
27037      * Customizable piece of the default paging text (defaults to "of %0")
27038      * @type String
27039      */
27040     afterPageText : "of {0}",
27041     /**
27042      * Customizable piece of the default paging text (defaults to "First Page")
27043      * @type String
27044      */
27045     firstText : "First Page",
27046     /**
27047      * Customizable piece of the default paging text (defaults to "Previous Page")
27048      * @type String
27049      */
27050     prevText : "Previous Page",
27051     /**
27052      * Customizable piece of the default paging text (defaults to "Next Page")
27053      * @type String
27054      */
27055     nextText : "Next Page",
27056     /**
27057      * Customizable piece of the default paging text (defaults to "Last Page")
27058      * @type String
27059      */
27060     lastText : "Last Page",
27061     /**
27062      * Customizable piece of the default paging text (defaults to "Refresh")
27063      * @type String
27064      */
27065     refreshText : "Refresh",
27066
27067     // private
27068     renderButtons : function(el){
27069         Roo.PagingToolbar.superclass.render.call(this, el);
27070         this.first = this.addButton({
27071             tooltip: this.firstText,
27072             cls: "x-btn-icon x-grid-page-first",
27073             disabled: true,
27074             handler: this.onClick.createDelegate(this, ["first"])
27075         });
27076         this.prev = this.addButton({
27077             tooltip: this.prevText,
27078             cls: "x-btn-icon x-grid-page-prev",
27079             disabled: true,
27080             handler: this.onClick.createDelegate(this, ["prev"])
27081         });
27082         this.addSeparator();
27083         this.add(this.beforePageText);
27084         this.field = Roo.get(this.addDom({
27085            tag: "input",
27086            type: "text",
27087            size: "3",
27088            value: "1",
27089            cls: "x-grid-page-number"
27090         }).el);
27091         this.field.on("keydown", this.onPagingKeydown, this);
27092         this.field.on("focus", function(){this.dom.select();});
27093         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27094         this.field.setHeight(18);
27095         this.addSeparator();
27096         this.next = this.addButton({
27097             tooltip: this.nextText,
27098             cls: "x-btn-icon x-grid-page-next",
27099             disabled: true,
27100             handler: this.onClick.createDelegate(this, ["next"])
27101         });
27102         this.last = this.addButton({
27103             tooltip: this.lastText,
27104             cls: "x-btn-icon x-grid-page-last",
27105             disabled: true,
27106             handler: this.onClick.createDelegate(this, ["last"])
27107         });
27108         this.addSeparator();
27109         this.loading = this.addButton({
27110             tooltip: this.refreshText,
27111             cls: "x-btn-icon x-grid-loading",
27112             handler: this.onClick.createDelegate(this, ["refresh"])
27113         });
27114
27115         if(this.displayInfo){
27116             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27117         }
27118     },
27119
27120     // private
27121     updateInfo : function(){
27122         if(this.displayEl){
27123             var count = this.ds.getCount();
27124             var msg = count == 0 ?
27125                 this.emptyMsg :
27126                 String.format(
27127                     this.displayMsg,
27128                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27129                 );
27130             this.displayEl.update(msg);
27131         }
27132     },
27133
27134     // private
27135     onLoad : function(ds, r, o){
27136        this.cursor = o.params ? o.params.start : 0;
27137        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27138
27139        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27140        this.field.dom.value = ap;
27141        this.first.setDisabled(ap == 1);
27142        this.prev.setDisabled(ap == 1);
27143        this.next.setDisabled(ap == ps);
27144        this.last.setDisabled(ap == ps);
27145        this.loading.enable();
27146        this.updateInfo();
27147     },
27148
27149     // private
27150     getPageData : function(){
27151         var total = this.ds.getTotalCount();
27152         return {
27153             total : total,
27154             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27155             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27156         };
27157     },
27158
27159     // private
27160     onLoadError : function(){
27161         this.loading.enable();
27162     },
27163
27164     // private
27165     onPagingKeydown : function(e){
27166         var k = e.getKey();
27167         var d = this.getPageData();
27168         if(k == e.RETURN){
27169             var v = this.field.dom.value, pageNum;
27170             if(!v || isNaN(pageNum = parseInt(v, 10))){
27171                 this.field.dom.value = d.activePage;
27172                 return;
27173             }
27174             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27175             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27176             e.stopEvent();
27177         }
27178         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))
27179         {
27180           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27181           this.field.dom.value = pageNum;
27182           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27183           e.stopEvent();
27184         }
27185         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27186         {
27187           var v = this.field.dom.value, pageNum; 
27188           var increment = (e.shiftKey) ? 10 : 1;
27189           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27190             increment *= -1;
27191           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27192             this.field.dom.value = d.activePage;
27193             return;
27194           }
27195           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27196           {
27197             this.field.dom.value = parseInt(v, 10) + increment;
27198             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27199             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27200           }
27201           e.stopEvent();
27202         }
27203     },
27204
27205     // private
27206     beforeLoad : function(){
27207         if(this.loading){
27208             this.loading.disable();
27209         }
27210     },
27211
27212     // private
27213     onClick : function(which){
27214         var ds = this.ds;
27215         switch(which){
27216             case "first":
27217                 ds.load({params:{start: 0, limit: this.pageSize}});
27218             break;
27219             case "prev":
27220                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27221             break;
27222             case "next":
27223                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27224             break;
27225             case "last":
27226                 var total = ds.getTotalCount();
27227                 var extra = total % this.pageSize;
27228                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27229                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27230             break;
27231             case "refresh":
27232                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27233             break;
27234         }
27235     },
27236
27237     /**
27238      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27239      * @param {Roo.data.Store} store The data store to unbind
27240      */
27241     unbind : function(ds){
27242         ds.un("beforeload", this.beforeLoad, this);
27243         ds.un("load", this.onLoad, this);
27244         ds.un("loadexception", this.onLoadError, this);
27245         ds.un("remove", this.updateInfo, this);
27246         ds.un("add", this.updateInfo, this);
27247         this.ds = undefined;
27248     },
27249
27250     /**
27251      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27252      * @param {Roo.data.Store} store The data store to bind
27253      */
27254     bind : function(ds){
27255         ds.on("beforeload", this.beforeLoad, this);
27256         ds.on("load", this.onLoad, this);
27257         ds.on("loadexception", this.onLoadError, this);
27258         ds.on("remove", this.updateInfo, this);
27259         ds.on("add", this.updateInfo, this);
27260         this.ds = ds;
27261     }
27262 });/*
27263  * Based on:
27264  * Ext JS Library 1.1.1
27265  * Copyright(c) 2006-2007, Ext JS, LLC.
27266  *
27267  * Originally Released Under LGPL - original licence link has changed is not relivant.
27268  *
27269  * Fork - LGPL
27270  * <script type="text/javascript">
27271  */
27272
27273 /**
27274  * @class Roo.Resizable
27275  * @extends Roo.util.Observable
27276  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27277  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27278  * 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
27279  * the element will be wrapped for you automatically.</p>
27280  * <p>Here is the list of valid resize handles:</p>
27281  * <pre>
27282 Value   Description
27283 ------  -------------------
27284  'n'     north
27285  's'     south
27286  'e'     east
27287  'w'     west
27288  'nw'    northwest
27289  'sw'    southwest
27290  'se'    southeast
27291  'ne'    northeast
27292  'all'   all
27293 </pre>
27294  * <p>Here's an example showing the creation of a typical Resizable:</p>
27295  * <pre><code>
27296 var resizer = new Roo.Resizable("element-id", {
27297     handles: 'all',
27298     minWidth: 200,
27299     minHeight: 100,
27300     maxWidth: 500,
27301     maxHeight: 400,
27302     pinned: true
27303 });
27304 resizer.on("resize", myHandler);
27305 </code></pre>
27306  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27307  * resizer.east.setDisplayed(false);</p>
27308  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27309  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27310  * resize operation's new size (defaults to [0, 0])
27311  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27312  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27313  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27314  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27315  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27316  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27317  * @cfg {Number} width The width of the element in pixels (defaults to null)
27318  * @cfg {Number} height The height of the element in pixels (defaults to null)
27319  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27320  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27321  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27322  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27323  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27324  * in favor of the handles config option (defaults to false)
27325  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27326  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27327  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27328  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27329  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27330  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27331  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27332  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27333  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27334  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27335  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27336  * @constructor
27337  * Create a new resizable component
27338  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27339  * @param {Object} config configuration options
27340   */
27341 Roo.Resizable = function(el, config){
27342     this.el = Roo.get(el);
27343
27344     if(config && config.wrap){
27345         config.resizeChild = this.el;
27346         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27347         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27348         this.el.setStyle("overflow", "hidden");
27349         this.el.setPositioning(config.resizeChild.getPositioning());
27350         config.resizeChild.clearPositioning();
27351         if(!config.width || !config.height){
27352             var csize = config.resizeChild.getSize();
27353             this.el.setSize(csize.width, csize.height);
27354         }
27355         if(config.pinned && !config.adjustments){
27356             config.adjustments = "auto";
27357         }
27358     }
27359
27360     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27361     this.proxy.unselectable();
27362     this.proxy.enableDisplayMode('block');
27363
27364     Roo.apply(this, config);
27365
27366     if(this.pinned){
27367         this.disableTrackOver = true;
27368         this.el.addClass("x-resizable-pinned");
27369     }
27370     // if the element isn't positioned, make it relative
27371     var position = this.el.getStyle("position");
27372     if(position != "absolute" && position != "fixed"){
27373         this.el.setStyle("position", "relative");
27374     }
27375     if(!this.handles){ // no handles passed, must be legacy style
27376         this.handles = 's,e,se';
27377         if(this.multiDirectional){
27378             this.handles += ',n,w';
27379         }
27380     }
27381     if(this.handles == "all"){
27382         this.handles = "n s e w ne nw se sw";
27383     }
27384     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27385     var ps = Roo.Resizable.positions;
27386     for(var i = 0, len = hs.length; i < len; i++){
27387         if(hs[i] && ps[hs[i]]){
27388             var pos = ps[hs[i]];
27389             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27390         }
27391     }
27392     // legacy
27393     this.corner = this.southeast;
27394
27395     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27396         this.updateBox = true;
27397     }
27398
27399     this.activeHandle = null;
27400
27401     if(this.resizeChild){
27402         if(typeof this.resizeChild == "boolean"){
27403             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27404         }else{
27405             this.resizeChild = Roo.get(this.resizeChild, true);
27406         }
27407     }
27408
27409     if(this.adjustments == "auto"){
27410         var rc = this.resizeChild;
27411         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27412         if(rc && (hw || hn)){
27413             rc.position("relative");
27414             rc.setLeft(hw ? hw.el.getWidth() : 0);
27415             rc.setTop(hn ? hn.el.getHeight() : 0);
27416         }
27417         this.adjustments = [
27418             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27419             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27420         ];
27421     }
27422
27423     if(this.draggable){
27424         this.dd = this.dynamic ?
27425             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27426         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27427     }
27428
27429     // public events
27430     this.addEvents({
27431         /**
27432          * @event beforeresize
27433          * Fired before resize is allowed. Set enabled to false to cancel resize.
27434          * @param {Roo.Resizable} this
27435          * @param {Roo.EventObject} e The mousedown event
27436          */
27437         "beforeresize" : true,
27438         /**
27439          * @event resize
27440          * Fired after a resize.
27441          * @param {Roo.Resizable} this
27442          * @param {Number} width The new width
27443          * @param {Number} height The new height
27444          * @param {Roo.EventObject} e The mouseup event
27445          */
27446         "resize" : true
27447     });
27448
27449     if(this.width !== null && this.height !== null){
27450         this.resizeTo(this.width, this.height);
27451     }else{
27452         this.updateChildSize();
27453     }
27454     if(Roo.isIE){
27455         this.el.dom.style.zoom = 1;
27456     }
27457     Roo.Resizable.superclass.constructor.call(this);
27458 };
27459
27460 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27461         resizeChild : false,
27462         adjustments : [0, 0],
27463         minWidth : 5,
27464         minHeight : 5,
27465         maxWidth : 10000,
27466         maxHeight : 10000,
27467         enabled : true,
27468         animate : false,
27469         duration : .35,
27470         dynamic : false,
27471         handles : false,
27472         multiDirectional : false,
27473         disableTrackOver : false,
27474         easing : 'easeOutStrong',
27475         widthIncrement : 0,
27476         heightIncrement : 0,
27477         pinned : false,
27478         width : null,
27479         height : null,
27480         preserveRatio : false,
27481         transparent: false,
27482         minX: 0,
27483         minY: 0,
27484         draggable: false,
27485
27486         /**
27487          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27488          */
27489         constrainTo: undefined,
27490         /**
27491          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27492          */
27493         resizeRegion: undefined,
27494
27495
27496     /**
27497      * Perform a manual resize
27498      * @param {Number} width
27499      * @param {Number} height
27500      */
27501     resizeTo : function(width, height){
27502         this.el.setSize(width, height);
27503         this.updateChildSize();
27504         this.fireEvent("resize", this, width, height, null);
27505     },
27506
27507     // private
27508     startSizing : function(e, handle){
27509         this.fireEvent("beforeresize", this, e);
27510         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27511
27512             if(!this.overlay){
27513                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27514                 this.overlay.unselectable();
27515                 this.overlay.enableDisplayMode("block");
27516                 this.overlay.on("mousemove", this.onMouseMove, this);
27517                 this.overlay.on("mouseup", this.onMouseUp, this);
27518             }
27519             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27520
27521             this.resizing = true;
27522             this.startBox = this.el.getBox();
27523             this.startPoint = e.getXY();
27524             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27525                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27526
27527             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27528             this.overlay.show();
27529
27530             if(this.constrainTo) {
27531                 var ct = Roo.get(this.constrainTo);
27532                 this.resizeRegion = ct.getRegion().adjust(
27533                     ct.getFrameWidth('t'),
27534                     ct.getFrameWidth('l'),
27535                     -ct.getFrameWidth('b'),
27536                     -ct.getFrameWidth('r')
27537                 );
27538             }
27539
27540             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27541             this.proxy.show();
27542             this.proxy.setBox(this.startBox);
27543             if(!this.dynamic){
27544                 this.proxy.setStyle('visibility', 'visible');
27545             }
27546         }
27547     },
27548
27549     // private
27550     onMouseDown : function(handle, e){
27551         if(this.enabled){
27552             e.stopEvent();
27553             this.activeHandle = handle;
27554             this.startSizing(e, handle);
27555         }
27556     },
27557
27558     // private
27559     onMouseUp : function(e){
27560         var size = this.resizeElement();
27561         this.resizing = false;
27562         this.handleOut();
27563         this.overlay.hide();
27564         this.proxy.hide();
27565         this.fireEvent("resize", this, size.width, size.height, e);
27566     },
27567
27568     // private
27569     updateChildSize : function(){
27570         if(this.resizeChild){
27571             var el = this.el;
27572             var child = this.resizeChild;
27573             var adj = this.adjustments;
27574             if(el.dom.offsetWidth){
27575                 var b = el.getSize(true);
27576                 child.setSize(b.width+adj[0], b.height+adj[1]);
27577             }
27578             // Second call here for IE
27579             // The first call enables instant resizing and
27580             // the second call corrects scroll bars if they
27581             // exist
27582             if(Roo.isIE){
27583                 setTimeout(function(){
27584                     if(el.dom.offsetWidth){
27585                         var b = el.getSize(true);
27586                         child.setSize(b.width+adj[0], b.height+adj[1]);
27587                     }
27588                 }, 10);
27589             }
27590         }
27591     },
27592
27593     // private
27594     snap : function(value, inc, min){
27595         if(!inc || !value) return value;
27596         var newValue = value;
27597         var m = value % inc;
27598         if(m > 0){
27599             if(m > (inc/2)){
27600                 newValue = value + (inc-m);
27601             }else{
27602                 newValue = value - m;
27603             }
27604         }
27605         return Math.max(min, newValue);
27606     },
27607
27608     // private
27609     resizeElement : function(){
27610         var box = this.proxy.getBox();
27611         if(this.updateBox){
27612             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27613         }else{
27614             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27615         }
27616         this.updateChildSize();
27617         if(!this.dynamic){
27618             this.proxy.hide();
27619         }
27620         return box;
27621     },
27622
27623     // private
27624     constrain : function(v, diff, m, mx){
27625         if(v - diff < m){
27626             diff = v - m;
27627         }else if(v - diff > mx){
27628             diff = mx - v;
27629         }
27630         return diff;
27631     },
27632
27633     // private
27634     onMouseMove : function(e){
27635         if(this.enabled){
27636             try{// try catch so if something goes wrong the user doesn't get hung
27637
27638             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27639                 return;
27640             }
27641
27642             //var curXY = this.startPoint;
27643             var curSize = this.curSize || this.startBox;
27644             var x = this.startBox.x, y = this.startBox.y;
27645             var ox = x, oy = y;
27646             var w = curSize.width, h = curSize.height;
27647             var ow = w, oh = h;
27648             var mw = this.minWidth, mh = this.minHeight;
27649             var mxw = this.maxWidth, mxh = this.maxHeight;
27650             var wi = this.widthIncrement;
27651             var hi = this.heightIncrement;
27652
27653             var eventXY = e.getXY();
27654             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27655             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27656
27657             var pos = this.activeHandle.position;
27658
27659             switch(pos){
27660                 case "east":
27661                     w += diffX;
27662                     w = Math.min(Math.max(mw, w), mxw);
27663                     break;
27664                 case "south":
27665                     h += diffY;
27666                     h = Math.min(Math.max(mh, h), mxh);
27667                     break;
27668                 case "southeast":
27669                     w += diffX;
27670                     h += diffY;
27671                     w = Math.min(Math.max(mw, w), mxw);
27672                     h = Math.min(Math.max(mh, h), mxh);
27673                     break;
27674                 case "north":
27675                     diffY = this.constrain(h, diffY, mh, mxh);
27676                     y += diffY;
27677                     h -= diffY;
27678                     break;
27679                 case "west":
27680                     diffX = this.constrain(w, diffX, mw, mxw);
27681                     x += diffX;
27682                     w -= diffX;
27683                     break;
27684                 case "northeast":
27685                     w += diffX;
27686                     w = Math.min(Math.max(mw, w), mxw);
27687                     diffY = this.constrain(h, diffY, mh, mxh);
27688                     y += diffY;
27689                     h -= diffY;
27690                     break;
27691                 case "northwest":
27692                     diffX = this.constrain(w, diffX, mw, mxw);
27693                     diffY = this.constrain(h, diffY, mh, mxh);
27694                     y += diffY;
27695                     h -= diffY;
27696                     x += diffX;
27697                     w -= diffX;
27698                     break;
27699                case "southwest":
27700                     diffX = this.constrain(w, diffX, mw, mxw);
27701                     h += diffY;
27702                     h = Math.min(Math.max(mh, h), mxh);
27703                     x += diffX;
27704                     w -= diffX;
27705                     break;
27706             }
27707
27708             var sw = this.snap(w, wi, mw);
27709             var sh = this.snap(h, hi, mh);
27710             if(sw != w || sh != h){
27711                 switch(pos){
27712                     case "northeast":
27713                         y -= sh - h;
27714                     break;
27715                     case "north":
27716                         y -= sh - h;
27717                         break;
27718                     case "southwest":
27719                         x -= sw - w;
27720                     break;
27721                     case "west":
27722                         x -= sw - w;
27723                         break;
27724                     case "northwest":
27725                         x -= sw - w;
27726                         y -= sh - h;
27727                     break;
27728                 }
27729                 w = sw;
27730                 h = sh;
27731             }
27732
27733             if(this.preserveRatio){
27734                 switch(pos){
27735                     case "southeast":
27736                     case "east":
27737                         h = oh * (w/ow);
27738                         h = Math.min(Math.max(mh, h), mxh);
27739                         w = ow * (h/oh);
27740                        break;
27741                     case "south":
27742                         w = ow * (h/oh);
27743                         w = Math.min(Math.max(mw, w), mxw);
27744                         h = oh * (w/ow);
27745                         break;
27746                     case "northeast":
27747                         w = ow * (h/oh);
27748                         w = Math.min(Math.max(mw, w), mxw);
27749                         h = oh * (w/ow);
27750                     break;
27751                     case "north":
27752                         var tw = w;
27753                         w = ow * (h/oh);
27754                         w = Math.min(Math.max(mw, w), mxw);
27755                         h = oh * (w/ow);
27756                         x += (tw - w) / 2;
27757                         break;
27758                     case "southwest":
27759                         h = oh * (w/ow);
27760                         h = Math.min(Math.max(mh, h), mxh);
27761                         var tw = w;
27762                         w = ow * (h/oh);
27763                         x += tw - w;
27764                         break;
27765                     case "west":
27766                         var th = h;
27767                         h = oh * (w/ow);
27768                         h = Math.min(Math.max(mh, h), mxh);
27769                         y += (th - h) / 2;
27770                         var tw = w;
27771                         w = ow * (h/oh);
27772                         x += tw - w;
27773                        break;
27774                     case "northwest":
27775                         var tw = w;
27776                         var th = h;
27777                         h = oh * (w/ow);
27778                         h = Math.min(Math.max(mh, h), mxh);
27779                         w = ow * (h/oh);
27780                         y += th - h;
27781                          x += tw - w;
27782                        break;
27783
27784                 }
27785             }
27786             this.proxy.setBounds(x, y, w, h);
27787             if(this.dynamic){
27788                 this.resizeElement();
27789             }
27790             }catch(e){}
27791         }
27792     },
27793
27794     // private
27795     handleOver : function(){
27796         if(this.enabled){
27797             this.el.addClass("x-resizable-over");
27798         }
27799     },
27800
27801     // private
27802     handleOut : function(){
27803         if(!this.resizing){
27804             this.el.removeClass("x-resizable-over");
27805         }
27806     },
27807
27808     /**
27809      * Returns the element this component is bound to.
27810      * @return {Roo.Element}
27811      */
27812     getEl : function(){
27813         return this.el;
27814     },
27815
27816     /**
27817      * Returns the resizeChild element (or null).
27818      * @return {Roo.Element}
27819      */
27820     getResizeChild : function(){
27821         return this.resizeChild;
27822     },
27823
27824     /**
27825      * Destroys this resizable. If the element was wrapped and
27826      * removeEl is not true then the element remains.
27827      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27828      */
27829     destroy : function(removeEl){
27830         this.proxy.remove();
27831         if(this.overlay){
27832             this.overlay.removeAllListeners();
27833             this.overlay.remove();
27834         }
27835         var ps = Roo.Resizable.positions;
27836         for(var k in ps){
27837             if(typeof ps[k] != "function" && this[ps[k]]){
27838                 var h = this[ps[k]];
27839                 h.el.removeAllListeners();
27840                 h.el.remove();
27841             }
27842         }
27843         if(removeEl){
27844             this.el.update("");
27845             this.el.remove();
27846         }
27847     }
27848 });
27849
27850 // private
27851 // hash to map config positions to true positions
27852 Roo.Resizable.positions = {
27853     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27854 };
27855
27856 // private
27857 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27858     if(!this.tpl){
27859         // only initialize the template if resizable is used
27860         var tpl = Roo.DomHelper.createTemplate(
27861             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27862         );
27863         tpl.compile();
27864         Roo.Resizable.Handle.prototype.tpl = tpl;
27865     }
27866     this.position = pos;
27867     this.rz = rz;
27868     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27869     this.el.unselectable();
27870     if(transparent){
27871         this.el.setOpacity(0);
27872     }
27873     this.el.on("mousedown", this.onMouseDown, this);
27874     if(!disableTrackOver){
27875         this.el.on("mouseover", this.onMouseOver, this);
27876         this.el.on("mouseout", this.onMouseOut, this);
27877     }
27878 };
27879
27880 // private
27881 Roo.Resizable.Handle.prototype = {
27882     afterResize : function(rz){
27883         // do nothing
27884     },
27885     // private
27886     onMouseDown : function(e){
27887         this.rz.onMouseDown(this, e);
27888     },
27889     // private
27890     onMouseOver : function(e){
27891         this.rz.handleOver(this, e);
27892     },
27893     // private
27894     onMouseOut : function(e){
27895         this.rz.handleOut(this, e);
27896     }
27897 };/*
27898  * Based on:
27899  * Ext JS Library 1.1.1
27900  * Copyright(c) 2006-2007, Ext JS, LLC.
27901  *
27902  * Originally Released Under LGPL - original licence link has changed is not relivant.
27903  *
27904  * Fork - LGPL
27905  * <script type="text/javascript">
27906  */
27907
27908 /**
27909  * @class Roo.Editor
27910  * @extends Roo.Component
27911  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27912  * @constructor
27913  * Create a new Editor
27914  * @param {Roo.form.Field} field The Field object (or descendant)
27915  * @param {Object} config The config object
27916  */
27917 Roo.Editor = function(field, config){
27918     Roo.Editor.superclass.constructor.call(this, config);
27919     this.field = field;
27920     this.addEvents({
27921         /**
27922              * @event beforestartedit
27923              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27924              * false from the handler of this event.
27925              * @param {Editor} this
27926              * @param {Roo.Element} boundEl The underlying element bound to this editor
27927              * @param {Mixed} value The field value being set
27928              */
27929         "beforestartedit" : true,
27930         /**
27931              * @event startedit
27932              * Fires when this editor is displayed
27933              * @param {Roo.Element} boundEl The underlying element bound to this editor
27934              * @param {Mixed} value The starting field value
27935              */
27936         "startedit" : true,
27937         /**
27938              * @event beforecomplete
27939              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27940              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27941              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27942              * event will not fire since no edit actually occurred.
27943              * @param {Editor} this
27944              * @param {Mixed} value The current field value
27945              * @param {Mixed} startValue The original field value
27946              */
27947         "beforecomplete" : true,
27948         /**
27949              * @event complete
27950              * Fires after editing is complete and any changed value has been written to the underlying field.
27951              * @param {Editor} this
27952              * @param {Mixed} value The current field value
27953              * @param {Mixed} startValue The original field value
27954              */
27955         "complete" : true,
27956         /**
27957          * @event specialkey
27958          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27959          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27960          * @param {Roo.form.Field} this
27961          * @param {Roo.EventObject} e The event object
27962          */
27963         "specialkey" : true
27964     });
27965 };
27966
27967 Roo.extend(Roo.Editor, Roo.Component, {
27968     /**
27969      * @cfg {Boolean/String} autosize
27970      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27971      * or "height" to adopt the height only (defaults to false)
27972      */
27973     /**
27974      * @cfg {Boolean} revertInvalid
27975      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27976      * validation fails (defaults to true)
27977      */
27978     /**
27979      * @cfg {Boolean} ignoreNoChange
27980      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27981      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27982      * will never be ignored.
27983      */
27984     /**
27985      * @cfg {Boolean} hideEl
27986      * False to keep the bound element visible while the editor is displayed (defaults to true)
27987      */
27988     /**
27989      * @cfg {Mixed} value
27990      * The data value of the underlying field (defaults to "")
27991      */
27992     value : "",
27993     /**
27994      * @cfg {String} alignment
27995      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27996      */
27997     alignment: "c-c?",
27998     /**
27999      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28000      * for bottom-right shadow (defaults to "frame")
28001      */
28002     shadow : "frame",
28003     /**
28004      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28005      */
28006     constrain : false,
28007     /**
28008      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28009      */
28010     completeOnEnter : false,
28011     /**
28012      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28013      */
28014     cancelOnEsc : false,
28015     /**
28016      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28017      */
28018     updateEl : false,
28019
28020     // private
28021     onRender : function(ct, position){
28022         this.el = new Roo.Layer({
28023             shadow: this.shadow,
28024             cls: "x-editor",
28025             parentEl : ct,
28026             shim : this.shim,
28027             shadowOffset:4,
28028             id: this.id,
28029             constrain: this.constrain
28030         });
28031         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28032         if(this.field.msgTarget != 'title'){
28033             this.field.msgTarget = 'qtip';
28034         }
28035         this.field.render(this.el);
28036         if(Roo.isGecko){
28037             this.field.el.dom.setAttribute('autocomplete', 'off');
28038         }
28039         this.field.on("specialkey", this.onSpecialKey, this);
28040         if(this.swallowKeys){
28041             this.field.el.swallowEvent(['keydown','keypress']);
28042         }
28043         this.field.show();
28044         this.field.on("blur", this.onBlur, this);
28045         if(this.field.grow){
28046             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28047         }
28048     },
28049
28050     onSpecialKey : function(field, e){
28051         if(this.completeOnEnter && e.getKey() == e.ENTER){
28052             e.stopEvent();
28053             this.completeEdit();
28054         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28055             this.cancelEdit();
28056         }else{
28057             this.fireEvent('specialkey', field, e);
28058         }
28059     },
28060
28061     /**
28062      * Starts the editing process and shows the editor.
28063      * @param {String/HTMLElement/Element} el The element to edit
28064      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28065       * to the innerHTML of el.
28066      */
28067     startEdit : function(el, value){
28068         if(this.editing){
28069             this.completeEdit();
28070         }
28071         this.boundEl = Roo.get(el);
28072         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28073         if(!this.rendered){
28074             this.render(this.parentEl || document.body);
28075         }
28076         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28077             return;
28078         }
28079         this.startValue = v;
28080         this.field.setValue(v);
28081         if(this.autoSize){
28082             var sz = this.boundEl.getSize();
28083             switch(this.autoSize){
28084                 case "width":
28085                 this.setSize(sz.width,  "");
28086                 break;
28087                 case "height":
28088                 this.setSize("",  sz.height);
28089                 break;
28090                 default:
28091                 this.setSize(sz.width,  sz.height);
28092             }
28093         }
28094         this.el.alignTo(this.boundEl, this.alignment);
28095         this.editing = true;
28096         if(Roo.QuickTips){
28097             Roo.QuickTips.disable();
28098         }
28099         this.show();
28100     },
28101
28102     /**
28103      * Sets the height and width of this editor.
28104      * @param {Number} width The new width
28105      * @param {Number} height The new height
28106      */
28107     setSize : function(w, h){
28108         this.field.setSize(w, h);
28109         if(this.el){
28110             this.el.sync();
28111         }
28112     },
28113
28114     /**
28115      * Realigns the editor to the bound field based on the current alignment config value.
28116      */
28117     realign : function(){
28118         this.el.alignTo(this.boundEl, this.alignment);
28119     },
28120
28121     /**
28122      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28123      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28124      */
28125     completeEdit : function(remainVisible){
28126         if(!this.editing){
28127             return;
28128         }
28129         var v = this.getValue();
28130         if(this.revertInvalid !== false && !this.field.isValid()){
28131             v = this.startValue;
28132             this.cancelEdit(true);
28133         }
28134         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28135             this.editing = false;
28136             this.hide();
28137             return;
28138         }
28139         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28140             this.editing = false;
28141             if(this.updateEl && this.boundEl){
28142                 this.boundEl.update(v);
28143             }
28144             if(remainVisible !== true){
28145                 this.hide();
28146             }
28147             this.fireEvent("complete", this, v, this.startValue);
28148         }
28149     },
28150
28151     // private
28152     onShow : function(){
28153         this.el.show();
28154         if(this.hideEl !== false){
28155             this.boundEl.hide();
28156         }
28157         this.field.show();
28158         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28159             this.fixIEFocus = true;
28160             this.deferredFocus.defer(50, this);
28161         }else{
28162             this.field.focus();
28163         }
28164         this.fireEvent("startedit", this.boundEl, this.startValue);
28165     },
28166
28167     deferredFocus : function(){
28168         if(this.editing){
28169             this.field.focus();
28170         }
28171     },
28172
28173     /**
28174      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28175      * reverted to the original starting value.
28176      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28177      * cancel (defaults to false)
28178      */
28179     cancelEdit : function(remainVisible){
28180         if(this.editing){
28181             this.setValue(this.startValue);
28182             if(remainVisible !== true){
28183                 this.hide();
28184             }
28185         }
28186     },
28187
28188     // private
28189     onBlur : function(){
28190         if(this.allowBlur !== true && this.editing){
28191             this.completeEdit();
28192         }
28193     },
28194
28195     // private
28196     onHide : function(){
28197         if(this.editing){
28198             this.completeEdit();
28199             return;
28200         }
28201         this.field.blur();
28202         if(this.field.collapse){
28203             this.field.collapse();
28204         }
28205         this.el.hide();
28206         if(this.hideEl !== false){
28207             this.boundEl.show();
28208         }
28209         if(Roo.QuickTips){
28210             Roo.QuickTips.enable();
28211         }
28212     },
28213
28214     /**
28215      * Sets the data value of the editor
28216      * @param {Mixed} value Any valid value supported by the underlying field
28217      */
28218     setValue : function(v){
28219         this.field.setValue(v);
28220     },
28221
28222     /**
28223      * Gets the data value of the editor
28224      * @return {Mixed} The data value
28225      */
28226     getValue : function(){
28227         return this.field.getValue();
28228     }
28229 });/*
28230  * Based on:
28231  * Ext JS Library 1.1.1
28232  * Copyright(c) 2006-2007, Ext JS, LLC.
28233  *
28234  * Originally Released Under LGPL - original licence link has changed is not relivant.
28235  *
28236  * Fork - LGPL
28237  * <script type="text/javascript">
28238  */
28239  
28240 /**
28241  * @class Roo.BasicDialog
28242  * @extends Roo.util.Observable
28243  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28244  * <pre><code>
28245 var dlg = new Roo.BasicDialog("my-dlg", {
28246     height: 200,
28247     width: 300,
28248     minHeight: 100,
28249     minWidth: 150,
28250     modal: true,
28251     proxyDrag: true,
28252     shadow: true
28253 });
28254 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28255 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28256 dlg.addButton('Cancel', dlg.hide, dlg);
28257 dlg.show();
28258 </code></pre>
28259   <b>A Dialog should always be a direct child of the body element.</b>
28260  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28261  * @cfg {String} title Default text to display in the title bar (defaults to null)
28262  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28263  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28264  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28265  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28266  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28267  * (defaults to null with no animation)
28268  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28269  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28270  * property for valid values (defaults to 'all')
28271  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28272  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28273  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28274  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28275  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28276  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28277  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28278  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28279  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28280  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28281  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28282  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28283  * draggable = true (defaults to false)
28284  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28285  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28286  * shadow (defaults to false)
28287  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28288  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28289  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28290  * @cfg {Array} buttons Array of buttons
28291  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28292  * @constructor
28293  * Create a new BasicDialog.
28294  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28295  * @param {Object} config Configuration options
28296  */
28297 Roo.BasicDialog = function(el, config){
28298     this.el = Roo.get(el);
28299     var dh = Roo.DomHelper;
28300     if(!this.el && config && config.autoCreate){
28301         if(typeof config.autoCreate == "object"){
28302             if(!config.autoCreate.id){
28303                 config.autoCreate.id = el;
28304             }
28305             this.el = dh.append(document.body,
28306                         config.autoCreate, true);
28307         }else{
28308             this.el = dh.append(document.body,
28309                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28310         }
28311     }
28312     el = this.el;
28313     el.setDisplayed(true);
28314     el.hide = this.hideAction;
28315     this.id = el.id;
28316     el.addClass("x-dlg");
28317
28318     Roo.apply(this, config);
28319
28320     this.proxy = el.createProxy("x-dlg-proxy");
28321     this.proxy.hide = this.hideAction;
28322     this.proxy.setOpacity(.5);
28323     this.proxy.hide();
28324
28325     if(config.width){
28326         el.setWidth(config.width);
28327     }
28328     if(config.height){
28329         el.setHeight(config.height);
28330     }
28331     this.size = el.getSize();
28332     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28333         this.xy = [config.x,config.y];
28334     }else{
28335         this.xy = el.getCenterXY(true);
28336     }
28337     /** The header element @type Roo.Element */
28338     this.header = el.child("> .x-dlg-hd");
28339     /** The body element @type Roo.Element */
28340     this.body = el.child("> .x-dlg-bd");
28341     /** The footer element @type Roo.Element */
28342     this.footer = el.child("> .x-dlg-ft");
28343
28344     if(!this.header){
28345         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28346     }
28347     if(!this.body){
28348         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28349     }
28350
28351     this.header.unselectable();
28352     if(this.title){
28353         this.header.update(this.title);
28354     }
28355     // this element allows the dialog to be focused for keyboard event
28356     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28357     this.focusEl.swallowEvent("click", true);
28358
28359     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28360
28361     // wrap the body and footer for special rendering
28362     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28363     if(this.footer){
28364         this.bwrap.dom.appendChild(this.footer.dom);
28365     }
28366
28367     this.bg = this.el.createChild({
28368         tag: "div", cls:"x-dlg-bg",
28369         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28370     });
28371     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28372
28373
28374     if(this.autoScroll !== false && !this.autoTabs){
28375         this.body.setStyle("overflow", "auto");
28376     }
28377
28378     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28379
28380     if(this.closable !== false){
28381         this.el.addClass("x-dlg-closable");
28382         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28383         this.close.on("click", this.closeClick, this);
28384         this.close.addClassOnOver("x-dlg-close-over");
28385     }
28386     if(this.collapsible !== false){
28387         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28388         this.collapseBtn.on("click", this.collapseClick, this);
28389         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28390         this.header.on("dblclick", this.collapseClick, this);
28391     }
28392     if(this.resizable !== false){
28393         this.el.addClass("x-dlg-resizable");
28394         this.resizer = new Roo.Resizable(el, {
28395             minWidth: this.minWidth || 80,
28396             minHeight:this.minHeight || 80,
28397             handles: this.resizeHandles || "all",
28398             pinned: true
28399         });
28400         this.resizer.on("beforeresize", this.beforeResize, this);
28401         this.resizer.on("resize", this.onResize, this);
28402     }
28403     if(this.draggable !== false){
28404         el.addClass("x-dlg-draggable");
28405         if (!this.proxyDrag) {
28406             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28407         }
28408         else {
28409             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28410         }
28411         dd.setHandleElId(this.header.id);
28412         dd.endDrag = this.endMove.createDelegate(this);
28413         dd.startDrag = this.startMove.createDelegate(this);
28414         dd.onDrag = this.onDrag.createDelegate(this);
28415         dd.scroll = false;
28416         this.dd = dd;
28417     }
28418     if(this.modal){
28419         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28420         this.mask.enableDisplayMode("block");
28421         this.mask.hide();
28422         this.el.addClass("x-dlg-modal");
28423     }
28424     if(this.shadow){
28425         this.shadow = new Roo.Shadow({
28426             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28427             offset : this.shadowOffset
28428         });
28429     }else{
28430         this.shadowOffset = 0;
28431     }
28432     if(Roo.useShims && this.shim !== false){
28433         this.shim = this.el.createShim();
28434         this.shim.hide = this.hideAction;
28435         this.shim.hide();
28436     }else{
28437         this.shim = false;
28438     }
28439     if(this.autoTabs){
28440         this.initTabs();
28441     }
28442     if (this.buttons) { 
28443         var bts= this.buttons;
28444         this.buttons = [];
28445         Roo.each(bts, function(b) {
28446             this.addButton(b);
28447         }, this);
28448     }
28449     
28450     
28451     this.addEvents({
28452         /**
28453          * @event keydown
28454          * Fires when a key is pressed
28455          * @param {Roo.BasicDialog} this
28456          * @param {Roo.EventObject} e
28457          */
28458         "keydown" : true,
28459         /**
28460          * @event move
28461          * Fires when this dialog is moved by the user.
28462          * @param {Roo.BasicDialog} this
28463          * @param {Number} x The new page X
28464          * @param {Number} y The new page Y
28465          */
28466         "move" : true,
28467         /**
28468          * @event resize
28469          * Fires when this dialog is resized by the user.
28470          * @param {Roo.BasicDialog} this
28471          * @param {Number} width The new width
28472          * @param {Number} height The new height
28473          */
28474         "resize" : true,
28475         /**
28476          * @event beforehide
28477          * Fires before this dialog is hidden.
28478          * @param {Roo.BasicDialog} this
28479          */
28480         "beforehide" : true,
28481         /**
28482          * @event hide
28483          * Fires when this dialog is hidden.
28484          * @param {Roo.BasicDialog} this
28485          */
28486         "hide" : true,
28487         /**
28488          * @event beforeshow
28489          * Fires before this dialog is shown.
28490          * @param {Roo.BasicDialog} this
28491          */
28492         "beforeshow" : true,
28493         /**
28494          * @event show
28495          * Fires when this dialog is shown.
28496          * @param {Roo.BasicDialog} this
28497          */
28498         "show" : true
28499     });
28500     el.on("keydown", this.onKeyDown, this);
28501     el.on("mousedown", this.toFront, this);
28502     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28503     this.el.hide();
28504     Roo.DialogManager.register(this);
28505     Roo.BasicDialog.superclass.constructor.call(this);
28506 };
28507
28508 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28509     shadowOffset: Roo.isIE ? 6 : 5,
28510     minHeight: 80,
28511     minWidth: 200,
28512     minButtonWidth: 75,
28513     defaultButton: null,
28514     buttonAlign: "right",
28515     tabTag: 'div',
28516     firstShow: true,
28517
28518     /**
28519      * Sets the dialog title text
28520      * @param {String} text The title text to display
28521      * @return {Roo.BasicDialog} this
28522      */
28523     setTitle : function(text){
28524         this.header.update(text);
28525         return this;
28526     },
28527
28528     // private
28529     closeClick : function(){
28530         this.hide();
28531     },
28532
28533     // private
28534     collapseClick : function(){
28535         this[this.collapsed ? "expand" : "collapse"]();
28536     },
28537
28538     /**
28539      * Collapses the dialog to its minimized state (only the title bar is visible).
28540      * Equivalent to the user clicking the collapse dialog button.
28541      */
28542     collapse : function(){
28543         if(!this.collapsed){
28544             this.collapsed = true;
28545             this.el.addClass("x-dlg-collapsed");
28546             this.restoreHeight = this.el.getHeight();
28547             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28548         }
28549     },
28550
28551     /**
28552      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28553      * clicking the expand dialog button.
28554      */
28555     expand : function(){
28556         if(this.collapsed){
28557             this.collapsed = false;
28558             this.el.removeClass("x-dlg-collapsed");
28559             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28560         }
28561     },
28562
28563     /**
28564      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28565      * @return {Roo.TabPanel} The tabs component
28566      */
28567     initTabs : function(){
28568         var tabs = this.getTabs();
28569         while(tabs.getTab(0)){
28570             tabs.removeTab(0);
28571         }
28572         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28573             var dom = el.dom;
28574             tabs.addTab(Roo.id(dom), dom.title);
28575             dom.title = "";
28576         });
28577         tabs.activate(0);
28578         return tabs;
28579     },
28580
28581     // private
28582     beforeResize : function(){
28583         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28584     },
28585
28586     // private
28587     onResize : function(){
28588         this.refreshSize();
28589         this.syncBodyHeight();
28590         this.adjustAssets();
28591         this.focus();
28592         this.fireEvent("resize", this, this.size.width, this.size.height);
28593     },
28594
28595     // private
28596     onKeyDown : function(e){
28597         if(this.isVisible()){
28598             this.fireEvent("keydown", this, e);
28599         }
28600     },
28601
28602     /**
28603      * Resizes the dialog.
28604      * @param {Number} width
28605      * @param {Number} height
28606      * @return {Roo.BasicDialog} this
28607      */
28608     resizeTo : function(width, height){
28609         this.el.setSize(width, height);
28610         this.size = {width: width, height: height};
28611         this.syncBodyHeight();
28612         if(this.fixedcenter){
28613             this.center();
28614         }
28615         if(this.isVisible()){
28616             this.constrainXY();
28617             this.adjustAssets();
28618         }
28619         this.fireEvent("resize", this, width, height);
28620         return this;
28621     },
28622
28623
28624     /**
28625      * Resizes the dialog to fit the specified content size.
28626      * @param {Number} width
28627      * @param {Number} height
28628      * @return {Roo.BasicDialog} this
28629      */
28630     setContentSize : function(w, h){
28631         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28632         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28633         //if(!this.el.isBorderBox()){
28634             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28635             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28636         //}
28637         if(this.tabs){
28638             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28639             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28640         }
28641         this.resizeTo(w, h);
28642         return this;
28643     },
28644
28645     /**
28646      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28647      * executed in response to a particular key being pressed while the dialog is active.
28648      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28649      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28650      * @param {Function} fn The function to call
28651      * @param {Object} scope (optional) The scope of the function
28652      * @return {Roo.BasicDialog} this
28653      */
28654     addKeyListener : function(key, fn, scope){
28655         var keyCode, shift, ctrl, alt;
28656         if(typeof key == "object" && !(key instanceof Array)){
28657             keyCode = key["key"];
28658             shift = key["shift"];
28659             ctrl = key["ctrl"];
28660             alt = key["alt"];
28661         }else{
28662             keyCode = key;
28663         }
28664         var handler = function(dlg, e){
28665             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28666                 var k = e.getKey();
28667                 if(keyCode instanceof Array){
28668                     for(var i = 0, len = keyCode.length; i < len; i++){
28669                         if(keyCode[i] == k){
28670                           fn.call(scope || window, dlg, k, e);
28671                           return;
28672                         }
28673                     }
28674                 }else{
28675                     if(k == keyCode){
28676                         fn.call(scope || window, dlg, k, e);
28677                     }
28678                 }
28679             }
28680         };
28681         this.on("keydown", handler);
28682         return this;
28683     },
28684
28685     /**
28686      * Returns the TabPanel component (creates it if it doesn't exist).
28687      * Note: If you wish to simply check for the existence of tabs without creating them,
28688      * check for a null 'tabs' property.
28689      * @return {Roo.TabPanel} The tabs component
28690      */
28691     getTabs : function(){
28692         if(!this.tabs){
28693             this.el.addClass("x-dlg-auto-tabs");
28694             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28695             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28696         }
28697         return this.tabs;
28698     },
28699
28700     /**
28701      * Adds a button to the footer section of the dialog.
28702      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28703      * object or a valid Roo.DomHelper element config
28704      * @param {Function} handler The function called when the button is clicked
28705      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28706      * @return {Roo.Button} The new button
28707      */
28708     addButton : function(config, handler, scope){
28709         var dh = Roo.DomHelper;
28710         if(!this.footer){
28711             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28712         }
28713         if(!this.btnContainer){
28714             var tb = this.footer.createChild({
28715
28716                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28717                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28718             }, null, true);
28719             this.btnContainer = tb.firstChild.firstChild.firstChild;
28720         }
28721         var bconfig = {
28722             handler: handler,
28723             scope: scope,
28724             minWidth: this.minButtonWidth,
28725             hideParent:true
28726         };
28727         if(typeof config == "string"){
28728             bconfig.text = config;
28729         }else{
28730             if(config.tag){
28731                 bconfig.dhconfig = config;
28732             }else{
28733                 Roo.apply(bconfig, config);
28734             }
28735         }
28736         var fc = false;
28737         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28738             bconfig.position = Math.max(0, bconfig.position);
28739             fc = this.btnContainer.childNodes[bconfig.position];
28740         }
28741          
28742         var btn = new Roo.Button(
28743             fc ? 
28744                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28745                 : this.btnContainer.appendChild(document.createElement("td")),
28746             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28747             bconfig
28748         );
28749         this.syncBodyHeight();
28750         if(!this.buttons){
28751             /**
28752              * Array of all the buttons that have been added to this dialog via addButton
28753              * @type Array
28754              */
28755             this.buttons = [];
28756         }
28757         this.buttons.push(btn);
28758         return btn;
28759     },
28760
28761     /**
28762      * Sets the default button to be focused when the dialog is displayed.
28763      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28764      * @return {Roo.BasicDialog} this
28765      */
28766     setDefaultButton : function(btn){
28767         this.defaultButton = btn;
28768         return this;
28769     },
28770
28771     // private
28772     getHeaderFooterHeight : function(safe){
28773         var height = 0;
28774         if(this.header){
28775            height += this.header.getHeight();
28776         }
28777         if(this.footer){
28778            var fm = this.footer.getMargins();
28779             height += (this.footer.getHeight()+fm.top+fm.bottom);
28780         }
28781         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28782         height += this.centerBg.getPadding("tb");
28783         return height;
28784     },
28785
28786     // private
28787     syncBodyHeight : function(){
28788         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28789         var height = this.size.height - this.getHeaderFooterHeight(false);
28790         bd.setHeight(height-bd.getMargins("tb"));
28791         var hh = this.header.getHeight();
28792         var h = this.size.height-hh;
28793         cb.setHeight(h);
28794         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28795         bw.setHeight(h-cb.getPadding("tb"));
28796         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28797         bd.setWidth(bw.getWidth(true));
28798         if(this.tabs){
28799             this.tabs.syncHeight();
28800             if(Roo.isIE){
28801                 this.tabs.el.repaint();
28802             }
28803         }
28804     },
28805
28806     /**
28807      * Restores the previous state of the dialog if Roo.state is configured.
28808      * @return {Roo.BasicDialog} this
28809      */
28810     restoreState : function(){
28811         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28812         if(box && box.width){
28813             this.xy = [box.x, box.y];
28814             this.resizeTo(box.width, box.height);
28815         }
28816         return this;
28817     },
28818
28819     // private
28820     beforeShow : function(){
28821         this.expand();
28822         if(this.fixedcenter){
28823             this.xy = this.el.getCenterXY(true);
28824         }
28825         if(this.modal){
28826             Roo.get(document.body).addClass("x-body-masked");
28827             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28828             this.mask.show();
28829         }
28830         this.constrainXY();
28831     },
28832
28833     // private
28834     animShow : function(){
28835         var b = Roo.get(this.animateTarget, true).getBox();
28836         this.proxy.setSize(b.width, b.height);
28837         this.proxy.setLocation(b.x, b.y);
28838         this.proxy.show();
28839         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28840                     true, .35, this.showEl.createDelegate(this));
28841     },
28842
28843     /**
28844      * Shows the dialog.
28845      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28846      * @return {Roo.BasicDialog} this
28847      */
28848     show : function(animateTarget){
28849         if (this.fireEvent("beforeshow", this) === false){
28850             return;
28851         }
28852         if(this.syncHeightBeforeShow){
28853             this.syncBodyHeight();
28854         }else if(this.firstShow){
28855             this.firstShow = false;
28856             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28857         }
28858         this.animateTarget = animateTarget || this.animateTarget;
28859         if(!this.el.isVisible()){
28860             this.beforeShow();
28861             if(this.animateTarget){
28862                 this.animShow();
28863             }else{
28864                 this.showEl();
28865             }
28866         }
28867         return this;
28868     },
28869
28870     // private
28871     showEl : function(){
28872         this.proxy.hide();
28873         this.el.setXY(this.xy);
28874         this.el.show();
28875         this.adjustAssets(true);
28876         this.toFront();
28877         this.focus();
28878         // IE peekaboo bug - fix found by Dave Fenwick
28879         if(Roo.isIE){
28880             this.el.repaint();
28881         }
28882         this.fireEvent("show", this);
28883     },
28884
28885     /**
28886      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28887      * dialog itself will receive focus.
28888      */
28889     focus : function(){
28890         if(this.defaultButton){
28891             this.defaultButton.focus();
28892         }else{
28893             this.focusEl.focus();
28894         }
28895     },
28896
28897     // private
28898     constrainXY : function(){
28899         if(this.constraintoviewport !== false){
28900             if(!this.viewSize){
28901                 if(this.container){
28902                     var s = this.container.getSize();
28903                     this.viewSize = [s.width, s.height];
28904                 }else{
28905                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28906                 }
28907             }
28908             var s = Roo.get(this.container||document).getScroll();
28909
28910             var x = this.xy[0], y = this.xy[1];
28911             var w = this.size.width, h = this.size.height;
28912             var vw = this.viewSize[0], vh = this.viewSize[1];
28913             // only move it if it needs it
28914             var moved = false;
28915             // first validate right/bottom
28916             if(x + w > vw+s.left){
28917                 x = vw - w;
28918                 moved = true;
28919             }
28920             if(y + h > vh+s.top){
28921                 y = vh - h;
28922                 moved = true;
28923             }
28924             // then make sure top/left isn't negative
28925             if(x < s.left){
28926                 x = s.left;
28927                 moved = true;
28928             }
28929             if(y < s.top){
28930                 y = s.top;
28931                 moved = true;
28932             }
28933             if(moved){
28934                 // cache xy
28935                 this.xy = [x, y];
28936                 if(this.isVisible()){
28937                     this.el.setLocation(x, y);
28938                     this.adjustAssets();
28939                 }
28940             }
28941         }
28942     },
28943
28944     // private
28945     onDrag : function(){
28946         if(!this.proxyDrag){
28947             this.xy = this.el.getXY();
28948             this.adjustAssets();
28949         }
28950     },
28951
28952     // private
28953     adjustAssets : function(doShow){
28954         var x = this.xy[0], y = this.xy[1];
28955         var w = this.size.width, h = this.size.height;
28956         if(doShow === true){
28957             if(this.shadow){
28958                 this.shadow.show(this.el);
28959             }
28960             if(this.shim){
28961                 this.shim.show();
28962             }
28963         }
28964         if(this.shadow && this.shadow.isVisible()){
28965             this.shadow.show(this.el);
28966         }
28967         if(this.shim && this.shim.isVisible()){
28968             this.shim.setBounds(x, y, w, h);
28969         }
28970     },
28971
28972     // private
28973     adjustViewport : function(w, h){
28974         if(!w || !h){
28975             w = Roo.lib.Dom.getViewWidth();
28976             h = Roo.lib.Dom.getViewHeight();
28977         }
28978         // cache the size
28979         this.viewSize = [w, h];
28980         if(this.modal && this.mask.isVisible()){
28981             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28982             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28983         }
28984         if(this.isVisible()){
28985             this.constrainXY();
28986         }
28987     },
28988
28989     /**
28990      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28991      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28992      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28993      */
28994     destroy : function(removeEl){
28995         if(this.isVisible()){
28996             this.animateTarget = null;
28997             this.hide();
28998         }
28999         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29000         if(this.tabs){
29001             this.tabs.destroy(removeEl);
29002         }
29003         Roo.destroy(
29004              this.shim,
29005              this.proxy,
29006              this.resizer,
29007              this.close,
29008              this.mask
29009         );
29010         if(this.dd){
29011             this.dd.unreg();
29012         }
29013         if(this.buttons){
29014            for(var i = 0, len = this.buttons.length; i < len; i++){
29015                this.buttons[i].destroy();
29016            }
29017         }
29018         this.el.removeAllListeners();
29019         if(removeEl === true){
29020             this.el.update("");
29021             this.el.remove();
29022         }
29023         Roo.DialogManager.unregister(this);
29024     },
29025
29026     // private
29027     startMove : function(){
29028         if(this.proxyDrag){
29029             this.proxy.show();
29030         }
29031         if(this.constraintoviewport !== false){
29032             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29033         }
29034     },
29035
29036     // private
29037     endMove : function(){
29038         if(!this.proxyDrag){
29039             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29040         }else{
29041             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29042             this.proxy.hide();
29043         }
29044         this.refreshSize();
29045         this.adjustAssets();
29046         this.focus();
29047         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29048     },
29049
29050     /**
29051      * Brings this dialog to the front of any other visible dialogs
29052      * @return {Roo.BasicDialog} this
29053      */
29054     toFront : function(){
29055         Roo.DialogManager.bringToFront(this);
29056         return this;
29057     },
29058
29059     /**
29060      * Sends this dialog to the back (under) of any other visible dialogs
29061      * @return {Roo.BasicDialog} this
29062      */
29063     toBack : function(){
29064         Roo.DialogManager.sendToBack(this);
29065         return this;
29066     },
29067
29068     /**
29069      * Centers this dialog in the viewport
29070      * @return {Roo.BasicDialog} this
29071      */
29072     center : function(){
29073         var xy = this.el.getCenterXY(true);
29074         this.moveTo(xy[0], xy[1]);
29075         return this;
29076     },
29077
29078     /**
29079      * Moves the dialog's top-left corner to the specified point
29080      * @param {Number} x
29081      * @param {Number} y
29082      * @return {Roo.BasicDialog} this
29083      */
29084     moveTo : function(x, y){
29085         this.xy = [x,y];
29086         if(this.isVisible()){
29087             this.el.setXY(this.xy);
29088             this.adjustAssets();
29089         }
29090         return this;
29091     },
29092
29093     /**
29094      * Aligns the dialog to the specified element
29095      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29096      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29097      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29098      * @return {Roo.BasicDialog} this
29099      */
29100     alignTo : function(element, position, offsets){
29101         this.xy = this.el.getAlignToXY(element, position, offsets);
29102         if(this.isVisible()){
29103             this.el.setXY(this.xy);
29104             this.adjustAssets();
29105         }
29106         return this;
29107     },
29108
29109     /**
29110      * Anchors an element to another element and realigns it when the window is resized.
29111      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29112      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29113      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29114      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29115      * is a number, it is used as the buffer delay (defaults to 50ms).
29116      * @return {Roo.BasicDialog} this
29117      */
29118     anchorTo : function(el, alignment, offsets, monitorScroll){
29119         var action = function(){
29120             this.alignTo(el, alignment, offsets);
29121         };
29122         Roo.EventManager.onWindowResize(action, this);
29123         var tm = typeof monitorScroll;
29124         if(tm != 'undefined'){
29125             Roo.EventManager.on(window, 'scroll', action, this,
29126                 {buffer: tm == 'number' ? monitorScroll : 50});
29127         }
29128         action.call(this);
29129         return this;
29130     },
29131
29132     /**
29133      * Returns true if the dialog is visible
29134      * @return {Boolean}
29135      */
29136     isVisible : function(){
29137         return this.el.isVisible();
29138     },
29139
29140     // private
29141     animHide : function(callback){
29142         var b = Roo.get(this.animateTarget).getBox();
29143         this.proxy.show();
29144         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29145         this.el.hide();
29146         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29147                     this.hideEl.createDelegate(this, [callback]));
29148     },
29149
29150     /**
29151      * Hides the dialog.
29152      * @param {Function} callback (optional) Function to call when the dialog is hidden
29153      * @return {Roo.BasicDialog} this
29154      */
29155     hide : function(callback){
29156         if (this.fireEvent("beforehide", this) === false){
29157             return;
29158         }
29159         if(this.shadow){
29160             this.shadow.hide();
29161         }
29162         if(this.shim) {
29163           this.shim.hide();
29164         }
29165         if(this.animateTarget){
29166            this.animHide(callback);
29167         }else{
29168             this.el.hide();
29169             this.hideEl(callback);
29170         }
29171         return this;
29172     },
29173
29174     // private
29175     hideEl : function(callback){
29176         this.proxy.hide();
29177         if(this.modal){
29178             this.mask.hide();
29179             Roo.get(document.body).removeClass("x-body-masked");
29180         }
29181         this.fireEvent("hide", this);
29182         if(typeof callback == "function"){
29183             callback();
29184         }
29185     },
29186
29187     // private
29188     hideAction : function(){
29189         this.setLeft("-10000px");
29190         this.setTop("-10000px");
29191         this.setStyle("visibility", "hidden");
29192     },
29193
29194     // private
29195     refreshSize : function(){
29196         this.size = this.el.getSize();
29197         this.xy = this.el.getXY();
29198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29199     },
29200
29201     // private
29202     // z-index is managed by the DialogManager and may be overwritten at any time
29203     setZIndex : function(index){
29204         if(this.modal){
29205             this.mask.setStyle("z-index", index);
29206         }
29207         if(this.shim){
29208             this.shim.setStyle("z-index", ++index);
29209         }
29210         if(this.shadow){
29211             this.shadow.setZIndex(++index);
29212         }
29213         this.el.setStyle("z-index", ++index);
29214         if(this.proxy){
29215             this.proxy.setStyle("z-index", ++index);
29216         }
29217         if(this.resizer){
29218             this.resizer.proxy.setStyle("z-index", ++index);
29219         }
29220
29221         this.lastZIndex = index;
29222     },
29223
29224     /**
29225      * Returns the element for this dialog
29226      * @return {Roo.Element} The underlying dialog Element
29227      */
29228     getEl : function(){
29229         return this.el;
29230     }
29231 });
29232
29233 /**
29234  * @class Roo.DialogManager
29235  * Provides global access to BasicDialogs that have been created and
29236  * support for z-indexing (layering) multiple open dialogs.
29237  */
29238 Roo.DialogManager = function(){
29239     var list = {};
29240     var accessList = [];
29241     var front = null;
29242
29243     // private
29244     var sortDialogs = function(d1, d2){
29245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29246     };
29247
29248     // private
29249     var orderDialogs = function(){
29250         accessList.sort(sortDialogs);
29251         var seed = Roo.DialogManager.zseed;
29252         for(var i = 0, len = accessList.length; i < len; i++){
29253             var dlg = accessList[i];
29254             if(dlg){
29255                 dlg.setZIndex(seed + (i*10));
29256             }
29257         }
29258     };
29259
29260     return {
29261         /**
29262          * The starting z-index for BasicDialogs (defaults to 9000)
29263          * @type Number The z-index value
29264          */
29265         zseed : 9000,
29266
29267         // private
29268         register : function(dlg){
29269             list[dlg.id] = dlg;
29270             accessList.push(dlg);
29271         },
29272
29273         // private
29274         unregister : function(dlg){
29275             delete list[dlg.id];
29276             var i=0;
29277             var len=0;
29278             if(!accessList.indexOf){
29279                 for(  i = 0, len = accessList.length; i < len; i++){
29280                     if(accessList[i] == dlg){
29281                         accessList.splice(i, 1);
29282                         return;
29283                     }
29284                 }
29285             }else{
29286                  i = accessList.indexOf(dlg);
29287                 if(i != -1){
29288                     accessList.splice(i, 1);
29289                 }
29290             }
29291         },
29292
29293         /**
29294          * Gets a registered dialog by id
29295          * @param {String/Object} id The id of the dialog or a dialog
29296          * @return {Roo.BasicDialog} this
29297          */
29298         get : function(id){
29299             return typeof id == "object" ? id : list[id];
29300         },
29301
29302         /**
29303          * Brings the specified dialog to the front
29304          * @param {String/Object} dlg The id of the dialog or a dialog
29305          * @return {Roo.BasicDialog} this
29306          */
29307         bringToFront : function(dlg){
29308             dlg = this.get(dlg);
29309             if(dlg != front){
29310                 front = dlg;
29311                 dlg._lastAccess = new Date().getTime();
29312                 orderDialogs();
29313             }
29314             return dlg;
29315         },
29316
29317         /**
29318          * Sends the specified dialog to the back
29319          * @param {String/Object} dlg The id of the dialog or a dialog
29320          * @return {Roo.BasicDialog} this
29321          */
29322         sendToBack : function(dlg){
29323             dlg = this.get(dlg);
29324             dlg._lastAccess = -(new Date().getTime());
29325             orderDialogs();
29326             return dlg;
29327         },
29328
29329         /**
29330          * Hides all dialogs
29331          */
29332         hideAll : function(){
29333             for(var id in list){
29334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29335                     list[id].hide();
29336                 }
29337             }
29338         }
29339     };
29340 }();
29341
29342 /**
29343  * @class Roo.LayoutDialog
29344  * @extends Roo.BasicDialog
29345  * Dialog which provides adjustments for working with a layout in a Dialog.
29346  * Add your necessary layout config options to the dialog's config.<br>
29347  * Example usage (including a nested layout):
29348  * <pre><code>
29349 if(!dialog){
29350     dialog = new Roo.LayoutDialog("download-dlg", {
29351         modal: true,
29352         width:600,
29353         height:450,
29354         shadow:true,
29355         minWidth:500,
29356         minHeight:350,
29357         autoTabs:true,
29358         proxyDrag:true,
29359         // layout config merges with the dialog config
29360         center:{
29361             tabPosition: "top",
29362             alwaysShowTabs: true
29363         }
29364     });
29365     dialog.addKeyListener(27, dialog.hide, dialog);
29366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29367     dialog.addButton("Build It!", this.getDownload, this);
29368
29369     // we can even add nested layouts
29370     var innerLayout = new Roo.BorderLayout("dl-inner", {
29371         east: {
29372             initialSize: 200,
29373             autoScroll:true,
29374             split:true
29375         },
29376         center: {
29377             autoScroll:true
29378         }
29379     });
29380     innerLayout.beginUpdate();
29381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29383     innerLayout.endUpdate(true);
29384
29385     var layout = dialog.getLayout();
29386     layout.beginUpdate();
29387     layout.add("center", new Roo.ContentPanel("standard-panel",
29388                         {title: "Download the Source", fitToFrame:true}));
29389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29390                {title: "Build your own roo.js"}));
29391     layout.getRegion("center").showPanel(sp);
29392     layout.endUpdate();
29393 }
29394 </code></pre>
29395     * @constructor
29396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29397     * @param {Object} config configuration options
29398   */
29399 Roo.LayoutDialog = function(el, cfg){
29400     
29401     var config=  cfg;
29402     if (typeof(cfg) == 'undefined') {
29403         config = Roo.apply({}, el);
29404         el = Roo.get( document.documentElement || document.body).createChild();
29405         //config.autoCreate = true;
29406     }
29407     
29408     
29409     config.autoTabs = false;
29410     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29411     this.body.setStyle({overflow:"hidden", position:"relative"});
29412     this.layout = new Roo.BorderLayout(this.body.dom, config);
29413     this.layout.monitorWindowResize = false;
29414     this.el.addClass("x-dlg-auto-layout");
29415     // fix case when center region overwrites center function
29416     this.center = Roo.BasicDialog.prototype.center;
29417     this.on("show", this.layout.layout, this.layout, true);
29418     if (config.items) {
29419         var xitems = config.items;
29420         delete config.items;
29421         Roo.each(xitems, this.addxtype, this);
29422     }
29423     
29424     
29425 };
29426 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29427     /**
29428      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29429      * @deprecated
29430      */
29431     endUpdate : function(){
29432         this.layout.endUpdate();
29433     },
29434
29435     /**
29436      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29437      *  @deprecated
29438      */
29439     beginUpdate : function(){
29440         this.layout.beginUpdate();
29441     },
29442
29443     /**
29444      * Get the BorderLayout for this dialog
29445      * @return {Roo.BorderLayout}
29446      */
29447     getLayout : function(){
29448         return this.layout;
29449     },
29450
29451     showEl : function(){
29452         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29453         if(Roo.isIE7){
29454             this.layout.layout();
29455         }
29456     },
29457
29458     // private
29459     // Use the syncHeightBeforeShow config option to control this automatically
29460     syncBodyHeight : function(){
29461         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29462         if(this.layout){this.layout.layout();}
29463     },
29464     
29465       /**
29466      * Add an xtype element (actually adds to the layout.)
29467      * @return {Object} xdata xtype object data.
29468      */
29469     
29470     addxtype : function(c) {
29471         return this.layout.addxtype(c);
29472     }
29473 });/*
29474  * Based on:
29475  * Ext JS Library 1.1.1
29476  * Copyright(c) 2006-2007, Ext JS, LLC.
29477  *
29478  * Originally Released Under LGPL - original licence link has changed is not relivant.
29479  *
29480  * Fork - LGPL
29481  * <script type="text/javascript">
29482  */
29483  
29484 /**
29485  * @class Roo.MessageBox
29486  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29487  * Example usage:
29488  *<pre><code>
29489 // Basic alert:
29490 Roo.Msg.alert('Status', 'Changes saved successfully.');
29491
29492 // Prompt for user data:
29493 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29494     if (btn == 'ok'){
29495         // process text value...
29496     }
29497 });
29498
29499 // Show a dialog using config options:
29500 Roo.Msg.show({
29501    title:'Save Changes?',
29502    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29503    buttons: Roo.Msg.YESNOCANCEL,
29504    fn: processResult,
29505    animEl: 'elId'
29506 });
29507 </code></pre>
29508  * @singleton
29509  */
29510 Roo.MessageBox = function(){
29511     var dlg, opt, mask, waitTimer;
29512     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29513     var buttons, activeTextEl, bwidth;
29514
29515     // private
29516     var handleButton = function(button){
29517         dlg.hide();
29518         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29519     };
29520
29521     // private
29522     var handleHide = function(){
29523         if(opt && opt.cls){
29524             dlg.el.removeClass(opt.cls);
29525         }
29526         if(waitTimer){
29527             Roo.TaskMgr.stop(waitTimer);
29528             waitTimer = null;
29529         }
29530     };
29531
29532     // private
29533     var updateButtons = function(b){
29534         var width = 0;
29535         if(!b){
29536             buttons["ok"].hide();
29537             buttons["cancel"].hide();
29538             buttons["yes"].hide();
29539             buttons["no"].hide();
29540             dlg.footer.dom.style.display = 'none';
29541             return width;
29542         }
29543         dlg.footer.dom.style.display = '';
29544         for(var k in buttons){
29545             if(typeof buttons[k] != "function"){
29546                 if(b[k]){
29547                     buttons[k].show();
29548                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29549                     width += buttons[k].el.getWidth()+15;
29550                 }else{
29551                     buttons[k].hide();
29552                 }
29553             }
29554         }
29555         return width;
29556     };
29557
29558     // private
29559     var handleEsc = function(d, k, e){
29560         if(opt && opt.closable !== false){
29561             dlg.hide();
29562         }
29563         if(e){
29564             e.stopEvent();
29565         }
29566     };
29567
29568     return {
29569         /**
29570          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29571          * @return {Roo.BasicDialog} The BasicDialog element
29572          */
29573         getDialog : function(){
29574            if(!dlg){
29575                 dlg = new Roo.BasicDialog("x-msg-box", {
29576                     autoCreate : true,
29577                     shadow: true,
29578                     draggable: true,
29579                     resizable:false,
29580                     constraintoviewport:false,
29581                     fixedcenter:true,
29582                     collapsible : false,
29583                     shim:true,
29584                     modal: true,
29585                     width:400, height:100,
29586                     buttonAlign:"center",
29587                     closeClick : function(){
29588                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29589                             handleButton("no");
29590                         }else{
29591                             handleButton("cancel");
29592                         }
29593                     }
29594                 });
29595                 dlg.on("hide", handleHide);
29596                 mask = dlg.mask;
29597                 dlg.addKeyListener(27, handleEsc);
29598                 buttons = {};
29599                 var bt = this.buttonText;
29600                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29601                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29602                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29603                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29604                 bodyEl = dlg.body.createChild({
29605
29606                     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>'
29607                 });
29608                 msgEl = bodyEl.dom.firstChild;
29609                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29610                 textboxEl.enableDisplayMode();
29611                 textboxEl.addKeyListener([10,13], function(){
29612                     if(dlg.isVisible() && opt && opt.buttons){
29613                         if(opt.buttons.ok){
29614                             handleButton("ok");
29615                         }else if(opt.buttons.yes){
29616                             handleButton("yes");
29617                         }
29618                     }
29619                 });
29620                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29621                 textareaEl.enableDisplayMode();
29622                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29623                 progressEl.enableDisplayMode();
29624                 var pf = progressEl.dom.firstChild;
29625                 if (pf) {
29626                     pp = Roo.get(pf.firstChild);
29627                     pp.setHeight(pf.offsetHeight);
29628                 }
29629                 
29630             }
29631             return dlg;
29632         },
29633
29634         /**
29635          * Updates the message box body text
29636          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29637          * the XHTML-compliant non-breaking space character '&amp;#160;')
29638          * @return {Roo.MessageBox} This message box
29639          */
29640         updateText : function(text){
29641             if(!dlg.isVisible() && !opt.width){
29642                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29643             }
29644             msgEl.innerHTML = text || '&#160;';
29645             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29646                         Math.max(opt.minWidth || this.minWidth, bwidth));
29647             if(opt.prompt){
29648                 activeTextEl.setWidth(w);
29649             }
29650             if(dlg.isVisible()){
29651                 dlg.fixedcenter = false;
29652             }
29653             dlg.setContentSize(w, bodyEl.getHeight());
29654             if(dlg.isVisible()){
29655                 dlg.fixedcenter = true;
29656             }
29657             return this;
29658         },
29659
29660         /**
29661          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29662          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29663          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29664          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29665          * @return {Roo.MessageBox} This message box
29666          */
29667         updateProgress : function(value, text){
29668             if(text){
29669                 this.updateText(text);
29670             }
29671             if (pp) { // weird bug on my firefox - for some reason this is not defined
29672                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29673             }
29674             return this;
29675         },        
29676
29677         /**
29678          * Returns true if the message box is currently displayed
29679          * @return {Boolean} True if the message box is visible, else false
29680          */
29681         isVisible : function(){
29682             return dlg && dlg.isVisible();  
29683         },
29684
29685         /**
29686          * Hides the message box if it is displayed
29687          */
29688         hide : function(){
29689             if(this.isVisible()){
29690                 dlg.hide();
29691             }  
29692         },
29693
29694         /**
29695          * Displays a new message box, or reinitializes an existing message box, based on the config options
29696          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29697          * The following config object properties are supported:
29698          * <pre>
29699 Property    Type             Description
29700 ----------  ---------------  ------------------------------------------------------------------------------------
29701 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29702                                    closes (defaults to undefined)
29703 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29704                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29705 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29706                                    progress and wait dialogs will ignore this property and always hide the
29707                                    close button as they can only be closed programmatically.
29708 cls               String           A custom CSS class to apply to the message box element
29709 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29710                                    displayed (defaults to 75)
29711 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29712                                    function will be btn (the name of the button that was clicked, if applicable,
29713                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29714                                    Progress and wait dialogs will ignore this option since they do not respond to
29715                                    user actions and can only be closed programmatically, so any required function
29716                                    should be called by the same code after it closes the dialog.
29717 icon              String           A CSS class that provides a background image to be used as an icon for
29718                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29719 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29720 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29721 modal             Boolean          False to allow user interaction with the page while the message box is
29722                                    displayed (defaults to true)
29723 msg               String           A string that will replace the existing message box body text (defaults
29724                                    to the XHTML-compliant non-breaking space character '&#160;')
29725 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29726 progress          Boolean          True to display a progress bar (defaults to false)
29727 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29728 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29729 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29730 title             String           The title text
29731 value             String           The string value to set into the active textbox element if displayed
29732 wait              Boolean          True to display a progress bar (defaults to false)
29733 width             Number           The width of the dialog in pixels
29734 </pre>
29735          *
29736          * Example usage:
29737          * <pre><code>
29738 Roo.Msg.show({
29739    title: 'Address',
29740    msg: 'Please enter your address:',
29741    width: 300,
29742    buttons: Roo.MessageBox.OKCANCEL,
29743    multiline: true,
29744    fn: saveAddress,
29745    animEl: 'addAddressBtn'
29746 });
29747 </code></pre>
29748          * @param {Object} config Configuration options
29749          * @return {Roo.MessageBox} This message box
29750          */
29751         show : function(options){
29752             if(this.isVisible()){
29753                 this.hide();
29754             }
29755             var d = this.getDialog();
29756             opt = options;
29757             d.setTitle(opt.title || "&#160;");
29758             d.close.setDisplayed(opt.closable !== false);
29759             activeTextEl = textboxEl;
29760             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29761             if(opt.prompt){
29762                 if(opt.multiline){
29763                     textboxEl.hide();
29764                     textareaEl.show();
29765                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29766                         opt.multiline : this.defaultTextHeight);
29767                     activeTextEl = textareaEl;
29768                 }else{
29769                     textboxEl.show();
29770                     textareaEl.hide();
29771                 }
29772             }else{
29773                 textboxEl.hide();
29774                 textareaEl.hide();
29775             }
29776             progressEl.setDisplayed(opt.progress === true);
29777             this.updateProgress(0);
29778             activeTextEl.dom.value = opt.value || "";
29779             if(opt.prompt){
29780                 dlg.setDefaultButton(activeTextEl);
29781             }else{
29782                 var bs = opt.buttons;
29783                 var db = null;
29784                 if(bs && bs.ok){
29785                     db = buttons["ok"];
29786                 }else if(bs && bs.yes){
29787                     db = buttons["yes"];
29788                 }
29789                 dlg.setDefaultButton(db);
29790             }
29791             bwidth = updateButtons(opt.buttons);
29792             this.updateText(opt.msg);
29793             if(opt.cls){
29794                 d.el.addClass(opt.cls);
29795             }
29796             d.proxyDrag = opt.proxyDrag === true;
29797             d.modal = opt.modal !== false;
29798             d.mask = opt.modal !== false ? mask : false;
29799             if(!d.isVisible()){
29800                 // force it to the end of the z-index stack so it gets a cursor in FF
29801                 document.body.appendChild(dlg.el.dom);
29802                 d.animateTarget = null;
29803                 d.show(options.animEl);
29804             }
29805             return this;
29806         },
29807
29808         /**
29809          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29810          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29811          * and closing the message box when the process is complete.
29812          * @param {String} title The title bar text
29813          * @param {String} msg The message box body text
29814          * @return {Roo.MessageBox} This message box
29815          */
29816         progress : function(title, msg){
29817             this.show({
29818                 title : title,
29819                 msg : msg,
29820                 buttons: false,
29821                 progress:true,
29822                 closable:false,
29823                 minWidth: this.minProgressWidth,
29824                 modal : true
29825             });
29826             return this;
29827         },
29828
29829         /**
29830          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29831          * If a callback function is passed it will be called after the user clicks the button, and the
29832          * id of the button that was clicked will be passed as the only parameter to the callback
29833          * (could also be the top-right close button).
29834          * @param {String} title The title bar text
29835          * @param {String} msg The message box body text
29836          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29837          * @param {Object} scope (optional) The scope of the callback function
29838          * @return {Roo.MessageBox} This message box
29839          */
29840         alert : function(title, msg, fn, scope){
29841             this.show({
29842                 title : title,
29843                 msg : msg,
29844                 buttons: this.OK,
29845                 fn: fn,
29846                 scope : scope,
29847                 modal : true
29848             });
29849             return this;
29850         },
29851
29852         /**
29853          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29854          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29855          * You are responsible for closing the message box when the process is complete.
29856          * @param {String} msg The message box body text
29857          * @param {String} title (optional) The title bar text
29858          * @return {Roo.MessageBox} This message box
29859          */
29860         wait : function(msg, title){
29861             this.show({
29862                 title : title,
29863                 msg : msg,
29864                 buttons: false,
29865                 closable:false,
29866                 progress:true,
29867                 modal:true,
29868                 width:300,
29869                 wait:true
29870             });
29871             waitTimer = Roo.TaskMgr.start({
29872                 run: function(i){
29873                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29874                 },
29875                 interval: 1000
29876             });
29877             return this;
29878         },
29879
29880         /**
29881          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29882          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29883          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29884          * @param {String} title The title bar text
29885          * @param {String} msg The message box body text
29886          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29887          * @param {Object} scope (optional) The scope of the callback function
29888          * @return {Roo.MessageBox} This message box
29889          */
29890         confirm : function(title, msg, fn, scope){
29891             this.show({
29892                 title : title,
29893                 msg : msg,
29894                 buttons: this.YESNO,
29895                 fn: fn,
29896                 scope : scope,
29897                 modal : true
29898             });
29899             return this;
29900         },
29901
29902         /**
29903          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29904          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29905          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29906          * (could also be the top-right close button) and the text that was entered will be passed as the two
29907          * parameters to the callback.
29908          * @param {String} title The title bar text
29909          * @param {String} msg The message box body text
29910          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29911          * @param {Object} scope (optional) The scope of the callback function
29912          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29913          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29914          * @return {Roo.MessageBox} This message box
29915          */
29916         prompt : function(title, msg, fn, scope, multiline){
29917             this.show({
29918                 title : title,
29919                 msg : msg,
29920                 buttons: this.OKCANCEL,
29921                 fn: fn,
29922                 minWidth:250,
29923                 scope : scope,
29924                 prompt:true,
29925                 multiline: multiline,
29926                 modal : true
29927             });
29928             return this;
29929         },
29930
29931         /**
29932          * Button config that displays a single OK button
29933          * @type Object
29934          */
29935         OK : {ok:true},
29936         /**
29937          * Button config that displays Yes and No buttons
29938          * @type Object
29939          */
29940         YESNO : {yes:true, no:true},
29941         /**
29942          * Button config that displays OK and Cancel buttons
29943          * @type Object
29944          */
29945         OKCANCEL : {ok:true, cancel:true},
29946         /**
29947          * Button config that displays Yes, No and Cancel buttons
29948          * @type Object
29949          */
29950         YESNOCANCEL : {yes:true, no:true, cancel:true},
29951
29952         /**
29953          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29954          * @type Number
29955          */
29956         defaultTextHeight : 75,
29957         /**
29958          * The maximum width in pixels of the message box (defaults to 600)
29959          * @type Number
29960          */
29961         maxWidth : 600,
29962         /**
29963          * The minimum width in pixels of the message box (defaults to 100)
29964          * @type Number
29965          */
29966         minWidth : 100,
29967         /**
29968          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29969          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29970          * @type Number
29971          */
29972         minProgressWidth : 250,
29973         /**
29974          * An object containing the default button text strings that can be overriden for localized language support.
29975          * Supported properties are: ok, cancel, yes and no.
29976          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29977          * @type Object
29978          */
29979         buttonText : {
29980             ok : "OK",
29981             cancel : "Cancel",
29982             yes : "Yes",
29983             no : "No"
29984         }
29985     };
29986 }();
29987
29988 /**
29989  * Shorthand for {@link Roo.MessageBox}
29990  */
29991 Roo.Msg = Roo.MessageBox;/*
29992  * Based on:
29993  * Ext JS Library 1.1.1
29994  * Copyright(c) 2006-2007, Ext JS, LLC.
29995  *
29996  * Originally Released Under LGPL - original licence link has changed is not relivant.
29997  *
29998  * Fork - LGPL
29999  * <script type="text/javascript">
30000  */
30001 /**
30002  * @class Roo.QuickTips
30003  * Provides attractive and customizable tooltips for any element.
30004  * @singleton
30005  */
30006 Roo.QuickTips = function(){
30007     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30008     var ce, bd, xy, dd;
30009     var visible = false, disabled = true, inited = false;
30010     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30011     
30012     var onOver = function(e){
30013         if(disabled){
30014             return;
30015         }
30016         var t = e.getTarget();
30017         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30018             return;
30019         }
30020         if(ce && t == ce.el){
30021             clearTimeout(hideProc);
30022             return;
30023         }
30024         if(t && tagEls[t.id]){
30025             tagEls[t.id].el = t;
30026             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30027             return;
30028         }
30029         var ttp, et = Roo.fly(t);
30030         var ns = cfg.namespace;
30031         if(tm.interceptTitles && t.title){
30032             ttp = t.title;
30033             t.qtip = ttp;
30034             t.removeAttribute("title");
30035             e.preventDefault();
30036         }else{
30037             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30038         }
30039         if(ttp){
30040             showProc = show.defer(tm.showDelay, tm, [{
30041                 el: t, 
30042                 text: ttp, 
30043                 width: et.getAttributeNS(ns, cfg.width),
30044                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30045                 title: et.getAttributeNS(ns, cfg.title),
30046                     cls: et.getAttributeNS(ns, cfg.cls)
30047             }]);
30048         }
30049     };
30050     
30051     var onOut = function(e){
30052         clearTimeout(showProc);
30053         var t = e.getTarget();
30054         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30055             hideProc = setTimeout(hide, tm.hideDelay);
30056         }
30057     };
30058     
30059     var onMove = function(e){
30060         if(disabled){
30061             return;
30062         }
30063         xy = e.getXY();
30064         xy[1] += 18;
30065         if(tm.trackMouse && ce){
30066             el.setXY(xy);
30067         }
30068     };
30069     
30070     var onDown = function(e){
30071         clearTimeout(showProc);
30072         clearTimeout(hideProc);
30073         if(!e.within(el)){
30074             if(tm.hideOnClick){
30075                 hide();
30076                 tm.disable();
30077                 tm.enable.defer(100, tm);
30078             }
30079         }
30080     };
30081     
30082     var getPad = function(){
30083         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30084     };
30085
30086     var show = function(o){
30087         if(disabled){
30088             return;
30089         }
30090         clearTimeout(dismissProc);
30091         ce = o;
30092         if(removeCls){ // in case manually hidden
30093             el.removeClass(removeCls);
30094             removeCls = null;
30095         }
30096         if(ce.cls){
30097             el.addClass(ce.cls);
30098             removeCls = ce.cls;
30099         }
30100         if(ce.title){
30101             tipTitle.update(ce.title);
30102             tipTitle.show();
30103         }else{
30104             tipTitle.update('');
30105             tipTitle.hide();
30106         }
30107         el.dom.style.width  = tm.maxWidth+'px';
30108         //tipBody.dom.style.width = '';
30109         tipBodyText.update(o.text);
30110         var p = getPad(), w = ce.width;
30111         if(!w){
30112             var td = tipBodyText.dom;
30113             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30114             if(aw > tm.maxWidth){
30115                 w = tm.maxWidth;
30116             }else if(aw < tm.minWidth){
30117                 w = tm.minWidth;
30118             }else{
30119                 w = aw;
30120             }
30121         }
30122         //tipBody.setWidth(w);
30123         el.setWidth(parseInt(w, 10) + p);
30124         if(ce.autoHide === false){
30125             close.setDisplayed(true);
30126             if(dd){
30127                 dd.unlock();
30128             }
30129         }else{
30130             close.setDisplayed(false);
30131             if(dd){
30132                 dd.lock();
30133             }
30134         }
30135         if(xy){
30136             el.avoidY = xy[1]-18;
30137             el.setXY(xy);
30138         }
30139         if(tm.animate){
30140             el.setOpacity(.1);
30141             el.setStyle("visibility", "visible");
30142             el.fadeIn({callback: afterShow});
30143         }else{
30144             afterShow();
30145         }
30146     };
30147     
30148     var afterShow = function(){
30149         if(ce){
30150             el.show();
30151             esc.enable();
30152             if(tm.autoDismiss && ce.autoHide !== false){
30153                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30154             }
30155         }
30156     };
30157     
30158     var hide = function(noanim){
30159         clearTimeout(dismissProc);
30160         clearTimeout(hideProc);
30161         ce = null;
30162         if(el.isVisible()){
30163             esc.disable();
30164             if(noanim !== true && tm.animate){
30165                 el.fadeOut({callback: afterHide});
30166             }else{
30167                 afterHide();
30168             } 
30169         }
30170     };
30171     
30172     var afterHide = function(){
30173         el.hide();
30174         if(removeCls){
30175             el.removeClass(removeCls);
30176             removeCls = null;
30177         }
30178     };
30179     
30180     return {
30181         /**
30182         * @cfg {Number} minWidth
30183         * The minimum width of the quick tip (defaults to 40)
30184         */
30185        minWidth : 40,
30186         /**
30187         * @cfg {Number} maxWidth
30188         * The maximum width of the quick tip (defaults to 300)
30189         */
30190        maxWidth : 300,
30191         /**
30192         * @cfg {Boolean} interceptTitles
30193         * True to automatically use the element's DOM title value if available (defaults to false)
30194         */
30195        interceptTitles : false,
30196         /**
30197         * @cfg {Boolean} trackMouse
30198         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30199         */
30200        trackMouse : false,
30201         /**
30202         * @cfg {Boolean} hideOnClick
30203         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30204         */
30205        hideOnClick : true,
30206         /**
30207         * @cfg {Number} showDelay
30208         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30209         */
30210        showDelay : 500,
30211         /**
30212         * @cfg {Number} hideDelay
30213         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30214         */
30215        hideDelay : 200,
30216         /**
30217         * @cfg {Boolean} autoHide
30218         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30219         * Used in conjunction with hideDelay.
30220         */
30221        autoHide : true,
30222         /**
30223         * @cfg {Boolean}
30224         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30225         * (defaults to true).  Used in conjunction with autoDismissDelay.
30226         */
30227        autoDismiss : true,
30228         /**
30229         * @cfg {Number}
30230         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30231         */
30232        autoDismissDelay : 5000,
30233        /**
30234         * @cfg {Boolean} animate
30235         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30236         */
30237        animate : false,
30238
30239        /**
30240         * @cfg {String} title
30241         * Title text to display (defaults to '').  This can be any valid HTML markup.
30242         */
30243         title: '',
30244        /**
30245         * @cfg {String} text
30246         * Body text to display (defaults to '').  This can be any valid HTML markup.
30247         */
30248         text : '',
30249        /**
30250         * @cfg {String} cls
30251         * A CSS class to apply to the base quick tip element (defaults to '').
30252         */
30253         cls : '',
30254        /**
30255         * @cfg {Number} width
30256         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30257         * minWidth or maxWidth.
30258         */
30259         width : null,
30260
30261     /**
30262      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30263      * or display QuickTips in a page.
30264      */
30265        init : function(){
30266           tm = Roo.QuickTips;
30267           cfg = tm.tagConfig;
30268           if(!inited){
30269               if(!Roo.isReady){ // allow calling of init() before onReady
30270                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30271                   return;
30272               }
30273               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30274               el.fxDefaults = {stopFx: true};
30275               // maximum custom styling
30276               //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>');
30277               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>');              
30278               tipTitle = el.child('h3');
30279               tipTitle.enableDisplayMode("block");
30280               tipBody = el.child('div.x-tip-bd');
30281               tipBodyText = el.child('div.x-tip-bd-inner');
30282               //bdLeft = el.child('div.x-tip-bd-left');
30283               //bdRight = el.child('div.x-tip-bd-right');
30284               close = el.child('div.x-tip-close');
30285               close.enableDisplayMode("block");
30286               close.on("click", hide);
30287               var d = Roo.get(document);
30288               d.on("mousedown", onDown);
30289               d.on("mouseover", onOver);
30290               d.on("mouseout", onOut);
30291               d.on("mousemove", onMove);
30292               esc = d.addKeyListener(27, hide);
30293               esc.disable();
30294               if(Roo.dd.DD){
30295                   dd = el.initDD("default", null, {
30296                       onDrag : function(){
30297                           el.sync();  
30298                       }
30299                   });
30300                   dd.setHandleElId(tipTitle.id);
30301                   dd.lock();
30302               }
30303               inited = true;
30304           }
30305           this.enable(); 
30306        },
30307
30308     /**
30309      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30310      * are supported:
30311      * <pre>
30312 Property    Type                   Description
30313 ----------  ---------------------  ------------------------------------------------------------------------
30314 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30315      * </ul>
30316      * @param {Object} config The config object
30317      */
30318        register : function(config){
30319            var cs = config instanceof Array ? config : arguments;
30320            for(var i = 0, len = cs.length; i < len; i++) {
30321                var c = cs[i];
30322                var target = c.target;
30323                if(target){
30324                    if(target instanceof Array){
30325                        for(var j = 0, jlen = target.length; j < jlen; j++){
30326                            tagEls[target[j]] = c;
30327                        }
30328                    }else{
30329                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30330                    }
30331                }
30332            }
30333        },
30334
30335     /**
30336      * Removes this quick tip from its element and destroys it.
30337      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30338      */
30339        unregister : function(el){
30340            delete tagEls[Roo.id(el)];
30341        },
30342
30343     /**
30344      * Enable this quick tip.
30345      */
30346        enable : function(){
30347            if(inited && disabled){
30348                locks.pop();
30349                if(locks.length < 1){
30350                    disabled = false;
30351                }
30352            }
30353        },
30354
30355     /**
30356      * Disable this quick tip.
30357      */
30358        disable : function(){
30359           disabled = true;
30360           clearTimeout(showProc);
30361           clearTimeout(hideProc);
30362           clearTimeout(dismissProc);
30363           if(ce){
30364               hide(true);
30365           }
30366           locks.push(1);
30367        },
30368
30369     /**
30370      * Returns true if the quick tip is enabled, else false.
30371      */
30372        isEnabled : function(){
30373             return !disabled;
30374        },
30375
30376         // private
30377        tagConfig : {
30378            namespace : "ext",
30379            attribute : "qtip",
30380            width : "width",
30381            target : "target",
30382            title : "qtitle",
30383            hide : "hide",
30384            cls : "qclass"
30385        }
30386    };
30387 }();
30388
30389 // backwards compat
30390 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30391  * Based on:
30392  * Ext JS Library 1.1.1
30393  * Copyright(c) 2006-2007, Ext JS, LLC.
30394  *
30395  * Originally Released Under LGPL - original licence link has changed is not relivant.
30396  *
30397  * Fork - LGPL
30398  * <script type="text/javascript">
30399  */
30400  
30401
30402 /**
30403  * @class Roo.tree.TreePanel
30404  * @extends Roo.data.Tree
30405
30406  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30407  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30408  * @cfg {Boolean} enableDD true to enable drag and drop
30409  * @cfg {Boolean} enableDrag true to enable just drag
30410  * @cfg {Boolean} enableDrop true to enable just drop
30411  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30412  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30413  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30414  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30415  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30416  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30417  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30418  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30419  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30420  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30421  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30422  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30423  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30424  * @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>
30425  * @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>
30426  * 
30427  * @constructor
30428  * @param {String/HTMLElement/Element} el The container element
30429  * @param {Object} config
30430  */
30431 Roo.tree.TreePanel = function(el, config){
30432     var root = false;
30433     var loader = false;
30434     if (config.root) {
30435         root = config.root;
30436         delete config.root;
30437     }
30438     if (config.loader) {
30439         loader = config.loader;
30440         delete config.loader;
30441     }
30442     
30443     Roo.apply(this, config);
30444     Roo.tree.TreePanel.superclass.constructor.call(this);
30445     this.el = Roo.get(el);
30446     this.el.addClass('x-tree');
30447     //console.log(root);
30448     if (root) {
30449         this.setRootNode( Roo.factory(root, Roo.tree));
30450     }
30451     if (loader) {
30452         this.loader = Roo.factory(loader, Roo.tree);
30453     }
30454    /**
30455     * Read-only. The id of the container element becomes this TreePanel's id.
30456     */
30457    this.id = this.el.id;
30458    this.addEvents({
30459         /**
30460         * @event beforeload
30461         * Fires before a node is loaded, return false to cancel
30462         * @param {Node} node The node being loaded
30463         */
30464         "beforeload" : true,
30465         /**
30466         * @event load
30467         * Fires when a node is loaded
30468         * @param {Node} node The node that was loaded
30469         */
30470         "load" : true,
30471         /**
30472         * @event textchange
30473         * Fires when the text for a node is changed
30474         * @param {Node} node The node
30475         * @param {String} text The new text
30476         * @param {String} oldText The old text
30477         */
30478         "textchange" : true,
30479         /**
30480         * @event beforeexpand
30481         * Fires before a node is expanded, return false to cancel.
30482         * @param {Node} node The node
30483         * @param {Boolean} deep
30484         * @param {Boolean} anim
30485         */
30486         "beforeexpand" : true,
30487         /**
30488         * @event beforecollapse
30489         * Fires before a node is collapsed, return false to cancel.
30490         * @param {Node} node The node
30491         * @param {Boolean} deep
30492         * @param {Boolean} anim
30493         */
30494         "beforecollapse" : true,
30495         /**
30496         * @event expand
30497         * Fires when a node is expanded
30498         * @param {Node} node The node
30499         */
30500         "expand" : true,
30501         /**
30502         * @event disabledchange
30503         * Fires when the disabled status of a node changes
30504         * @param {Node} node The node
30505         * @param {Boolean} disabled
30506         */
30507         "disabledchange" : true,
30508         /**
30509         * @event collapse
30510         * Fires when a node is collapsed
30511         * @param {Node} node The node
30512         */
30513         "collapse" : true,
30514         /**
30515         * @event beforeclick
30516         * Fires before click processing on a node. Return false to cancel the default action.
30517         * @param {Node} node The node
30518         * @param {Roo.EventObject} e The event object
30519         */
30520         "beforeclick":true,
30521         /**
30522         * @event checkchange
30523         * Fires when a node with a checkbox's checked property changes
30524         * @param {Node} this This node
30525         * @param {Boolean} checked
30526         */
30527         "checkchange":true,
30528         /**
30529         * @event click
30530         * Fires when a node is clicked
30531         * @param {Node} node The node
30532         * @param {Roo.EventObject} e The event object
30533         */
30534         "click":true,
30535         /**
30536         * @event dblclick
30537         * Fires when a node is double clicked
30538         * @param {Node} node The node
30539         * @param {Roo.EventObject} e The event object
30540         */
30541         "dblclick":true,
30542         /**
30543         * @event contextmenu
30544         * Fires when a node is right clicked
30545         * @param {Node} node The node
30546         * @param {Roo.EventObject} e The event object
30547         */
30548         "contextmenu":true,
30549         /**
30550         * @event beforechildrenrendered
30551         * Fires right before the child nodes for a node are rendered
30552         * @param {Node} node The node
30553         */
30554         "beforechildrenrendered":true,
30555        /**
30556              * @event startdrag
30557              * Fires when a node starts being dragged
30558              * @param {Roo.tree.TreePanel} this
30559              * @param {Roo.tree.TreeNode} node
30560              * @param {event} e The raw browser event
30561              */ 
30562             "startdrag" : true,
30563             /**
30564              * @event enddrag
30565              * Fires when a drag operation is complete
30566              * @param {Roo.tree.TreePanel} this
30567              * @param {Roo.tree.TreeNode} node
30568              * @param {event} e The raw browser event
30569              */
30570             "enddrag" : true,
30571             /**
30572              * @event dragdrop
30573              * Fires when a dragged node is dropped on a valid DD target
30574              * @param {Roo.tree.TreePanel} this
30575              * @param {Roo.tree.TreeNode} node
30576              * @param {DD} dd The dd it was dropped on
30577              * @param {event} e The raw browser event
30578              */
30579             "dragdrop" : true,
30580             /**
30581              * @event beforenodedrop
30582              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30583              * passed to handlers has the following properties:<br />
30584              * <ul style="padding:5px;padding-left:16px;">
30585              * <li>tree - The TreePanel</li>
30586              * <li>target - The node being targeted for the drop</li>
30587              * <li>data - The drag data from the drag source</li>
30588              * <li>point - The point of the drop - append, above or below</li>
30589              * <li>source - The drag source</li>
30590              * <li>rawEvent - Raw mouse event</li>
30591              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30592              * to be inserted by setting them on this object.</li>
30593              * <li>cancel - Set this to true to cancel the drop.</li>
30594              * </ul>
30595              * @param {Object} dropEvent
30596              */
30597             "beforenodedrop" : true,
30598             /**
30599              * @event nodedrop
30600              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30601              * passed to handlers has the following properties:<br />
30602              * <ul style="padding:5px;padding-left:16px;">
30603              * <li>tree - The TreePanel</li>
30604              * <li>target - The node being targeted for the drop</li>
30605              * <li>data - The drag data from the drag source</li>
30606              * <li>point - The point of the drop - append, above or below</li>
30607              * <li>source - The drag source</li>
30608              * <li>rawEvent - Raw mouse event</li>
30609              * <li>dropNode - Dropped node(s).</li>
30610              * </ul>
30611              * @param {Object} dropEvent
30612              */
30613             "nodedrop" : true,
30614              /**
30615              * @event nodedragover
30616              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30617              * passed to handlers has the following properties:<br />
30618              * <ul style="padding:5px;padding-left:16px;">
30619              * <li>tree - The TreePanel</li>
30620              * <li>target - The node being targeted for the drop</li>
30621              * <li>data - The drag data from the drag source</li>
30622              * <li>point - The point of the drop - append, above or below</li>
30623              * <li>source - The drag source</li>
30624              * <li>rawEvent - Raw mouse event</li>
30625              * <li>dropNode - Drop node(s) provided by the source.</li>
30626              * <li>cancel - Set this to true to signal drop not allowed.</li>
30627              * </ul>
30628              * @param {Object} dragOverEvent
30629              */
30630             "nodedragover" : true
30631         
30632    });
30633    if(this.singleExpand){
30634        this.on("beforeexpand", this.restrictExpand, this);
30635    }
30636 };
30637 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30638     rootVisible : true,
30639     animate: Roo.enableFx,
30640     lines : true,
30641     enableDD : false,
30642     hlDrop : Roo.enableFx,
30643   
30644     renderer: false,
30645     
30646     rendererTip: false,
30647     // private
30648     restrictExpand : function(node){
30649         var p = node.parentNode;
30650         if(p){
30651             if(p.expandedChild && p.expandedChild.parentNode == p){
30652                 p.expandedChild.collapse();
30653             }
30654             p.expandedChild = node;
30655         }
30656     },
30657
30658     // private override
30659     setRootNode : function(node){
30660         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30661         if(!this.rootVisible){
30662             node.ui = new Roo.tree.RootTreeNodeUI(node);
30663         }
30664         return node;
30665     },
30666
30667     /**
30668      * Returns the container element for this TreePanel
30669      */
30670     getEl : function(){
30671         return this.el;
30672     },
30673
30674     /**
30675      * Returns the default TreeLoader for this TreePanel
30676      */
30677     getLoader : function(){
30678         return this.loader;
30679     },
30680
30681     /**
30682      * Expand all nodes
30683      */
30684     expandAll : function(){
30685         this.root.expand(true);
30686     },
30687
30688     /**
30689      * Collapse all nodes
30690      */
30691     collapseAll : function(){
30692         this.root.collapse(true);
30693     },
30694
30695     /**
30696      * Returns the selection model used by this TreePanel
30697      */
30698     getSelectionModel : function(){
30699         if(!this.selModel){
30700             this.selModel = new Roo.tree.DefaultSelectionModel();
30701         }
30702         return this.selModel;
30703     },
30704
30705     /**
30706      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30707      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30708      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30709      * @return {Array}
30710      */
30711     getChecked : function(a, startNode){
30712         startNode = startNode || this.root;
30713         var r = [];
30714         var f = function(){
30715             if(this.attributes.checked){
30716                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30717             }
30718         }
30719         startNode.cascade(f);
30720         return r;
30721     },
30722
30723     /**
30724      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30725      * @param {String} path
30726      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30727      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30728      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30729      */
30730     expandPath : function(path, attr, callback){
30731         attr = attr || "id";
30732         var keys = path.split(this.pathSeparator);
30733         var curNode = this.root;
30734         if(curNode.attributes[attr] != keys[1]){ // invalid root
30735             if(callback){
30736                 callback(false, null);
30737             }
30738             return;
30739         }
30740         var index = 1;
30741         var f = function(){
30742             if(++index == keys.length){
30743                 if(callback){
30744                     callback(true, curNode);
30745                 }
30746                 return;
30747             }
30748             var c = curNode.findChild(attr, keys[index]);
30749             if(!c){
30750                 if(callback){
30751                     callback(false, curNode);
30752                 }
30753                 return;
30754             }
30755             curNode = c;
30756             c.expand(false, false, f);
30757         };
30758         curNode.expand(false, false, f);
30759     },
30760
30761     /**
30762      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30763      * @param {String} path
30764      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30765      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30766      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30767      */
30768     selectPath : function(path, attr, callback){
30769         attr = attr || "id";
30770         var keys = path.split(this.pathSeparator);
30771         var v = keys.pop();
30772         if(keys.length > 0){
30773             var f = function(success, node){
30774                 if(success && node){
30775                     var n = node.findChild(attr, v);
30776                     if(n){
30777                         n.select();
30778                         if(callback){
30779                             callback(true, n);
30780                         }
30781                     }else if(callback){
30782                         callback(false, n);
30783                     }
30784                 }else{
30785                     if(callback){
30786                         callback(false, n);
30787                     }
30788                 }
30789             };
30790             this.expandPath(keys.join(this.pathSeparator), attr, f);
30791         }else{
30792             this.root.select();
30793             if(callback){
30794                 callback(true, this.root);
30795             }
30796         }
30797     },
30798
30799     getTreeEl : function(){
30800         return this.el;
30801     },
30802
30803     /**
30804      * Trigger rendering of this TreePanel
30805      */
30806     render : function(){
30807         if (this.innerCt) {
30808             return this; // stop it rendering more than once!!
30809         }
30810         
30811         this.innerCt = this.el.createChild({tag:"ul",
30812                cls:"x-tree-root-ct " +
30813                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30814
30815         if(this.containerScroll){
30816             Roo.dd.ScrollManager.register(this.el);
30817         }
30818         if((this.enableDD || this.enableDrop) && !this.dropZone){
30819            /**
30820             * The dropZone used by this tree if drop is enabled
30821             * @type Roo.tree.TreeDropZone
30822             */
30823              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30824                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30825            });
30826         }
30827         if((this.enableDD || this.enableDrag) && !this.dragZone){
30828            /**
30829             * The dragZone used by this tree if drag is enabled
30830             * @type Roo.tree.TreeDragZone
30831             */
30832             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30833                ddGroup: this.ddGroup || "TreeDD",
30834                scroll: this.ddScroll
30835            });
30836         }
30837         this.getSelectionModel().init(this);
30838         if (!this.root) {
30839             console.log("ROOT not set in tree");
30840             return;
30841         }
30842         this.root.render();
30843         if(!this.rootVisible){
30844             this.root.renderChildren();
30845         }
30846         return this;
30847     }
30848 });/*
30849  * Based on:
30850  * Ext JS Library 1.1.1
30851  * Copyright(c) 2006-2007, Ext JS, LLC.
30852  *
30853  * Originally Released Under LGPL - original licence link has changed is not relivant.
30854  *
30855  * Fork - LGPL
30856  * <script type="text/javascript">
30857  */
30858  
30859
30860 /**
30861  * @class Roo.tree.DefaultSelectionModel
30862  * @extends Roo.util.Observable
30863  * The default single selection for a TreePanel.
30864  */
30865 Roo.tree.DefaultSelectionModel = function(){
30866    this.selNode = null;
30867    
30868    this.addEvents({
30869        /**
30870         * @event selectionchange
30871         * Fires when the selected node changes
30872         * @param {DefaultSelectionModel} this
30873         * @param {TreeNode} node the new selection
30874         */
30875        "selectionchange" : true,
30876
30877        /**
30878         * @event beforeselect
30879         * Fires before the selected node changes, return false to cancel the change
30880         * @param {DefaultSelectionModel} this
30881         * @param {TreeNode} node the new selection
30882         * @param {TreeNode} node the old selection
30883         */
30884        "beforeselect" : true
30885    });
30886 };
30887
30888 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30889     init : function(tree){
30890         this.tree = tree;
30891         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30892         tree.on("click", this.onNodeClick, this);
30893     },
30894     
30895     onNodeClick : function(node, e){
30896         if (e.ctrlKey && this.selNode == node)  {
30897             this.unselect(node);
30898             return;
30899         }
30900         this.select(node);
30901     },
30902     
30903     /**
30904      * Select a node.
30905      * @param {TreeNode} node The node to select
30906      * @return {TreeNode} The selected node
30907      */
30908     select : function(node){
30909         var last = this.selNode;
30910         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30911             if(last){
30912                 last.ui.onSelectedChange(false);
30913             }
30914             this.selNode = node;
30915             node.ui.onSelectedChange(true);
30916             this.fireEvent("selectionchange", this, node, last);
30917         }
30918         return node;
30919     },
30920     
30921     /**
30922      * Deselect a node.
30923      * @param {TreeNode} node The node to unselect
30924      */
30925     unselect : function(node){
30926         if(this.selNode == node){
30927             this.clearSelections();
30928         }    
30929     },
30930     
30931     /**
30932      * Clear all selections
30933      */
30934     clearSelections : function(){
30935         var n = this.selNode;
30936         if(n){
30937             n.ui.onSelectedChange(false);
30938             this.selNode = null;
30939             this.fireEvent("selectionchange", this, null);
30940         }
30941         return n;
30942     },
30943     
30944     /**
30945      * Get the selected node
30946      * @return {TreeNode} The selected node
30947      */
30948     getSelectedNode : function(){
30949         return this.selNode;    
30950     },
30951     
30952     /**
30953      * Returns true if the node is selected
30954      * @param {TreeNode} node The node to check
30955      * @return {Boolean}
30956      */
30957     isSelected : function(node){
30958         return this.selNode == node;  
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     selectPrevious : function(){
30966         var s = this.selNode || this.lastSelNode;
30967         if(!s){
30968             return null;
30969         }
30970         var ps = s.previousSibling;
30971         if(ps){
30972             if(!ps.isExpanded() || ps.childNodes.length < 1){
30973                 return this.select(ps);
30974             } else{
30975                 var lc = ps.lastChild;
30976                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30977                     lc = lc.lastChild;
30978                 }
30979                 return this.select(lc);
30980             }
30981         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30982             return this.select(s.parentNode);
30983         }
30984         return null;
30985     },
30986
30987     /**
30988      * Selects the node above the selected node in the tree, intelligently walking the nodes
30989      * @return TreeNode The new selection
30990      */
30991     selectNext : function(){
30992         var s = this.selNode || this.lastSelNode;
30993         if(!s){
30994             return null;
30995         }
30996         if(s.firstChild && s.isExpanded()){
30997              return this.select(s.firstChild);
30998          }else if(s.nextSibling){
30999              return this.select(s.nextSibling);
31000          }else if(s.parentNode){
31001             var newS = null;
31002             s.parentNode.bubble(function(){
31003                 if(this.nextSibling){
31004                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31005                     return false;
31006                 }
31007             });
31008             return newS;
31009          }
31010         return null;
31011     },
31012
31013     onKeyDown : function(e){
31014         var s = this.selNode || this.lastSelNode;
31015         // undesirable, but required
31016         var sm = this;
31017         if(!s){
31018             return;
31019         }
31020         var k = e.getKey();
31021         switch(k){
31022              case e.DOWN:
31023                  e.stopEvent();
31024                  this.selectNext();
31025              break;
31026              case e.UP:
31027                  e.stopEvent();
31028                  this.selectPrevious();
31029              break;
31030              case e.RIGHT:
31031                  e.preventDefault();
31032                  if(s.hasChildNodes()){
31033                      if(!s.isExpanded()){
31034                          s.expand();
31035                      }else if(s.firstChild){
31036                          this.select(s.firstChild, e);
31037                      }
31038                  }
31039              break;
31040              case e.LEFT:
31041                  e.preventDefault();
31042                  if(s.hasChildNodes() && s.isExpanded()){
31043                      s.collapse();
31044                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31045                      this.select(s.parentNode, e);
31046                  }
31047              break;
31048         };
31049     }
31050 });
31051
31052 /**
31053  * @class Roo.tree.MultiSelectionModel
31054  * @extends Roo.util.Observable
31055  * Multi selection for a TreePanel.
31056  */
31057 Roo.tree.MultiSelectionModel = function(){
31058    this.selNodes = [];
31059    this.selMap = {};
31060    this.addEvents({
31061        /**
31062         * @event selectionchange
31063         * Fires when the selected nodes change
31064         * @param {MultiSelectionModel} this
31065         * @param {Array} nodes Array of the selected nodes
31066         */
31067        "selectionchange" : true
31068    });
31069 };
31070
31071 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31072     init : function(tree){
31073         this.tree = tree;
31074         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31075         tree.on("click", this.onNodeClick, this);
31076     },
31077     
31078     onNodeClick : function(node, e){
31079         this.select(node, e, e.ctrlKey);
31080     },
31081     
31082     /**
31083      * Select a node.
31084      * @param {TreeNode} node The node to select
31085      * @param {EventObject} e (optional) An event associated with the selection
31086      * @param {Boolean} keepExisting True to retain existing selections
31087      * @return {TreeNode} The selected node
31088      */
31089     select : function(node, e, keepExisting){
31090         if(keepExisting !== true){
31091             this.clearSelections(true);
31092         }
31093         if(this.isSelected(node)){
31094             this.lastSelNode = node;
31095             return node;
31096         }
31097         this.selNodes.push(node);
31098         this.selMap[node.id] = node;
31099         this.lastSelNode = node;
31100         node.ui.onSelectedChange(true);
31101         this.fireEvent("selectionchange", this, this.selNodes);
31102         return node;
31103     },
31104     
31105     /**
31106      * Deselect a node.
31107      * @param {TreeNode} node The node to unselect
31108      */
31109     unselect : function(node){
31110         if(this.selMap[node.id]){
31111             node.ui.onSelectedChange(false);
31112             var sn = this.selNodes;
31113             var index = -1;
31114             if(sn.indexOf){
31115                 index = sn.indexOf(node);
31116             }else{
31117                 for(var i = 0, len = sn.length; i < len; i++){
31118                     if(sn[i] == node){
31119                         index = i;
31120                         break;
31121                     }
31122                 }
31123             }
31124             if(index != -1){
31125                 this.selNodes.splice(index, 1);
31126             }
31127             delete this.selMap[node.id];
31128             this.fireEvent("selectionchange", this, this.selNodes);
31129         }
31130     },
31131     
31132     /**
31133      * Clear all selections
31134      */
31135     clearSelections : function(suppressEvent){
31136         var sn = this.selNodes;
31137         if(sn.length > 0){
31138             for(var i = 0, len = sn.length; i < len; i++){
31139                 sn[i].ui.onSelectedChange(false);
31140             }
31141             this.selNodes = [];
31142             this.selMap = {};
31143             if(suppressEvent !== true){
31144                 this.fireEvent("selectionchange", this, this.selNodes);
31145             }
31146         }
31147     },
31148     
31149     /**
31150      * Returns true if the node is selected
31151      * @param {TreeNode} node The node to check
31152      * @return {Boolean}
31153      */
31154     isSelected : function(node){
31155         return this.selMap[node.id] ? true : false;  
31156     },
31157     
31158     /**
31159      * Returns an array of the selected nodes
31160      * @return {Array}
31161      */
31162     getSelectedNodes : function(){
31163         return this.selNodes;    
31164     },
31165
31166     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31167
31168     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31169
31170     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31171 });/*
31172  * Based on:
31173  * Ext JS Library 1.1.1
31174  * Copyright(c) 2006-2007, Ext JS, LLC.
31175  *
31176  * Originally Released Under LGPL - original licence link has changed is not relivant.
31177  *
31178  * Fork - LGPL
31179  * <script type="text/javascript">
31180  */
31181  
31182 /**
31183  * @class Roo.tree.TreeNode
31184  * @extends Roo.data.Node
31185  * @cfg {String} text The text for this node
31186  * @cfg {Boolean} expanded true to start the node expanded
31187  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31188  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31189  * @cfg {Boolean} disabled true to start the node disabled
31190  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31191  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31192  * @cfg {String} cls A css class to be added to the node
31193  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31194  * @cfg {String} href URL of the link used for the node (defaults to #)
31195  * @cfg {String} hrefTarget target frame for the link
31196  * @cfg {String} qtip An Ext QuickTip for the node
31197  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31198  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31199  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31200  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31201  * (defaults to undefined with no checkbox rendered)
31202  * @constructor
31203  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31204  */
31205 Roo.tree.TreeNode = function(attributes){
31206     attributes = attributes || {};
31207     if(typeof attributes == "string"){
31208         attributes = {text: attributes};
31209     }
31210     this.childrenRendered = false;
31211     this.rendered = false;
31212     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31213     this.expanded = attributes.expanded === true;
31214     this.isTarget = attributes.isTarget !== false;
31215     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31216     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31217
31218     /**
31219      * Read-only. The text for this node. To change it use setText().
31220      * @type String
31221      */
31222     this.text = attributes.text;
31223     /**
31224      * True if this node is disabled.
31225      * @type Boolean
31226      */
31227     this.disabled = attributes.disabled === true;
31228
31229     this.addEvents({
31230         /**
31231         * @event textchange
31232         * Fires when the text for this node is changed
31233         * @param {Node} this This node
31234         * @param {String} text The new text
31235         * @param {String} oldText The old text
31236         */
31237         "textchange" : true,
31238         /**
31239         * @event beforeexpand
31240         * Fires before this node is expanded, return false to cancel.
31241         * @param {Node} this This node
31242         * @param {Boolean} deep
31243         * @param {Boolean} anim
31244         */
31245         "beforeexpand" : true,
31246         /**
31247         * @event beforecollapse
31248         * Fires before this node is collapsed, return false to cancel.
31249         * @param {Node} this This node
31250         * @param {Boolean} deep
31251         * @param {Boolean} anim
31252         */
31253         "beforecollapse" : true,
31254         /**
31255         * @event expand
31256         * Fires when this node is expanded
31257         * @param {Node} this This node
31258         */
31259         "expand" : true,
31260         /**
31261         * @event disabledchange
31262         * Fires when the disabled status of this node changes
31263         * @param {Node} this This node
31264         * @param {Boolean} disabled
31265         */
31266         "disabledchange" : true,
31267         /**
31268         * @event collapse
31269         * Fires when this node is collapsed
31270         * @param {Node} this This node
31271         */
31272         "collapse" : true,
31273         /**
31274         * @event beforeclick
31275         * Fires before click processing. Return false to cancel the default action.
31276         * @param {Node} this This node
31277         * @param {Roo.EventObject} e The event object
31278         */
31279         "beforeclick":true,
31280         /**
31281         * @event checkchange
31282         * Fires when a node with a checkbox's checked property changes
31283         * @param {Node} this This node
31284         * @param {Boolean} checked
31285         */
31286         "checkchange":true,
31287         /**
31288         * @event click
31289         * Fires when this node is clicked
31290         * @param {Node} this This node
31291         * @param {Roo.EventObject} e The event object
31292         */
31293         "click":true,
31294         /**
31295         * @event dblclick
31296         * Fires when this node is double clicked
31297         * @param {Node} this This node
31298         * @param {Roo.EventObject} e The event object
31299         */
31300         "dblclick":true,
31301         /**
31302         * @event contextmenu
31303         * Fires when this node is right clicked
31304         * @param {Node} this This node
31305         * @param {Roo.EventObject} e The event object
31306         */
31307         "contextmenu":true,
31308         /**
31309         * @event beforechildrenrendered
31310         * Fires right before the child nodes for this node are rendered
31311         * @param {Node} this This node
31312         */
31313         "beforechildrenrendered":true
31314     });
31315
31316     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31317
31318     /**
31319      * Read-only. The UI for this node
31320      * @type TreeNodeUI
31321      */
31322     this.ui = new uiClass(this);
31323 };
31324 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31325     preventHScroll: true,
31326     /**
31327      * Returns true if this node is expanded
31328      * @return {Boolean}
31329      */
31330     isExpanded : function(){
31331         return this.expanded;
31332     },
31333
31334     /**
31335      * Returns the UI object for this node
31336      * @return {TreeNodeUI}
31337      */
31338     getUI : function(){
31339         return this.ui;
31340     },
31341
31342     // private override
31343     setFirstChild : function(node){
31344         var of = this.firstChild;
31345         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31346         if(this.childrenRendered && of && node != of){
31347             of.renderIndent(true, true);
31348         }
31349         if(this.rendered){
31350             this.renderIndent(true, true);
31351         }
31352     },
31353
31354     // private override
31355     setLastChild : function(node){
31356         var ol = this.lastChild;
31357         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31358         if(this.childrenRendered && ol && node != ol){
31359             ol.renderIndent(true, true);
31360         }
31361         if(this.rendered){
31362             this.renderIndent(true, true);
31363         }
31364     },
31365
31366     // these methods are overridden to provide lazy rendering support
31367     // private override
31368     appendChild : function(){
31369         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31370         if(node && this.childrenRendered){
31371             node.render();
31372         }
31373         this.ui.updateExpandIcon();
31374         return node;
31375     },
31376
31377     // private override
31378     removeChild : function(node){
31379         this.ownerTree.getSelectionModel().unselect(node);
31380         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31381         // if it's been rendered remove dom node
31382         if(this.childrenRendered){
31383             node.ui.remove();
31384         }
31385         if(this.childNodes.length < 1){
31386             this.collapse(false, false);
31387         }else{
31388             this.ui.updateExpandIcon();
31389         }
31390         if(!this.firstChild) {
31391             this.childrenRendered = false;
31392         }
31393         return node;
31394     },
31395
31396     // private override
31397     insertBefore : function(node, refNode){
31398         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31399         if(newNode && refNode && this.childrenRendered){
31400             node.render();
31401         }
31402         this.ui.updateExpandIcon();
31403         return newNode;
31404     },
31405
31406     /**
31407      * Sets the text for this node
31408      * @param {String} text
31409      */
31410     setText : function(text){
31411         var oldText = this.text;
31412         this.text = text;
31413         this.attributes.text = text;
31414         if(this.rendered){ // event without subscribing
31415             this.ui.onTextChange(this, text, oldText);
31416         }
31417         this.fireEvent("textchange", this, text, oldText);
31418     },
31419
31420     /**
31421      * Triggers selection of this node
31422      */
31423     select : function(){
31424         this.getOwnerTree().getSelectionModel().select(this);
31425     },
31426
31427     /**
31428      * Triggers deselection of this node
31429      */
31430     unselect : function(){
31431         this.getOwnerTree().getSelectionModel().unselect(this);
31432     },
31433
31434     /**
31435      * Returns true if this node is selected
31436      * @return {Boolean}
31437      */
31438     isSelected : function(){
31439         return this.getOwnerTree().getSelectionModel().isSelected(this);
31440     },
31441
31442     /**
31443      * Expand this node.
31444      * @param {Boolean} deep (optional) True to expand all children as well
31445      * @param {Boolean} anim (optional) false to cancel the default animation
31446      * @param {Function} callback (optional) A callback to be called when
31447      * expanding this node completes (does not wait for deep expand to complete).
31448      * Called with 1 parameter, this node.
31449      */
31450     expand : function(deep, anim, callback){
31451         if(!this.expanded){
31452             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31453                 return;
31454             }
31455             if(!this.childrenRendered){
31456                 this.renderChildren();
31457             }
31458             this.expanded = true;
31459             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31460                 this.ui.animExpand(function(){
31461                     this.fireEvent("expand", this);
31462                     if(typeof callback == "function"){
31463                         callback(this);
31464                     }
31465                     if(deep === true){
31466                         this.expandChildNodes(true);
31467                     }
31468                 }.createDelegate(this));
31469                 return;
31470             }else{
31471                 this.ui.expand();
31472                 this.fireEvent("expand", this);
31473                 if(typeof callback == "function"){
31474                     callback(this);
31475                 }
31476             }
31477         }else{
31478            if(typeof callback == "function"){
31479                callback(this);
31480            }
31481         }
31482         if(deep === true){
31483             this.expandChildNodes(true);
31484         }
31485     },
31486
31487     isHiddenRoot : function(){
31488         return this.isRoot && !this.getOwnerTree().rootVisible;
31489     },
31490
31491     /**
31492      * Collapse this node.
31493      * @param {Boolean} deep (optional) True to collapse all children as well
31494      * @param {Boolean} anim (optional) false to cancel the default animation
31495      */
31496     collapse : function(deep, anim){
31497         if(this.expanded && !this.isHiddenRoot()){
31498             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31499                 return;
31500             }
31501             this.expanded = false;
31502             if((this.getOwnerTree().animate && anim !== false) || anim){
31503                 this.ui.animCollapse(function(){
31504                     this.fireEvent("collapse", this);
31505                     if(deep === true){
31506                         this.collapseChildNodes(true);
31507                     }
31508                 }.createDelegate(this));
31509                 return;
31510             }else{
31511                 this.ui.collapse();
31512                 this.fireEvent("collapse", this);
31513             }
31514         }
31515         if(deep === true){
31516             var cs = this.childNodes;
31517             for(var i = 0, len = cs.length; i < len; i++) {
31518                 cs[i].collapse(true, false);
31519             }
31520         }
31521     },
31522
31523     // private
31524     delayedExpand : function(delay){
31525         if(!this.expandProcId){
31526             this.expandProcId = this.expand.defer(delay, this);
31527         }
31528     },
31529
31530     // private
31531     cancelExpand : function(){
31532         if(this.expandProcId){
31533             clearTimeout(this.expandProcId);
31534         }
31535         this.expandProcId = false;
31536     },
31537
31538     /**
31539      * Toggles expanded/collapsed state of the node
31540      */
31541     toggle : function(){
31542         if(this.expanded){
31543             this.collapse();
31544         }else{
31545             this.expand();
31546         }
31547     },
31548
31549     /**
31550      * Ensures all parent nodes are expanded
31551      */
31552     ensureVisible : function(callback){
31553         var tree = this.getOwnerTree();
31554         tree.expandPath(this.parentNode.getPath(), false, function(){
31555             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31556             Roo.callback(callback);
31557         }.createDelegate(this));
31558     },
31559
31560     /**
31561      * Expand all child nodes
31562      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31563      */
31564     expandChildNodes : function(deep){
31565         var cs = this.childNodes;
31566         for(var i = 0, len = cs.length; i < len; i++) {
31567                 cs[i].expand(deep);
31568         }
31569     },
31570
31571     /**
31572      * Collapse all child nodes
31573      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31574      */
31575     collapseChildNodes : function(deep){
31576         var cs = this.childNodes;
31577         for(var i = 0, len = cs.length; i < len; i++) {
31578                 cs[i].collapse(deep);
31579         }
31580     },
31581
31582     /**
31583      * Disables this node
31584      */
31585     disable : function(){
31586         this.disabled = true;
31587         this.unselect();
31588         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31589             this.ui.onDisableChange(this, true);
31590         }
31591         this.fireEvent("disabledchange", this, true);
31592     },
31593
31594     /**
31595      * Enables this node
31596      */
31597     enable : function(){
31598         this.disabled = false;
31599         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31600             this.ui.onDisableChange(this, false);
31601         }
31602         this.fireEvent("disabledchange", this, false);
31603     },
31604
31605     // private
31606     renderChildren : function(suppressEvent){
31607         if(suppressEvent !== false){
31608             this.fireEvent("beforechildrenrendered", this);
31609         }
31610         var cs = this.childNodes;
31611         for(var i = 0, len = cs.length; i < len; i++){
31612             cs[i].render(true);
31613         }
31614         this.childrenRendered = true;
31615     },
31616
31617     // private
31618     sort : function(fn, scope){
31619         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31620         if(this.childrenRendered){
31621             var cs = this.childNodes;
31622             for(var i = 0, len = cs.length; i < len; i++){
31623                 cs[i].render(true);
31624             }
31625         }
31626     },
31627
31628     // private
31629     render : function(bulkRender){
31630         this.ui.render(bulkRender);
31631         if(!this.rendered){
31632             this.rendered = true;
31633             if(this.expanded){
31634                 this.expanded = false;
31635                 this.expand(false, false);
31636             }
31637         }
31638     },
31639
31640     // private
31641     renderIndent : function(deep, refresh){
31642         if(refresh){
31643             this.ui.childIndent = null;
31644         }
31645         this.ui.renderIndent();
31646         if(deep === true && this.childrenRendered){
31647             var cs = this.childNodes;
31648             for(var i = 0, len = cs.length; i < len; i++){
31649                 cs[i].renderIndent(true, refresh);
31650             }
31651         }
31652     }
31653 });/*
31654  * Based on:
31655  * Ext JS Library 1.1.1
31656  * Copyright(c) 2006-2007, Ext JS, LLC.
31657  *
31658  * Originally Released Under LGPL - original licence link has changed is not relivant.
31659  *
31660  * Fork - LGPL
31661  * <script type="text/javascript">
31662  */
31663  
31664 /**
31665  * @class Roo.tree.AsyncTreeNode
31666  * @extends Roo.tree.TreeNode
31667  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31668  * @constructor
31669  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31670  */
31671  Roo.tree.AsyncTreeNode = function(config){
31672     this.loaded = false;
31673     this.loading = false;
31674     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31675     /**
31676     * @event beforeload
31677     * Fires before this node is loaded, return false to cancel
31678     * @param {Node} this This node
31679     */
31680     this.addEvents({'beforeload':true, 'load': true});
31681     /**
31682     * @event load
31683     * Fires when this node is loaded
31684     * @param {Node} this This node
31685     */
31686     /**
31687      * The loader used by this node (defaults to using the tree's defined loader)
31688      * @type TreeLoader
31689      * @property loader
31690      */
31691 };
31692 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31693     expand : function(deep, anim, callback){
31694         if(this.loading){ // if an async load is already running, waiting til it's done
31695             var timer;
31696             var f = function(){
31697                 if(!this.loading){ // done loading
31698                     clearInterval(timer);
31699                     this.expand(deep, anim, callback);
31700                 }
31701             }.createDelegate(this);
31702             timer = setInterval(f, 200);
31703             return;
31704         }
31705         if(!this.loaded){
31706             if(this.fireEvent("beforeload", this) === false){
31707                 return;
31708             }
31709             this.loading = true;
31710             this.ui.beforeLoad(this);
31711             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31712             if(loader){
31713                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31714                 return;
31715             }
31716         }
31717         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31718     },
31719     
31720     /**
31721      * Returns true if this node is currently loading
31722      * @return {Boolean}
31723      */
31724     isLoading : function(){
31725         return this.loading;  
31726     },
31727     
31728     loadComplete : function(deep, anim, callback){
31729         this.loading = false;
31730         this.loaded = true;
31731         this.ui.afterLoad(this);
31732         this.fireEvent("load", this);
31733         this.expand(deep, anim, callback);
31734     },
31735     
31736     /**
31737      * Returns true if this node has been loaded
31738      * @return {Boolean}
31739      */
31740     isLoaded : function(){
31741         return this.loaded;
31742     },
31743     
31744     hasChildNodes : function(){
31745         if(!this.isLeaf() && !this.loaded){
31746             return true;
31747         }else{
31748             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31749         }
31750     },
31751
31752     /**
31753      * Trigger a reload for this node
31754      * @param {Function} callback
31755      */
31756     reload : function(callback){
31757         this.collapse(false, false);
31758         while(this.firstChild){
31759             this.removeChild(this.firstChild);
31760         }
31761         this.childrenRendered = false;
31762         this.loaded = false;
31763         if(this.isHiddenRoot()){
31764             this.expanded = false;
31765         }
31766         this.expand(false, false, callback);
31767     }
31768 });/*
31769  * Based on:
31770  * Ext JS Library 1.1.1
31771  * Copyright(c) 2006-2007, Ext JS, LLC.
31772  *
31773  * Originally Released Under LGPL - original licence link has changed is not relivant.
31774  *
31775  * Fork - LGPL
31776  * <script type="text/javascript">
31777  */
31778  
31779 /**
31780  * @class Roo.tree.TreeNodeUI
31781  * @constructor
31782  * @param {Object} node The node to render
31783  * The TreeNode UI implementation is separate from the
31784  * tree implementation. Unless you are customizing the tree UI,
31785  * you should never have to use this directly.
31786  */
31787 Roo.tree.TreeNodeUI = function(node){
31788     this.node = node;
31789     this.rendered = false;
31790     this.animating = false;
31791     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31792 };
31793
31794 Roo.tree.TreeNodeUI.prototype = {
31795     removeChild : function(node){
31796         if(this.rendered){
31797             this.ctNode.removeChild(node.ui.getEl());
31798         }
31799     },
31800
31801     beforeLoad : function(){
31802          this.addClass("x-tree-node-loading");
31803     },
31804
31805     afterLoad : function(){
31806          this.removeClass("x-tree-node-loading");
31807     },
31808
31809     onTextChange : function(node, text, oldText){
31810         if(this.rendered){
31811             this.textNode.innerHTML = text;
31812         }
31813     },
31814
31815     onDisableChange : function(node, state){
31816         this.disabled = state;
31817         if(state){
31818             this.addClass("x-tree-node-disabled");
31819         }else{
31820             this.removeClass("x-tree-node-disabled");
31821         }
31822     },
31823
31824     onSelectedChange : function(state){
31825         if(state){
31826             this.focus();
31827             this.addClass("x-tree-selected");
31828         }else{
31829             //this.blur();
31830             this.removeClass("x-tree-selected");
31831         }
31832     },
31833
31834     onMove : function(tree, node, oldParent, newParent, index, refNode){
31835         this.childIndent = null;
31836         if(this.rendered){
31837             var targetNode = newParent.ui.getContainer();
31838             if(!targetNode){//target not rendered
31839                 this.holder = document.createElement("div");
31840                 this.holder.appendChild(this.wrap);
31841                 return;
31842             }
31843             var insertBefore = refNode ? refNode.ui.getEl() : null;
31844             if(insertBefore){
31845                 targetNode.insertBefore(this.wrap, insertBefore);
31846             }else{
31847                 targetNode.appendChild(this.wrap);
31848             }
31849             this.node.renderIndent(true);
31850         }
31851     },
31852
31853     addClass : function(cls){
31854         if(this.elNode){
31855             Roo.fly(this.elNode).addClass(cls);
31856         }
31857     },
31858
31859     removeClass : function(cls){
31860         if(this.elNode){
31861             Roo.fly(this.elNode).removeClass(cls);
31862         }
31863     },
31864
31865     remove : function(){
31866         if(this.rendered){
31867             this.holder = document.createElement("div");
31868             this.holder.appendChild(this.wrap);
31869         }
31870     },
31871
31872     fireEvent : function(){
31873         return this.node.fireEvent.apply(this.node, arguments);
31874     },
31875
31876     initEvents : function(){
31877         this.node.on("move", this.onMove, this);
31878         var E = Roo.EventManager;
31879         var a = this.anchor;
31880
31881         var el = Roo.fly(a, '_treeui');
31882
31883         if(Roo.isOpera){ // opera render bug ignores the CSS
31884             el.setStyle("text-decoration", "none");
31885         }
31886
31887         el.on("click", this.onClick, this);
31888         el.on("dblclick", this.onDblClick, this);
31889
31890         if(this.checkbox){
31891             Roo.EventManager.on(this.checkbox,
31892                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31893         }
31894
31895         el.on("contextmenu", this.onContextMenu, this);
31896
31897         var icon = Roo.fly(this.iconNode);
31898         icon.on("click", this.onClick, this);
31899         icon.on("dblclick", this.onDblClick, this);
31900         icon.on("contextmenu", this.onContextMenu, this);
31901         E.on(this.ecNode, "click", this.ecClick, this, true);
31902
31903         if(this.node.disabled){
31904             this.addClass("x-tree-node-disabled");
31905         }
31906         if(this.node.hidden){
31907             this.addClass("x-tree-node-disabled");
31908         }
31909         var ot = this.node.getOwnerTree();
31910         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31911         if(dd && (!this.node.isRoot || ot.rootVisible)){
31912             Roo.dd.Registry.register(this.elNode, {
31913                 node: this.node,
31914                 handles: this.getDDHandles(),
31915                 isHandle: false
31916             });
31917         }
31918     },
31919
31920     getDDHandles : function(){
31921         return [this.iconNode, this.textNode];
31922     },
31923
31924     hide : function(){
31925         if(this.rendered){
31926             this.wrap.style.display = "none";
31927         }
31928     },
31929
31930     show : function(){
31931         if(this.rendered){
31932             this.wrap.style.display = "";
31933         }
31934     },
31935
31936     onContextMenu : function(e){
31937         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31938             e.preventDefault();
31939             this.focus();
31940             this.fireEvent("contextmenu", this.node, e);
31941         }
31942     },
31943
31944     onClick : function(e){
31945         if(this.dropping){
31946             e.stopEvent();
31947             return;
31948         }
31949         if(this.fireEvent("beforeclick", this.node, e) !== false){
31950             if(!this.disabled && this.node.attributes.href){
31951                 this.fireEvent("click", this.node, e);
31952                 return;
31953             }
31954             e.preventDefault();
31955             if(this.disabled){
31956                 return;
31957             }
31958
31959             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31960                 this.node.toggle();
31961             }
31962
31963             this.fireEvent("click", this.node, e);
31964         }else{
31965             e.stopEvent();
31966         }
31967     },
31968
31969     onDblClick : function(e){
31970         e.preventDefault();
31971         if(this.disabled){
31972             return;
31973         }
31974         if(this.checkbox){
31975             this.toggleCheck();
31976         }
31977         if(!this.animating && this.node.hasChildNodes()){
31978             this.node.toggle();
31979         }
31980         this.fireEvent("dblclick", this.node, e);
31981     },
31982
31983     onCheckChange : function(){
31984         var checked = this.checkbox.checked;
31985         this.node.attributes.checked = checked;
31986         this.fireEvent('checkchange', this.node, checked);
31987     },
31988
31989     ecClick : function(e){
31990         if(!this.animating && this.node.hasChildNodes()){
31991             this.node.toggle();
31992         }
31993     },
31994
31995     startDrop : function(){
31996         this.dropping = true;
31997     },
31998
31999     // delayed drop so the click event doesn't get fired on a drop
32000     endDrop : function(){
32001        setTimeout(function(){
32002            this.dropping = false;
32003        }.createDelegate(this), 50);
32004     },
32005
32006     expand : function(){
32007         this.updateExpandIcon();
32008         this.ctNode.style.display = "";
32009     },
32010
32011     focus : function(){
32012         if(!this.node.preventHScroll){
32013             try{this.anchor.focus();
32014             }catch(e){}
32015         }else if(!Roo.isIE){
32016             try{
32017                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32018                 var l = noscroll.scrollLeft;
32019                 this.anchor.focus();
32020                 noscroll.scrollLeft = l;
32021             }catch(e){}
32022         }
32023     },
32024
32025     toggleCheck : function(value){
32026         var cb = this.checkbox;
32027         if(cb){
32028             cb.checked = (value === undefined ? !cb.checked : value);
32029         }
32030     },
32031
32032     blur : function(){
32033         try{
32034             this.anchor.blur();
32035         }catch(e){}
32036     },
32037
32038     animExpand : function(callback){
32039         var ct = Roo.get(this.ctNode);
32040         ct.stopFx();
32041         if(!this.node.hasChildNodes()){
32042             this.updateExpandIcon();
32043             this.ctNode.style.display = "";
32044             Roo.callback(callback);
32045             return;
32046         }
32047         this.animating = true;
32048         this.updateExpandIcon();
32049
32050         ct.slideIn('t', {
32051            callback : function(){
32052                this.animating = false;
32053                Roo.callback(callback);
32054             },
32055             scope: this,
32056             duration: this.node.ownerTree.duration || .25
32057         });
32058     },
32059
32060     highlight : function(){
32061         var tree = this.node.getOwnerTree();
32062         Roo.fly(this.wrap).highlight(
32063             tree.hlColor || "C3DAF9",
32064             {endColor: tree.hlBaseColor}
32065         );
32066     },
32067
32068     collapse : function(){
32069         this.updateExpandIcon();
32070         this.ctNode.style.display = "none";
32071     },
32072
32073     animCollapse : function(callback){
32074         var ct = Roo.get(this.ctNode);
32075         ct.enableDisplayMode('block');
32076         ct.stopFx();
32077
32078         this.animating = true;
32079         this.updateExpandIcon();
32080
32081         ct.slideOut('t', {
32082             callback : function(){
32083                this.animating = false;
32084                Roo.callback(callback);
32085             },
32086             scope: this,
32087             duration: this.node.ownerTree.duration || .25
32088         });
32089     },
32090
32091     getContainer : function(){
32092         return this.ctNode;
32093     },
32094
32095     getEl : function(){
32096         return this.wrap;
32097     },
32098
32099     appendDDGhost : function(ghostNode){
32100         ghostNode.appendChild(this.elNode.cloneNode(true));
32101     },
32102
32103     getDDRepairXY : function(){
32104         return Roo.lib.Dom.getXY(this.iconNode);
32105     },
32106
32107     onRender : function(){
32108         this.render();
32109     },
32110
32111     render : function(bulkRender){
32112         var n = this.node, a = n.attributes;
32113         var targetNode = n.parentNode ?
32114               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32115
32116         if(!this.rendered){
32117             this.rendered = true;
32118
32119             this.renderElements(n, a, targetNode, bulkRender);
32120
32121             if(a.qtip){
32122                if(this.textNode.setAttributeNS){
32123                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32124                    if(a.qtipTitle){
32125                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32126                    }
32127                }else{
32128                    this.textNode.setAttribute("ext:qtip", a.qtip);
32129                    if(a.qtipTitle){
32130                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32131                    }
32132                }
32133             }else if(a.qtipCfg){
32134                 a.qtipCfg.target = Roo.id(this.textNode);
32135                 Roo.QuickTips.register(a.qtipCfg);
32136             }
32137             this.initEvents();
32138             if(!this.node.expanded){
32139                 this.updateExpandIcon();
32140             }
32141         }else{
32142             if(bulkRender === true) {
32143                 targetNode.appendChild(this.wrap);
32144             }
32145         }
32146     },
32147
32148     renderElements : function(n, a, targetNode, bulkRender){
32149         // add some indent caching, this helps performance when rendering a large tree
32150         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32151         var t = n.getOwnerTree();
32152         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32153         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32154         var cb = typeof a.checked == 'boolean';
32155         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32156         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32157             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32158             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32159             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32160             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32161             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32162              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32163                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32164             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32165             "</li>"];
32166
32167         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32168             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32169                                 n.nextSibling.ui.getEl(), buf.join(""));
32170         }else{
32171             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32172         }
32173
32174         this.elNode = this.wrap.childNodes[0];
32175         this.ctNode = this.wrap.childNodes[1];
32176         var cs = this.elNode.childNodes;
32177         this.indentNode = cs[0];
32178         this.ecNode = cs[1];
32179         this.iconNode = cs[2];
32180         var index = 3;
32181         if(cb){
32182             this.checkbox = cs[3];
32183             index++;
32184         }
32185         this.anchor = cs[index];
32186         this.textNode = cs[index].firstChild;
32187     },
32188
32189     getAnchor : function(){
32190         return this.anchor;
32191     },
32192
32193     getTextEl : function(){
32194         return this.textNode;
32195     },
32196
32197     getIconEl : function(){
32198         return this.iconNode;
32199     },
32200
32201     isChecked : function(){
32202         return this.checkbox ? this.checkbox.checked : false;
32203     },
32204
32205     updateExpandIcon : function(){
32206         if(this.rendered){
32207             var n = this.node, c1, c2;
32208             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32209             var hasChild = n.hasChildNodes();
32210             if(hasChild){
32211                 if(n.expanded){
32212                     cls += "-minus";
32213                     c1 = "x-tree-node-collapsed";
32214                     c2 = "x-tree-node-expanded";
32215                 }else{
32216                     cls += "-plus";
32217                     c1 = "x-tree-node-expanded";
32218                     c2 = "x-tree-node-collapsed";
32219                 }
32220                 if(this.wasLeaf){
32221                     this.removeClass("x-tree-node-leaf");
32222                     this.wasLeaf = false;
32223                 }
32224                 if(this.c1 != c1 || this.c2 != c2){
32225                     Roo.fly(this.elNode).replaceClass(c1, c2);
32226                     this.c1 = c1; this.c2 = c2;
32227                 }
32228             }else{
32229                 if(!this.wasLeaf){
32230                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32231                     delete this.c1;
32232                     delete this.c2;
32233                     this.wasLeaf = true;
32234                 }
32235             }
32236             var ecc = "x-tree-ec-icon "+cls;
32237             if(this.ecc != ecc){
32238                 this.ecNode.className = ecc;
32239                 this.ecc = ecc;
32240             }
32241         }
32242     },
32243
32244     getChildIndent : function(){
32245         if(!this.childIndent){
32246             var buf = [];
32247             var p = this.node;
32248             while(p){
32249                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32250                     if(!p.isLast()) {
32251                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32252                     } else {
32253                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32254                     }
32255                 }
32256                 p = p.parentNode;
32257             }
32258             this.childIndent = buf.join("");
32259         }
32260         return this.childIndent;
32261     },
32262
32263     renderIndent : function(){
32264         if(this.rendered){
32265             var indent = "";
32266             var p = this.node.parentNode;
32267             if(p){
32268                 indent = p.ui.getChildIndent();
32269             }
32270             if(this.indentMarkup != indent){ // don't rerender if not required
32271                 this.indentNode.innerHTML = indent;
32272                 this.indentMarkup = indent;
32273             }
32274             this.updateExpandIcon();
32275         }
32276     }
32277 };
32278
32279 Roo.tree.RootTreeNodeUI = function(){
32280     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32281 };
32282 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32283     render : function(){
32284         if(!this.rendered){
32285             var targetNode = this.node.ownerTree.innerCt.dom;
32286             this.node.expanded = true;
32287             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32288             this.wrap = this.ctNode = targetNode.firstChild;
32289         }
32290     },
32291     collapse : function(){
32292     },
32293     expand : function(){
32294     }
32295 });/*
32296  * Based on:
32297  * Ext JS Library 1.1.1
32298  * Copyright(c) 2006-2007, Ext JS, LLC.
32299  *
32300  * Originally Released Under LGPL - original licence link has changed is not relivant.
32301  *
32302  * Fork - LGPL
32303  * <script type="text/javascript">
32304  */
32305 /**
32306  * @class Roo.tree.TreeLoader
32307  * @extends Roo.util.Observable
32308  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32309  * nodes from a specified URL. The response must be a javascript Array definition
32310  * who's elements are node definition objects. eg:
32311  * <pre><code>
32312    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32313     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32314 </code></pre>
32315  * <br><br>
32316  * A server request is sent, and child nodes are loaded only when a node is expanded.
32317  * The loading node's id is passed to the server under the parameter name "node" to
32318  * enable the server to produce the correct child nodes.
32319  * <br><br>
32320  * To pass extra parameters, an event handler may be attached to the "beforeload"
32321  * event, and the parameters specified in the TreeLoader's baseParams property:
32322  * <pre><code>
32323     myTreeLoader.on("beforeload", function(treeLoader, node) {
32324         this.baseParams.category = node.attributes.category;
32325     }, this);
32326 </code></pre><
32327  * This would pass an HTTP parameter called "category" to the server containing
32328  * the value of the Node's "category" attribute.
32329  * @constructor
32330  * Creates a new Treeloader.
32331  * @param {Object} config A config object containing config properties.
32332  */
32333 Roo.tree.TreeLoader = function(config){
32334     this.baseParams = {};
32335     this.requestMethod = "POST";
32336     Roo.apply(this, config);
32337
32338     this.addEvents({
32339     
32340         /**
32341          * @event beforeload
32342          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32343          * @param {Object} This TreeLoader object.
32344          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32345          * @param {Object} callback The callback function specified in the {@link #load} call.
32346          */
32347         beforeload : true,
32348         /**
32349          * @event load
32350          * Fires when the node has been successfuly loaded.
32351          * @param {Object} This TreeLoader object.
32352          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32353          * @param {Object} response The response object containing the data from the server.
32354          */
32355         load : true,
32356         /**
32357          * @event loadexception
32358          * Fires if the network request failed.
32359          * @param {Object} This TreeLoader object.
32360          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32361          * @param {Object} response The response object containing the data from the server.
32362          */
32363         loadexception : true,
32364         /**
32365          * @event create
32366          * Fires before a node is created, enabling you to return custom Node types 
32367          * @param {Object} This TreeLoader object.
32368          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32369          */
32370         create : true
32371     });
32372
32373     Roo.tree.TreeLoader.superclass.constructor.call(this);
32374 };
32375
32376 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32377     /**
32378     * @cfg {String} dataUrl The URL from which to request a Json string which
32379     * specifies an array of node definition object representing the child nodes
32380     * to be loaded.
32381     */
32382     /**
32383     * @cfg {Object} baseParams (optional) An object containing properties which
32384     * specify HTTP parameters to be passed to each request for child nodes.
32385     */
32386     /**
32387     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32388     * created by this loader. If the attributes sent by the server have an attribute in this object,
32389     * they take priority.
32390     */
32391     /**
32392     * @cfg {Object} uiProviders (optional) An object containing properties which
32393     * 
32394     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32395     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32396     * <i>uiProvider</i> attribute of a returned child node is a string rather
32397     * than a reference to a TreeNodeUI implementation, this that string value
32398     * is used as a property name in the uiProviders object. You can define the provider named
32399     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32400     */
32401     uiProviders : {},
32402
32403     /**
32404     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32405     * child nodes before loading.
32406     */
32407     clearOnLoad : true,
32408
32409     /**
32410     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32411     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32412     * Grid query { data : [ .....] }
32413     */
32414     
32415     root : false,
32416      /**
32417     * @cfg {String} queryParam (optional) 
32418     * Name of the query as it will be passed on the querystring (defaults to 'node')
32419     * eg. the request will be ?node=[id]
32420     */
32421     
32422     
32423     queryParam: false,
32424     
32425     /**
32426      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32427      * This is called automatically when a node is expanded, but may be used to reload
32428      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32429      * @param {Roo.tree.TreeNode} node
32430      * @param {Function} callback
32431      */
32432     load : function(node, callback){
32433         if(this.clearOnLoad){
32434             while(node.firstChild){
32435                 node.removeChild(node.firstChild);
32436             }
32437         }
32438         if(node.attributes.children){ // preloaded json children
32439             var cs = node.attributes.children;
32440             for(var i = 0, len = cs.length; i < len; i++){
32441                 node.appendChild(this.createNode(cs[i]));
32442             }
32443             if(typeof callback == "function"){
32444                 callback();
32445             }
32446         }else if(this.dataUrl){
32447             this.requestData(node, callback);
32448         }
32449     },
32450
32451     getParams: function(node){
32452         var buf = [], bp = this.baseParams;
32453         for(var key in bp){
32454             if(typeof bp[key] != "function"){
32455                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32456             }
32457         }
32458         var n = this.queryParam === false ? 'node' : this.queryParam;
32459         buf.push(n + "=", encodeURIComponent(node.id));
32460         return buf.join("");
32461     },
32462
32463     requestData : function(node, callback){
32464         if(this.fireEvent("beforeload", this, node, callback) !== false){
32465             this.transId = Roo.Ajax.request({
32466                 method:this.requestMethod,
32467                 url: this.dataUrl||this.url,
32468                 success: this.handleResponse,
32469                 failure: this.handleFailure,
32470                 scope: this,
32471                 argument: {callback: callback, node: node},
32472                 params: this.getParams(node)
32473             });
32474         }else{
32475             // if the load is cancelled, make sure we notify
32476             // the node that we are done
32477             if(typeof callback == "function"){
32478                 callback();
32479             }
32480         }
32481     },
32482
32483     isLoading : function(){
32484         return this.transId ? true : false;
32485     },
32486
32487     abort : function(){
32488         if(this.isLoading()){
32489             Roo.Ajax.abort(this.transId);
32490         }
32491     },
32492
32493     // private
32494     createNode : function(attr){
32495         // apply baseAttrs, nice idea Corey!
32496         if(this.baseAttrs){
32497             Roo.applyIf(attr, this.baseAttrs);
32498         }
32499         if(this.applyLoader !== false){
32500             attr.loader = this;
32501         }
32502         // uiProvider = depreciated..
32503         
32504         if(typeof(attr.uiProvider) == 'string'){
32505            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32506                 /**  eval:var:attr */ eval(attr.uiProvider);
32507         }
32508         if(typeof(this.uiProviders['default']) != 'undefined') {
32509             attr.uiProvider = this.uiProviders['default'];
32510         }
32511         
32512         this.fireEvent('create', this, attr);
32513         
32514         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32515         return(attr.leaf ?
32516                         new Roo.tree.TreeNode(attr) :
32517                         new Roo.tree.AsyncTreeNode(attr));
32518     },
32519
32520     processResponse : function(response, node, callback){
32521         var json = response.responseText;
32522         try {
32523             
32524             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32525             if (this.root !== false) {
32526                 o = o[this.root];
32527             }
32528             
32529             for(var i = 0, len = o.length; i < len; i++){
32530                 var n = this.createNode(o[i]);
32531                 if(n){
32532                     node.appendChild(n);
32533                 }
32534             }
32535             if(typeof callback == "function"){
32536                 callback(this, node);
32537             }
32538         }catch(e){
32539             this.handleFailure(response);
32540         }
32541     },
32542
32543     handleResponse : function(response){
32544         this.transId = false;
32545         var a = response.argument;
32546         this.processResponse(response, a.node, a.callback);
32547         this.fireEvent("load", this, a.node, response);
32548     },
32549
32550     handleFailure : function(response){
32551         this.transId = false;
32552         var a = response.argument;
32553         this.fireEvent("loadexception", this, a.node, response);
32554         if(typeof a.callback == "function"){
32555             a.callback(this, a.node);
32556         }
32557     }
32558 });/*
32559  * Based on:
32560  * Ext JS Library 1.1.1
32561  * Copyright(c) 2006-2007, Ext JS, LLC.
32562  *
32563  * Originally Released Under LGPL - original licence link has changed is not relivant.
32564  *
32565  * Fork - LGPL
32566  * <script type="text/javascript">
32567  */
32568
32569 /**
32570 * @class Roo.tree.TreeFilter
32571 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32572 * @param {TreePanel} tree
32573 * @param {Object} config (optional)
32574  */
32575 Roo.tree.TreeFilter = function(tree, config){
32576     this.tree = tree;
32577     this.filtered = {};
32578     Roo.apply(this, config);
32579 };
32580
32581 Roo.tree.TreeFilter.prototype = {
32582     clearBlank:false,
32583     reverse:false,
32584     autoClear:false,
32585     remove:false,
32586
32587      /**
32588      * Filter the data by a specific attribute.
32589      * @param {String/RegExp} value Either string that the attribute value
32590      * should start with or a RegExp to test against the attribute
32591      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32592      * @param {TreeNode} startNode (optional) The node to start the filter at.
32593      */
32594     filter : function(value, attr, startNode){
32595         attr = attr || "text";
32596         var f;
32597         if(typeof value == "string"){
32598             var vlen = value.length;
32599             // auto clear empty filter
32600             if(vlen == 0 && this.clearBlank){
32601                 this.clear();
32602                 return;
32603             }
32604             value = value.toLowerCase();
32605             f = function(n){
32606                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32607             };
32608         }else if(value.exec){ // regex?
32609             f = function(n){
32610                 return value.test(n.attributes[attr]);
32611             };
32612         }else{
32613             throw 'Illegal filter type, must be string or regex';
32614         }
32615         this.filterBy(f, null, startNode);
32616         },
32617
32618     /**
32619      * Filter by a function. The passed function will be called with each
32620      * node in the tree (or from the startNode). If the function returns true, the node is kept
32621      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32622      * @param {Function} fn The filter function
32623      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32624      */
32625     filterBy : function(fn, scope, startNode){
32626         startNode = startNode || this.tree.root;
32627         if(this.autoClear){
32628             this.clear();
32629         }
32630         var af = this.filtered, rv = this.reverse;
32631         var f = function(n){
32632             if(n == startNode){
32633                 return true;
32634             }
32635             if(af[n.id]){
32636                 return false;
32637             }
32638             var m = fn.call(scope || n, n);
32639             if(!m || rv){
32640                 af[n.id] = n;
32641                 n.ui.hide();
32642                 return false;
32643             }
32644             return true;
32645         };
32646         startNode.cascade(f);
32647         if(this.remove){
32648            for(var id in af){
32649                if(typeof id != "function"){
32650                    var n = af[id];
32651                    if(n && n.parentNode){
32652                        n.parentNode.removeChild(n);
32653                    }
32654                }
32655            }
32656         }
32657     },
32658
32659     /**
32660      * Clears the current filter. Note: with the "remove" option
32661      * set a filter cannot be cleared.
32662      */
32663     clear : function(){
32664         var t = this.tree;
32665         var af = this.filtered;
32666         for(var id in af){
32667             if(typeof id != "function"){
32668                 var n = af[id];
32669                 if(n){
32670                     n.ui.show();
32671                 }
32672             }
32673         }
32674         this.filtered = {};
32675     }
32676 };
32677 /*
32678  * Based on:
32679  * Ext JS Library 1.1.1
32680  * Copyright(c) 2006-2007, Ext JS, LLC.
32681  *
32682  * Originally Released Under LGPL - original licence link has changed is not relivant.
32683  *
32684  * Fork - LGPL
32685  * <script type="text/javascript">
32686  */
32687  
32688
32689 /**
32690  * @class Roo.tree.TreeSorter
32691  * Provides sorting of nodes in a TreePanel
32692  * 
32693  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32694  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32695  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32696  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32697  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32698  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32699  * @constructor
32700  * @param {TreePanel} tree
32701  * @param {Object} config
32702  */
32703 Roo.tree.TreeSorter = function(tree, config){
32704     Roo.apply(this, config);
32705     tree.on("beforechildrenrendered", this.doSort, this);
32706     tree.on("append", this.updateSort, this);
32707     tree.on("insert", this.updateSort, this);
32708     
32709     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32710     var p = this.property || "text";
32711     var sortType = this.sortType;
32712     var fs = this.folderSort;
32713     var cs = this.caseSensitive === true;
32714     var leafAttr = this.leafAttr || 'leaf';
32715
32716     this.sortFn = function(n1, n2){
32717         if(fs){
32718             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32719                 return 1;
32720             }
32721             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32722                 return -1;
32723             }
32724         }
32725         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32726         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32727         if(v1 < v2){
32728                         return dsc ? +1 : -1;
32729                 }else if(v1 > v2){
32730                         return dsc ? -1 : +1;
32731         }else{
32732                 return 0;
32733         }
32734     };
32735 };
32736
32737 Roo.tree.TreeSorter.prototype = {
32738     doSort : function(node){
32739         node.sort(this.sortFn);
32740     },
32741     
32742     compareNodes : function(n1, n2){
32743         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32744     },
32745     
32746     updateSort : function(tree, node){
32747         if(node.childrenRendered){
32748             this.doSort.defer(1, this, [node]);
32749         }
32750     }
32751 };/*
32752  * Based on:
32753  * Ext JS Library 1.1.1
32754  * Copyright(c) 2006-2007, Ext JS, LLC.
32755  *
32756  * Originally Released Under LGPL - original licence link has changed is not relivant.
32757  *
32758  * Fork - LGPL
32759  * <script type="text/javascript">
32760  */
32761
32762 if(Roo.dd.DropZone){
32763     
32764 Roo.tree.TreeDropZone = function(tree, config){
32765     this.allowParentInsert = false;
32766     this.allowContainerDrop = false;
32767     this.appendOnly = false;
32768     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32769     this.tree = tree;
32770     this.lastInsertClass = "x-tree-no-status";
32771     this.dragOverData = {};
32772 };
32773
32774 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32775     ddGroup : "TreeDD",
32776     
32777     expandDelay : 1000,
32778     
32779     expandNode : function(node){
32780         if(node.hasChildNodes() && !node.isExpanded()){
32781             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32782         }
32783     },
32784     
32785     queueExpand : function(node){
32786         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32787     },
32788     
32789     cancelExpand : function(){
32790         if(this.expandProcId){
32791             clearTimeout(this.expandProcId);
32792             this.expandProcId = false;
32793         }
32794     },
32795     
32796     isValidDropPoint : function(n, pt, dd, e, data){
32797         if(!n || !data){ return false; }
32798         var targetNode = n.node;
32799         var dropNode = data.node;
32800         // default drop rules
32801         if(!(targetNode && targetNode.isTarget && pt)){
32802             return false;
32803         }
32804         if(pt == "append" && targetNode.allowChildren === false){
32805             return false;
32806         }
32807         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32808             return false;
32809         }
32810         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32811             return false;
32812         }
32813         // reuse the object
32814         var overEvent = this.dragOverData;
32815         overEvent.tree = this.tree;
32816         overEvent.target = targetNode;
32817         overEvent.data = data;
32818         overEvent.point = pt;
32819         overEvent.source = dd;
32820         overEvent.rawEvent = e;
32821         overEvent.dropNode = dropNode;
32822         overEvent.cancel = false;  
32823         var result = this.tree.fireEvent("nodedragover", overEvent);
32824         return overEvent.cancel === false && result !== false;
32825     },
32826     
32827     getDropPoint : function(e, n, dd){
32828         var tn = n.node;
32829         if(tn.isRoot){
32830             return tn.allowChildren !== false ? "append" : false; // always append for root
32831         }
32832         var dragEl = n.ddel;
32833         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32834         var y = Roo.lib.Event.getPageY(e);
32835         var noAppend = tn.allowChildren === false || tn.isLeaf();
32836         if(this.appendOnly || tn.parentNode.allowChildren === false){
32837             return noAppend ? false : "append";
32838         }
32839         var noBelow = false;
32840         if(!this.allowParentInsert){
32841             noBelow = tn.hasChildNodes() && tn.isExpanded();
32842         }
32843         var q = (b - t) / (noAppend ? 2 : 3);
32844         if(y >= t && y < (t + q)){
32845             return "above";
32846         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32847             return "below";
32848         }else{
32849             return "append";
32850         }
32851     },
32852     
32853     onNodeEnter : function(n, dd, e, data){
32854         this.cancelExpand();
32855     },
32856     
32857     onNodeOver : function(n, dd, e, data){
32858         var pt = this.getDropPoint(e, n, dd);
32859         var node = n.node;
32860         
32861         // auto node expand check
32862         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32863             this.queueExpand(node);
32864         }else if(pt != "append"){
32865             this.cancelExpand();
32866         }
32867         
32868         // set the insert point style on the target node
32869         var returnCls = this.dropNotAllowed;
32870         if(this.isValidDropPoint(n, pt, dd, e, data)){
32871            if(pt){
32872                var el = n.ddel;
32873                var cls;
32874                if(pt == "above"){
32875                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32876                    cls = "x-tree-drag-insert-above";
32877                }else if(pt == "below"){
32878                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32879                    cls = "x-tree-drag-insert-below";
32880                }else{
32881                    returnCls = "x-tree-drop-ok-append";
32882                    cls = "x-tree-drag-append";
32883                }
32884                if(this.lastInsertClass != cls){
32885                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32886                    this.lastInsertClass = cls;
32887                }
32888            }
32889        }
32890        return returnCls;
32891     },
32892     
32893     onNodeOut : function(n, dd, e, data){
32894         this.cancelExpand();
32895         this.removeDropIndicators(n);
32896     },
32897     
32898     onNodeDrop : function(n, dd, e, data){
32899         var point = this.getDropPoint(e, n, dd);
32900         var targetNode = n.node;
32901         targetNode.ui.startDrop();
32902         if(!this.isValidDropPoint(n, point, dd, e, data)){
32903             targetNode.ui.endDrop();
32904             return false;
32905         }
32906         // first try to find the drop node
32907         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32908         var dropEvent = {
32909             tree : this.tree,
32910             target: targetNode,
32911             data: data,
32912             point: point,
32913             source: dd,
32914             rawEvent: e,
32915             dropNode: dropNode,
32916             cancel: !dropNode   
32917         };
32918         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32919         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32920             targetNode.ui.endDrop();
32921             return false;
32922         }
32923         // allow target changing
32924         targetNode = dropEvent.target;
32925         if(point == "append" && !targetNode.isExpanded()){
32926             targetNode.expand(false, null, function(){
32927                 this.completeDrop(dropEvent);
32928             }.createDelegate(this));
32929         }else{
32930             this.completeDrop(dropEvent);
32931         }
32932         return true;
32933     },
32934     
32935     completeDrop : function(de){
32936         var ns = de.dropNode, p = de.point, t = de.target;
32937         if(!(ns instanceof Array)){
32938             ns = [ns];
32939         }
32940         var n;
32941         for(var i = 0, len = ns.length; i < len; i++){
32942             n = ns[i];
32943             if(p == "above"){
32944                 t.parentNode.insertBefore(n, t);
32945             }else if(p == "below"){
32946                 t.parentNode.insertBefore(n, t.nextSibling);
32947             }else{
32948                 t.appendChild(n);
32949             }
32950         }
32951         n.ui.focus();
32952         if(this.tree.hlDrop){
32953             n.ui.highlight();
32954         }
32955         t.ui.endDrop();
32956         this.tree.fireEvent("nodedrop", de);
32957     },
32958     
32959     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32960         if(this.tree.hlDrop){
32961             dropNode.ui.focus();
32962             dropNode.ui.highlight();
32963         }
32964         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32965     },
32966     
32967     getTree : function(){
32968         return this.tree;
32969     },
32970     
32971     removeDropIndicators : function(n){
32972         if(n && n.ddel){
32973             var el = n.ddel;
32974             Roo.fly(el).removeClass([
32975                     "x-tree-drag-insert-above",
32976                     "x-tree-drag-insert-below",
32977                     "x-tree-drag-append"]);
32978             this.lastInsertClass = "_noclass";
32979         }
32980     },
32981     
32982     beforeDragDrop : function(target, e, id){
32983         this.cancelExpand();
32984         return true;
32985     },
32986     
32987     afterRepair : function(data){
32988         if(data && Roo.enableFx){
32989             data.node.ui.highlight();
32990         }
32991         this.hideProxy();
32992     }    
32993 });
32994
32995 }/*
32996  * Based on:
32997  * Ext JS Library 1.1.1
32998  * Copyright(c) 2006-2007, Ext JS, LLC.
32999  *
33000  * Originally Released Under LGPL - original licence link has changed is not relivant.
33001  *
33002  * Fork - LGPL
33003  * <script type="text/javascript">
33004  */
33005  
33006
33007 if(Roo.dd.DragZone){
33008 Roo.tree.TreeDragZone = function(tree, config){
33009     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33010     this.tree = tree;
33011 };
33012
33013 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33014     ddGroup : "TreeDD",
33015     
33016     onBeforeDrag : function(data, e){
33017         var n = data.node;
33018         return n && n.draggable && !n.disabled;
33019     },
33020     
33021     onInitDrag : function(e){
33022         var data = this.dragData;
33023         this.tree.getSelectionModel().select(data.node);
33024         this.proxy.update("");
33025         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33026         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33027     },
33028     
33029     getRepairXY : function(e, data){
33030         return data.node.ui.getDDRepairXY();
33031     },
33032     
33033     onEndDrag : function(data, e){
33034         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33035     },
33036     
33037     onValidDrop : function(dd, e, id){
33038         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33039         this.hideProxy();
33040     },
33041     
33042     beforeInvalidDrop : function(e, id){
33043         // this scrolls the original position back into view
33044         var sm = this.tree.getSelectionModel();
33045         sm.clearSelections();
33046         sm.select(this.dragData.node);
33047     }
33048 });
33049 }/*
33050  * Based on:
33051  * Ext JS Library 1.1.1
33052  * Copyright(c) 2006-2007, Ext JS, LLC.
33053  *
33054  * Originally Released Under LGPL - original licence link has changed is not relivant.
33055  *
33056  * Fork - LGPL
33057  * <script type="text/javascript">
33058  */
33059 /**
33060  * @class Roo.tree.TreeEditor
33061  * @extends Roo.Editor
33062  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33063  * as the editor field.
33064  * @constructor
33065  * @param {TreePanel} tree
33066  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33067  */
33068 Roo.tree.TreeEditor = function(tree, config){
33069     config = config || {};
33070     var field = config.events ? config : new Roo.form.TextField(config);
33071     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33072
33073     this.tree = tree;
33074
33075     tree.on('beforeclick', this.beforeNodeClick, this);
33076     tree.getTreeEl().on('mousedown', this.hide, this);
33077     this.on('complete', this.updateNode, this);
33078     this.on('beforestartedit', this.fitToTree, this);
33079     this.on('startedit', this.bindScroll, this, {delay:10});
33080     this.on('specialkey', this.onSpecialKey, this);
33081 };
33082
33083 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33084     /**
33085      * @cfg {String} alignment
33086      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33087      */
33088     alignment: "l-l",
33089     // inherit
33090     autoSize: false,
33091     /**
33092      * @cfg {Boolean} hideEl
33093      * True to hide the bound element while the editor is displayed (defaults to false)
33094      */
33095     hideEl : false,
33096     /**
33097      * @cfg {String} cls
33098      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33099      */
33100     cls: "x-small-editor x-tree-editor",
33101     /**
33102      * @cfg {Boolean} shim
33103      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33104      */
33105     shim:false,
33106     // inherit
33107     shadow:"frame",
33108     /**
33109      * @cfg {Number} maxWidth
33110      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33111      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33112      * scroll and client offsets into account prior to each edit.
33113      */
33114     maxWidth: 250,
33115
33116     editDelay : 350,
33117
33118     // private
33119     fitToTree : function(ed, el){
33120         var td = this.tree.getTreeEl().dom, nd = el.dom;
33121         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33122             td.scrollLeft = nd.offsetLeft;
33123         }
33124         var w = Math.min(
33125                 this.maxWidth,
33126                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33127         this.setSize(w, '');
33128     },
33129
33130     // private
33131     triggerEdit : function(node){
33132         this.completeEdit();
33133         this.editNode = node;
33134         this.startEdit(node.ui.textNode, node.text);
33135     },
33136
33137     // private
33138     bindScroll : function(){
33139         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33140     },
33141
33142     // private
33143     beforeNodeClick : function(node, e){
33144         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33145         this.lastClick = new Date();
33146         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33147             e.stopEvent();
33148             this.triggerEdit(node);
33149             return false;
33150         }
33151     },
33152
33153     // private
33154     updateNode : function(ed, value){
33155         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33156         this.editNode.setText(value);
33157     },
33158
33159     // private
33160     onHide : function(){
33161         Roo.tree.TreeEditor.superclass.onHide.call(this);
33162         if(this.editNode){
33163             this.editNode.ui.focus();
33164         }
33165     },
33166
33167     // private
33168     onSpecialKey : function(field, e){
33169         var k = e.getKey();
33170         if(k == e.ESC){
33171             e.stopEvent();
33172             this.cancelEdit();
33173         }else if(k == e.ENTER && !e.hasModifier()){
33174             e.stopEvent();
33175             this.completeEdit();
33176         }
33177     }
33178 });//<Script type="text/javascript">
33179 /*
33180  * Based on:
33181  * Ext JS Library 1.1.1
33182  * Copyright(c) 2006-2007, Ext JS, LLC.
33183  *
33184  * Originally Released Under LGPL - original licence link has changed is not relivant.
33185  *
33186  * Fork - LGPL
33187  * <script type="text/javascript">
33188  */
33189  
33190 /**
33191  * Not documented??? - probably should be...
33192  */
33193
33194 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33195     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33196     
33197     renderElements : function(n, a, targetNode, bulkRender){
33198         //consel.log("renderElements?");
33199         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33200
33201         var t = n.getOwnerTree();
33202         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33203         
33204         var cols = t.columns;
33205         var bw = t.borderWidth;
33206         var c = cols[0];
33207         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33208          var cb = typeof a.checked == "boolean";
33209         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33210         var colcls = 'x-t-' + tid + '-c0';
33211         var buf = [
33212             '<li class="x-tree-node">',
33213             
33214                 
33215                 '<div class="x-tree-node-el ', a.cls,'">',
33216                     // extran...
33217                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33218                 
33219                 
33220                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33221                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33222                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33223                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33224                            (a.iconCls ? ' '+a.iconCls : ''),
33225                            '" unselectable="on" />',
33226                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33227                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33228                              
33229                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33230                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33231                             '<span unselectable="on" qtip="' + tx + '">',
33232                              tx,
33233                              '</span></a>' ,
33234                     '</div>',
33235                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33236                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33237                  ];
33238         
33239         for(var i = 1, len = cols.length; i < len; i++){
33240             c = cols[i];
33241             colcls = 'x-t-' + tid + '-c' +i;
33242             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33243             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33244                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33245                       "</div>");
33246          }
33247          
33248          buf.push(
33249             '</a>',
33250             '<div class="x-clear"></div></div>',
33251             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33252             "</li>");
33253         
33254         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33255             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33256                                 n.nextSibling.ui.getEl(), buf.join(""));
33257         }else{
33258             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33259         }
33260         var el = this.wrap.firstChild;
33261         this.elRow = el;
33262         this.elNode = el.firstChild;
33263         this.ranchor = el.childNodes[1];
33264         this.ctNode = this.wrap.childNodes[1];
33265         var cs = el.firstChild.childNodes;
33266         this.indentNode = cs[0];
33267         this.ecNode = cs[1];
33268         this.iconNode = cs[2];
33269         var index = 3;
33270         if(cb){
33271             this.checkbox = cs[3];
33272             index++;
33273         }
33274         this.anchor = cs[index];
33275         
33276         this.textNode = cs[index].firstChild;
33277         
33278         //el.on("click", this.onClick, this);
33279         //el.on("dblclick", this.onDblClick, this);
33280         
33281         
33282        // console.log(this);
33283     },
33284     initEvents : function(){
33285         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33286         
33287             
33288         var a = this.ranchor;
33289
33290         var el = Roo.get(a);
33291
33292         if(Roo.isOpera){ // opera render bug ignores the CSS
33293             el.setStyle("text-decoration", "none");
33294         }
33295
33296         el.on("click", this.onClick, this);
33297         el.on("dblclick", this.onDblClick, this);
33298         el.on("contextmenu", this.onContextMenu, this);
33299         
33300     },
33301     
33302     /*onSelectedChange : function(state){
33303         if(state){
33304             this.focus();
33305             this.addClass("x-tree-selected");
33306         }else{
33307             //this.blur();
33308             this.removeClass("x-tree-selected");
33309         }
33310     },*/
33311     addClass : function(cls){
33312         if(this.elRow){
33313             Roo.fly(this.elRow).addClass(cls);
33314         }
33315         
33316     },
33317     
33318     
33319     removeClass : function(cls){
33320         if(this.elRow){
33321             Roo.fly(this.elRow).removeClass(cls);
33322         }
33323     }
33324
33325     
33326     
33327 });//<Script type="text/javascript">
33328
33329 /*
33330  * Based on:
33331  * Ext JS Library 1.1.1
33332  * Copyright(c) 2006-2007, Ext JS, LLC.
33333  *
33334  * Originally Released Under LGPL - original licence link has changed is not relivant.
33335  *
33336  * Fork - LGPL
33337  * <script type="text/javascript">
33338  */
33339  
33340
33341 /**
33342  * @class Roo.tree.ColumnTree
33343  * @extends Roo.data.TreePanel
33344  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33345  * @cfg {int} borderWidth  compined right/left border allowance
33346  * @constructor
33347  * @param {String/HTMLElement/Element} el The container element
33348  * @param {Object} config
33349  */
33350 Roo.tree.ColumnTree =  function(el, config)
33351 {
33352    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33353    this.addEvents({
33354         /**
33355         * @event resize
33356         * Fire this event on a container when it resizes
33357         * @param {int} w Width
33358         * @param {int} h Height
33359         */
33360        "resize" : true
33361     });
33362     this.on('resize', this.onResize, this);
33363 };
33364
33365 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33366     //lines:false,
33367     
33368     
33369     borderWidth: Roo.isBorderBox ? 0 : 2, 
33370     headEls : false,
33371     
33372     render : function(){
33373         // add the header.....
33374        
33375         Roo.tree.ColumnTree.superclass.render.apply(this);
33376         
33377         this.el.addClass('x-column-tree');
33378         
33379         this.headers = this.el.createChild(
33380             {cls:'x-tree-headers'},this.innerCt.dom);
33381    
33382         var cols = this.columns, c;
33383         var totalWidth = 0;
33384         this.headEls = [];
33385         var  len = cols.length;
33386         for(var i = 0; i < len; i++){
33387              c = cols[i];
33388              totalWidth += c.width;
33389             this.headEls.push(this.headers.createChild({
33390                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33391                  cn: {
33392                      cls:'x-tree-hd-text',
33393                      html: c.header
33394                  },
33395                  style:'width:'+(c.width-this.borderWidth)+'px;'
33396              }));
33397         }
33398         this.headers.createChild({cls:'x-clear'});
33399         // prevent floats from wrapping when clipped
33400         this.headers.setWidth(totalWidth);
33401         //this.innerCt.setWidth(totalWidth);
33402         this.innerCt.setStyle({ overflow: 'auto' });
33403         this.onResize(this.width, this.height);
33404              
33405         
33406     },
33407     onResize : function(w,h)
33408     {
33409         this.height = h;
33410         this.width = w;
33411         // resize cols..
33412         this.innerCt.setWidth(this.width);
33413         this.innerCt.setHeight(this.height-20);
33414         
33415         // headers...
33416         var cols = this.columns, c;
33417         var totalWidth = 0;
33418         var expEl = false;
33419         var len = cols.length;
33420         for(var i = 0; i < len; i++){
33421             c = cols[i];
33422             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33423                 // it's the expander..
33424                 expEl  = this.headEls[i];
33425                 continue;
33426             }
33427             totalWidth += c.width;
33428             
33429         }
33430         if (expEl) {
33431             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33432         }
33433         this.headers.setWidth(w-20);
33434
33435         
33436         
33437         
33438     }
33439 });
33440 /*
33441  * Based on:
33442  * Ext JS Library 1.1.1
33443  * Copyright(c) 2006-2007, Ext JS, LLC.
33444  *
33445  * Originally Released Under LGPL - original licence link has changed is not relivant.
33446  *
33447  * Fork - LGPL
33448  * <script type="text/javascript">
33449  */
33450  
33451 /**
33452  * @class Roo.menu.Menu
33453  * @extends Roo.util.Observable
33454  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33455  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33456  * @constructor
33457  * Creates a new Menu
33458  * @param {Object} config Configuration options
33459  */
33460 Roo.menu.Menu = function(config){
33461     Roo.apply(this, config);
33462     this.id = this.id || Roo.id();
33463     this.addEvents({
33464         /**
33465          * @event beforeshow
33466          * Fires before this menu is displayed
33467          * @param {Roo.menu.Menu} this
33468          */
33469         beforeshow : true,
33470         /**
33471          * @event beforehide
33472          * Fires before this menu is hidden
33473          * @param {Roo.menu.Menu} this
33474          */
33475         beforehide : true,
33476         /**
33477          * @event show
33478          * Fires after this menu is displayed
33479          * @param {Roo.menu.Menu} this
33480          */
33481         show : true,
33482         /**
33483          * @event hide
33484          * Fires after this menu is hidden
33485          * @param {Roo.menu.Menu} this
33486          */
33487         hide : true,
33488         /**
33489          * @event click
33490          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33491          * @param {Roo.menu.Menu} this
33492          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33493          * @param {Roo.EventObject} e
33494          */
33495         click : true,
33496         /**
33497          * @event mouseover
33498          * Fires when the mouse is hovering over this menu
33499          * @param {Roo.menu.Menu} this
33500          * @param {Roo.EventObject} e
33501          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33502          */
33503         mouseover : true,
33504         /**
33505          * @event mouseout
33506          * Fires when the mouse exits this menu
33507          * @param {Roo.menu.Menu} this
33508          * @param {Roo.EventObject} e
33509          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33510          */
33511         mouseout : true,
33512         /**
33513          * @event itemclick
33514          * Fires when a menu item contained in this menu is clicked
33515          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33516          * @param {Roo.EventObject} e
33517          */
33518         itemclick: true
33519     });
33520     if (this.registerMenu) {
33521         Roo.menu.MenuMgr.register(this);
33522     }
33523     
33524     var mis = this.items;
33525     this.items = new Roo.util.MixedCollection();
33526     if(mis){
33527         this.add.apply(this, mis);
33528     }
33529 };
33530
33531 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33532     /**
33533      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33534      */
33535     minWidth : 120,
33536     /**
33537      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33538      * for bottom-right shadow (defaults to "sides")
33539      */
33540     shadow : "sides",
33541     /**
33542      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33543      * this menu (defaults to "tl-tr?")
33544      */
33545     subMenuAlign : "tl-tr?",
33546     /**
33547      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33548      * relative to its element of origin (defaults to "tl-bl?")
33549      */
33550     defaultAlign : "tl-bl?",
33551     /**
33552      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33553      */
33554     allowOtherMenus : false,
33555     /**
33556      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33557      */
33558     registerMenu : true,
33559
33560     hidden:true,
33561
33562     // private
33563     render : function(){
33564         if(this.el){
33565             return;
33566         }
33567         var el = this.el = new Roo.Layer({
33568             cls: "x-menu",
33569             shadow:this.shadow,
33570             constrain: false,
33571             parentEl: this.parentEl || document.body,
33572             zindex:15000
33573         });
33574
33575         this.keyNav = new Roo.menu.MenuNav(this);
33576
33577         if(this.plain){
33578             el.addClass("x-menu-plain");
33579         }
33580         if(this.cls){
33581             el.addClass(this.cls);
33582         }
33583         // generic focus element
33584         this.focusEl = el.createChild({
33585             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33586         });
33587         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33588         ul.on("click", this.onClick, this);
33589         ul.on("mouseover", this.onMouseOver, this);
33590         ul.on("mouseout", this.onMouseOut, this);
33591         this.items.each(function(item){
33592             var li = document.createElement("li");
33593             li.className = "x-menu-list-item";
33594             ul.dom.appendChild(li);
33595             item.render(li, this);
33596         }, this);
33597         this.ul = ul;
33598         this.autoWidth();
33599     },
33600
33601     // private
33602     autoWidth : function(){
33603         var el = this.el, ul = this.ul;
33604         if(!el){
33605             return;
33606         }
33607         var w = this.width;
33608         if(w){
33609             el.setWidth(w);
33610         }else if(Roo.isIE){
33611             el.setWidth(this.minWidth);
33612             var t = el.dom.offsetWidth; // force recalc
33613             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33614         }
33615     },
33616
33617     // private
33618     delayAutoWidth : function(){
33619         if(this.rendered){
33620             if(!this.awTask){
33621                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33622             }
33623             this.awTask.delay(20);
33624         }
33625     },
33626
33627     // private
33628     findTargetItem : function(e){
33629         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33630         if(t && t.menuItemId){
33631             return this.items.get(t.menuItemId);
33632         }
33633     },
33634
33635     // private
33636     onClick : function(e){
33637         var t;
33638         if(t = this.findTargetItem(e)){
33639             t.onClick(e);
33640             this.fireEvent("click", this, t, e);
33641         }
33642     },
33643
33644     // private
33645     setActiveItem : function(item, autoExpand){
33646         if(item != this.activeItem){
33647             if(this.activeItem){
33648                 this.activeItem.deactivate();
33649             }
33650             this.activeItem = item;
33651             item.activate(autoExpand);
33652         }else if(autoExpand){
33653             item.expandMenu();
33654         }
33655     },
33656
33657     // private
33658     tryActivate : function(start, step){
33659         var items = this.items;
33660         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33661             var item = items.get(i);
33662             if(!item.disabled && item.canActivate){
33663                 this.setActiveItem(item, false);
33664                 return item;
33665             }
33666         }
33667         return false;
33668     },
33669
33670     // private
33671     onMouseOver : function(e){
33672         var t;
33673         if(t = this.findTargetItem(e)){
33674             if(t.canActivate && !t.disabled){
33675                 this.setActiveItem(t, true);
33676             }
33677         }
33678         this.fireEvent("mouseover", this, e, t);
33679     },
33680
33681     // private
33682     onMouseOut : function(e){
33683         var t;
33684         if(t = this.findTargetItem(e)){
33685             if(t == this.activeItem && t.shouldDeactivate(e)){
33686                 this.activeItem.deactivate();
33687                 delete this.activeItem;
33688             }
33689         }
33690         this.fireEvent("mouseout", this, e, t);
33691     },
33692
33693     /**
33694      * Read-only.  Returns true if the menu is currently displayed, else false.
33695      * @type Boolean
33696      */
33697     isVisible : function(){
33698         return this.el && !this.hidden;
33699     },
33700
33701     /**
33702      * Displays this menu relative to another element
33703      * @param {String/HTMLElement/Roo.Element} element The element to align to
33704      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33705      * the element (defaults to this.defaultAlign)
33706      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33707      */
33708     show : function(el, pos, parentMenu){
33709         this.parentMenu = parentMenu;
33710         if(!this.el){
33711             this.render();
33712         }
33713         this.fireEvent("beforeshow", this);
33714         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33715     },
33716
33717     /**
33718      * Displays this menu at a specific xy position
33719      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33720      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33721      */
33722     showAt : function(xy, parentMenu, /* private: */_e){
33723         this.parentMenu = parentMenu;
33724         if(!this.el){
33725             this.render();
33726         }
33727         if(_e !== false){
33728             this.fireEvent("beforeshow", this);
33729             xy = this.el.adjustForConstraints(xy);
33730         }
33731         this.el.setXY(xy);
33732         this.el.show();
33733         this.hidden = false;
33734         this.focus();
33735         this.fireEvent("show", this);
33736     },
33737
33738     focus : function(){
33739         if(!this.hidden){
33740             this.doFocus.defer(50, this);
33741         }
33742     },
33743
33744     doFocus : function(){
33745         if(!this.hidden){
33746             this.focusEl.focus();
33747         }
33748     },
33749
33750     /**
33751      * Hides this menu and optionally all parent menus
33752      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33753      */
33754     hide : function(deep){
33755         if(this.el && this.isVisible()){
33756             this.fireEvent("beforehide", this);
33757             if(this.activeItem){
33758                 this.activeItem.deactivate();
33759                 this.activeItem = null;
33760             }
33761             this.el.hide();
33762             this.hidden = true;
33763             this.fireEvent("hide", this);
33764         }
33765         if(deep === true && this.parentMenu){
33766             this.parentMenu.hide(true);
33767         }
33768     },
33769
33770     /**
33771      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33772      * Any of the following are valid:
33773      * <ul>
33774      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33775      * <li>An HTMLElement object which will be converted to a menu item</li>
33776      * <li>A menu item config object that will be created as a new menu item</li>
33777      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33778      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33779      * </ul>
33780      * Usage:
33781      * <pre><code>
33782 // Create the menu
33783 var menu = new Roo.menu.Menu();
33784
33785 // Create a menu item to add by reference
33786 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33787
33788 // Add a bunch of items at once using different methods.
33789 // Only the last item added will be returned.
33790 var item = menu.add(
33791     menuItem,                // add existing item by ref
33792     'Dynamic Item',          // new TextItem
33793     '-',                     // new separator
33794     { text: 'Config Item' }  // new item by config
33795 );
33796 </code></pre>
33797      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33798      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33799      */
33800     add : function(){
33801         var a = arguments, l = a.length, item;
33802         for(var i = 0; i < l; i++){
33803             var el = a[i];
33804             if(el.render){ // some kind of Item
33805                 item = this.addItem(el);
33806             }else if(typeof el == "string"){ // string
33807                 if(el == "separator" || el == "-"){
33808                     item = this.addSeparator();
33809                 }else{
33810                     item = this.addText(el);
33811                 }
33812             }else if(el.tagName || el.el){ // element
33813                 item = this.addElement(el);
33814             }else if(typeof el == "object"){ // must be menu item config?
33815                 item = this.addMenuItem(el);
33816             }
33817         }
33818         return item;
33819     },
33820
33821     /**
33822      * Returns this menu's underlying {@link Roo.Element} object
33823      * @return {Roo.Element} The element
33824      */
33825     getEl : function(){
33826         if(!this.el){
33827             this.render();
33828         }
33829         return this.el;
33830     },
33831
33832     /**
33833      * Adds a separator bar to the menu
33834      * @return {Roo.menu.Item} The menu item that was added
33835      */
33836     addSeparator : function(){
33837         return this.addItem(new Roo.menu.Separator());
33838     },
33839
33840     /**
33841      * Adds an {@link Roo.Element} object to the menu
33842      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33843      * @return {Roo.menu.Item} The menu item that was added
33844      */
33845     addElement : function(el){
33846         return this.addItem(new Roo.menu.BaseItem(el));
33847     },
33848
33849     /**
33850      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33851      * @param {Roo.menu.Item} item The menu item to add
33852      * @return {Roo.menu.Item} The menu item that was added
33853      */
33854     addItem : function(item){
33855         this.items.add(item);
33856         if(this.ul){
33857             var li = document.createElement("li");
33858             li.className = "x-menu-list-item";
33859             this.ul.dom.appendChild(li);
33860             item.render(li, this);
33861             this.delayAutoWidth();
33862         }
33863         return item;
33864     },
33865
33866     /**
33867      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33868      * @param {Object} config A MenuItem config object
33869      * @return {Roo.menu.Item} The menu item that was added
33870      */
33871     addMenuItem : function(config){
33872         if(!(config instanceof Roo.menu.Item)){
33873             if(typeof config.checked == "boolean"){ // must be check menu item config?
33874                 config = new Roo.menu.CheckItem(config);
33875             }else{
33876                 config = new Roo.menu.Item(config);
33877             }
33878         }
33879         return this.addItem(config);
33880     },
33881
33882     /**
33883      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33884      * @param {String} text The text to display in the menu item
33885      * @return {Roo.menu.Item} The menu item that was added
33886      */
33887     addText : function(text){
33888         return this.addItem(new Roo.menu.TextItem(text));
33889     },
33890
33891     /**
33892      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33893      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33894      * @param {Roo.menu.Item} item The menu item to add
33895      * @return {Roo.menu.Item} The menu item that was added
33896      */
33897     insert : function(index, item){
33898         this.items.insert(index, item);
33899         if(this.ul){
33900             var li = document.createElement("li");
33901             li.className = "x-menu-list-item";
33902             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33903             item.render(li, this);
33904             this.delayAutoWidth();
33905         }
33906         return item;
33907     },
33908
33909     /**
33910      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33911      * @param {Roo.menu.Item} item The menu item to remove
33912      */
33913     remove : function(item){
33914         this.items.removeKey(item.id);
33915         item.destroy();
33916     },
33917
33918     /**
33919      * Removes and destroys all items in the menu
33920      */
33921     removeAll : function(){
33922         var f;
33923         while(f = this.items.first()){
33924             this.remove(f);
33925         }
33926     }
33927 });
33928
33929 // MenuNav is a private utility class used internally by the Menu
33930 Roo.menu.MenuNav = function(menu){
33931     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33932     this.scope = this.menu = menu;
33933 };
33934
33935 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33936     doRelay : function(e, h){
33937         var k = e.getKey();
33938         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33939             this.menu.tryActivate(0, 1);
33940             return false;
33941         }
33942         return h.call(this.scope || this, e, this.menu);
33943     },
33944
33945     up : function(e, m){
33946         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33947             m.tryActivate(m.items.length-1, -1);
33948         }
33949     },
33950
33951     down : function(e, m){
33952         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33953             m.tryActivate(0, 1);
33954         }
33955     },
33956
33957     right : function(e, m){
33958         if(m.activeItem){
33959             m.activeItem.expandMenu(true);
33960         }
33961     },
33962
33963     left : function(e, m){
33964         m.hide();
33965         if(m.parentMenu && m.parentMenu.activeItem){
33966             m.parentMenu.activeItem.activate();
33967         }
33968     },
33969
33970     enter : function(e, m){
33971         if(m.activeItem){
33972             e.stopPropagation();
33973             m.activeItem.onClick(e);
33974             m.fireEvent("click", this, m.activeItem);
33975             return true;
33976         }
33977     }
33978 });/*
33979  * Based on:
33980  * Ext JS Library 1.1.1
33981  * Copyright(c) 2006-2007, Ext JS, LLC.
33982  *
33983  * Originally Released Under LGPL - original licence link has changed is not relivant.
33984  *
33985  * Fork - LGPL
33986  * <script type="text/javascript">
33987  */
33988  
33989 /**
33990  * @class Roo.menu.MenuMgr
33991  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33992  * @singleton
33993  */
33994 Roo.menu.MenuMgr = function(){
33995    var menus, active, groups = {}, attached = false, lastShow = new Date();
33996
33997    // private - called when first menu is created
33998    function init(){
33999        menus = {};
34000        active = new Roo.util.MixedCollection();
34001        Roo.get(document).addKeyListener(27, function(){
34002            if(active.length > 0){
34003                hideAll();
34004            }
34005        });
34006    }
34007
34008    // private
34009    function hideAll(){
34010        if(active && active.length > 0){
34011            var c = active.clone();
34012            c.each(function(m){
34013                m.hide();
34014            });
34015        }
34016    }
34017
34018    // private
34019    function onHide(m){
34020        active.remove(m);
34021        if(active.length < 1){
34022            Roo.get(document).un("mousedown", onMouseDown);
34023            attached = false;
34024        }
34025    }
34026
34027    // private
34028    function onShow(m){
34029        var last = active.last();
34030        lastShow = new Date();
34031        active.add(m);
34032        if(!attached){
34033            Roo.get(document).on("mousedown", onMouseDown);
34034            attached = true;
34035        }
34036        if(m.parentMenu){
34037           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34038           m.parentMenu.activeChild = m;
34039        }else if(last && last.isVisible()){
34040           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34041        }
34042    }
34043
34044    // private
34045    function onBeforeHide(m){
34046        if(m.activeChild){
34047            m.activeChild.hide();
34048        }
34049        if(m.autoHideTimer){
34050            clearTimeout(m.autoHideTimer);
34051            delete m.autoHideTimer;
34052        }
34053    }
34054
34055    // private
34056    function onBeforeShow(m){
34057        var pm = m.parentMenu;
34058        if(!pm && !m.allowOtherMenus){
34059            hideAll();
34060        }else if(pm && pm.activeChild && active != m){
34061            pm.activeChild.hide();
34062        }
34063    }
34064
34065    // private
34066    function onMouseDown(e){
34067        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34068            hideAll();
34069        }
34070    }
34071
34072    // private
34073    function onBeforeCheck(mi, state){
34074        if(state){
34075            var g = groups[mi.group];
34076            for(var i = 0, l = g.length; i < l; i++){
34077                if(g[i] != mi){
34078                    g[i].setChecked(false);
34079                }
34080            }
34081        }
34082    }
34083
34084    return {
34085
34086        /**
34087         * Hides all menus that are currently visible
34088         */
34089        hideAll : function(){
34090             hideAll();  
34091        },
34092
34093        // private
34094        register : function(menu){
34095            if(!menus){
34096                init();
34097            }
34098            menus[menu.id] = menu;
34099            menu.on("beforehide", onBeforeHide);
34100            menu.on("hide", onHide);
34101            menu.on("beforeshow", onBeforeShow);
34102            menu.on("show", onShow);
34103            var g = menu.group;
34104            if(g && menu.events["checkchange"]){
34105                if(!groups[g]){
34106                    groups[g] = [];
34107                }
34108                groups[g].push(menu);
34109                menu.on("checkchange", onCheck);
34110            }
34111        },
34112
34113         /**
34114          * Returns a {@link Roo.menu.Menu} object
34115          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34116          * be used to generate and return a new Menu instance.
34117          */
34118        get : function(menu){
34119            if(typeof menu == "string"){ // menu id
34120                return menus[menu];
34121            }else if(menu.events){  // menu instance
34122                return menu;
34123            }else if(typeof menu.length == 'number'){ // array of menu items?
34124                return new Roo.menu.Menu({items:menu});
34125            }else{ // otherwise, must be a config
34126                return new Roo.menu.Menu(menu);
34127            }
34128        },
34129
34130        // private
34131        unregister : function(menu){
34132            delete menus[menu.id];
34133            menu.un("beforehide", onBeforeHide);
34134            menu.un("hide", onHide);
34135            menu.un("beforeshow", onBeforeShow);
34136            menu.un("show", onShow);
34137            var g = menu.group;
34138            if(g && menu.events["checkchange"]){
34139                groups[g].remove(menu);
34140                menu.un("checkchange", onCheck);
34141            }
34142        },
34143
34144        // private
34145        registerCheckable : function(menuItem){
34146            var g = menuItem.group;
34147            if(g){
34148                if(!groups[g]){
34149                    groups[g] = [];
34150                }
34151                groups[g].push(menuItem);
34152                menuItem.on("beforecheckchange", onBeforeCheck);
34153            }
34154        },
34155
34156        // private
34157        unregisterCheckable : function(menuItem){
34158            var g = menuItem.group;
34159            if(g){
34160                groups[g].remove(menuItem);
34161                menuItem.un("beforecheckchange", onBeforeCheck);
34162            }
34163        }
34164    };
34165 }();/*
34166  * Based on:
34167  * Ext JS Library 1.1.1
34168  * Copyright(c) 2006-2007, Ext JS, LLC.
34169  *
34170  * Originally Released Under LGPL - original licence link has changed is not relivant.
34171  *
34172  * Fork - LGPL
34173  * <script type="text/javascript">
34174  */
34175  
34176
34177 /**
34178  * @class Roo.menu.BaseItem
34179  * @extends Roo.Component
34180  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34181  * management and base configuration options shared by all menu components.
34182  * @constructor
34183  * Creates a new BaseItem
34184  * @param {Object} config Configuration options
34185  */
34186 Roo.menu.BaseItem = function(config){
34187     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34188
34189     this.addEvents({
34190         /**
34191          * @event click
34192          * Fires when this item is clicked
34193          * @param {Roo.menu.BaseItem} this
34194          * @param {Roo.EventObject} e
34195          */
34196         click: true,
34197         /**
34198          * @event activate
34199          * Fires when this item is activated
34200          * @param {Roo.menu.BaseItem} this
34201          */
34202         activate : true,
34203         /**
34204          * @event deactivate
34205          * Fires when this item is deactivated
34206          * @param {Roo.menu.BaseItem} this
34207          */
34208         deactivate : true
34209     });
34210
34211     if(this.handler){
34212         this.on("click", this.handler, this.scope, true);
34213     }
34214 };
34215
34216 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34217     /**
34218      * @cfg {Function} handler
34219      * A function that will handle the click event of this menu item (defaults to undefined)
34220      */
34221     /**
34222      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34223      */
34224     canActivate : false,
34225     /**
34226      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34227      */
34228     activeClass : "x-menu-item-active",
34229     /**
34230      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34231      */
34232     hideOnClick : true,
34233     /**
34234      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34235      */
34236     hideDelay : 100,
34237
34238     // private
34239     ctype: "Roo.menu.BaseItem",
34240
34241     // private
34242     actionMode : "container",
34243
34244     // private
34245     render : function(container, parentMenu){
34246         this.parentMenu = parentMenu;
34247         Roo.menu.BaseItem.superclass.render.call(this, container);
34248         this.container.menuItemId = this.id;
34249     },
34250
34251     // private
34252     onRender : function(container, position){
34253         this.el = Roo.get(this.el);
34254         container.dom.appendChild(this.el.dom);
34255     },
34256
34257     // private
34258     onClick : function(e){
34259         if(!this.disabled && this.fireEvent("click", this, e) !== false
34260                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34261             this.handleClick(e);
34262         }else{
34263             e.stopEvent();
34264         }
34265     },
34266
34267     // private
34268     activate : function(){
34269         if(this.disabled){
34270             return false;
34271         }
34272         var li = this.container;
34273         li.addClass(this.activeClass);
34274         this.region = li.getRegion().adjust(2, 2, -2, -2);
34275         this.fireEvent("activate", this);
34276         return true;
34277     },
34278
34279     // private
34280     deactivate : function(){
34281         this.container.removeClass(this.activeClass);
34282         this.fireEvent("deactivate", this);
34283     },
34284
34285     // private
34286     shouldDeactivate : function(e){
34287         return !this.region || !this.region.contains(e.getPoint());
34288     },
34289
34290     // private
34291     handleClick : function(e){
34292         if(this.hideOnClick){
34293             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34294         }
34295     },
34296
34297     // private
34298     expandMenu : function(autoActivate){
34299         // do nothing
34300     },
34301
34302     // private
34303     hideMenu : function(){
34304         // do nothing
34305     }
34306 });/*
34307  * Based on:
34308  * Ext JS Library 1.1.1
34309  * Copyright(c) 2006-2007, Ext JS, LLC.
34310  *
34311  * Originally Released Under LGPL - original licence link has changed is not relivant.
34312  *
34313  * Fork - LGPL
34314  * <script type="text/javascript">
34315  */
34316  
34317 /**
34318  * @class Roo.menu.Adapter
34319  * @extends Roo.menu.BaseItem
34320  * 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.
34321  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34322  * @constructor
34323  * Creates a new Adapter
34324  * @param {Object} config Configuration options
34325  */
34326 Roo.menu.Adapter = function(component, config){
34327     Roo.menu.Adapter.superclass.constructor.call(this, config);
34328     this.component = component;
34329 };
34330 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34331     // private
34332     canActivate : true,
34333
34334     // private
34335     onRender : function(container, position){
34336         this.component.render(container);
34337         this.el = this.component.getEl();
34338     },
34339
34340     // private
34341     activate : function(){
34342         if(this.disabled){
34343             return false;
34344         }
34345         this.component.focus();
34346         this.fireEvent("activate", this);
34347         return true;
34348     },
34349
34350     // private
34351     deactivate : function(){
34352         this.fireEvent("deactivate", this);
34353     },
34354
34355     // private
34356     disable : function(){
34357         this.component.disable();
34358         Roo.menu.Adapter.superclass.disable.call(this);
34359     },
34360
34361     // private
34362     enable : function(){
34363         this.component.enable();
34364         Roo.menu.Adapter.superclass.enable.call(this);
34365     }
34366 });/*
34367  * Based on:
34368  * Ext JS Library 1.1.1
34369  * Copyright(c) 2006-2007, Ext JS, LLC.
34370  *
34371  * Originally Released Under LGPL - original licence link has changed is not relivant.
34372  *
34373  * Fork - LGPL
34374  * <script type="text/javascript">
34375  */
34376
34377 /**
34378  * @class Roo.menu.TextItem
34379  * @extends Roo.menu.BaseItem
34380  * Adds a static text string to a menu, usually used as either a heading or group separator.
34381  * @constructor
34382  * Creates a new TextItem
34383  * @param {String} text The text to display
34384  */
34385 Roo.menu.TextItem = function(text){
34386     this.text = text;
34387     Roo.menu.TextItem.superclass.constructor.call(this);
34388 };
34389
34390 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34391     /**
34392      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34393      */
34394     hideOnClick : false,
34395     /**
34396      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34397      */
34398     itemCls : "x-menu-text",
34399
34400     // private
34401     onRender : function(){
34402         var s = document.createElement("span");
34403         s.className = this.itemCls;
34404         s.innerHTML = this.text;
34405         this.el = s;
34406         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34407     }
34408 });/*
34409  * Based on:
34410  * Ext JS Library 1.1.1
34411  * Copyright(c) 2006-2007, Ext JS, LLC.
34412  *
34413  * Originally Released Under LGPL - original licence link has changed is not relivant.
34414  *
34415  * Fork - LGPL
34416  * <script type="text/javascript">
34417  */
34418
34419 /**
34420  * @class Roo.menu.Separator
34421  * @extends Roo.menu.BaseItem
34422  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34423  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34424  * @constructor
34425  * @param {Object} config Configuration options
34426  */
34427 Roo.menu.Separator = function(config){
34428     Roo.menu.Separator.superclass.constructor.call(this, config);
34429 };
34430
34431 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34432     /**
34433      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34434      */
34435     itemCls : "x-menu-sep",
34436     /**
34437      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34438      */
34439     hideOnClick : false,
34440
34441     // private
34442     onRender : function(li){
34443         var s = document.createElement("span");
34444         s.className = this.itemCls;
34445         s.innerHTML = "&#160;";
34446         this.el = s;
34447         li.addClass("x-menu-sep-li");
34448         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34449     }
34450 });/*
34451  * Based on:
34452  * Ext JS Library 1.1.1
34453  * Copyright(c) 2006-2007, Ext JS, LLC.
34454  *
34455  * Originally Released Under LGPL - original licence link has changed is not relivant.
34456  *
34457  * Fork - LGPL
34458  * <script type="text/javascript">
34459  */
34460 /**
34461  * @class Roo.menu.Item
34462  * @extends Roo.menu.BaseItem
34463  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34464  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34465  * activation and click handling.
34466  * @constructor
34467  * Creates a new Item
34468  * @param {Object} config Configuration options
34469  */
34470 Roo.menu.Item = function(config){
34471     Roo.menu.Item.superclass.constructor.call(this, config);
34472     if(this.menu){
34473         this.menu = Roo.menu.MenuMgr.get(this.menu);
34474     }
34475 };
34476 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34477     /**
34478      * @cfg {String} icon
34479      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34480      */
34481     /**
34482      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34483      */
34484     itemCls : "x-menu-item",
34485     /**
34486      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34487      */
34488     canActivate : true,
34489     /**
34490      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34491      */
34492     showDelay: 200,
34493     // doc'd in BaseItem
34494     hideDelay: 200,
34495
34496     // private
34497     ctype: "Roo.menu.Item",
34498     
34499     // private
34500     onRender : function(container, position){
34501         var el = document.createElement("a");
34502         el.hideFocus = true;
34503         el.unselectable = "on";
34504         el.href = this.href || "#";
34505         if(this.hrefTarget){
34506             el.target = this.hrefTarget;
34507         }
34508         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34509         el.innerHTML = String.format(
34510                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34511                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34512         this.el = el;
34513         Roo.menu.Item.superclass.onRender.call(this, container, position);
34514     },
34515
34516     /**
34517      * Sets the text to display in this menu item
34518      * @param {String} text The text to display
34519      */
34520     setText : function(text){
34521         this.text = text;
34522         if(this.rendered){
34523             this.el.update(String.format(
34524                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34525                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34526             this.parentMenu.autoWidth();
34527         }
34528     },
34529
34530     // private
34531     handleClick : function(e){
34532         if(!this.href){ // if no link defined, stop the event automatically
34533             e.stopEvent();
34534         }
34535         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34536     },
34537
34538     // private
34539     activate : function(autoExpand){
34540         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34541             this.focus();
34542             if(autoExpand){
34543                 this.expandMenu();
34544             }
34545         }
34546         return true;
34547     },
34548
34549     // private
34550     shouldDeactivate : function(e){
34551         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34552             if(this.menu && this.menu.isVisible()){
34553                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34554             }
34555             return true;
34556         }
34557         return false;
34558     },
34559
34560     // private
34561     deactivate : function(){
34562         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34563         this.hideMenu();
34564     },
34565
34566     // private
34567     expandMenu : function(autoActivate){
34568         if(!this.disabled && this.menu){
34569             clearTimeout(this.hideTimer);
34570             delete this.hideTimer;
34571             if(!this.menu.isVisible() && !this.showTimer){
34572                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34573             }else if (this.menu.isVisible() && autoActivate){
34574                 this.menu.tryActivate(0, 1);
34575             }
34576         }
34577     },
34578
34579     // private
34580     deferExpand : function(autoActivate){
34581         delete this.showTimer;
34582         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34583         if(autoActivate){
34584             this.menu.tryActivate(0, 1);
34585         }
34586     },
34587
34588     // private
34589     hideMenu : function(){
34590         clearTimeout(this.showTimer);
34591         delete this.showTimer;
34592         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34593             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34594         }
34595     },
34596
34597     // private
34598     deferHide : function(){
34599         delete this.hideTimer;
34600         this.menu.hide();
34601     }
34602 });/*
34603  * Based on:
34604  * Ext JS Library 1.1.1
34605  * Copyright(c) 2006-2007, Ext JS, LLC.
34606  *
34607  * Originally Released Under LGPL - original licence link has changed is not relivant.
34608  *
34609  * Fork - LGPL
34610  * <script type="text/javascript">
34611  */
34612  
34613 /**
34614  * @class Roo.menu.CheckItem
34615  * @extends Roo.menu.Item
34616  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34617  * @constructor
34618  * Creates a new CheckItem
34619  * @param {Object} config Configuration options
34620  */
34621 Roo.menu.CheckItem = function(config){
34622     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34623     this.addEvents({
34624         /**
34625          * @event beforecheckchange
34626          * Fires before the checked value is set, providing an opportunity to cancel if needed
34627          * @param {Roo.menu.CheckItem} this
34628          * @param {Boolean} checked The new checked value that will be set
34629          */
34630         "beforecheckchange" : true,
34631         /**
34632          * @event checkchange
34633          * Fires after the checked value has been set
34634          * @param {Roo.menu.CheckItem} this
34635          * @param {Boolean} checked The checked value that was set
34636          */
34637         "checkchange" : true
34638     });
34639     if(this.checkHandler){
34640         this.on('checkchange', this.checkHandler, this.scope);
34641     }
34642 };
34643 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34644     /**
34645      * @cfg {String} group
34646      * All check items with the same group name will automatically be grouped into a single-select
34647      * radio button group (defaults to '')
34648      */
34649     /**
34650      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34651      */
34652     itemCls : "x-menu-item x-menu-check-item",
34653     /**
34654      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34655      */
34656     groupClass : "x-menu-group-item",
34657
34658     /**
34659      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34660      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34661      * initialized with checked = true will be rendered as checked.
34662      */
34663     checked: false,
34664
34665     // private
34666     ctype: "Roo.menu.CheckItem",
34667
34668     // private
34669     onRender : function(c){
34670         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34671         if(this.group){
34672             this.el.addClass(this.groupClass);
34673         }
34674         Roo.menu.MenuMgr.registerCheckable(this);
34675         if(this.checked){
34676             this.checked = false;
34677             this.setChecked(true, true);
34678         }
34679     },
34680
34681     // private
34682     destroy : function(){
34683         if(this.rendered){
34684             Roo.menu.MenuMgr.unregisterCheckable(this);
34685         }
34686         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34687     },
34688
34689     /**
34690      * Set the checked state of this item
34691      * @param {Boolean} checked The new checked value
34692      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34693      */
34694     setChecked : function(state, suppressEvent){
34695         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34696             if(this.container){
34697                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34698             }
34699             this.checked = state;
34700             if(suppressEvent !== true){
34701                 this.fireEvent("checkchange", this, state);
34702             }
34703         }
34704     },
34705
34706     // private
34707     handleClick : function(e){
34708        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34709            this.setChecked(!this.checked);
34710        }
34711        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34712     }
34713 });/*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724 /**
34725  * @class Roo.menu.DateItem
34726  * @extends Roo.menu.Adapter
34727  * A menu item that wraps the {@link Roo.DatPicker} component.
34728  * @constructor
34729  * Creates a new DateItem
34730  * @param {Object} config Configuration options
34731  */
34732 Roo.menu.DateItem = function(config){
34733     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34734     /** The Roo.DatePicker object @type Roo.DatePicker */
34735     this.picker = this.component;
34736     this.addEvents({select: true});
34737     
34738     this.picker.on("render", function(picker){
34739         picker.getEl().swallowEvent("click");
34740         picker.container.addClass("x-menu-date-item");
34741     });
34742
34743     this.picker.on("select", this.onSelect, this);
34744 };
34745
34746 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34747     // private
34748     onSelect : function(picker, date){
34749         this.fireEvent("select", this, date, picker);
34750         Roo.menu.DateItem.superclass.handleClick.call(this);
34751     }
34752 });/*
34753  * Based on:
34754  * Ext JS Library 1.1.1
34755  * Copyright(c) 2006-2007, Ext JS, LLC.
34756  *
34757  * Originally Released Under LGPL - original licence link has changed is not relivant.
34758  *
34759  * Fork - LGPL
34760  * <script type="text/javascript">
34761  */
34762  
34763 /**
34764  * @class Roo.menu.ColorItem
34765  * @extends Roo.menu.Adapter
34766  * A menu item that wraps the {@link Roo.ColorPalette} component.
34767  * @constructor
34768  * Creates a new ColorItem
34769  * @param {Object} config Configuration options
34770  */
34771 Roo.menu.ColorItem = function(config){
34772     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34773     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34774     this.palette = this.component;
34775     this.relayEvents(this.palette, ["select"]);
34776     if(this.selectHandler){
34777         this.on('select', this.selectHandler, this.scope);
34778     }
34779 };
34780 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34781  * Based on:
34782  * Ext JS Library 1.1.1
34783  * Copyright(c) 2006-2007, Ext JS, LLC.
34784  *
34785  * Originally Released Under LGPL - original licence link has changed is not relivant.
34786  *
34787  * Fork - LGPL
34788  * <script type="text/javascript">
34789  */
34790  
34791
34792 /**
34793  * @class Roo.menu.DateMenu
34794  * @extends Roo.menu.Menu
34795  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34796  * @constructor
34797  * Creates a new DateMenu
34798  * @param {Object} config Configuration options
34799  */
34800 Roo.menu.DateMenu = function(config){
34801     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34802     this.plain = true;
34803     var di = new Roo.menu.DateItem(config);
34804     this.add(di);
34805     /**
34806      * The {@link Roo.DatePicker} instance for this DateMenu
34807      * @type DatePicker
34808      */
34809     this.picker = di.picker;
34810     /**
34811      * @event select
34812      * @param {DatePicker} picker
34813      * @param {Date} date
34814      */
34815     this.relayEvents(di, ["select"]);
34816
34817     this.on('beforeshow', function(){
34818         if(this.picker){
34819             this.picker.hideMonthPicker(true);
34820         }
34821     }, this);
34822 };
34823 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34824     cls:'x-date-menu'
34825 });/*
34826  * Based on:
34827  * Ext JS Library 1.1.1
34828  * Copyright(c) 2006-2007, Ext JS, LLC.
34829  *
34830  * Originally Released Under LGPL - original licence link has changed is not relivant.
34831  *
34832  * Fork - LGPL
34833  * <script type="text/javascript">
34834  */
34835  
34836
34837 /**
34838  * @class Roo.menu.ColorMenu
34839  * @extends Roo.menu.Menu
34840  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34841  * @constructor
34842  * Creates a new ColorMenu
34843  * @param {Object} config Configuration options
34844  */
34845 Roo.menu.ColorMenu = function(config){
34846     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34847     this.plain = true;
34848     var ci = new Roo.menu.ColorItem(config);
34849     this.add(ci);
34850     /**
34851      * The {@link Roo.ColorPalette} instance for this ColorMenu
34852      * @type ColorPalette
34853      */
34854     this.palette = ci.palette;
34855     /**
34856      * @event select
34857      * @param {ColorPalette} palette
34858      * @param {String} color
34859      */
34860     this.relayEvents(ci, ["select"]);
34861 };
34862 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34863  * Based on:
34864  * Ext JS Library 1.1.1
34865  * Copyright(c) 2006-2007, Ext JS, LLC.
34866  *
34867  * Originally Released Under LGPL - original licence link has changed is not relivant.
34868  *
34869  * Fork - LGPL
34870  * <script type="text/javascript">
34871  */
34872  
34873 /**
34874  * @class Roo.form.Field
34875  * @extends Roo.BoxComponent
34876  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34877  * @constructor
34878  * Creates a new Field
34879  * @param {Object} config Configuration options
34880  */
34881 Roo.form.Field = function(config){
34882     Roo.form.Field.superclass.constructor.call(this, config);
34883 };
34884
34885 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34886     /**
34887      * @cfg {String} fieldLabel Label to use when rendering a form.
34888      */
34889        /**
34890      * @cfg {String} qtip Mouse over tip
34891      */
34892      
34893     /**
34894      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34895      */
34896     invalidClass : "x-form-invalid",
34897     /**
34898      * @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")
34899      */
34900     invalidText : "The value in this field is invalid",
34901     /**
34902      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34903      */
34904     focusClass : "x-form-focus",
34905     /**
34906      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34907       automatic validation (defaults to "keyup").
34908      */
34909     validationEvent : "keyup",
34910     /**
34911      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34912      */
34913     validateOnBlur : true,
34914     /**
34915      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34916      */
34917     validationDelay : 250,
34918     /**
34919      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34920      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34921      */
34922     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34923     /**
34924      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34925      */
34926     fieldClass : "x-form-field",
34927     /**
34928      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34929      *<pre>
34930 Value         Description
34931 -----------   ----------------------------------------------------------------------
34932 qtip          Display a quick tip when the user hovers over the field
34933 title         Display a default browser title attribute popup
34934 under         Add a block div beneath the field containing the error text
34935 side          Add an error icon to the right of the field with a popup on hover
34936 [element id]  Add the error text directly to the innerHTML of the specified element
34937 </pre>
34938      */
34939     msgTarget : 'qtip',
34940     /**
34941      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34942      */
34943     msgFx : 'normal',
34944
34945     /**
34946      * @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.
34947      */
34948     readOnly : false,
34949
34950     /**
34951      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34952      */
34953     disabled : false,
34954
34955     /**
34956      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34957      */
34958     inputType : undefined,
34959     
34960     /**
34961      * @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).
34962          */
34963         tabIndex : undefined,
34964         
34965     // private
34966     isFormField : true,
34967
34968     // private
34969     hasFocus : false,
34970     /**
34971      * @property {Roo.Element} fieldEl
34972      * Element Containing the rendered Field (with label etc.)
34973      */
34974     /**
34975      * @cfg {Mixed} value A value to initialize this field with.
34976      */
34977     value : undefined,
34978
34979     /**
34980      * @cfg {String} name The field's HTML name attribute.
34981      */
34982     /**
34983      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34984      */
34985
34986         // private ??
34987         initComponent : function(){
34988         Roo.form.Field.superclass.initComponent.call(this);
34989         this.addEvents({
34990             /**
34991              * @event focus
34992              * Fires when this field receives input focus.
34993              * @param {Roo.form.Field} this
34994              */
34995             focus : true,
34996             /**
34997              * @event blur
34998              * Fires when this field loses input focus.
34999              * @param {Roo.form.Field} this
35000              */
35001             blur : true,
35002             /**
35003              * @event specialkey
35004              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35005              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35006              * @param {Roo.form.Field} this
35007              * @param {Roo.EventObject} e The event object
35008              */
35009             specialkey : true,
35010             /**
35011              * @event change
35012              * Fires just before the field blurs if the field value has changed.
35013              * @param {Roo.form.Field} this
35014              * @param {Mixed} newValue The new value
35015              * @param {Mixed} oldValue The original value
35016              */
35017             change : true,
35018             /**
35019              * @event invalid
35020              * Fires after the field has been marked as invalid.
35021              * @param {Roo.form.Field} this
35022              * @param {String} msg The validation message
35023              */
35024             invalid : true,
35025             /**
35026              * @event valid
35027              * Fires after the field has been validated with no errors.
35028              * @param {Roo.form.Field} this
35029              */
35030             valid : true
35031         });
35032     },
35033
35034     /**
35035      * Returns the name attribute of the field if available
35036      * @return {String} name The field name
35037      */
35038     getName: function(){
35039          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35040     },
35041
35042     // private
35043     onRender : function(ct, position){
35044         Roo.form.Field.superclass.onRender.call(this, ct, position);
35045         if(!this.el){
35046             var cfg = this.getAutoCreate();
35047             if(!cfg.name){
35048                 cfg.name = this.name || this.id;
35049             }
35050             if(this.inputType){
35051                 cfg.type = this.inputType;
35052             }
35053             this.el = ct.createChild(cfg, position);
35054         }
35055         var type = this.el.dom.type;
35056         if(type){
35057             if(type == 'password'){
35058                 type = 'text';
35059             }
35060             this.el.addClass('x-form-'+type);
35061         }
35062         if(this.readOnly){
35063             this.el.dom.readOnly = true;
35064         }
35065         if(this.tabIndex !== undefined){
35066             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35067         }
35068
35069         this.el.addClass([this.fieldClass, this.cls]);
35070         this.initValue();
35071     },
35072
35073     /**
35074      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35075      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35076      * @return {Roo.form.Field} this
35077      */
35078     applyTo : function(target){
35079         this.allowDomMove = false;
35080         this.el = Roo.get(target);
35081         this.render(this.el.dom.parentNode);
35082         return this;
35083     },
35084
35085     // private
35086     initValue : function(){
35087         if(this.value !== undefined){
35088             this.setValue(this.value);
35089         }else if(this.el.dom.value.length > 0){
35090             this.setValue(this.el.dom.value);
35091         }
35092     },
35093
35094     /**
35095      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35096      */
35097     isDirty : function() {
35098         if(this.disabled) {
35099             return false;
35100         }
35101         return String(this.getValue()) !== String(this.originalValue);
35102     },
35103
35104     // private
35105     afterRender : function(){
35106         Roo.form.Field.superclass.afterRender.call(this);
35107         this.initEvents();
35108     },
35109
35110     // private
35111     fireKey : function(e){
35112         if(e.isNavKeyPress()){
35113             this.fireEvent("specialkey", this, e);
35114         }
35115     },
35116
35117     /**
35118      * Resets the current field value to the originally loaded value and clears any validation messages
35119      */
35120     reset : function(){
35121         this.setValue(this.originalValue);
35122         this.clearInvalid();
35123     },
35124
35125     // private
35126     initEvents : function(){
35127         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35128         this.el.on("focus", this.onFocus,  this);
35129         this.el.on("blur", this.onBlur,  this);
35130
35131         // reference to original value for reset
35132         this.originalValue = this.getValue();
35133     },
35134
35135     // private
35136     onFocus : function(){
35137         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35138             this.el.addClass(this.focusClass);
35139         }
35140         if(!this.hasFocus){
35141             this.hasFocus = true;
35142             this.startValue = this.getValue();
35143             this.fireEvent("focus", this);
35144         }
35145     },
35146
35147     beforeBlur : Roo.emptyFn,
35148
35149     // private
35150     onBlur : function(){
35151         this.beforeBlur();
35152         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35153             this.el.removeClass(this.focusClass);
35154         }
35155         this.hasFocus = false;
35156         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35157             this.validate();
35158         }
35159         var v = this.getValue();
35160         if(String(v) !== String(this.startValue)){
35161             this.fireEvent('change', this, v, this.startValue);
35162         }
35163         this.fireEvent("blur", this);
35164     },
35165
35166     /**
35167      * Returns whether or not the field value is currently valid
35168      * @param {Boolean} preventMark True to disable marking the field invalid
35169      * @return {Boolean} True if the value is valid, else false
35170      */
35171     isValid : function(preventMark){
35172         if(this.disabled){
35173             return true;
35174         }
35175         var restore = this.preventMark;
35176         this.preventMark = preventMark === true;
35177         var v = this.validateValue(this.processValue(this.getRawValue()));
35178         this.preventMark = restore;
35179         return v;
35180     },
35181
35182     /**
35183      * Validates the field value
35184      * @return {Boolean} True if the value is valid, else false
35185      */
35186     validate : function(){
35187         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35188             this.clearInvalid();
35189             return true;
35190         }
35191         return false;
35192     },
35193
35194     processValue : function(value){
35195         return value;
35196     },
35197
35198     // private
35199     // Subclasses should provide the validation implementation by overriding this
35200     validateValue : function(value){
35201         return true;
35202     },
35203
35204     /**
35205      * Mark this field as invalid
35206      * @param {String} msg The validation message
35207      */
35208     markInvalid : function(msg){
35209         if(!this.rendered || this.preventMark){ // not rendered
35210             return;
35211         }
35212         this.el.addClass(this.invalidClass);
35213         msg = msg || this.invalidText;
35214         switch(this.msgTarget){
35215             case 'qtip':
35216                 this.el.dom.qtip = msg;
35217                 this.el.dom.qclass = 'x-form-invalid-tip';
35218                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35219                     Roo.QuickTips.enable();
35220                 }
35221                 break;
35222             case 'title':
35223                 this.el.dom.title = msg;
35224                 break;
35225             case 'under':
35226                 if(!this.errorEl){
35227                     var elp = this.el.findParent('.x-form-element', 5, true);
35228                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35229                     this.errorEl.setWidth(elp.getWidth(true)-20);
35230                 }
35231                 this.errorEl.update(msg);
35232                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35233                 break;
35234             case 'side':
35235                 if(!this.errorIcon){
35236                     var elp = this.el.findParent('.x-form-element', 5, true);
35237                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35238                 }
35239                 this.alignErrorIcon();
35240                 this.errorIcon.dom.qtip = msg;
35241                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35242                 this.errorIcon.show();
35243                 this.on('resize', this.alignErrorIcon, this);
35244                 break;
35245             default:
35246                 var t = Roo.getDom(this.msgTarget);
35247                 t.innerHTML = msg;
35248                 t.style.display = this.msgDisplay;
35249                 break;
35250         }
35251         this.fireEvent('invalid', this, msg);
35252     },
35253
35254     // private
35255     alignErrorIcon : function(){
35256         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35257     },
35258
35259     /**
35260      * Clear any invalid styles/messages for this field
35261      */
35262     clearInvalid : function(){
35263         if(!this.rendered || this.preventMark){ // not rendered
35264             return;
35265         }
35266         this.el.removeClass(this.invalidClass);
35267         switch(this.msgTarget){
35268             case 'qtip':
35269                 this.el.dom.qtip = '';
35270                 break;
35271             case 'title':
35272                 this.el.dom.title = '';
35273                 break;
35274             case 'under':
35275                 if(this.errorEl){
35276                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35277                 }
35278                 break;
35279             case 'side':
35280                 if(this.errorIcon){
35281                     this.errorIcon.dom.qtip = '';
35282                     this.errorIcon.hide();
35283                     this.un('resize', this.alignErrorIcon, this);
35284                 }
35285                 break;
35286             default:
35287                 var t = Roo.getDom(this.msgTarget);
35288                 t.innerHTML = '';
35289                 t.style.display = 'none';
35290                 break;
35291         }
35292         this.fireEvent('valid', this);
35293     },
35294
35295     /**
35296      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35297      * @return {Mixed} value The field value
35298      */
35299     getRawValue : function(){
35300         var v = this.el.getValue();
35301         if(v === this.emptyText){
35302             v = '';
35303         }
35304         return v;
35305     },
35306
35307     /**
35308      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35309      * @return {Mixed} value The field value
35310      */
35311     getValue : function(){
35312         var v = this.el.getValue();
35313         if(v === this.emptyText || v === undefined){
35314             v = '';
35315         }
35316         return v;
35317     },
35318
35319     /**
35320      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35321      * @param {Mixed} value The value to set
35322      */
35323     setRawValue : function(v){
35324         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35325     },
35326
35327     /**
35328      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35329      * @param {Mixed} value The value to set
35330      */
35331     setValue : function(v){
35332         this.value = v;
35333         if(this.rendered){
35334             this.el.dom.value = (v === null || v === undefined ? '' : v);
35335             this.validate();
35336         }
35337     },
35338
35339     adjustSize : function(w, h){
35340         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35341         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35342         return s;
35343     },
35344
35345     adjustWidth : function(tag, w){
35346         tag = tag.toLowerCase();
35347         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35348             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35349                 if(tag == 'input'){
35350                     return w + 2;
35351                 }
35352                 if(tag = 'textarea'){
35353                     return w-2;
35354                 }
35355             }else if(Roo.isOpera){
35356                 if(tag == 'input'){
35357                     return w + 2;
35358                 }
35359                 if(tag = 'textarea'){
35360                     return w-2;
35361                 }
35362             }
35363         }
35364         return w;
35365     }
35366 });
35367
35368
35369 // anything other than normal should be considered experimental
35370 Roo.form.Field.msgFx = {
35371     normal : {
35372         show: function(msgEl, f){
35373             msgEl.setDisplayed('block');
35374         },
35375
35376         hide : function(msgEl, f){
35377             msgEl.setDisplayed(false).update('');
35378         }
35379     },
35380
35381     slide : {
35382         show: function(msgEl, f){
35383             msgEl.slideIn('t', {stopFx:true});
35384         },
35385
35386         hide : function(msgEl, f){
35387             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35388         }
35389     },
35390
35391     slideRight : {
35392         show: function(msgEl, f){
35393             msgEl.fixDisplay();
35394             msgEl.alignTo(f.el, 'tl-tr');
35395             msgEl.slideIn('l', {stopFx:true});
35396         },
35397
35398         hide : function(msgEl, f){
35399             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35400         }
35401     }
35402 };/*
35403  * Based on:
35404  * Ext JS Library 1.1.1
35405  * Copyright(c) 2006-2007, Ext JS, LLC.
35406  *
35407  * Originally Released Under LGPL - original licence link has changed is not relivant.
35408  *
35409  * Fork - LGPL
35410  * <script type="text/javascript">
35411  */
35412  
35413
35414 /**
35415  * @class Roo.form.TextField
35416  * @extends Roo.form.Field
35417  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35418  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35419  * @constructor
35420  * Creates a new TextField
35421  * @param {Object} config Configuration options
35422  */
35423 Roo.form.TextField = function(config){
35424     Roo.form.TextField.superclass.constructor.call(this, config);
35425     this.addEvents({
35426         /**
35427          * @event autosize
35428          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35429          * according to the default logic, but this event provides a hook for the developer to apply additional
35430          * logic at runtime to resize the field if needed.
35431              * @param {Roo.form.Field} this This text field
35432              * @param {Number} width The new field width
35433              */
35434         autosize : true
35435     });
35436 };
35437
35438 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35439     /**
35440      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35441      */
35442     grow : false,
35443     /**
35444      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35445      */
35446     growMin : 30,
35447     /**
35448      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35449      */
35450     growMax : 800,
35451     /**
35452      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35453      */
35454     vtype : null,
35455     /**
35456      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35457      */
35458     maskRe : null,
35459     /**
35460      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35461      */
35462     disableKeyFilter : false,
35463     /**
35464      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35465      */
35466     allowBlank : true,
35467     /**
35468      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35469      */
35470     minLength : 0,
35471     /**
35472      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35473      */
35474     maxLength : Number.MAX_VALUE,
35475     /**
35476      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35477      */
35478     minLengthText : "The minimum length for this field is {0}",
35479     /**
35480      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35481      */
35482     maxLengthText : "The maximum length for this field is {0}",
35483     /**
35484      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35485      */
35486     selectOnFocus : false,
35487     /**
35488      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35489      */
35490     blankText : "This field is required",
35491     /**
35492      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35493      * If available, this function will be called only after the basic validators all return true, and will be passed the
35494      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35495      */
35496     validator : null,
35497     /**
35498      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35499      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35500      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35501      */
35502     regex : null,
35503     /**
35504      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35505      */
35506     regexText : "",
35507     /**
35508      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35509      */
35510     emptyText : null,
35511     /**
35512      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35513      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35514      */
35515     emptyClass : 'x-form-empty-field',
35516
35517     // private
35518     initEvents : function(){
35519         Roo.form.TextField.superclass.initEvents.call(this);
35520         if(this.validationEvent == 'keyup'){
35521             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35522             this.el.on('keyup', this.filterValidation, this);
35523         }
35524         else if(this.validationEvent !== false){
35525             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35526         }
35527         if(this.selectOnFocus || this.emptyText){
35528             this.on("focus", this.preFocus, this);
35529             if(this.emptyText){
35530                 this.on('blur', this.postBlur, this);
35531                 this.applyEmptyText();
35532             }
35533         }
35534         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35535             this.el.on("keypress", this.filterKeys, this);
35536         }
35537         if(this.grow){
35538             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35539             this.el.on("click", this.autoSize,  this);
35540         }
35541     },
35542
35543     processValue : function(value){
35544         if(this.stripCharsRe){
35545             var newValue = value.replace(this.stripCharsRe, '');
35546             if(newValue !== value){
35547                 this.setRawValue(newValue);
35548                 return newValue;
35549             }
35550         }
35551         return value;
35552     },
35553
35554     filterValidation : function(e){
35555         if(!e.isNavKeyPress()){
35556             this.validationTask.delay(this.validationDelay);
35557         }
35558     },
35559
35560     // private
35561     onKeyUp : function(e){
35562         if(!e.isNavKeyPress()){
35563             this.autoSize();
35564         }
35565     },
35566
35567     /**
35568      * Resets the current field value to the originally-loaded value and clears any validation messages.
35569      * Also adds emptyText and emptyClass if the original value was blank.
35570      */
35571     reset : function(){
35572         Roo.form.TextField.superclass.reset.call(this);
35573         this.applyEmptyText();
35574     },
35575
35576     applyEmptyText : function(){
35577         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35578             this.setRawValue(this.emptyText);
35579             this.el.addClass(this.emptyClass);
35580         }
35581     },
35582
35583     // private
35584     preFocus : function(){
35585         if(this.emptyText){
35586             if(this.el.dom.value == this.emptyText){
35587                 this.setRawValue('');
35588             }
35589             this.el.removeClass(this.emptyClass);
35590         }
35591         if(this.selectOnFocus){
35592             this.el.dom.select();
35593         }
35594     },
35595
35596     // private
35597     postBlur : function(){
35598         this.applyEmptyText();
35599     },
35600
35601     // private
35602     filterKeys : function(e){
35603         var k = e.getKey();
35604         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35605             return;
35606         }
35607         var c = e.getCharCode(), cc = String.fromCharCode(c);
35608         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35609             return;
35610         }
35611         if(!this.maskRe.test(cc)){
35612             e.stopEvent();
35613         }
35614     },
35615
35616     setValue : function(v){
35617         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35618             this.el.removeClass(this.emptyClass);
35619         }
35620         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35621         this.applyEmptyText();
35622         this.autoSize();
35623     },
35624
35625     /**
35626      * Validates a value according to the field's validation rules and marks the field as invalid
35627      * if the validation fails
35628      * @param {Mixed} value The value to validate
35629      * @return {Boolean} True if the value is valid, else false
35630      */
35631     validateValue : function(value){
35632         if(value.length < 1 || value === this.emptyText){ // if it's blank
35633              if(this.allowBlank){
35634                 this.clearInvalid();
35635                 return true;
35636              }else{
35637                 this.markInvalid(this.blankText);
35638                 return false;
35639              }
35640         }
35641         if(value.length < this.minLength){
35642             this.markInvalid(String.format(this.minLengthText, this.minLength));
35643             return false;
35644         }
35645         if(value.length > this.maxLength){
35646             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35647             return false;
35648         }
35649         if(this.vtype){
35650             var vt = Roo.form.VTypes;
35651             if(!vt[this.vtype](value, this)){
35652                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35653                 return false;
35654             }
35655         }
35656         if(typeof this.validator == "function"){
35657             var msg = this.validator(value);
35658             if(msg !== true){
35659                 this.markInvalid(msg);
35660                 return false;
35661             }
35662         }
35663         if(this.regex && !this.regex.test(value)){
35664             this.markInvalid(this.regexText);
35665             return false;
35666         }
35667         return true;
35668     },
35669
35670     /**
35671      * Selects text in this field
35672      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35673      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35674      */
35675     selectText : function(start, end){
35676         var v = this.getRawValue();
35677         if(v.length > 0){
35678             start = start === undefined ? 0 : start;
35679             end = end === undefined ? v.length : end;
35680             var d = this.el.dom;
35681             if(d.setSelectionRange){
35682                 d.setSelectionRange(start, end);
35683             }else if(d.createTextRange){
35684                 var range = d.createTextRange();
35685                 range.moveStart("character", start);
35686                 range.moveEnd("character", v.length-end);
35687                 range.select();
35688             }
35689         }
35690     },
35691
35692     /**
35693      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35694      * This only takes effect if grow = true, and fires the autosize event.
35695      */
35696     autoSize : function(){
35697         if(!this.grow || !this.rendered){
35698             return;
35699         }
35700         if(!this.metrics){
35701             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35702         }
35703         var el = this.el;
35704         var v = el.dom.value;
35705         var d = document.createElement('div');
35706         d.appendChild(document.createTextNode(v));
35707         v = d.innerHTML;
35708         d = null;
35709         v += "&#160;";
35710         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35711         this.el.setWidth(w);
35712         this.fireEvent("autosize", this, w);
35713     }
35714 });/*
35715  * Based on:
35716  * Ext JS Library 1.1.1
35717  * Copyright(c) 2006-2007, Ext JS, LLC.
35718  *
35719  * Originally Released Under LGPL - original licence link has changed is not relivant.
35720  *
35721  * Fork - LGPL
35722  * <script type="text/javascript">
35723  */
35724  
35725 /**
35726  * @class Roo.form.Hidden
35727  * @extends Roo.form.TextField
35728  * Simple Hidden element used on forms 
35729  * 
35730  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35731  * 
35732  * @constructor
35733  * Creates a new Hidden form element.
35734  * @param {Object} config Configuration options
35735  */
35736
35737
35738
35739 // easy hidden field...
35740 Roo.form.Hidden = function(config){
35741     Roo.form.Hidden.superclass.constructor.call(this, config);
35742 };
35743   
35744 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35745     fieldLabel:      '',
35746     inputType:      'hidden',
35747     width:          50,
35748     allowBlank:     true,
35749     labelSeparator: '',
35750     hidden:         true,
35751     itemCls :       'x-form-item-display-none'
35752
35753
35754 });
35755
35756
35757 /*
35758  * Based on:
35759  * Ext JS Library 1.1.1
35760  * Copyright(c) 2006-2007, Ext JS, LLC.
35761  *
35762  * Originally Released Under LGPL - original licence link has changed is not relivant.
35763  *
35764  * Fork - LGPL
35765  * <script type="text/javascript">
35766  */
35767  
35768 /**
35769  * @class Roo.form.TriggerField
35770  * @extends Roo.form.TextField
35771  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35772  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35773  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35774  * for which you can provide a custom implementation.  For example:
35775  * <pre><code>
35776 var trigger = new Roo.form.TriggerField();
35777 trigger.onTriggerClick = myTriggerFn;
35778 trigger.applyTo('my-field');
35779 </code></pre>
35780  *
35781  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35782  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35783  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35784  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35785  * @constructor
35786  * Create a new TriggerField.
35787  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35788  * to the base TextField)
35789  */
35790 Roo.form.TriggerField = function(config){
35791     this.mimicing = false;
35792     Roo.form.TriggerField.superclass.constructor.call(this, config);
35793 };
35794
35795 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35796     /**
35797      * @cfg {String} triggerClass A CSS class to apply to the trigger
35798      */
35799     /**
35800      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35801      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35802      */
35803     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35804     /**
35805      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35806      */
35807     hideTrigger:false,
35808
35809     /** @cfg {Boolean} grow @hide */
35810     /** @cfg {Number} growMin @hide */
35811     /** @cfg {Number} growMax @hide */
35812
35813     /**
35814      * @hide 
35815      * @method
35816      */
35817     autoSize: Roo.emptyFn,
35818     // private
35819     monitorTab : true,
35820     // private
35821     deferHeight : true,
35822
35823     
35824     actionMode : 'wrap',
35825     // private
35826     onResize : function(w, h){
35827         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35828         if(typeof w == 'number'){
35829             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35830         }
35831     },
35832
35833     // private
35834     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35835
35836     // private
35837     getResizeEl : function(){
35838         return this.wrap;
35839     },
35840
35841     // private
35842     getPositionEl : function(){
35843         return this.wrap;
35844     },
35845
35846     // private
35847     alignErrorIcon : function(){
35848         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35849     },
35850
35851     // private
35852     onRender : function(ct, position){
35853         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35854         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35855         this.trigger = this.wrap.createChild(this.triggerConfig ||
35856                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35857         if(this.hideTrigger){
35858             this.trigger.setDisplayed(false);
35859         }
35860         this.initTrigger();
35861         if(!this.width){
35862             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35863         }
35864     },
35865
35866     // private
35867     initTrigger : function(){
35868         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35869         this.trigger.addClassOnOver('x-form-trigger-over');
35870         this.trigger.addClassOnClick('x-form-trigger-click');
35871     },
35872
35873     // private
35874     onDestroy : function(){
35875         if(this.trigger){
35876             this.trigger.removeAllListeners();
35877             this.trigger.remove();
35878         }
35879         if(this.wrap){
35880             this.wrap.remove();
35881         }
35882         Roo.form.TriggerField.superclass.onDestroy.call(this);
35883     },
35884
35885     // private
35886     onFocus : function(){
35887         Roo.form.TriggerField.superclass.onFocus.call(this);
35888         if(!this.mimicing){
35889             this.wrap.addClass('x-trigger-wrap-focus');
35890             this.mimicing = true;
35891             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35892             if(this.monitorTab){
35893                 this.el.on("keydown", this.checkTab, this);
35894             }
35895         }
35896     },
35897
35898     // private
35899     checkTab : function(e){
35900         if(e.getKey() == e.TAB){
35901             this.triggerBlur();
35902         }
35903     },
35904
35905     // private
35906     onBlur : function(){
35907         // do nothing
35908     },
35909
35910     // private
35911     mimicBlur : function(e, t){
35912         if(!this.wrap.contains(t) && this.validateBlur()){
35913             this.triggerBlur();
35914         }
35915     },
35916
35917     // private
35918     triggerBlur : function(){
35919         this.mimicing = false;
35920         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35921         if(this.monitorTab){
35922             this.el.un("keydown", this.checkTab, this);
35923         }
35924         this.wrap.removeClass('x-trigger-wrap-focus');
35925         Roo.form.TriggerField.superclass.onBlur.call(this);
35926     },
35927
35928     // private
35929     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35930     validateBlur : function(e, t){
35931         return true;
35932     },
35933
35934     // private
35935     onDisable : function(){
35936         Roo.form.TriggerField.superclass.onDisable.call(this);
35937         if(this.wrap){
35938             this.wrap.addClass('x-item-disabled');
35939         }
35940     },
35941
35942     // private
35943     onEnable : function(){
35944         Roo.form.TriggerField.superclass.onEnable.call(this);
35945         if(this.wrap){
35946             this.wrap.removeClass('x-item-disabled');
35947         }
35948     },
35949
35950     // private
35951     onShow : function(){
35952         var ae = this.getActionEl();
35953         
35954         if(ae){
35955             ae.dom.style.display = '';
35956             ae.dom.style.visibility = 'visible';
35957         }
35958     },
35959
35960     // private
35961     
35962     onHide : function(){
35963         var ae = this.getActionEl();
35964         ae.dom.style.display = 'none';
35965     },
35966
35967     /**
35968      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35969      * by an implementing function.
35970      * @method
35971      * @param {EventObject} e
35972      */
35973     onTriggerClick : Roo.emptyFn
35974 });
35975
35976 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35977 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35978 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35979 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35980     initComponent : function(){
35981         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35982
35983         this.triggerConfig = {
35984             tag:'span', cls:'x-form-twin-triggers', cn:[
35985             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35986             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35987         ]};
35988     },
35989
35990     getTrigger : function(index){
35991         return this.triggers[index];
35992     },
35993
35994     initTrigger : function(){
35995         var ts = this.trigger.select('.x-form-trigger', true);
35996         this.wrap.setStyle('overflow', 'hidden');
35997         var triggerField = this;
35998         ts.each(function(t, all, index){
35999             t.hide = function(){
36000                 var w = triggerField.wrap.getWidth();
36001                 this.dom.style.display = 'none';
36002                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36003             };
36004             t.show = function(){
36005                 var w = triggerField.wrap.getWidth();
36006                 this.dom.style.display = '';
36007                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36008             };
36009             var triggerIndex = 'Trigger'+(index+1);
36010
36011             if(this['hide'+triggerIndex]){
36012                 t.dom.style.display = 'none';
36013             }
36014             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36015             t.addClassOnOver('x-form-trigger-over');
36016             t.addClassOnClick('x-form-trigger-click');
36017         }, this);
36018         this.triggers = ts.elements;
36019     },
36020
36021     onTrigger1Click : Roo.emptyFn,
36022     onTrigger2Click : Roo.emptyFn
36023 });/*
36024  * Based on:
36025  * Ext JS Library 1.1.1
36026  * Copyright(c) 2006-2007, Ext JS, LLC.
36027  *
36028  * Originally Released Under LGPL - original licence link has changed is not relivant.
36029  *
36030  * Fork - LGPL
36031  * <script type="text/javascript">
36032  */
36033  
36034 /**
36035  * @class Roo.form.TextArea
36036  * @extends Roo.form.TextField
36037  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36038  * support for auto-sizing.
36039  * @constructor
36040  * Creates a new TextArea
36041  * @param {Object} config Configuration options
36042  */
36043 Roo.form.TextArea = function(config){
36044     Roo.form.TextArea.superclass.constructor.call(this, config);
36045     // these are provided exchanges for backwards compat
36046     // minHeight/maxHeight were replaced by growMin/growMax to be
36047     // compatible with TextField growing config values
36048     if(this.minHeight !== undefined){
36049         this.growMin = this.minHeight;
36050     }
36051     if(this.maxHeight !== undefined){
36052         this.growMax = this.maxHeight;
36053     }
36054 };
36055
36056 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36057     /**
36058      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36059      */
36060     growMin : 60,
36061     /**
36062      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36063      */
36064     growMax: 1000,
36065     /**
36066      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36067      * in the field (equivalent to setting overflow: hidden, defaults to false)
36068      */
36069     preventScrollbars: false,
36070     /**
36071      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36072      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36073      */
36074
36075     // private
36076     onRender : function(ct, position){
36077         if(!this.el){
36078             this.defaultAutoCreate = {
36079                 tag: "textarea",
36080                 style:"width:300px;height:60px;",
36081                 autocomplete: "off"
36082             };
36083         }
36084         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36085         if(this.grow){
36086             this.textSizeEl = Roo.DomHelper.append(document.body, {
36087                 tag: "pre", cls: "x-form-grow-sizer"
36088             });
36089             if(this.preventScrollbars){
36090                 this.el.setStyle("overflow", "hidden");
36091             }
36092             this.el.setHeight(this.growMin);
36093         }
36094     },
36095
36096     onDestroy : function(){
36097         if(this.textSizeEl){
36098             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36099         }
36100         Roo.form.TextArea.superclass.onDestroy.call(this);
36101     },
36102
36103     // private
36104     onKeyUp : function(e){
36105         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36106             this.autoSize();
36107         }
36108     },
36109
36110     /**
36111      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36112      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36113      */
36114     autoSize : function(){
36115         if(!this.grow || !this.textSizeEl){
36116             return;
36117         }
36118         var el = this.el;
36119         var v = el.dom.value;
36120         var ts = this.textSizeEl;
36121
36122         ts.innerHTML = '';
36123         ts.appendChild(document.createTextNode(v));
36124         v = ts.innerHTML;
36125
36126         Roo.fly(ts).setWidth(this.el.getWidth());
36127         if(v.length < 1){
36128             v = "&#160;&#160;";
36129         }else{
36130             if(Roo.isIE){
36131                 v = v.replace(/\n/g, '<p>&#160;</p>');
36132             }
36133             v += "&#160;\n&#160;";
36134         }
36135         ts.innerHTML = v;
36136         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36137         if(h != this.lastHeight){
36138             this.lastHeight = h;
36139             this.el.setHeight(h);
36140             this.fireEvent("autosize", this, h);
36141         }
36142     }
36143 });/*
36144  * Based on:
36145  * Ext JS Library 1.1.1
36146  * Copyright(c) 2006-2007, Ext JS, LLC.
36147  *
36148  * Originally Released Under LGPL - original licence link has changed is not relivant.
36149  *
36150  * Fork - LGPL
36151  * <script type="text/javascript">
36152  */
36153  
36154
36155 /**
36156  * @class Roo.form.NumberField
36157  * @extends Roo.form.TextField
36158  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36159  * @constructor
36160  * Creates a new NumberField
36161  * @param {Object} config Configuration options
36162  */
36163 Roo.form.NumberField = function(config){
36164     Roo.form.NumberField.superclass.constructor.call(this, config);
36165 };
36166
36167 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36168     /**
36169      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36170      */
36171     fieldClass: "x-form-field x-form-num-field",
36172     /**
36173      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36174      */
36175     allowDecimals : true,
36176     /**
36177      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36178      */
36179     decimalSeparator : ".",
36180     /**
36181      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36182      */
36183     decimalPrecision : 2,
36184     /**
36185      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36186      */
36187     allowNegative : true,
36188     /**
36189      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36190      */
36191     minValue : Number.NEGATIVE_INFINITY,
36192     /**
36193      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36194      */
36195     maxValue : Number.MAX_VALUE,
36196     /**
36197      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36198      */
36199     minText : "The minimum value for this field is {0}",
36200     /**
36201      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36202      */
36203     maxText : "The maximum value for this field is {0}",
36204     /**
36205      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36206      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36207      */
36208     nanText : "{0} is not a valid number",
36209
36210     // private
36211     initEvents : function(){
36212         Roo.form.NumberField.superclass.initEvents.call(this);
36213         var allowed = "0123456789";
36214         if(this.allowDecimals){
36215             allowed += this.decimalSeparator;
36216         }
36217         if(this.allowNegative){
36218             allowed += "-";
36219         }
36220         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36221         var keyPress = function(e){
36222             var k = e.getKey();
36223             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36224                 return;
36225             }
36226             var c = e.getCharCode();
36227             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36228                 e.stopEvent();
36229             }
36230         };
36231         this.el.on("keypress", keyPress, this);
36232     },
36233
36234     // private
36235     validateValue : function(value){
36236         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36237             return false;
36238         }
36239         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36240              return true;
36241         }
36242         var num = this.parseValue(value);
36243         if(isNaN(num)){
36244             this.markInvalid(String.format(this.nanText, value));
36245             return false;
36246         }
36247         if(num < this.minValue){
36248             this.markInvalid(String.format(this.minText, this.minValue));
36249             return false;
36250         }
36251         if(num > this.maxValue){
36252             this.markInvalid(String.format(this.maxText, this.maxValue));
36253             return false;
36254         }
36255         return true;
36256     },
36257
36258     getValue : function(){
36259         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36260     },
36261
36262     // private
36263     parseValue : function(value){
36264         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36265         return isNaN(value) ? '' : value;
36266     },
36267
36268     // private
36269     fixPrecision : function(value){
36270         var nan = isNaN(value);
36271         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36272             return nan ? '' : value;
36273         }
36274         return parseFloat(value).toFixed(this.decimalPrecision);
36275     },
36276
36277     setValue : function(v){
36278         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36279     },
36280
36281     // private
36282     decimalPrecisionFcn : function(v){
36283         return Math.floor(v);
36284     },
36285
36286     beforeBlur : function(){
36287         var v = this.parseValue(this.getRawValue());
36288         if(v){
36289             this.setValue(this.fixPrecision(v));
36290         }
36291     }
36292 });/*
36293  * Based on:
36294  * Ext JS Library 1.1.1
36295  * Copyright(c) 2006-2007, Ext JS, LLC.
36296  *
36297  * Originally Released Under LGPL - original licence link has changed is not relivant.
36298  *
36299  * Fork - LGPL
36300  * <script type="text/javascript">
36301  */
36302  
36303 /**
36304  * @class Roo.form.DateField
36305  * @extends Roo.form.TriggerField
36306  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36307 * @constructor
36308 * Create a new DateField
36309 * @param {Object} config
36310  */
36311 Roo.form.DateField = function(config){
36312     Roo.form.DateField.superclass.constructor.call(this, config);
36313     
36314       this.addEvents({
36315          
36316         /**
36317          * @event select
36318          * Fires when a date is selected
36319              * @param {Roo.form.DateField} combo This combo box
36320              * @param {Date} date The date selected
36321              */
36322         'select' : true
36323          
36324     });
36325     
36326     
36327     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36328     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36329     this.ddMatch = null;
36330     if(this.disabledDates){
36331         var dd = this.disabledDates;
36332         var re = "(?:";
36333         for(var i = 0; i < dd.length; i++){
36334             re += dd[i];
36335             if(i != dd.length-1) re += "|";
36336         }
36337         this.ddMatch = new RegExp(re + ")");
36338     }
36339 };
36340
36341 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36342     /**
36343      * @cfg {String} format
36344      * The default date format string which can be overriden for localization support.  The format must be
36345      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36346      */
36347     format : "m/d/y",
36348     /**
36349      * @cfg {String} altFormats
36350      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36351      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36352      */
36353     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36354     /**
36355      * @cfg {Array} disabledDays
36356      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36357      */
36358     disabledDays : null,
36359     /**
36360      * @cfg {String} disabledDaysText
36361      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36362      */
36363     disabledDaysText : "Disabled",
36364     /**
36365      * @cfg {Array} disabledDates
36366      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36367      * expression so they are very powerful. Some examples:
36368      * <ul>
36369      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36370      * <li>["03/08", "09/16"] would disable those days for every year</li>
36371      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36372      * <li>["03/../2006"] would disable every day in March 2006</li>
36373      * <li>["^03"] would disable every day in every March</li>
36374      * </ul>
36375      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36376      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36377      */
36378     disabledDates : null,
36379     /**
36380      * @cfg {String} disabledDatesText
36381      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36382      */
36383     disabledDatesText : "Disabled",
36384     /**
36385      * @cfg {Date/String} minValue
36386      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36387      * valid format (defaults to null).
36388      */
36389     minValue : null,
36390     /**
36391      * @cfg {Date/String} maxValue
36392      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36393      * valid format (defaults to null).
36394      */
36395     maxValue : null,
36396     /**
36397      * @cfg {String} minText
36398      * The error text to display when the date in the cell is before minValue (defaults to
36399      * 'The date in this field must be after {minValue}').
36400      */
36401     minText : "The date in this field must be equal to or after {0}",
36402     /**
36403      * @cfg {String} maxText
36404      * The error text to display when the date in the cell is after maxValue (defaults to
36405      * 'The date in this field must be before {maxValue}').
36406      */
36407     maxText : "The date in this field must be equal to or before {0}",
36408     /**
36409      * @cfg {String} invalidText
36410      * The error text to display when the date in the field is invalid (defaults to
36411      * '{value} is not a valid date - it must be in the format {format}').
36412      */
36413     invalidText : "{0} is not a valid date - it must be in the format {1}",
36414     /**
36415      * @cfg {String} triggerClass
36416      * An additional CSS class used to style the trigger button.  The trigger will always get the
36417      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36418      * which displays a calendar icon).
36419      */
36420     triggerClass : 'x-form-date-trigger',
36421     
36422
36423     /**
36424      * @cfg {bool} useIso
36425      * if enabled, then the date field will use a hidden field to store the 
36426      * real value as iso formated date. default (false)
36427      */ 
36428     useIso : false,
36429     /**
36430      * @cfg {String/Object} autoCreate
36431      * A DomHelper element spec, or true for a default element spec (defaults to
36432      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36433      */ 
36434     // private
36435     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36436     
36437     // private
36438     hiddenField: false,
36439     
36440     onRender : function(ct, position)
36441     {
36442         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36443         if (this.useIso) {
36444             this.el.dom.removeAttribute('name'); 
36445             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36446                     'before', true);
36447             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36448             // prevent input submission
36449             this.hiddenName = this.name;
36450         }
36451             
36452             
36453     },
36454     
36455     // private
36456     validateValue : function(value)
36457     {
36458         value = this.formatDate(value);
36459         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36460             return false;
36461         }
36462         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36463              return true;
36464         }
36465         var svalue = value;
36466         value = this.parseDate(value);
36467         if(!value){
36468             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36469             return false;
36470         }
36471         var time = value.getTime();
36472         if(this.minValue && time < this.minValue.getTime()){
36473             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36474             return false;
36475         }
36476         if(this.maxValue && time > this.maxValue.getTime()){
36477             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36478             return false;
36479         }
36480         if(this.disabledDays){
36481             var day = value.getDay();
36482             for(var i = 0; i < this.disabledDays.length; i++) {
36483                 if(day === this.disabledDays[i]){
36484                     this.markInvalid(this.disabledDaysText);
36485                     return false;
36486                 }
36487             }
36488         }
36489         var fvalue = this.formatDate(value);
36490         if(this.ddMatch && this.ddMatch.test(fvalue)){
36491             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36492             return false;
36493         }
36494         return true;
36495     },
36496
36497     // private
36498     // Provides logic to override the default TriggerField.validateBlur which just returns true
36499     validateBlur : function(){
36500         return !this.menu || !this.menu.isVisible();
36501     },
36502
36503     /**
36504      * Returns the current date value of the date field.
36505      * @return {Date} The date value
36506      */
36507     getValue : function(){
36508         
36509         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36510     },
36511
36512     /**
36513      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36514      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36515      * (the default format used is "m/d/y").
36516      * <br />Usage:
36517      * <pre><code>
36518 //All of these calls set the same date value (May 4, 2006)
36519
36520 //Pass a date object:
36521 var dt = new Date('5/4/06');
36522 dateField.setValue(dt);
36523
36524 //Pass a date string (default format):
36525 dateField.setValue('5/4/06');
36526
36527 //Pass a date string (custom format):
36528 dateField.format = 'Y-m-d';
36529 dateField.setValue('2006-5-4');
36530 </code></pre>
36531      * @param {String/Date} date The date or valid date string
36532      */
36533     setValue : function(date){
36534         if (this.hiddenField) {
36535             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36536         }
36537         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36538     },
36539
36540     // private
36541     parseDate : function(value){
36542         if(!value || value instanceof Date){
36543             return value;
36544         }
36545         var v = Date.parseDate(value, this.format);
36546         if(!v && this.altFormats){
36547             if(!this.altFormatsArray){
36548                 this.altFormatsArray = this.altFormats.split("|");
36549             }
36550             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36551                 v = Date.parseDate(value, this.altFormatsArray[i]);
36552             }
36553         }
36554         return v;
36555     },
36556
36557     // private
36558     formatDate : function(date, fmt){
36559         return (!date || !(date instanceof Date)) ?
36560                date : date.dateFormat(fmt || this.format);
36561     },
36562
36563     // private
36564     menuListeners : {
36565         select: function(m, d){
36566             this.setValue(d);
36567             this.fireEvent('select', this, d);
36568         },
36569         show : function(){ // retain focus styling
36570             this.onFocus();
36571         },
36572         hide : function(){
36573             this.focus.defer(10, this);
36574             var ml = this.menuListeners;
36575             this.menu.un("select", ml.select,  this);
36576             this.menu.un("show", ml.show,  this);
36577             this.menu.un("hide", ml.hide,  this);
36578         }
36579     },
36580
36581     // private
36582     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36583     onTriggerClick : function(){
36584         if(this.disabled){
36585             return;
36586         }
36587         if(this.menu == null){
36588             this.menu = new Roo.menu.DateMenu();
36589         }
36590         Roo.apply(this.menu.picker,  {
36591             showClear: this.allowBlank,
36592             minDate : this.minValue,
36593             maxDate : this.maxValue,
36594             disabledDatesRE : this.ddMatch,
36595             disabledDatesText : this.disabledDatesText,
36596             disabledDays : this.disabledDays,
36597             disabledDaysText : this.disabledDaysText,
36598             format : this.format,
36599             minText : String.format(this.minText, this.formatDate(this.minValue)),
36600             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36601         });
36602         this.menu.on(Roo.apply({}, this.menuListeners, {
36603             scope:this
36604         }));
36605         this.menu.picker.setValue(this.getValue() || new Date());
36606         this.menu.show(this.el, "tl-bl?");
36607     },
36608
36609     beforeBlur : function(){
36610         var v = this.parseDate(this.getRawValue());
36611         if(v){
36612             this.setValue(v);
36613         }
36614     }
36615
36616     /** @cfg {Boolean} grow @hide */
36617     /** @cfg {Number} growMin @hide */
36618     /** @cfg {Number} growMax @hide */
36619     /**
36620      * @hide
36621      * @method autoSize
36622      */
36623 });/*
36624  * Based on:
36625  * Ext JS Library 1.1.1
36626  * Copyright(c) 2006-2007, Ext JS, LLC.
36627  *
36628  * Originally Released Under LGPL - original licence link has changed is not relivant.
36629  *
36630  * Fork - LGPL
36631  * <script type="text/javascript">
36632  */
36633  
36634
36635 /**
36636  * @class Roo.form.ComboBox
36637  * @extends Roo.form.TriggerField
36638  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36639  * @constructor
36640  * Create a new ComboBox.
36641  * @param {Object} config Configuration options
36642  */
36643 Roo.form.ComboBox = function(config){
36644     Roo.form.ComboBox.superclass.constructor.call(this, config);
36645     this.addEvents({
36646         /**
36647          * @event expand
36648          * Fires when the dropdown list is expanded
36649              * @param {Roo.form.ComboBox} combo This combo box
36650              */
36651         'expand' : true,
36652         /**
36653          * @event collapse
36654          * Fires when the dropdown list is collapsed
36655              * @param {Roo.form.ComboBox} combo This combo box
36656              */
36657         'collapse' : true,
36658         /**
36659          * @event beforeselect
36660          * Fires before a list item is selected. Return false to cancel the selection.
36661              * @param {Roo.form.ComboBox} combo This combo box
36662              * @param {Roo.data.Record} record The data record returned from the underlying store
36663              * @param {Number} index The index of the selected item in the dropdown list
36664              */
36665         'beforeselect' : true,
36666         /**
36667          * @event select
36668          * Fires when a list item is selected
36669              * @param {Roo.form.ComboBox} combo This combo box
36670              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36671              * @param {Number} index The index of the selected item in the dropdown list
36672              */
36673         'select' : true,
36674         /**
36675          * @event beforequery
36676          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36677          * The event object passed has these properties:
36678              * @param {Roo.form.ComboBox} combo This combo box
36679              * @param {String} query The query
36680              * @param {Boolean} forceAll true to force "all" query
36681              * @param {Boolean} cancel true to cancel the query
36682              * @param {Object} e The query event object
36683              */
36684         'beforequery': true
36685     });
36686     if(this.transform){
36687         this.allowDomMove = false;
36688         var s = Roo.getDom(this.transform);
36689         if(!this.hiddenName){
36690             this.hiddenName = s.name;
36691         }
36692         if(!this.store){
36693             this.mode = 'local';
36694             var d = [], opts = s.options;
36695             for(var i = 0, len = opts.length;i < len; i++){
36696                 var o = opts[i];
36697                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36698                 if(o.selected) {
36699                     this.value = value;
36700                 }
36701                 d.push([value, o.text]);
36702             }
36703             this.store = new Roo.data.SimpleStore({
36704                 'id': 0,
36705                 fields: ['value', 'text'],
36706                 data : d
36707             });
36708             this.valueField = 'value';
36709             this.displayField = 'text';
36710         }
36711         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36712         if(!this.lazyRender){
36713             this.target = true;
36714             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36715             s.parentNode.removeChild(s); // remove it
36716             this.render(this.el.parentNode);
36717         }else{
36718             s.parentNode.removeChild(s); // remove it
36719         }
36720
36721     }
36722     if (this.store) {
36723         this.store = Roo.factory(this.store, Roo.data);
36724     }
36725     
36726     this.selectedIndex = -1;
36727     if(this.mode == 'local'){
36728         if(config.queryDelay === undefined){
36729             this.queryDelay = 10;
36730         }
36731         if(config.minChars === undefined){
36732             this.minChars = 0;
36733         }
36734     }
36735 };
36736
36737 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36738     /**
36739      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36740      */
36741     /**
36742      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36743      * rendering into an Roo.Editor, defaults to false)
36744      */
36745     /**
36746      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36747      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36748      */
36749     /**
36750      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36751      */
36752     /**
36753      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36754      * the dropdown list (defaults to undefined, with no header element)
36755      */
36756
36757      /**
36758      * @cfg {String/Roo.Template} tpl The template to use to render the output
36759      */
36760      
36761     // private
36762     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36763     /**
36764      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36765      */
36766     listWidth: undefined,
36767     /**
36768      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36769      * mode = 'remote' or 'text' if mode = 'local')
36770      */
36771     displayField: undefined,
36772     /**
36773      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36774      * mode = 'remote' or 'value' if mode = 'local'). 
36775      * Note: use of a valueField requires the user make a selection
36776      * in order for a value to be mapped.
36777      */
36778     valueField: undefined,
36779     /**
36780      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36781      * field's data value (defaults to the underlying DOM element's name)
36782      */
36783     hiddenName: undefined,
36784     /**
36785      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36786      */
36787     listClass: '',
36788     /**
36789      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36790      */
36791     selectedClass: 'x-combo-selected',
36792     /**
36793      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36794      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36795      * which displays a downward arrow icon).
36796      */
36797     triggerClass : 'x-form-arrow-trigger',
36798     /**
36799      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36800      */
36801     shadow:'sides',
36802     /**
36803      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36804      * anchor positions (defaults to 'tl-bl')
36805      */
36806     listAlign: 'tl-bl?',
36807     /**
36808      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36809      */
36810     maxHeight: 300,
36811     /**
36812      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36813      * query specified by the allQuery config option (defaults to 'query')
36814      */
36815     triggerAction: 'query',
36816     /**
36817      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36818      * (defaults to 4, does not apply if editable = false)
36819      */
36820     minChars : 4,
36821     /**
36822      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36823      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36824      */
36825     typeAhead: false,
36826     /**
36827      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36828      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36829      */
36830     queryDelay: 500,
36831     /**
36832      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36833      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36834      */
36835     pageSize: 0,
36836     /**
36837      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36838      * when editable = true (defaults to false)
36839      */
36840     selectOnFocus:false,
36841     /**
36842      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36843      */
36844     queryParam: 'query',
36845     /**
36846      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36847      * when mode = 'remote' (defaults to 'Loading...')
36848      */
36849     loadingText: 'Loading...',
36850     /**
36851      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36852      */
36853     resizable: false,
36854     /**
36855      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36856      */
36857     handleHeight : 8,
36858     /**
36859      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36860      * traditional select (defaults to true)
36861      */
36862     editable: true,
36863     /**
36864      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36865      */
36866     allQuery: '',
36867     /**
36868      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36869      */
36870     mode: 'remote',
36871     /**
36872      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36873      * listWidth has a higher value)
36874      */
36875     minListWidth : 70,
36876     /**
36877      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36878      * allow the user to set arbitrary text into the field (defaults to false)
36879      */
36880     forceSelection:false,
36881     /**
36882      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36883      * if typeAhead = true (defaults to 250)
36884      */
36885     typeAheadDelay : 250,
36886     /**
36887      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36888      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36889      */
36890     valueNotFoundText : undefined,
36891     /**
36892      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36893      */
36894     blockFocus : false,
36895     
36896     /**
36897      * @cfg {bool} disableClear Disable showing of clear button.
36898      */
36899     disableClear : false,
36900     
36901     // private
36902     onRender : function(ct, position){
36903         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36904         if(this.hiddenName){
36905             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36906                     'before', true);
36907             this.hiddenField.value =
36908                 this.hiddenValue !== undefined ? this.hiddenValue :
36909                 this.value !== undefined ? this.value : '';
36910
36911             // prevent input submission
36912             this.el.dom.removeAttribute('name');
36913         }
36914         if(Roo.isGecko){
36915             this.el.dom.setAttribute('autocomplete', 'off');
36916         }
36917
36918         var cls = 'x-combo-list';
36919
36920         this.list = new Roo.Layer({
36921             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36922         });
36923
36924         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36925         this.list.setWidth(lw);
36926         this.list.swallowEvent('mousewheel');
36927         this.assetHeight = 0;
36928
36929         if(this.title){
36930             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36931             this.assetHeight += this.header.getHeight();
36932         }
36933
36934         this.innerList = this.list.createChild({cls:cls+'-inner'});
36935         this.innerList.on('mouseover', this.onViewOver, this);
36936         this.innerList.on('mousemove', this.onViewMove, this);
36937         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36938         
36939         if(this.allowBlank && !this.pageSize && !this.disableClear){
36940             this.footer = this.list.createChild({cls:cls+'-ft'});
36941             this.pageTb = new Roo.Toolbar(this.footer);
36942            
36943         }
36944         if(this.pageSize){
36945             this.footer = this.list.createChild({cls:cls+'-ft'});
36946             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36947                     {pageSize: this.pageSize});
36948             
36949         }
36950         
36951         if (this.pageTb && this.allowBlank && !this.disableClear) {
36952             var _this = this;
36953             this.pageTb.add(new Roo.Toolbar.Fill(), {
36954                 cls: 'x-btn-icon x-btn-clear',
36955                 text: '&#160;',
36956                 handler: function()
36957                 {
36958                     _this.collapse();
36959                     _this.clearValue();
36960                     _this.onSelect(false, -1);
36961                 }
36962             });
36963         }
36964         if (this.footer) {
36965             this.assetHeight += this.footer.getHeight();
36966         }
36967         
36968
36969         if(!this.tpl){
36970             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36971         }
36972
36973         this.view = new Roo.View(this.innerList, this.tpl, {
36974             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36975         });
36976
36977         this.view.on('click', this.onViewClick, this);
36978
36979         this.store.on('beforeload', this.onBeforeLoad, this);
36980         this.store.on('load', this.onLoad, this);
36981         this.store.on('loadexception', this.collapse, this);
36982
36983         if(this.resizable){
36984             this.resizer = new Roo.Resizable(this.list,  {
36985                pinned:true, handles:'se'
36986             });
36987             this.resizer.on('resize', function(r, w, h){
36988                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36989                 this.listWidth = w;
36990                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36991                 this.restrictHeight();
36992             }, this);
36993             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36994         }
36995         if(!this.editable){
36996             this.editable = true;
36997             this.setEditable(false);
36998         }
36999     },
37000
37001     // private
37002     initEvents : function(){
37003         Roo.form.ComboBox.superclass.initEvents.call(this);
37004
37005         this.keyNav = new Roo.KeyNav(this.el, {
37006             "up" : function(e){
37007                 this.inKeyMode = true;
37008                 this.selectPrev();
37009             },
37010
37011             "down" : function(e){
37012                 if(!this.isExpanded()){
37013                     this.onTriggerClick();
37014                 }else{
37015                     this.inKeyMode = true;
37016                     this.selectNext();
37017                 }
37018             },
37019
37020             "enter" : function(e){
37021                 this.onViewClick();
37022                 //return true;
37023             },
37024
37025             "esc" : function(e){
37026                 this.collapse();
37027             },
37028
37029             "tab" : function(e){
37030                 this.onViewClick(false);
37031                 return true;
37032             },
37033
37034             scope : this,
37035
37036             doRelay : function(foo, bar, hname){
37037                 if(hname == 'down' || this.scope.isExpanded()){
37038                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37039                 }
37040                 return true;
37041             },
37042
37043             forceKeyDown: true
37044         });
37045         this.queryDelay = Math.max(this.queryDelay || 10,
37046                 this.mode == 'local' ? 10 : 250);
37047         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37048         if(this.typeAhead){
37049             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37050         }
37051         if(this.editable !== false){
37052             this.el.on("keyup", this.onKeyUp, this);
37053         }
37054         if(this.forceSelection){
37055             this.on('blur', this.doForce, this);
37056         }
37057     },
37058
37059     onDestroy : function(){
37060         if(this.view){
37061             this.view.setStore(null);
37062             this.view.el.removeAllListeners();
37063             this.view.el.remove();
37064             this.view.purgeListeners();
37065         }
37066         if(this.list){
37067             this.list.destroy();
37068         }
37069         if(this.store){
37070             this.store.un('beforeload', this.onBeforeLoad, this);
37071             this.store.un('load', this.onLoad, this);
37072             this.store.un('loadexception', this.collapse, this);
37073         }
37074         Roo.form.ComboBox.superclass.onDestroy.call(this);
37075     },
37076
37077     // private
37078     fireKey : function(e){
37079         if(e.isNavKeyPress() && !this.list.isVisible()){
37080             this.fireEvent("specialkey", this, e);
37081         }
37082     },
37083
37084     // private
37085     onResize: function(w, h){
37086         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37087         if(this.list && this.listWidth === undefined){
37088             var lw = Math.max(w, this.minListWidth);
37089             this.list.setWidth(lw);
37090             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37091         }
37092     },
37093
37094     /**
37095      * Allow or prevent the user from directly editing the field text.  If false is passed,
37096      * the user will only be able to select from the items defined in the dropdown list.  This method
37097      * is the runtime equivalent of setting the 'editable' config option at config time.
37098      * @param {Boolean} value True to allow the user to directly edit the field text
37099      */
37100     setEditable : function(value){
37101         if(value == this.editable){
37102             return;
37103         }
37104         this.editable = value;
37105         if(!value){
37106             this.el.dom.setAttribute('readOnly', true);
37107             this.el.on('mousedown', this.onTriggerClick,  this);
37108             this.el.addClass('x-combo-noedit');
37109         }else{
37110             this.el.dom.setAttribute('readOnly', false);
37111             this.el.un('mousedown', this.onTriggerClick,  this);
37112             this.el.removeClass('x-combo-noedit');
37113         }
37114     },
37115
37116     // private
37117     onBeforeLoad : function(){
37118         if(!this.hasFocus){
37119             return;
37120         }
37121         this.innerList.update(this.loadingText ?
37122                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37123         this.restrictHeight();
37124         this.selectedIndex = -1;
37125     },
37126
37127     // private
37128     onLoad : function(){
37129         if(!this.hasFocus){
37130             return;
37131         }
37132         if(this.store.getCount() > 0){
37133             this.expand();
37134             this.restrictHeight();
37135             if(this.lastQuery == this.allQuery){
37136                 if(this.editable){
37137                     this.el.dom.select();
37138                 }
37139                 if(!this.selectByValue(this.value, true)){
37140                     this.select(0, true);
37141                 }
37142             }else{
37143                 this.selectNext();
37144                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37145                     this.taTask.delay(this.typeAheadDelay);
37146                 }
37147             }
37148         }else{
37149             this.onEmptyResults();
37150         }
37151         //this.el.focus();
37152     },
37153
37154     // private
37155     onTypeAhead : function(){
37156         if(this.store.getCount() > 0){
37157             var r = this.store.getAt(0);
37158             var newValue = r.data[this.displayField];
37159             var len = newValue.length;
37160             var selStart = this.getRawValue().length;
37161             if(selStart != len){
37162                 this.setRawValue(newValue);
37163                 this.selectText(selStart, newValue.length);
37164             }
37165         }
37166     },
37167
37168     // private
37169     onSelect : function(record, index){
37170         if(this.fireEvent('beforeselect', this, record, index) !== false){
37171             this.setFromData(index > -1 ? record.data : false);
37172             this.collapse();
37173             this.fireEvent('select', this, record, index);
37174         }
37175     },
37176
37177     /**
37178      * Returns the currently selected field value or empty string if no value is set.
37179      * @return {String} value The selected value
37180      */
37181     getValue : function(){
37182         if(this.valueField){
37183             return typeof this.value != 'undefined' ? this.value : '';
37184         }else{
37185             return Roo.form.ComboBox.superclass.getValue.call(this);
37186         }
37187     },
37188
37189     /**
37190      * Clears any text/value currently set in the field
37191      */
37192     clearValue : function(){
37193         if(this.hiddenField){
37194             this.hiddenField.value = '';
37195         }
37196         this.value = '';
37197         this.setRawValue('');
37198         this.lastSelectionText = '';
37199         this.applyEmptyText();
37200     },
37201
37202     /**
37203      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37204      * will be displayed in the field.  If the value does not match the data value of an existing item,
37205      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37206      * Otherwise the field will be blank (although the value will still be set).
37207      * @param {String} value The value to match
37208      */
37209     setValue : function(v){
37210         var text = v;
37211         if(this.valueField){
37212             var r = this.findRecord(this.valueField, v);
37213             if(r){
37214                 text = r.data[this.displayField];
37215             }else if(this.valueNotFoundText !== undefined){
37216                 text = this.valueNotFoundText;
37217             }
37218         }
37219         this.lastSelectionText = text;
37220         if(this.hiddenField){
37221             this.hiddenField.value = v;
37222         }
37223         Roo.form.ComboBox.superclass.setValue.call(this, text);
37224         this.value = v;
37225     },
37226     /**
37227      * @property {Object} the last set data for the element
37228      */
37229     
37230     lastData : false,
37231     /**
37232      * Sets the value of the field based on a object which is related to the record format for the store.
37233      * @param {Object} value the value to set as. or false on reset?
37234      */
37235     setFromData : function(o){
37236         var dv = ''; // display value
37237         var vv = ''; // value value..
37238         this.lastData = o;
37239         if (this.displayField) {
37240             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37241         } else {
37242             // this is an error condition!!!
37243             console.log('no value field set for '+ this.name);
37244         }
37245         
37246         if(this.valueField){
37247             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37248         }
37249         if(this.hiddenField){
37250             this.hiddenField.value = vv;
37251             
37252             this.lastSelectionText = dv;
37253             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37254             this.value = vv;
37255             return;
37256         }
37257         // no hidden field.. - we store the value in 'value', but still display
37258         // display field!!!!
37259         this.lastSelectionText = dv;
37260         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37261         this.value = vv;
37262         
37263         
37264     },
37265     // private
37266     reset : function(){
37267         // overridden so that last data is reset..
37268         this.setValue(this.originalValue);
37269         this.clearInvalid();
37270         this.lastData = false;
37271     },
37272     // private
37273     findRecord : function(prop, value){
37274         var record;
37275         if(this.store.getCount() > 0){
37276             this.store.each(function(r){
37277                 if(r.data[prop] == value){
37278                     record = r;
37279                     return false;
37280                 }
37281             });
37282         }
37283         return record;
37284     },
37285
37286     // private
37287     onViewMove : function(e, t){
37288         this.inKeyMode = false;
37289     },
37290
37291     // private
37292     onViewOver : function(e, t){
37293         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37294             return;
37295         }
37296         var item = this.view.findItemFromChild(t);
37297         if(item){
37298             var index = this.view.indexOf(item);
37299             this.select(index, false);
37300         }
37301     },
37302
37303     // private
37304     onViewClick : function(doFocus){
37305         var index = this.view.getSelectedIndexes()[0];
37306         var r = this.store.getAt(index);
37307         if(r){
37308             this.onSelect(r, index);
37309         }
37310         if(doFocus !== false && !this.blockFocus){
37311             this.el.focus();
37312         }
37313     },
37314
37315     // private
37316     restrictHeight : function(){
37317         this.innerList.dom.style.height = '';
37318         var inner = this.innerList.dom;
37319         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37320         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37321         this.list.beginUpdate();
37322         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37323         this.list.alignTo(this.el, this.listAlign);
37324         this.list.endUpdate();
37325     },
37326
37327     // private
37328     onEmptyResults : function(){
37329         this.collapse();
37330     },
37331
37332     /**
37333      * Returns true if the dropdown list is expanded, else false.
37334      */
37335     isExpanded : function(){
37336         return this.list.isVisible();
37337     },
37338
37339     /**
37340      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37341      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37342      * @param {String} value The data value of the item to select
37343      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37344      * selected item if it is not currently in view (defaults to true)
37345      * @return {Boolean} True if the value matched an item in the list, else false
37346      */
37347     selectByValue : function(v, scrollIntoView){
37348         if(v !== undefined && v !== null){
37349             var r = this.findRecord(this.valueField || this.displayField, v);
37350             if(r){
37351                 this.select(this.store.indexOf(r), scrollIntoView);
37352                 return true;
37353             }
37354         }
37355         return false;
37356     },
37357
37358     /**
37359      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37360      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37361      * @param {Number} index The zero-based index of the list item to select
37362      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37363      * selected item if it is not currently in view (defaults to true)
37364      */
37365     select : function(index, scrollIntoView){
37366         this.selectedIndex = index;
37367         this.view.select(index);
37368         if(scrollIntoView !== false){
37369             var el = this.view.getNode(index);
37370             if(el){
37371                 this.innerList.scrollChildIntoView(el, false);
37372             }
37373         }
37374     },
37375
37376     // private
37377     selectNext : function(){
37378         var ct = this.store.getCount();
37379         if(ct > 0){
37380             if(this.selectedIndex == -1){
37381                 this.select(0);
37382             }else if(this.selectedIndex < ct-1){
37383                 this.select(this.selectedIndex+1);
37384             }
37385         }
37386     },
37387
37388     // private
37389     selectPrev : function(){
37390         var ct = this.store.getCount();
37391         if(ct > 0){
37392             if(this.selectedIndex == -1){
37393                 this.select(0);
37394             }else if(this.selectedIndex != 0){
37395                 this.select(this.selectedIndex-1);
37396             }
37397         }
37398     },
37399
37400     // private
37401     onKeyUp : function(e){
37402         if(this.editable !== false && !e.isSpecialKey()){
37403             this.lastKey = e.getKey();
37404             this.dqTask.delay(this.queryDelay);
37405         }
37406     },
37407
37408     // private
37409     validateBlur : function(){
37410         return !this.list || !this.list.isVisible();   
37411     },
37412
37413     // private
37414     initQuery : function(){
37415         this.doQuery(this.getRawValue());
37416     },
37417
37418     // private
37419     doForce : function(){
37420         if(this.el.dom.value.length > 0){
37421             this.el.dom.value =
37422                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37423             this.applyEmptyText();
37424         }
37425     },
37426
37427     /**
37428      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37429      * query allowing the query action to be canceled if needed.
37430      * @param {String} query The SQL query to execute
37431      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37432      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37433      * saved in the current store (defaults to false)
37434      */
37435     doQuery : function(q, forceAll){
37436         if(q === undefined || q === null){
37437             q = '';
37438         }
37439         var qe = {
37440             query: q,
37441             forceAll: forceAll,
37442             combo: this,
37443             cancel:false
37444         };
37445         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37446             return false;
37447         }
37448         q = qe.query;
37449         forceAll = qe.forceAll;
37450         if(forceAll === true || (q.length >= this.minChars)){
37451             if(this.lastQuery != q){
37452                 this.lastQuery = q;
37453                 if(this.mode == 'local'){
37454                     this.selectedIndex = -1;
37455                     if(forceAll){
37456                         this.store.clearFilter();
37457                     }else{
37458                         this.store.filter(this.displayField, q);
37459                     }
37460                     this.onLoad();
37461                 }else{
37462                     this.store.baseParams[this.queryParam] = q;
37463                     this.store.load({
37464                         params: this.getParams(q)
37465                     });
37466                     this.expand();
37467                 }
37468             }else{
37469                 this.selectedIndex = -1;
37470                 this.onLoad();   
37471             }
37472         }
37473     },
37474
37475     // private
37476     getParams : function(q){
37477         var p = {};
37478         //p[this.queryParam] = q;
37479         if(this.pageSize){
37480             p.start = 0;
37481             p.limit = this.pageSize;
37482         }
37483         return p;
37484     },
37485
37486     /**
37487      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37488      */
37489     collapse : function(){
37490         if(!this.isExpanded()){
37491             return;
37492         }
37493         this.list.hide();
37494         Roo.get(document).un('mousedown', this.collapseIf, this);
37495         Roo.get(document).un('mousewheel', this.collapseIf, this);
37496         this.fireEvent('collapse', this);
37497     },
37498
37499     // private
37500     collapseIf : function(e){
37501         if(!e.within(this.wrap) && !e.within(this.list)){
37502             this.collapse();
37503         }
37504     },
37505
37506     /**
37507      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37508      */
37509     expand : function(){
37510         if(this.isExpanded() || !this.hasFocus){
37511             return;
37512         }
37513         this.list.alignTo(this.el, this.listAlign);
37514         this.list.show();
37515         Roo.get(document).on('mousedown', this.collapseIf, this);
37516         Roo.get(document).on('mousewheel', this.collapseIf, this);
37517         this.fireEvent('expand', this);
37518     },
37519
37520     // private
37521     // Implements the default empty TriggerField.onTriggerClick function
37522     onTriggerClick : function(){
37523         if(this.disabled){
37524             return;
37525         }
37526         if(this.isExpanded()){
37527             this.collapse();
37528             if (!this.blockFocus) {
37529                 this.el.focus();
37530             }
37531             
37532         }else {
37533             this.hasFocus = true;
37534             if(this.triggerAction == 'all') {
37535                 this.doQuery(this.allQuery, true);
37536             } else {
37537                 this.doQuery(this.getRawValue());
37538             }
37539             if (!this.blockFocus) {
37540                 this.el.focus();
37541             }
37542         }
37543     }
37544
37545     /** 
37546     * @cfg {Boolean} grow 
37547     * @hide 
37548     */
37549     /** 
37550     * @cfg {Number} growMin 
37551     * @hide 
37552     */
37553     /** 
37554     * @cfg {Number} growMax 
37555     * @hide 
37556     */
37557     /**
37558      * @hide
37559      * @method autoSize
37560      */
37561 });/*
37562  * Based on:
37563  * Ext JS Library 1.1.1
37564  * Copyright(c) 2006-2007, Ext JS, LLC.
37565  *
37566  * Originally Released Under LGPL - original licence link has changed is not relivant.
37567  *
37568  * Fork - LGPL
37569  * <script type="text/javascript">
37570  */
37571 /**
37572  * @class Roo.form.Checkbox
37573  * @extends Roo.form.Field
37574  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37575  * @constructor
37576  * Creates a new Checkbox
37577  * @param {Object} config Configuration options
37578  */
37579 Roo.form.Checkbox = function(config){
37580     Roo.form.Checkbox.superclass.constructor.call(this, config);
37581     this.addEvents({
37582         /**
37583          * @event check
37584          * Fires when the checkbox is checked or unchecked.
37585              * @param {Roo.form.Checkbox} this This checkbox
37586              * @param {Boolean} checked The new checked value
37587              */
37588         check : true
37589     });
37590 };
37591
37592 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37593     /**
37594      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37595      */
37596     focusClass : undefined,
37597     /**
37598      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37599      */
37600     fieldClass: "x-form-field",
37601     /**
37602      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37603      */
37604     checked: false,
37605     /**
37606      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37607      * {tag: "input", type: "checkbox", autocomplete: "off"})
37608      */
37609     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37610     /**
37611      * @cfg {String} boxLabel The text that appears beside the checkbox
37612      */
37613     boxLabel : "",
37614     /**
37615      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37616      */  
37617     inputValue : '1',
37618     /**
37619      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37620      */
37621      valueOff: '0', // value when not checked..
37622
37623     actionMode : 'viewEl', 
37624     //
37625     // private
37626     itemCls : 'x-menu-check-item x-form-item',
37627     groupClass : 'x-menu-group-item',
37628     inputType : 'hidden',
37629     
37630     
37631     inSetChecked: false, // check that we are not calling self...
37632     
37633     inputElement: false, // real input element?
37634     basedOn: false, // ????
37635     
37636     isFormField: true, // not sure where this is needed!!!!
37637
37638     onResize : function(){
37639         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37640         if(!this.boxLabel){
37641             this.el.alignTo(this.wrap, 'c-c');
37642         }
37643     },
37644
37645     initEvents : function(){
37646         Roo.form.Checkbox.superclass.initEvents.call(this);
37647         this.el.on("click", this.onClick,  this);
37648         this.el.on("change", this.onClick,  this);
37649     },
37650
37651
37652     getResizeEl : function(){
37653         return this.wrap;
37654     },
37655
37656     getPositionEl : function(){
37657         return this.wrap;
37658     },
37659
37660     // private
37661     onRender : function(ct, position){
37662         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37663         /*
37664         if(this.inputValue !== undefined){
37665             this.el.dom.value = this.inputValue;
37666         }
37667         */
37668         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37669         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37670         var viewEl = this.wrap.createChild({ 
37671             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37672         this.viewEl = viewEl;   
37673         this.wrap.on('click', this.onClick,  this); 
37674         
37675         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37676         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37677         
37678         
37679         
37680         if(this.boxLabel){
37681             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37682         //    viewEl.on('click', this.onClick,  this); 
37683         }
37684         //if(this.checked){
37685             this.setChecked(this.checked);
37686         //}else{
37687             //this.checked = this.el.dom;
37688         //}
37689
37690     },
37691
37692     // private
37693     initValue : Roo.emptyFn,
37694
37695     /**
37696      * Returns the checked state of the checkbox.
37697      * @return {Boolean} True if checked, else false
37698      */
37699     getValue : function(){
37700         if(this.el){
37701             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37702         }
37703         return this.valueOff;
37704         
37705     },
37706
37707         // private
37708     onClick : function(){ 
37709         this.setChecked(!this.checked);
37710
37711         //if(this.el.dom.checked != this.checked){
37712         //    this.setValue(this.el.dom.checked);
37713        // }
37714     },
37715
37716     /**
37717      * Sets the checked state of the checkbox.
37718      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37719      */
37720     setValue : function(v,suppressEvent){
37721         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37722         //if(this.el && this.el.dom){
37723         //    this.el.dom.checked = this.checked;
37724         //    this.el.dom.defaultChecked = this.checked;
37725         //}
37726         this.setChecked(v === this.inputValue);
37727         //this.fireEvent("check", this, this.checked);
37728     },
37729     // private..
37730     setChecked : function(state,suppressEvent)
37731     {
37732         if (this.inSetChecked) {
37733             this.checked = state;
37734             return;
37735         }
37736         
37737     
37738         if(this.wrap){
37739             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37740         }
37741         this.checked = state;
37742         if(suppressEvent !== true){
37743             this.fireEvent('checkchange', this, state);
37744         }
37745         this.inSetChecked = true;
37746         this.el.dom.value = state ? this.inputValue : this.valueOff;
37747         this.inSetChecked = false;
37748         
37749     },
37750     // handle setting of hidden value by some other method!!?!?
37751     setFromHidden: function()
37752     {
37753         if(!this.el){
37754             return;
37755         }
37756         //console.log("SET FROM HIDDEN");
37757         //alert('setFrom hidden');
37758         this.setValue(this.el.dom.value);
37759     },
37760     
37761     onDestroy : function()
37762     {
37763         if(this.viewEl){
37764             Roo.get(this.viewEl).remove();
37765         }
37766          
37767         Roo.form.Checkbox.superclass.onDestroy.call(this);
37768     }
37769
37770 });/*
37771  * Based on:
37772  * Ext JS Library 1.1.1
37773  * Copyright(c) 2006-2007, Ext JS, LLC.
37774  *
37775  * Originally Released Under LGPL - original licence link has changed is not relivant.
37776  *
37777  * Fork - LGPL
37778  * <script type="text/javascript">
37779  */
37780  
37781 /**
37782  * @class Roo.form.Radio
37783  * @extends Roo.form.Checkbox
37784  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37785  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37786  * @constructor
37787  * Creates a new Radio
37788  * @param {Object} config Configuration options
37789  */
37790 Roo.form.Radio = function(){
37791     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37792 };
37793 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37794     inputType: 'radio',
37795
37796     /**
37797      * If this radio is part of a group, it will return the selected value
37798      * @return {String}
37799      */
37800     getGroupValue : function(){
37801         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37802     }
37803 });//<script type="text/javascript">
37804
37805 /*
37806  * Ext JS Library 1.1.1
37807  * Copyright(c) 2006-2007, Ext JS, LLC.
37808  * licensing@extjs.com
37809  * 
37810  * http://www.extjs.com/license
37811  */
37812  
37813  /*
37814   * 
37815   * Known bugs:
37816   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37817   * - IE ? - no idea how much works there.
37818   * 
37819   * 
37820   * 
37821   */
37822  
37823
37824 /**
37825  * @class Ext.form.HtmlEditor
37826  * @extends Ext.form.Field
37827  * Provides a lightweight HTML Editor component.
37828  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37829  * 
37830  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37831  * supported by this editor.</b><br/><br/>
37832  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37833  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37834  */
37835 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37836       /**
37837      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37838      */
37839     toolbars : false,
37840     /**
37841      * @cfg {String} createLinkText The default text for the create link prompt
37842      */
37843     createLinkText : 'Please enter the URL for the link:',
37844     /**
37845      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37846      */
37847     defaultLinkValue : 'http:/'+'/',
37848    
37849     
37850     // id of frame..
37851     frameId: false,
37852     
37853     // private properties
37854     validationEvent : false,
37855     deferHeight: true,
37856     initialized : false,
37857     activated : false,
37858     sourceEditMode : false,
37859     onFocus : Roo.emptyFn,
37860     iframePad:3,
37861     hideMode:'offsets',
37862     defaultAutoCreate : {
37863         tag: "textarea",
37864         style:"width:500px;height:300px;",
37865         autocomplete: "off"
37866     },
37867
37868     // private
37869     initComponent : function(){
37870         this.addEvents({
37871             /**
37872              * @event initialize
37873              * Fires when the editor is fully initialized (including the iframe)
37874              * @param {HtmlEditor} this
37875              */
37876             initialize: true,
37877             /**
37878              * @event activate
37879              * Fires when the editor is first receives the focus. Any insertion must wait
37880              * until after this event.
37881              * @param {HtmlEditor} this
37882              */
37883             activate: true,
37884              /**
37885              * @event beforesync
37886              * Fires before the textarea is updated with content from the editor iframe. Return false
37887              * to cancel the sync.
37888              * @param {HtmlEditor} this
37889              * @param {String} html
37890              */
37891             beforesync: true,
37892              /**
37893              * @event beforepush
37894              * Fires before the iframe editor is updated with content from the textarea. Return false
37895              * to cancel the push.
37896              * @param {HtmlEditor} this
37897              * @param {String} html
37898              */
37899             beforepush: true,
37900              /**
37901              * @event sync
37902              * Fires when the textarea is updated with content from the editor iframe.
37903              * @param {HtmlEditor} this
37904              * @param {String} html
37905              */
37906             sync: true,
37907              /**
37908              * @event push
37909              * Fires when the iframe editor is updated with content from the textarea.
37910              * @param {HtmlEditor} this
37911              * @param {String} html
37912              */
37913             push: true,
37914              /**
37915              * @event editmodechange
37916              * Fires when the editor switches edit modes
37917              * @param {HtmlEditor} this
37918              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37919              */
37920             editmodechange: true,
37921             /**
37922              * @event editorevent
37923              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37924              * @param {HtmlEditor} this
37925              */
37926             editorevent: true
37927         })
37928     },
37929
37930     /**
37931      * Protected method that will not generally be called directly. It
37932      * is called when the editor creates its toolbar. Override this method if you need to
37933      * add custom toolbar buttons.
37934      * @param {HtmlEditor} editor
37935      */
37936     createToolbar : function(editor){
37937         if (!editor.toolbars || !editor.toolbars.length) {
37938             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37939         }
37940         
37941         for (var i =0 ; i < editor.toolbars.length;i++) {
37942             editor.toolbars[i].init(editor);
37943         }
37944          
37945         
37946     },
37947
37948     /**
37949      * Protected method that will not generally be called directly. It
37950      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37951      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37952      */
37953     getDocMarkup : function(){
37954         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37955     },
37956
37957     // private
37958     onRender : function(ct, position){
37959         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37960         this.el.dom.style.border = '0 none';
37961         this.el.dom.setAttribute('tabIndex', -1);
37962         this.el.addClass('x-hidden');
37963         if(Roo.isIE){ // fix IE 1px bogus margin
37964             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37965         }
37966         this.wrap = this.el.wrap({
37967             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37968         });
37969
37970         this.frameId = Roo.id();
37971         this.createToolbar(this);
37972         
37973         
37974         
37975         
37976       
37977         
37978         var iframe = this.wrap.createChild({
37979             tag: 'iframe',
37980             id: this.frameId,
37981             name: this.frameId,
37982             frameBorder : 'no',
37983             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37984         });
37985         
37986        // console.log(iframe);
37987         //this.wrap.dom.appendChild(iframe);
37988
37989         this.iframe = iframe.dom;
37990
37991          this.assignDocWin();
37992         
37993         this.doc.designMode = 'on';
37994        
37995         this.doc.open();
37996         this.doc.write(this.getDocMarkup());
37997         this.doc.close();
37998
37999         
38000         var task = { // must defer to wait for browser to be ready
38001             run : function(){
38002                 //console.log("run task?" + this.doc.readyState);
38003                 this.assignDocWin();
38004                 if(this.doc.body || this.doc.readyState == 'complete'){
38005                     try {
38006                         
38007                        
38008                         this.doc.designMode="on";
38009                     } catch (e) {
38010                         return;
38011                     }
38012                     Roo.TaskMgr.stop(task);
38013                     this.initEditor.defer(10, this);
38014                 }
38015             },
38016             interval : 10,
38017             duration:10000,
38018             scope: this
38019         };
38020         Roo.TaskMgr.start(task);
38021
38022         if(!this.width){
38023             this.setSize(this.el.getSize());
38024         }
38025     },
38026
38027     // private
38028     onResize : function(w, h){
38029         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38030         if(this.el && this.iframe){
38031             if(typeof w == 'number'){
38032                 var aw = w - this.wrap.getFrameWidth('lr');
38033                 this.el.setWidth(this.adjustWidth('textarea', aw));
38034                 this.iframe.style.width = aw + 'px';
38035             }
38036             if(typeof h == 'number'){
38037                 var tbh = 0;
38038                 for (var i =0; i < this.toolbars.length;i++) {
38039                     // fixme - ask toolbars for heights?
38040                     tbh += this.toolbars[i].tb.el.getHeight();
38041                 }
38042                 
38043                 
38044                 
38045                 
38046                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38047                 this.el.setHeight(this.adjustWidth('textarea', ah));
38048                 this.iframe.style.height = ah + 'px';
38049                 if(this.doc){
38050                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38051                 }
38052             }
38053         }
38054     },
38055
38056     /**
38057      * Toggles the editor between standard and source edit mode.
38058      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38059      */
38060     toggleSourceEdit : function(sourceEditMode){
38061         
38062         this.sourceEditMode = sourceEditMode === true;
38063         
38064         if(this.sourceEditMode){
38065           
38066             this.syncValue();
38067             this.iframe.className = 'x-hidden';
38068             this.el.removeClass('x-hidden');
38069             this.el.dom.removeAttribute('tabIndex');
38070             this.el.focus();
38071         }else{
38072              
38073             this.pushValue();
38074             this.iframe.className = '';
38075             this.el.addClass('x-hidden');
38076             this.el.dom.setAttribute('tabIndex', -1);
38077             this.deferFocus();
38078         }
38079         this.setSize(this.wrap.getSize());
38080         this.fireEvent('editmodechange', this, this.sourceEditMode);
38081     },
38082
38083     // private used internally
38084     createLink : function(){
38085         var url = prompt(this.createLinkText, this.defaultLinkValue);
38086         if(url && url != 'http:/'+'/'){
38087             this.relayCmd('createlink', url);
38088         }
38089     },
38090
38091     // private (for BoxComponent)
38092     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38093
38094     // private (for BoxComponent)
38095     getResizeEl : function(){
38096         return this.wrap;
38097     },
38098
38099     // private (for BoxComponent)
38100     getPositionEl : function(){
38101         return this.wrap;
38102     },
38103
38104     // private
38105     initEvents : function(){
38106         this.originalValue = this.getValue();
38107     },
38108
38109     /**
38110      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38111      * @method
38112      */
38113     markInvalid : Roo.emptyFn,
38114     /**
38115      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38116      * @method
38117      */
38118     clearInvalid : Roo.emptyFn,
38119
38120     setValue : function(v){
38121         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38122         this.pushValue();
38123     },
38124
38125     /**
38126      * Protected method that will not generally be called directly. If you need/want
38127      * custom HTML cleanup, this is the method you should override.
38128      * @param {String} html The HTML to be cleaned
38129      * return {String} The cleaned HTML
38130      */
38131     cleanHtml : function(html){
38132         html = String(html);
38133         if(html.length > 5){
38134             if(Roo.isSafari){ // strip safari nonsense
38135                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38136             }
38137         }
38138         if(html == '&nbsp;'){
38139             html = '';
38140         }
38141         return html;
38142     },
38143
38144     /**
38145      * Protected method that will not generally be called directly. Syncs the contents
38146      * of the editor iframe with the textarea.
38147      */
38148     syncValue : function(){
38149         if(this.initialized){
38150             var bd = (this.doc.body || this.doc.documentElement);
38151             var html = bd.innerHTML;
38152             if(Roo.isSafari){
38153                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38154                 var m = bs.match(/text-align:(.*?);/i);
38155                 if(m && m[1]){
38156                     html = '<div style="'+m[0]+'">' + html + '</div>';
38157                 }
38158             }
38159             html = this.cleanHtml(html);
38160             if(this.fireEvent('beforesync', this, html) !== false){
38161                 this.el.dom.value = html;
38162                 this.fireEvent('sync', this, html);
38163             }
38164         }
38165     },
38166
38167     /**
38168      * Protected method that will not generally be called directly. Pushes the value of the textarea
38169      * into the iframe editor.
38170      */
38171     pushValue : function(){
38172         if(this.initialized){
38173             var v = this.el.dom.value;
38174             if(v.length < 1){
38175                 v = '&#160;';
38176             }
38177             if(this.fireEvent('beforepush', this, v) !== false){
38178                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38179                 this.fireEvent('push', this, v);
38180             }
38181         }
38182     },
38183
38184     // private
38185     deferFocus : function(){
38186         this.focus.defer(10, this);
38187     },
38188
38189     // doc'ed in Field
38190     focus : function(){
38191         if(this.win && !this.sourceEditMode){
38192             this.win.focus();
38193         }else{
38194             this.el.focus();
38195         }
38196     },
38197     
38198     assignDocWin: function()
38199     {
38200         var iframe = this.iframe;
38201         
38202          if(Roo.isIE){
38203             this.doc = iframe.contentWindow.document;
38204             this.win = iframe.contentWindow;
38205         } else {
38206             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38207             this.win = Roo.get(this.frameId).dom.contentWindow;
38208         }
38209     },
38210     
38211     // private
38212     initEditor : function(){
38213         //console.log("INIT EDITOR");
38214         this.assignDocWin();
38215         
38216         
38217         
38218         this.doc.designMode="on";
38219         this.doc.open();
38220         this.doc.write(this.getDocMarkup());
38221         this.doc.close();
38222         
38223         var dbody = (this.doc.body || this.doc.documentElement);
38224         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38225         // this copies styles from the containing element into thsi one..
38226         // not sure why we need all of this..
38227         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38228         ss['background-attachment'] = 'fixed'; // w3c
38229         dbody.bgProperties = 'fixed'; // ie
38230         Roo.DomHelper.applyStyles(dbody, ss);
38231         Roo.EventManager.on(this.doc, {
38232             'mousedown': this.onEditorEvent,
38233             'dblclick': this.onEditorEvent,
38234             'click': this.onEditorEvent,
38235             'keyup': this.onEditorEvent,
38236             buffer:100,
38237             scope: this
38238         });
38239         if(Roo.isGecko){
38240             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38241         }
38242         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38243             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38244         }
38245         this.initialized = true;
38246
38247         this.fireEvent('initialize', this);
38248         this.pushValue();
38249     },
38250
38251     // private
38252     onDestroy : function(){
38253         
38254         
38255         
38256         if(this.rendered){
38257             
38258             for (var i =0; i < this.toolbars.length;i++) {
38259                 // fixme - ask toolbars for heights?
38260                 this.toolbars[i].onDestroy();
38261             }
38262             
38263             this.wrap.dom.innerHTML = '';
38264             this.wrap.remove();
38265         }
38266     },
38267
38268     // private
38269     onFirstFocus : function(){
38270         
38271         this.assignDocWin();
38272         
38273         
38274         this.activated = true;
38275         for (var i =0; i < this.toolbars.length;i++) {
38276             this.toolbars[i].onFirstFocus();
38277         }
38278        
38279         if(Roo.isGecko){ // prevent silly gecko errors
38280             this.win.focus();
38281             var s = this.win.getSelection();
38282             if(!s.focusNode || s.focusNode.nodeType != 3){
38283                 var r = s.getRangeAt(0);
38284                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38285                 r.collapse(true);
38286                 this.deferFocus();
38287             }
38288             try{
38289                 this.execCmd('useCSS', true);
38290                 this.execCmd('styleWithCSS', false);
38291             }catch(e){}
38292         }
38293         this.fireEvent('activate', this);
38294     },
38295
38296     // private
38297     adjustFont: function(btn){
38298         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38299         //if(Roo.isSafari){ // safari
38300         //    adjust *= 2;
38301        // }
38302         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38303         if(Roo.isSafari){ // safari
38304             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38305             v =  (v < 10) ? 10 : v;
38306             v =  (v > 48) ? 48 : v;
38307             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38308             
38309         }
38310         
38311         
38312         v = Math.max(1, v+adjust);
38313         
38314         this.execCmd('FontSize', v  );
38315     },
38316
38317     onEditorEvent : function(e){
38318         this.fireEvent('editorevent', this, e);
38319       //  this.updateToolbar();
38320         this.syncValue();
38321     },
38322
38323     insertTag : function(tg)
38324     {
38325         // could be a bit smarter... -> wrap the current selected tRoo..
38326         
38327         this.execCmd("formatblock",   tg);
38328         
38329     },
38330     
38331     insertText : function(txt)
38332     {
38333         
38334         
38335         range = this.createRange();
38336         range.deleteContents();
38337                //alert(Sender.getAttribute('label'));
38338                
38339         range.insertNode(this.doc.createTextNode(txt));
38340     } ,
38341     
38342     // private
38343     relayBtnCmd : function(btn){
38344         this.relayCmd(btn.cmd);
38345     },
38346
38347     /**
38348      * Executes a Midas editor command on the editor document and performs necessary focus and
38349      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38350      * @param {String} cmd The Midas command
38351      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38352      */
38353     relayCmd : function(cmd, value){
38354         this.win.focus();
38355         this.execCmd(cmd, value);
38356         this.fireEvent('editorevent', this);
38357         //this.updateToolbar();
38358         this.deferFocus();
38359     },
38360
38361     /**
38362      * Executes a Midas editor command directly on the editor document.
38363      * For visual commands, you should use {@link #relayCmd} instead.
38364      * <b>This should only be called after the editor is initialized.</b>
38365      * @param {String} cmd The Midas command
38366      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38367      */
38368     execCmd : function(cmd, value){
38369         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38370         this.syncValue();
38371     },
38372
38373     // private
38374     applyCommand : function(e){
38375         if(e.ctrlKey){
38376             var c = e.getCharCode(), cmd;
38377             if(c > 0){
38378                 c = String.fromCharCode(c);
38379                 switch(c){
38380                     case 'b':
38381                         cmd = 'bold';
38382                     break;
38383                     case 'i':
38384                         cmd = 'italic';
38385                     break;
38386                     case 'u':
38387                         cmd = 'underline';
38388                     break;
38389                 }
38390                 if(cmd){
38391                     this.win.focus();
38392                     this.execCmd(cmd);
38393                     this.deferFocus();
38394                     e.preventDefault();
38395                 }
38396             }
38397         }
38398     },
38399
38400     /**
38401      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38402      * to insert tRoo.
38403      * @param {String} text
38404      */
38405     insertAtCursor : function(text){
38406         if(!this.activated){
38407             return;
38408         }
38409         if(Roo.isIE){
38410             this.win.focus();
38411             var r = this.doc.selection.createRange();
38412             if(r){
38413                 r.collapse(true);
38414                 r.pasteHTML(text);
38415                 this.syncValue();
38416                 this.deferFocus();
38417             }
38418         }else if(Roo.isGecko || Roo.isOpera){
38419             this.win.focus();
38420             this.execCmd('InsertHTML', text);
38421             this.deferFocus();
38422         }else if(Roo.isSafari){
38423             this.execCmd('InsertText', text);
38424             this.deferFocus();
38425         }
38426     },
38427
38428     // private
38429     fixKeys : function(){ // load time branching for fastest keydown performance
38430         if(Roo.isIE){
38431             return function(e){
38432                 var k = e.getKey(), r;
38433                 if(k == e.TAB){
38434                     e.stopEvent();
38435                     r = this.doc.selection.createRange();
38436                     if(r){
38437                         r.collapse(true);
38438                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38439                         this.deferFocus();
38440                     }
38441                 }else if(k == e.ENTER){
38442                     r = this.doc.selection.createRange();
38443                     if(r){
38444                         var target = r.parentElement();
38445                         if(!target || target.tagName.toLowerCase() != 'li'){
38446                             e.stopEvent();
38447                             r.pasteHTML('<br />');
38448                             r.collapse(false);
38449                             r.select();
38450                         }
38451                     }
38452                 }
38453             };
38454         }else if(Roo.isOpera){
38455             return function(e){
38456                 var k = e.getKey();
38457                 if(k == e.TAB){
38458                     e.stopEvent();
38459                     this.win.focus();
38460                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38461                     this.deferFocus();
38462                 }
38463             };
38464         }else if(Roo.isSafari){
38465             return function(e){
38466                 var k = e.getKey();
38467                 if(k == e.TAB){
38468                     e.stopEvent();
38469                     this.execCmd('InsertText','\t');
38470                     this.deferFocus();
38471                 }
38472              };
38473         }
38474     }(),
38475     
38476     getAllAncestors: function()
38477     {
38478         var p = this.getSelectedNode();
38479         var a = [];
38480         if (!p) {
38481             a.push(p); // push blank onto stack..
38482             p = this.getParentElement();
38483         }
38484         
38485         
38486         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38487             a.push(p);
38488             p = p.parentNode;
38489         }
38490         a.push(this.doc.body);
38491         return a;
38492     },
38493     lastSel : false,
38494     lastSelNode : false,
38495     
38496     
38497     getSelection : function() 
38498     {
38499         this.assignDocWin();
38500         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38501     },
38502     
38503     getSelectedNode: function() 
38504     {
38505         // this may only work on Gecko!!!
38506         
38507         // should we cache this!!!!
38508         
38509         
38510         
38511          
38512         var range = this.createRange(this.getSelection());
38513         
38514         if (Roo.isIE) {
38515             var parent = range.parentElement();
38516             while (true) {
38517                 var testRange = range.duplicate();
38518                 testRange.moveToElementText(parent);
38519                 if (testRange.inRange(range)) {
38520                     break;
38521                 }
38522                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38523                     break;
38524                 }
38525                 parent = parent.parentElement;
38526             }
38527             return parent;
38528         }
38529         
38530         
38531         var ar = range.endContainer.childNodes;
38532         if (!ar.length) {
38533             ar = range.commonAncestorContainer.childNodes;
38534             //alert(ar.length);
38535         }
38536         var nodes = [];
38537         var other_nodes = [];
38538         var has_other_nodes = false;
38539         for (var i=0;i<ar.length;i++) {
38540             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38541                 continue;
38542             }
38543             // fullly contained node.
38544             
38545             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38546                 nodes.push(ar[i]);
38547                 continue;
38548             }
38549             
38550             // probably selected..
38551             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38552                 other_nodes.push(ar[i]);
38553                 continue;
38554             }
38555             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38556                 continue;
38557             }
38558             
38559             
38560             has_other_nodes = true;
38561         }
38562         if (!nodes.length && other_nodes.length) {
38563             nodes= other_nodes;
38564         }
38565         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38566             return false;
38567         }
38568         
38569         return nodes[0];
38570     },
38571     createRange: function(sel)
38572     {
38573         // this has strange effects when using with 
38574         // top toolbar - not sure if it's a great idea.
38575         //this.editor.contentWindow.focus();
38576         if (typeof sel != "undefined") {
38577             try {
38578                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38579             } catch(e) {
38580                 return this.doc.createRange();
38581             }
38582         } else {
38583             return this.doc.createRange();
38584         }
38585     },
38586     getParentElement: function()
38587     {
38588         
38589         this.assignDocWin();
38590         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38591         
38592         var range = this.createRange(sel);
38593          
38594         try {
38595             var p = range.commonAncestorContainer;
38596             while (p.nodeType == 3) { // text node
38597                 p = p.parentNode;
38598             }
38599             return p;
38600         } catch (e) {
38601             return null;
38602         }
38603     
38604     },
38605     
38606     
38607     
38608     // BC Hacks - cause I cant work out what i was trying to do..
38609     rangeIntersectsNode : function(range, node)
38610     {
38611         var nodeRange = node.ownerDocument.createRange();
38612         try {
38613             nodeRange.selectNode(node);
38614         }
38615         catch (e) {
38616             nodeRange.selectNodeContents(node);
38617         }
38618
38619         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38620                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38621     },
38622     rangeCompareNode : function(range, node) {
38623         var nodeRange = node.ownerDocument.createRange();
38624         try {
38625             nodeRange.selectNode(node);
38626         } catch (e) {
38627             nodeRange.selectNodeContents(node);
38628         }
38629         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38630         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38631
38632         if (nodeIsBefore && !nodeIsAfter)
38633             return 0;
38634         if (!nodeIsBefore && nodeIsAfter)
38635             return 1;
38636         if (nodeIsBefore && nodeIsAfter)
38637             return 2;
38638
38639         return 3;
38640     }
38641
38642     
38643     
38644     // hide stuff that is not compatible
38645     /**
38646      * @event blur
38647      * @hide
38648      */
38649     /**
38650      * @event change
38651      * @hide
38652      */
38653     /**
38654      * @event focus
38655      * @hide
38656      */
38657     /**
38658      * @event specialkey
38659      * @hide
38660      */
38661     /**
38662      * @cfg {String} fieldClass @hide
38663      */
38664     /**
38665      * @cfg {String} focusClass @hide
38666      */
38667     /**
38668      * @cfg {String} autoCreate @hide
38669      */
38670     /**
38671      * @cfg {String} inputType @hide
38672      */
38673     /**
38674      * @cfg {String} invalidClass @hide
38675      */
38676     /**
38677      * @cfg {String} invalidText @hide
38678      */
38679     /**
38680      * @cfg {String} msgFx @hide
38681      */
38682     /**
38683      * @cfg {String} validateOnBlur @hide
38684      */
38685 });// <script type="text/javascript">
38686 /*
38687  * Based on
38688  * Ext JS Library 1.1.1
38689  * Copyright(c) 2006-2007, Ext JS, LLC.
38690  *  
38691  
38692  */
38693
38694 /**
38695  * @class Roo.form.HtmlEditorToolbar1
38696  * Basic Toolbar
38697  * 
38698  * Usage:
38699  *
38700  new Roo.form.HtmlEditor({
38701     ....
38702     toolbars : [
38703         new Roo.form.HtmlEditorToolbar1({
38704             disable : { fonts: 1 , format: 1, ..., ... , ...],
38705             btns : [ .... ]
38706         })
38707     }
38708      
38709  * 
38710  * @cfg {Object} disable List of elements to disable..
38711  * @cfg {Array} btns List of additional buttons.
38712  * 
38713  * 
38714  * NEEDS Extra CSS? 
38715  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38716  */
38717  
38718 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38719 {
38720     
38721     Roo.apply(this, config);
38722     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38723     // dont call parent... till later.
38724 }
38725
38726 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38727     
38728     tb: false,
38729     
38730     rendered: false,
38731     
38732     editor : false,
38733     /**
38734      * @cfg {Object} disable  List of toolbar elements to disable
38735          
38736      */
38737     disable : false,
38738       /**
38739      * @cfg {Array} fontFamilies An array of available font families
38740      */
38741     fontFamilies : [
38742         'Arial',
38743         'Courier New',
38744         'Tahoma',
38745         'Times New Roman',
38746         'Verdana'
38747     ],
38748     
38749     specialChars : [
38750            "&#169;",
38751           "&#174;",     
38752           "&#8482;",    
38753           "&#163;" ,    
38754          // "&#8212;",    
38755           "&#8230;",    
38756           "&#247;" ,    
38757         //  "&#225;" ,     ?? a acute?
38758            "&#8364;"    , //Euro
38759        //   "&#8220;"    ,
38760         //  "&#8221;"    ,
38761         //  "&#8226;"    ,
38762           "&#176;"  //   , // degrees
38763
38764          // "&#233;"     , // e ecute
38765          // "&#250;"     , // u ecute?
38766     ],
38767     inputElements : [ 
38768             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38769             "input:submit", "input:button", "select", "textarea", "label" ],
38770     formats : [
38771         ["p"] ,  
38772         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38773         ["pre"],[ "code"], 
38774         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38775     ],
38776      /**
38777      * @cfg {String} defaultFont default font to use.
38778      */
38779     defaultFont: 'tahoma',
38780    
38781     fontSelect : false,
38782     
38783     
38784     formatCombo : false,
38785     
38786     init : function(editor)
38787     {
38788         this.editor = editor;
38789         
38790         
38791         var fid = editor.frameId;
38792         var etb = this;
38793         function btn(id, toggle, handler){
38794             var xid = fid + '-'+ id ;
38795             return {
38796                 id : xid,
38797                 cmd : id,
38798                 cls : 'x-btn-icon x-edit-'+id,
38799                 enableToggle:toggle !== false,
38800                 scope: editor, // was editor...
38801                 handler:handler||editor.relayBtnCmd,
38802                 clickEvent:'mousedown',
38803                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38804                 tabIndex:-1
38805             };
38806         }
38807         
38808         
38809         
38810         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38811         this.tb = tb;
38812          // stop form submits
38813         tb.el.on('click', function(e){
38814             e.preventDefault(); // what does this do?
38815         });
38816
38817         if(!this.disable.font && !Roo.isSafari){
38818             /* why no safari for fonts
38819             editor.fontSelect = tb.el.createChild({
38820                 tag:'select',
38821                 tabIndex: -1,
38822                 cls:'x-font-select',
38823                 html: editor.createFontOptions()
38824             });
38825             editor.fontSelect.on('change', function(){
38826                 var font = editor.fontSelect.dom.value;
38827                 editor.relayCmd('fontname', font);
38828                 editor.deferFocus();
38829             }, editor);
38830             tb.add(
38831                 editor.fontSelect.dom,
38832                 '-'
38833             );
38834             */
38835         };
38836         if(!this.disable.formats){
38837             this.formatCombo = new Roo.form.ComboBox({
38838                 store: new Roo.data.SimpleStore({
38839                     id : 'tag',
38840                     fields: ['tag'],
38841                     data : this.formats // from states.js
38842                 }),
38843                 blockFocus : true,
38844                 //autoCreate : {tag: "div",  size: "20"},
38845                 displayField:'tag',
38846                 typeAhead: false,
38847                 mode: 'local',
38848                 editable : false,
38849                 triggerAction: 'all',
38850                 emptyText:'Add tag',
38851                 selectOnFocus:true,
38852                 width:135,
38853                 listeners : {
38854                     'select': function(c, r, i) {
38855                         editor.insertTag(r.get('tag'));
38856                         editor.focus();
38857                     }
38858                 }
38859
38860             });
38861             tb.addField(this.formatCombo);
38862             
38863         }
38864         
38865         if(!this.disable.format){
38866             tb.add(
38867                 btn('bold'),
38868                 btn('italic'),
38869                 btn('underline')
38870             );
38871         };
38872         if(!this.disable.fontSize){
38873             tb.add(
38874                 '-',
38875                 
38876                 
38877                 btn('increasefontsize', false, editor.adjustFont),
38878                 btn('decreasefontsize', false, editor.adjustFont)
38879             );
38880         };
38881         
38882         
38883         if(this.disable.colors){
38884             tb.add(
38885                 '-', {
38886                     id:editor.frameId +'-forecolor',
38887                     cls:'x-btn-icon x-edit-forecolor',
38888                     clickEvent:'mousedown',
38889                     tooltip: this.buttonTips['forecolor'] || undefined,
38890                     tabIndex:-1,
38891                     menu : new Roo.menu.ColorMenu({
38892                         allowReselect: true,
38893                         focus: Roo.emptyFn,
38894                         value:'000000',
38895                         plain:true,
38896                         selectHandler: function(cp, color){
38897                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38898                             editor.deferFocus();
38899                         },
38900                         scope: editor,
38901                         clickEvent:'mousedown'
38902                     })
38903                 }, {
38904                     id:editor.frameId +'backcolor',
38905                     cls:'x-btn-icon x-edit-backcolor',
38906                     clickEvent:'mousedown',
38907                     tooltip: this.buttonTips['backcolor'] || undefined,
38908                     tabIndex:-1,
38909                     menu : new Roo.menu.ColorMenu({
38910                         focus: Roo.emptyFn,
38911                         value:'FFFFFF',
38912                         plain:true,
38913                         allowReselect: true,
38914                         selectHandler: function(cp, color){
38915                             if(Roo.isGecko){
38916                                 editor.execCmd('useCSS', false);
38917                                 editor.execCmd('hilitecolor', color);
38918                                 editor.execCmd('useCSS', true);
38919                                 editor.deferFocus();
38920                             }else{
38921                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38922                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38923                                 editor.deferFocus();
38924                             }
38925                         },
38926                         scope:editor,
38927                         clickEvent:'mousedown'
38928                     })
38929                 }
38930             );
38931         };
38932         // now add all the items...
38933         
38934
38935         if(!this.disable.alignments){
38936             tb.add(
38937                 '-',
38938                 btn('justifyleft'),
38939                 btn('justifycenter'),
38940                 btn('justifyright')
38941             );
38942         };
38943
38944         //if(!Roo.isSafari){
38945             if(!this.disable.links){
38946                 tb.add(
38947                     '-',
38948                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38949                 );
38950             };
38951
38952             if(!this.disable.lists){
38953                 tb.add(
38954                     '-',
38955                     btn('insertorderedlist'),
38956                     btn('insertunorderedlist')
38957                 );
38958             }
38959             if(!this.disable.sourceEdit){
38960                 tb.add(
38961                     '-',
38962                     btn('sourceedit', true, function(btn){
38963                         this.toggleSourceEdit(btn.pressed);
38964                     })
38965                 );
38966             }
38967         //}
38968         
38969         var smenu = { };
38970         // special menu.. - needs to be tidied up..
38971         if (!this.disable.special) {
38972             smenu = {
38973                 text: "&#169;",
38974                 cls: 'x-edit-none',
38975                 menu : {
38976                     items : []
38977                    }
38978             };
38979             for (var i =0; i < this.specialChars.length; i++) {
38980                 smenu.menu.items.push({
38981                     
38982                     text: this.specialChars[i],
38983                     handler: function(a,b) {
38984                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38985                     },
38986                     tabIndex:-1
38987                 });
38988             }
38989             
38990             
38991             tb.add(smenu);
38992             
38993             
38994         }
38995         if (this.btns) {
38996             for(var i =0; i< this.btns.length;i++) {
38997                 var b = this.btns[i];
38998                 b.cls =  'x-edit-none';
38999                 b.scope = editor;
39000                 tb.add(b);
39001             }
39002         
39003         }
39004         
39005         
39006         
39007         // disable everything...
39008         
39009         this.tb.items.each(function(item){
39010            if(item.id != editor.frameId+ '-sourceedit'){
39011                 item.disable();
39012             }
39013         });
39014         this.rendered = true;
39015         
39016         // the all the btns;
39017         editor.on('editorevent', this.updateToolbar, this);
39018         // other toolbars need to implement this..
39019         //editor.on('editmodechange', this.updateToolbar, this);
39020     },
39021     
39022     
39023     
39024     /**
39025      * Protected method that will not generally be called directly. It triggers
39026      * a toolbar update by reading the markup state of the current selection in the editor.
39027      */
39028     updateToolbar: function(){
39029
39030         if(!this.editor.activated){
39031             this.editor.onFirstFocus();
39032             return;
39033         }
39034
39035         var btns = this.tb.items.map, 
39036             doc = this.editor.doc,
39037             frameId = this.editor.frameId;
39038
39039         if(!this.disable.font && !Roo.isSafari){
39040             /*
39041             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39042             if(name != this.fontSelect.dom.value){
39043                 this.fontSelect.dom.value = name;
39044             }
39045             */
39046         }
39047         if(!this.disable.format){
39048             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39049             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39050             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39051         }
39052         if(!this.disable.alignments){
39053             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39054             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39055             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39056         }
39057         if(!Roo.isSafari && !this.disable.lists){
39058             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39059             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39060         }
39061         
39062         var ans = this.editor.getAllAncestors();
39063         if (this.formatCombo) {
39064             
39065             
39066             var store = this.formatCombo.store;
39067             this.formatCombo.setValue("");
39068             for (var i =0; i < ans.length;i++) {
39069                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39070                     // select it..
39071                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39072                     break;
39073                 }
39074             }
39075         }
39076         
39077         
39078         
39079         // hides menus... - so this cant be on a menu...
39080         Roo.menu.MenuMgr.hideAll();
39081
39082         //this.editorsyncValue();
39083     },
39084    
39085     
39086     createFontOptions : function(){
39087         var buf = [], fs = this.fontFamilies, ff, lc;
39088         for(var i = 0, len = fs.length; i< len; i++){
39089             ff = fs[i];
39090             lc = ff.toLowerCase();
39091             buf.push(
39092                 '<option value="',lc,'" style="font-family:',ff,';"',
39093                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39094                     ff,
39095                 '</option>'
39096             );
39097         }
39098         return buf.join('');
39099     },
39100     
39101     toggleSourceEdit : function(sourceEditMode){
39102         if(sourceEditMode === undefined){
39103             sourceEditMode = !this.sourceEditMode;
39104         }
39105         this.sourceEditMode = sourceEditMode === true;
39106         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39107         // just toggle the button?
39108         if(btn.pressed !== this.editor.sourceEditMode){
39109             btn.toggle(this.editor.sourceEditMode);
39110             return;
39111         }
39112         
39113         if(this.sourceEditMode){
39114             this.tb.items.each(function(item){
39115                 if(item.cmd != 'sourceedit'){
39116                     item.disable();
39117                 }
39118             });
39119           
39120         }else{
39121             if(this.initialized){
39122                 this.tb.items.each(function(item){
39123                     item.enable();
39124                 });
39125             }
39126             
39127         }
39128         // tell the editor that it's been pressed..
39129         this.editor.toggleSourceEdit(sourceEditMode);
39130        
39131     },
39132      /**
39133      * Object collection of toolbar tooltips for the buttons in the editor. The key
39134      * is the command id associated with that button and the value is a valid QuickTips object.
39135      * For example:
39136 <pre><code>
39137 {
39138     bold : {
39139         title: 'Bold (Ctrl+B)',
39140         text: 'Make the selected text bold.',
39141         cls: 'x-html-editor-tip'
39142     },
39143     italic : {
39144         title: 'Italic (Ctrl+I)',
39145         text: 'Make the selected text italic.',
39146         cls: 'x-html-editor-tip'
39147     },
39148     ...
39149 </code></pre>
39150     * @type Object
39151      */
39152     buttonTips : {
39153         bold : {
39154             title: 'Bold (Ctrl+B)',
39155             text: 'Make the selected text bold.',
39156             cls: 'x-html-editor-tip'
39157         },
39158         italic : {
39159             title: 'Italic (Ctrl+I)',
39160             text: 'Make the selected text italic.',
39161             cls: 'x-html-editor-tip'
39162         },
39163         underline : {
39164             title: 'Underline (Ctrl+U)',
39165             text: 'Underline the selected text.',
39166             cls: 'x-html-editor-tip'
39167         },
39168         increasefontsize : {
39169             title: 'Grow Text',
39170             text: 'Increase the font size.',
39171             cls: 'x-html-editor-tip'
39172         },
39173         decreasefontsize : {
39174             title: 'Shrink Text',
39175             text: 'Decrease the font size.',
39176             cls: 'x-html-editor-tip'
39177         },
39178         backcolor : {
39179             title: 'Text Highlight Color',
39180             text: 'Change the background color of the selected text.',
39181             cls: 'x-html-editor-tip'
39182         },
39183         forecolor : {
39184             title: 'Font Color',
39185             text: 'Change the color of the selected text.',
39186             cls: 'x-html-editor-tip'
39187         },
39188         justifyleft : {
39189             title: 'Align Text Left',
39190             text: 'Align text to the left.',
39191             cls: 'x-html-editor-tip'
39192         },
39193         justifycenter : {
39194             title: 'Center Text',
39195             text: 'Center text in the editor.',
39196             cls: 'x-html-editor-tip'
39197         },
39198         justifyright : {
39199             title: 'Align Text Right',
39200             text: 'Align text to the right.',
39201             cls: 'x-html-editor-tip'
39202         },
39203         insertunorderedlist : {
39204             title: 'Bullet List',
39205             text: 'Start a bulleted list.',
39206             cls: 'x-html-editor-tip'
39207         },
39208         insertorderedlist : {
39209             title: 'Numbered List',
39210             text: 'Start a numbered list.',
39211             cls: 'x-html-editor-tip'
39212         },
39213         createlink : {
39214             title: 'Hyperlink',
39215             text: 'Make the selected text a hyperlink.',
39216             cls: 'x-html-editor-tip'
39217         },
39218         sourceedit : {
39219             title: 'Source Edit',
39220             text: 'Switch to source editing mode.',
39221             cls: 'x-html-editor-tip'
39222         }
39223     },
39224     // private
39225     onDestroy : function(){
39226         if(this.rendered){
39227             
39228             this.tb.items.each(function(item){
39229                 if(item.menu){
39230                     item.menu.removeAll();
39231                     if(item.menu.el){
39232                         item.menu.el.destroy();
39233                     }
39234                 }
39235                 item.destroy();
39236             });
39237              
39238         }
39239     },
39240     onFirstFocus: function() {
39241         this.tb.items.each(function(item){
39242            item.enable();
39243         });
39244     }
39245 });
39246
39247
39248
39249
39250 // <script type="text/javascript">
39251 /*
39252  * Based on
39253  * Ext JS Library 1.1.1
39254  * Copyright(c) 2006-2007, Ext JS, LLC.
39255  *  
39256  
39257  */
39258
39259  
39260 /**
39261  * @class Roo.form.HtmlEditor.ToolbarContext
39262  * Context Toolbar
39263  * 
39264  * Usage:
39265  *
39266  new Roo.form.HtmlEditor({
39267     ....
39268     toolbars : [
39269         new Roo.form.HtmlEditor.ToolbarStandard(),
39270         new Roo.form.HtmlEditor.ToolbarContext()
39271         })
39272     }
39273      
39274  * 
39275  * @config : {Object} disable List of elements to disable.. (not done yet.)
39276  * 
39277  * 
39278  */
39279
39280 Roo.form.HtmlEditor.ToolbarContext = function(config)
39281 {
39282     
39283     Roo.apply(this, config);
39284     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39285     // dont call parent... till later.
39286 }
39287 Roo.form.HtmlEditor.ToolbarContext.types = {
39288     'IMG' : {
39289         width : {
39290             title: "Width",
39291             width: 40
39292         },
39293         height:  {
39294             title: "Height",
39295             width: 40
39296         },
39297         align: {
39298             title: "Align",
39299             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39300             width : 80
39301             
39302         },
39303         border: {
39304             title: "Border",
39305             width: 40
39306         },
39307         alt: {
39308             title: "Alt",
39309             width: 120
39310         },
39311         src : {
39312             title: "Src",
39313             width: 220
39314         }
39315         
39316     },
39317     'A' : {
39318         name : {
39319             title: "Name",
39320             width: 50
39321         },
39322         href:  {
39323             title: "Href",
39324             width: 220
39325         } // border?
39326         
39327     },
39328     'TABLE' : {
39329         rows : {
39330             title: "Rows",
39331             width: 20
39332         },
39333         cols : {
39334             title: "Cols",
39335             width: 20
39336         },
39337         width : {
39338             title: "Width",
39339             width: 40
39340         },
39341         height : {
39342             title: "Height",
39343             width: 40
39344         },
39345         border : {
39346             title: "Border",
39347             width: 20
39348         }
39349     },
39350     'TD' : {
39351         width : {
39352             title: "Width",
39353             width: 40
39354         },
39355         height : {
39356             title: "Height",
39357             width: 40
39358         },   
39359         align: {
39360             title: "Align",
39361             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39362             width: 40
39363         },
39364         valign: {
39365             title: "Valign",
39366             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39367             width: 40
39368         },
39369         colspan: {
39370             title: "Colspan",
39371             width: 20
39372             
39373         }
39374     },
39375     'INPUT' : {
39376         name : {
39377             title: "name",
39378             width: 120
39379         },
39380         value : {
39381             title: "Value",
39382             width: 120
39383         },
39384         width : {
39385             title: "Width",
39386             width: 40
39387         }
39388     },
39389     'LABEL' : {
39390         'for' : {
39391             title: "For",
39392             width: 120
39393         }
39394     },
39395     'TEXTAREA' : {
39396           name : {
39397             title: "name",
39398             width: 120
39399         },
39400         rows : {
39401             title: "Rows",
39402             width: 20
39403         },
39404         cols : {
39405             title: "Cols",
39406             width: 20
39407         }
39408     },
39409     'SELECT' : {
39410         name : {
39411             title: "name",
39412             width: 120
39413         },
39414         selectoptions : {
39415             title: "Options",
39416             width: 200
39417         }
39418     },
39419     'BODY' : {
39420         title : {
39421             title: "title",
39422             width: 120,
39423             disabled : true
39424         }
39425     }
39426 };
39427
39428
39429
39430 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39431     
39432     tb: false,
39433     
39434     rendered: false,
39435     
39436     editor : false,
39437     /**
39438      * @cfg {Object} disable  List of toolbar elements to disable
39439          
39440      */
39441     disable : false,
39442     
39443     
39444     
39445     toolbars : false,
39446     
39447     init : function(editor)
39448     {
39449         this.editor = editor;
39450         
39451         
39452         var fid = editor.frameId;
39453         var etb = this;
39454         function btn(id, toggle, handler){
39455             var xid = fid + '-'+ id ;
39456             return {
39457                 id : xid,
39458                 cmd : id,
39459                 cls : 'x-btn-icon x-edit-'+id,
39460                 enableToggle:toggle !== false,
39461                 scope: editor, // was editor...
39462                 handler:handler||editor.relayBtnCmd,
39463                 clickEvent:'mousedown',
39464                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39465                 tabIndex:-1
39466             };
39467         }
39468         // create a new element.
39469         var wdiv = editor.wrap.createChild({
39470                 tag: 'div'
39471             }, editor.wrap.dom.firstChild.nextSibling, true);
39472         
39473         // can we do this more than once??
39474         
39475          // stop form submits
39476       
39477  
39478         // disable everything...
39479         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39480         this.toolbars = {};
39481            
39482         for (var i in  ty) {
39483             this.toolbars[i] = this.buildToolbar(ty[i],i);
39484         }
39485         this.tb = this.toolbars.BODY;
39486         this.tb.el.show();
39487         
39488          
39489         this.rendered = true;
39490         
39491         // the all the btns;
39492         editor.on('editorevent', this.updateToolbar, this);
39493         // other toolbars need to implement this..
39494         //editor.on('editmodechange', this.updateToolbar, this);
39495     },
39496     
39497     
39498     
39499     /**
39500      * Protected method that will not generally be called directly. It triggers
39501      * a toolbar update by reading the markup state of the current selection in the editor.
39502      */
39503     updateToolbar: function(){
39504
39505         if(!this.editor.activated){
39506             this.editor.onFirstFocus();
39507             return;
39508         }
39509
39510         
39511         var ans = this.editor.getAllAncestors();
39512         
39513         // pick
39514         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39515         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39516         sel = sel ? sel : this.editor.doc.body;
39517         sel = sel.tagName.length ? sel : this.editor.doc.body;
39518         var tn = sel.tagName.toUpperCase();
39519         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39520         tn = sel.tagName.toUpperCase();
39521         if (this.tb.name  == tn) {
39522             return; // no change
39523         }
39524         this.tb.el.hide();
39525         ///console.log("show: " + tn);
39526         this.tb =  this.toolbars[tn];
39527         this.tb.el.show();
39528         this.tb.fields.each(function(e) {
39529             e.setValue(sel.getAttribute(e.name));
39530         });
39531         this.tb.selectedNode = sel;
39532         
39533         
39534         Roo.menu.MenuMgr.hideAll();
39535
39536         //this.editorsyncValue();
39537     },
39538    
39539        
39540     // private
39541     onDestroy : function(){
39542         if(this.rendered){
39543             
39544             this.tb.items.each(function(item){
39545                 if(item.menu){
39546                     item.menu.removeAll();
39547                     if(item.menu.el){
39548                         item.menu.el.destroy();
39549                     }
39550                 }
39551                 item.destroy();
39552             });
39553              
39554         }
39555     },
39556     onFirstFocus: function() {
39557         // need to do this for all the toolbars..
39558         this.tb.items.each(function(item){
39559            item.enable();
39560         });
39561     },
39562     buildToolbar: function(tlist, nm)
39563     {
39564         var editor = this.editor;
39565          // create a new element.
39566         var wdiv = editor.wrap.createChild({
39567                 tag: 'div'
39568             }, editor.wrap.dom.firstChild.nextSibling, true);
39569         
39570        
39571         var tb = new Roo.Toolbar(wdiv);
39572         tb.add(nm+ ":&nbsp;");
39573         for (var i in tlist) {
39574             var item = tlist[i];
39575             tb.add(item.title + ":&nbsp;");
39576             if (item.opts) {
39577                 // fixme
39578                 
39579               
39580                 tb.addField( new Roo.form.ComboBox({
39581                     store: new Roo.data.SimpleStore({
39582                         id : 'val',
39583                         fields: ['val'],
39584                         data : item.opts // from states.js
39585                     }),
39586                     name : i,
39587                     displayField:'val',
39588                     typeAhead: false,
39589                     mode: 'local',
39590                     editable : false,
39591                     triggerAction: 'all',
39592                     emptyText:'Select',
39593                     selectOnFocus:true,
39594                     width: item.width ? item.width  : 130,
39595                     listeners : {
39596                         'select': function(c, r, i) {
39597                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39598                         }
39599                     }
39600
39601                 }));
39602                 continue;
39603                     
39604                 
39605                 
39606                 
39607                 
39608                 tb.addField( new Roo.form.TextField({
39609                     name: i,
39610                     width: 100,
39611                     //allowBlank:false,
39612                     value: ''
39613                 }));
39614                 continue;
39615             }
39616             tb.addField( new Roo.form.TextField({
39617                 name: i,
39618                 width: item.width,
39619                 //allowBlank:true,
39620                 value: '',
39621                 listeners: {
39622                     'change' : function(f, nv, ov) {
39623                         tb.selectedNode.setAttribute(f.name, nv);
39624                     }
39625                 }
39626             }));
39627              
39628         }
39629         tb.el.on('click', function(e){
39630             e.preventDefault(); // what does this do?
39631         });
39632         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39633         tb.el.hide();
39634         tb.name = nm;
39635         // dont need to disable them... as they will get hidden
39636         return tb;
39637          
39638         
39639     }
39640     
39641     
39642     
39643     
39644 });
39645
39646
39647
39648
39649
39650 /*
39651  * Based on:
39652  * Ext JS Library 1.1.1
39653  * Copyright(c) 2006-2007, Ext JS, LLC.
39654  *
39655  * Originally Released Under LGPL - original licence link has changed is not relivant.
39656  *
39657  * Fork - LGPL
39658  * <script type="text/javascript">
39659  */
39660  
39661 /**
39662  * @class Roo.form.BasicForm
39663  * @extends Roo.util.Observable
39664  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39665  * @constructor
39666  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39667  * @param {Object} config Configuration options
39668  */
39669 Roo.form.BasicForm = function(el, config){
39670     this.allItems = [];
39671     this.childForms = [];
39672     Roo.apply(this, config);
39673     /*
39674      * The Roo.form.Field items in this form.
39675      * @type MixedCollection
39676      */
39677      
39678      
39679     this.items = new Roo.util.MixedCollection(false, function(o){
39680         return o.id || (o.id = Roo.id());
39681     });
39682     this.addEvents({
39683         /**
39684          * @event beforeaction
39685          * Fires before any action is performed. Return false to cancel the action.
39686          * @param {Form} this
39687          * @param {Action} action The action to be performed
39688          */
39689         beforeaction: true,
39690         /**
39691          * @event actionfailed
39692          * Fires when an action fails.
39693          * @param {Form} this
39694          * @param {Action} action The action that failed
39695          */
39696         actionfailed : true,
39697         /**
39698          * @event actioncomplete
39699          * Fires when an action is completed.
39700          * @param {Form} this
39701          * @param {Action} action The action that completed
39702          */
39703         actioncomplete : true
39704     });
39705     if(el){
39706         this.initEl(el);
39707     }
39708     Roo.form.BasicForm.superclass.constructor.call(this);
39709 };
39710
39711 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39712     /**
39713      * @cfg {String} method
39714      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39715      */
39716     /**
39717      * @cfg {DataReader} reader
39718      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39719      * This is optional as there is built-in support for processing JSON.
39720      */
39721     /**
39722      * @cfg {DataReader} errorReader
39723      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39724      * This is completely optional as there is built-in support for processing JSON.
39725      */
39726     /**
39727      * @cfg {String} url
39728      * The URL to use for form actions if one isn't supplied in the action options.
39729      */
39730     /**
39731      * @cfg {Boolean} fileUpload
39732      * Set to true if this form is a file upload.
39733      */
39734     /**
39735      * @cfg {Object} baseParams
39736      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39737      */
39738     /**
39739      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39740      */
39741     timeout: 30,
39742
39743     // private
39744     activeAction : null,
39745
39746     /**
39747      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39748      * or setValues() data instead of when the form was first created.
39749      */
39750     trackResetOnLoad : false,
39751     
39752     
39753     /**
39754      * childForms - used for multi-tab forms
39755      * @type {Array}
39756      */
39757     childForms : false,
39758     
39759     /**
39760      * allItems - full list of fields.
39761      * @type {Array}
39762      */
39763     allItems : false,
39764     
39765     /**
39766      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39767      * element by passing it or its id or mask the form itself by passing in true.
39768      * @type Mixed
39769      */
39770     waitMsgTarget : undefined,
39771
39772     // private
39773     initEl : function(el){
39774         this.el = Roo.get(el);
39775         this.id = this.el.id || Roo.id();
39776         this.el.on('submit', this.onSubmit, this);
39777         this.el.addClass('x-form');
39778     },
39779
39780     // private
39781     onSubmit : function(e){
39782         e.stopEvent();
39783     },
39784
39785     /**
39786      * Returns true if client-side validation on the form is successful.
39787      * @return Boolean
39788      */
39789     isValid : function(){
39790         var valid = true;
39791         this.items.each(function(f){
39792            if(!f.validate()){
39793                valid = false;
39794            }
39795         });
39796         return valid;
39797     },
39798
39799     /**
39800      * Returns true if any fields in this form have changed since their original load.
39801      * @return Boolean
39802      */
39803     isDirty : function(){
39804         var dirty = false;
39805         this.items.each(function(f){
39806            if(f.isDirty()){
39807                dirty = true;
39808                return false;
39809            }
39810         });
39811         return dirty;
39812     },
39813
39814     /**
39815      * Performs a predefined action (submit or load) or custom actions you define on this form.
39816      * @param {String} actionName The name of the action type
39817      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39818      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39819      * accept other config options):
39820      * <pre>
39821 Property          Type             Description
39822 ----------------  ---------------  ----------------------------------------------------------------------------------
39823 url               String           The url for the action (defaults to the form's url)
39824 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39825 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39826 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39827                                    validate the form on the client (defaults to false)
39828      * </pre>
39829      * @return {BasicForm} this
39830      */
39831     doAction : function(action, options){
39832         if(typeof action == 'string'){
39833             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39834         }
39835         if(this.fireEvent('beforeaction', this, action) !== false){
39836             this.beforeAction(action);
39837             action.run.defer(100, action);
39838         }
39839         return this;
39840     },
39841
39842     /**
39843      * Shortcut to do a submit action.
39844      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39845      * @return {BasicForm} this
39846      */
39847     submit : function(options){
39848         this.doAction('submit', options);
39849         return this;
39850     },
39851
39852     /**
39853      * Shortcut to do a load action.
39854      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39855      * @return {BasicForm} this
39856      */
39857     load : function(options){
39858         this.doAction('load', options);
39859         return this;
39860     },
39861
39862     /**
39863      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39864      * @param {Record} record The record to edit
39865      * @return {BasicForm} this
39866      */
39867     updateRecord : function(record){
39868         record.beginEdit();
39869         var fs = record.fields;
39870         fs.each(function(f){
39871             var field = this.findField(f.name);
39872             if(field){
39873                 record.set(f.name, field.getValue());
39874             }
39875         }, this);
39876         record.endEdit();
39877         return this;
39878     },
39879
39880     /**
39881      * Loads an Roo.data.Record into this form.
39882      * @param {Record} record The record to load
39883      * @return {BasicForm} this
39884      */
39885     loadRecord : function(record){
39886         this.setValues(record.data);
39887         return this;
39888     },
39889
39890     // private
39891     beforeAction : function(action){
39892         var o = action.options;
39893         if(o.waitMsg){
39894             if(this.waitMsgTarget === true){
39895                 this.el.mask(o.waitMsg, 'x-mask-loading');
39896             }else if(this.waitMsgTarget){
39897                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39898                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39899             }else{
39900                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39901             }
39902         }
39903     },
39904
39905     // private
39906     afterAction : function(action, success){
39907         this.activeAction = null;
39908         var o = action.options;
39909         if(o.waitMsg){
39910             if(this.waitMsgTarget === true){
39911                 this.el.unmask();
39912             }else if(this.waitMsgTarget){
39913                 this.waitMsgTarget.unmask();
39914             }else{
39915                 Roo.MessageBox.updateProgress(1);
39916                 Roo.MessageBox.hide();
39917             }
39918         }
39919         if(success){
39920             if(o.reset){
39921                 this.reset();
39922             }
39923             Roo.callback(o.success, o.scope, [this, action]);
39924             this.fireEvent('actioncomplete', this, action);
39925         }else{
39926             Roo.callback(o.failure, o.scope, [this, action]);
39927             this.fireEvent('actionfailed', this, action);
39928         }
39929     },
39930
39931     /**
39932      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39933      * @param {String} id The value to search for
39934      * @return Field
39935      */
39936     findField : function(id){
39937         var field = this.items.get(id);
39938         if(!field){
39939             this.items.each(function(f){
39940                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39941                     field = f;
39942                     return false;
39943                 }
39944             });
39945         }
39946         return field || null;
39947     },
39948
39949     /**
39950      * Add a secondary form to this one, 
39951      * Used to provide tabbed forms. One form is primary, with hidden values 
39952      * which mirror the elements from the other forms.
39953      * 
39954      * @param {Roo.form.Form} form to add.
39955      * 
39956      */
39957     addForm : function(form){
39958        
39959         this.childForms.push(form);
39960         Roo.each(form.allItems, function (fe) {
39961             
39962             if (this.findField(fe.name)) { // already added..
39963                 return;
39964             }
39965             this.add( new Roo.form.Hidden({
39966                 name : fe.name
39967             }));
39968         }, this);
39969         
39970     },
39971     /**
39972      * Mark fields in this form invalid in bulk.
39973      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39974      * @return {BasicForm} this
39975      */
39976     markInvalid : function(errors){
39977         if(errors instanceof Array){
39978             for(var i = 0, len = errors.length; i < len; i++){
39979                 var fieldError = errors[i];
39980                 var f = this.findField(fieldError.id);
39981                 if(f){
39982                     f.markInvalid(fieldError.msg);
39983                 }
39984             }
39985         }else{
39986             var field, id;
39987             for(id in errors){
39988                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39989                     field.markInvalid(errors[id]);
39990                 }
39991             }
39992         }
39993         Roo.each(this.childForms || [], function (f) {
39994             f.markInvalid(errors);
39995         });
39996         
39997         return this;
39998     },
39999
40000     /**
40001      * Set values for fields in this form in bulk.
40002      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40003      * @return {BasicForm} this
40004      */
40005     setValues : function(values){
40006         if(values instanceof Array){ // array of objects
40007             for(var i = 0, len = values.length; i < len; i++){
40008                 var v = values[i];
40009                 var f = this.findField(v.id);
40010                 if(f){
40011                     f.setValue(v.value);
40012                     if(this.trackResetOnLoad){
40013                         f.originalValue = f.getValue();
40014                     }
40015                 }
40016             }
40017         }else{ // object hash
40018             var field, id;
40019             for(id in values){
40020                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40021                     
40022                     if (field.setFromData && 
40023                         field.valueField && 
40024                         field.displayField &&
40025                         // combos' with local stores can 
40026                         // be queried via setValue()
40027                         // to set their value..
40028                         (field.store && !field.store.isLocal)
40029                         ) {
40030                         // it's a combo
40031                         var sd = { };
40032                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40033                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40034                         field.setFromData(sd);
40035                         
40036                     } else {
40037                         field.setValue(values[id]);
40038                     }
40039                     
40040                     
40041                     if(this.trackResetOnLoad){
40042                         field.originalValue = field.getValue();
40043                     }
40044                 }
40045             }
40046         }
40047          
40048         Roo.each(this.childForms || [], function (f) {
40049             f.setValues(values);
40050         });
40051                 
40052         return this;
40053     },
40054
40055     /**
40056      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40057      * they are returned as an array.
40058      * @param {Boolean} asString
40059      * @return {Object}
40060      */
40061     getValues : function(asString){
40062         if (this.childForms) {
40063             // copy values from the child forms
40064             Roo.each(this.childForms, function (f) {
40065                 if (f.allFields) {
40066                     Roo.each(f.allFields, function (e) {
40067                         if (e.name && e.getValue && this.findField(e.name)) {
40068                             this.findField(e.name).setValue(e.getValue());
40069                         }
40070                     });
40071                 }
40072             }, this);
40073         }
40074         
40075         
40076         
40077         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40078         if(asString === true){
40079             return fs;
40080         }
40081         return Roo.urlDecode(fs);
40082     },
40083
40084     /**
40085      * Clears all invalid messages in this form.
40086      * @return {BasicForm} this
40087      */
40088     clearInvalid : function(){
40089         this.items.each(function(f){
40090            f.clearInvalid();
40091         });
40092         
40093         Roo.each(this.childForms || [], function (f) {
40094             f.clearInvalid();
40095         });
40096         
40097         
40098         return this;
40099     },
40100
40101     /**
40102      * Resets this form.
40103      * @return {BasicForm} this
40104      */
40105     reset : function(){
40106         this.items.each(function(f){
40107             f.reset();
40108         });
40109         
40110         Roo.each(this.childForms || [], function (f) {
40111             f.reset();
40112         });
40113        
40114         
40115         return this;
40116     },
40117
40118     /**
40119      * Add Roo.form components to this form.
40120      * @param {Field} field1
40121      * @param {Field} field2 (optional)
40122      * @param {Field} etc (optional)
40123      * @return {BasicForm} this
40124      */
40125     add : function(){
40126         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40127         return this;
40128     },
40129
40130
40131     /**
40132      * Removes a field from the items collection (does NOT remove its markup).
40133      * @param {Field} field
40134      * @return {BasicForm} this
40135      */
40136     remove : function(field){
40137         this.items.remove(field);
40138         return this;
40139     },
40140
40141     /**
40142      * Looks at the fields in this form, checks them for an id attribute,
40143      * and calls applyTo on the existing dom element with that id.
40144      * @return {BasicForm} this
40145      */
40146     render : function(){
40147         this.items.each(function(f){
40148             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40149                 f.applyTo(f.id);
40150             }
40151         });
40152         return this;
40153     },
40154
40155     /**
40156      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40157      * @param {Object} values
40158      * @return {BasicForm} this
40159      */
40160     applyToFields : function(o){
40161         this.items.each(function(f){
40162            Roo.apply(f, o);
40163         });
40164         return this;
40165     },
40166
40167     /**
40168      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40169      * @param {Object} values
40170      * @return {BasicForm} this
40171      */
40172     applyIfToFields : function(o){
40173         this.items.each(function(f){
40174            Roo.applyIf(f, o);
40175         });
40176         return this;
40177     }
40178 });
40179
40180 // back compat
40181 Roo.BasicForm = Roo.form.BasicForm;/*
40182  * Based on:
40183  * Ext JS Library 1.1.1
40184  * Copyright(c) 2006-2007, Ext JS, LLC.
40185  *
40186  * Originally Released Under LGPL - original licence link has changed is not relivant.
40187  *
40188  * Fork - LGPL
40189  * <script type="text/javascript">
40190  */
40191
40192 /**
40193  * @class Roo.form.Form
40194  * @extends Roo.form.BasicForm
40195  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40196  * @constructor
40197  * @param {Object} config Configuration options
40198  */
40199 Roo.form.Form = function(config){
40200     var xitems =  [];
40201     if (config.items) {
40202         xitems = config.items;
40203         delete config.items;
40204     }
40205    
40206     
40207     Roo.form.Form.superclass.constructor.call(this, null, config);
40208     this.url = this.url || this.action;
40209     if(!this.root){
40210         this.root = new Roo.form.Layout(Roo.applyIf({
40211             id: Roo.id()
40212         }, config));
40213     }
40214     this.active = this.root;
40215     /**
40216      * Array of all the buttons that have been added to this form via {@link addButton}
40217      * @type Array
40218      */
40219     this.buttons = [];
40220     this.allItems = [];
40221     this.addEvents({
40222         /**
40223          * @event clientvalidation
40224          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40225          * @param {Form} this
40226          * @param {Boolean} valid true if the form has passed client-side validation
40227          */
40228         clientvalidation: true,
40229         /**
40230          * @event rendered
40231          * Fires when the form is rendered
40232          * @param {Roo.form.Form} form
40233          */
40234         rendered : true
40235     });
40236     
40237     Roo.each(xitems, this.addxtype, this);
40238     
40239     
40240     
40241 };
40242
40243 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40244     /**
40245      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40246      */
40247     /**
40248      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40249      */
40250     /**
40251      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40252      */
40253     buttonAlign:'center',
40254
40255     /**
40256      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40257      */
40258     minButtonWidth:75,
40259
40260     /**
40261      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40262      * This property cascades to child containers if not set.
40263      */
40264     labelAlign:'left',
40265
40266     /**
40267      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40268      * fires a looping event with that state. This is required to bind buttons to the valid
40269      * state using the config value formBind:true on the button.
40270      */
40271     monitorValid : false,
40272
40273     /**
40274      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40275      */
40276     monitorPoll : 200,
40277
40278   
40279     /**
40280      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40281      * fields are added and the column is closed. If no fields are passed the column remains open
40282      * until end() is called.
40283      * @param {Object} config The config to pass to the column
40284      * @param {Field} field1 (optional)
40285      * @param {Field} field2 (optional)
40286      * @param {Field} etc (optional)
40287      * @return Column The column container object
40288      */
40289     column : function(c){
40290         var col = new Roo.form.Column(c);
40291         this.start(col);
40292         if(arguments.length > 1){ // duplicate code required because of Opera
40293             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40294             this.end();
40295         }
40296         return col;
40297     },
40298
40299     /**
40300      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40301      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40302      * until end() is called.
40303      * @param {Object} config The config to pass to the fieldset
40304      * @param {Field} field1 (optional)
40305      * @param {Field} field2 (optional)
40306      * @param {Field} etc (optional)
40307      * @return FieldSet The fieldset container object
40308      */
40309     fieldset : function(c){
40310         var fs = new Roo.form.FieldSet(c);
40311         this.start(fs);
40312         if(arguments.length > 1){ // duplicate code required because of Opera
40313             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40314             this.end();
40315         }
40316         return fs;
40317     },
40318
40319     /**
40320      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40321      * fields are added and the container is closed. If no fields are passed the container remains open
40322      * until end() is called.
40323      * @param {Object} config The config to pass to the Layout
40324      * @param {Field} field1 (optional)
40325      * @param {Field} field2 (optional)
40326      * @param {Field} etc (optional)
40327      * @return Layout The container object
40328      */
40329     container : function(c){
40330         var l = new Roo.form.Layout(c);
40331         this.start(l);
40332         if(arguments.length > 1){ // duplicate code required because of Opera
40333             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40334             this.end();
40335         }
40336         return l;
40337     },
40338
40339     /**
40340      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40341      * @param {Object} container A Roo.form.Layout or subclass of Layout
40342      * @return {Form} this
40343      */
40344     start : function(c){
40345         // cascade label info
40346         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40347         this.active.stack.push(c);
40348         c.ownerCt = this.active;
40349         this.active = c;
40350         return this;
40351     },
40352
40353     /**
40354      * Closes the current open container
40355      * @return {Form} this
40356      */
40357     end : function(){
40358         if(this.active == this.root){
40359             return this;
40360         }
40361         this.active = this.active.ownerCt;
40362         return this;
40363     },
40364
40365     /**
40366      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40367      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40368      * as the label of the field.
40369      * @param {Field} field1
40370      * @param {Field} field2 (optional)
40371      * @param {Field} etc. (optional)
40372      * @return {Form} this
40373      */
40374     add : function(){
40375         this.active.stack.push.apply(this.active.stack, arguments);
40376         this.allItems.push.apply(this.allItems,arguments);
40377         var r = [];
40378         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40379             if(a[i].isFormField){
40380                 r.push(a[i]);
40381             }
40382         }
40383         if(r.length > 0){
40384             Roo.form.Form.superclass.add.apply(this, r);
40385         }
40386         return this;
40387     },
40388     
40389
40390     
40391     
40392     
40393      /**
40394      * Find any element that has been added to a form, using it's ID or name
40395      * This can include framesets, columns etc. along with regular fields..
40396      * @param {String} id - id or name to find.
40397      
40398      * @return {Element} e - or false if nothing found.
40399      */
40400     findbyId : function(id)
40401     {
40402         var ret = false;
40403         if (!id) {
40404             return ret;
40405         }
40406         Ext.each(this.allItems, function(f){
40407             if (f.id == id || f.name == id ){
40408                 ret = f;
40409                 return false;
40410             }
40411         });
40412         return ret;
40413     },
40414
40415     
40416     
40417     /**
40418      * Render this form into the passed container. This should only be called once!
40419      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40420      * @return {Form} this
40421      */
40422     render : function(ct){
40423         ct = Roo.get(ct);
40424         var o = this.autoCreate || {
40425             tag: 'form',
40426             method : this.method || 'POST',
40427             id : this.id || Roo.id()
40428         };
40429         this.initEl(ct.createChild(o));
40430
40431         this.root.render(this.el);
40432
40433         this.items.each(function(f){
40434             f.render('x-form-el-'+f.id);
40435         });
40436
40437         if(this.buttons.length > 0){
40438             // tables are required to maintain order and for correct IE layout
40439             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40440                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40441                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40442             }}, null, true);
40443             var tr = tb.getElementsByTagName('tr')[0];
40444             for(var i = 0, len = this.buttons.length; i < len; i++) {
40445                 var b = this.buttons[i];
40446                 var td = document.createElement('td');
40447                 td.className = 'x-form-btn-td';
40448                 b.render(tr.appendChild(td));
40449             }
40450         }
40451         if(this.monitorValid){ // initialize after render
40452             this.startMonitoring();
40453         }
40454         this.fireEvent('rendered', this);
40455         return this;
40456     },
40457
40458     /**
40459      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40460      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40461      * object or a valid Roo.DomHelper element config
40462      * @param {Function} handler The function called when the button is clicked
40463      * @param {Object} scope (optional) The scope of the handler function
40464      * @return {Roo.Button}
40465      */
40466     addButton : function(config, handler, scope){
40467         var bc = {
40468             handler: handler,
40469             scope: scope,
40470             minWidth: this.minButtonWidth,
40471             hideParent:true
40472         };
40473         if(typeof config == "string"){
40474             bc.text = config;
40475         }else{
40476             Roo.apply(bc, config);
40477         }
40478         var btn = new Roo.Button(null, bc);
40479         this.buttons.push(btn);
40480         return btn;
40481     },
40482
40483      /**
40484      * Adds a series of form elements (using the xtype property as the factory method.
40485      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40486      * @param {Object} config 
40487      */
40488     
40489     addxtype : function()
40490     {
40491         var ar = Array.prototype.slice.call(arguments, 0);
40492         var ret = false;
40493         for(var i = 0; i < ar.length; i++) {
40494             if (!ar[i]) {
40495                 continue; // skip -- if this happends something invalid got sent, we 
40496                 // should ignore it, as basically that interface element will not show up
40497                 // and that should be pretty obvious!!
40498             }
40499             
40500             if (Roo.form[ar[i].xtype]) {
40501                 ar[i].form = this;
40502                 var fe = Roo.factory(ar[i], Roo.form);
40503                 if (!ret) {
40504                     ret = fe;
40505                 }
40506                 fe.form = this;
40507                 if (fe.store) {
40508                     fe.store.form = this;
40509                 }
40510                 if (fe.isLayout) {  
40511                          
40512                     this.start(fe);
40513                     this.allItems.push(fe);
40514                     if (fe.items && fe.addxtype) {
40515                         fe.addxtype.apply(fe, fe.items);
40516                         delete fe.items;
40517                     }
40518                      this.end();
40519                     continue;
40520                 }
40521                 
40522                 
40523                  
40524                 this.add(fe);
40525               //  console.log('adding ' + ar[i].xtype);
40526             }
40527             if (ar[i].xtype == 'Button') {  
40528                 //console.log('adding button');
40529                 //console.log(ar[i]);
40530                 this.addButton(ar[i]);
40531                 this.allItems.push(fe);
40532                 continue;
40533             }
40534             
40535             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40536                 alert('end is not supported on xtype any more, use items');
40537             //    this.end();
40538             //    //console.log('adding end');
40539             }
40540             
40541         }
40542         return ret;
40543     },
40544     
40545     /**
40546      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40547      * option "monitorValid"
40548      */
40549     startMonitoring : function(){
40550         if(!this.bound){
40551             this.bound = true;
40552             Roo.TaskMgr.start({
40553                 run : this.bindHandler,
40554                 interval : this.monitorPoll || 200,
40555                 scope: this
40556             });
40557         }
40558     },
40559
40560     /**
40561      * Stops monitoring of the valid state of this form
40562      */
40563     stopMonitoring : function(){
40564         this.bound = false;
40565     },
40566
40567     // private
40568     bindHandler : function(){
40569         if(!this.bound){
40570             return false; // stops binding
40571         }
40572         var valid = true;
40573         this.items.each(function(f){
40574             if(!f.isValid(true)){
40575                 valid = false;
40576                 return false;
40577             }
40578         });
40579         for(var i = 0, len = this.buttons.length; i < len; i++){
40580             var btn = this.buttons[i];
40581             if(btn.formBind === true && btn.disabled === valid){
40582                 btn.setDisabled(!valid);
40583             }
40584         }
40585         this.fireEvent('clientvalidation', this, valid);
40586     }
40587     
40588     
40589     
40590     
40591     
40592     
40593     
40594     
40595 });
40596
40597
40598 // back compat
40599 Roo.Form = Roo.form.Form;
40600 /*
40601  * Based on:
40602  * Ext JS Library 1.1.1
40603  * Copyright(c) 2006-2007, Ext JS, LLC.
40604  *
40605  * Originally Released Under LGPL - original licence link has changed is not relivant.
40606  *
40607  * Fork - LGPL
40608  * <script type="text/javascript">
40609  */
40610  
40611  /**
40612  * @class Roo.form.Action
40613  * Internal Class used to handle form actions
40614  * @constructor
40615  * @param {Roo.form.BasicForm} el The form element or its id
40616  * @param {Object} config Configuration options
40617  */
40618  
40619  
40620 // define the action interface
40621 Roo.form.Action = function(form, options){
40622     this.form = form;
40623     this.options = options || {};
40624 };
40625 /**
40626  * Client Validation Failed
40627  * @const 
40628  */
40629 Roo.form.Action.CLIENT_INVALID = 'client';
40630 /**
40631  * Server Validation Failed
40632  * @const 
40633  */
40634  Roo.form.Action.SERVER_INVALID = 'server';
40635  /**
40636  * Connect to Server Failed
40637  * @const 
40638  */
40639 Roo.form.Action.CONNECT_FAILURE = 'connect';
40640 /**
40641  * Reading Data from Server Failed
40642  * @const 
40643  */
40644 Roo.form.Action.LOAD_FAILURE = 'load';
40645
40646 Roo.form.Action.prototype = {
40647     type : 'default',
40648     failureType : undefined,
40649     response : undefined,
40650     result : undefined,
40651
40652     // interface method
40653     run : function(options){
40654
40655     },
40656
40657     // interface method
40658     success : function(response){
40659
40660     },
40661
40662     // interface method
40663     handleResponse : function(response){
40664
40665     },
40666
40667     // default connection failure
40668     failure : function(response){
40669         this.response = response;
40670         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40671         this.form.afterAction(this, false);
40672     },
40673
40674     processResponse : function(response){
40675         this.response = response;
40676         if(!response.responseText){
40677             return true;
40678         }
40679         this.result = this.handleResponse(response);
40680         return this.result;
40681     },
40682
40683     // utility functions used internally
40684     getUrl : function(appendParams){
40685         var url = this.options.url || this.form.url || this.form.el.dom.action;
40686         if(appendParams){
40687             var p = this.getParams();
40688             if(p){
40689                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40690             }
40691         }
40692         return url;
40693     },
40694
40695     getMethod : function(){
40696         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40697     },
40698
40699     getParams : function(){
40700         var bp = this.form.baseParams;
40701         var p = this.options.params;
40702         if(p){
40703             if(typeof p == "object"){
40704                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40705             }else if(typeof p == 'string' && bp){
40706                 p += '&' + Roo.urlEncode(bp);
40707             }
40708         }else if(bp){
40709             p = Roo.urlEncode(bp);
40710         }
40711         return p;
40712     },
40713
40714     createCallback : function(){
40715         return {
40716             success: this.success,
40717             failure: this.failure,
40718             scope: this,
40719             timeout: (this.form.timeout*1000),
40720             upload: this.form.fileUpload ? this.success : undefined
40721         };
40722     }
40723 };
40724
40725 Roo.form.Action.Submit = function(form, options){
40726     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40727 };
40728
40729 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40730     type : 'submit',
40731
40732     run : function(){
40733         var o = this.options;
40734         var method = this.getMethod();
40735         var isPost = method == 'POST';
40736         if(o.clientValidation === false || this.form.isValid()){
40737             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40738                 form:this.form.el.dom,
40739                 url:this.getUrl(!isPost),
40740                 method: method,
40741                 params:isPost ? this.getParams() : null,
40742                 isUpload: this.form.fileUpload
40743             }));
40744
40745         }else if (o.clientValidation !== false){ // client validation failed
40746             this.failureType = Roo.form.Action.CLIENT_INVALID;
40747             this.form.afterAction(this, false);
40748         }
40749     },
40750
40751     success : function(response){
40752         var result = this.processResponse(response);
40753         if(result === true || result.success){
40754             this.form.afterAction(this, true);
40755             return;
40756         }
40757         if(result.errors){
40758             this.form.markInvalid(result.errors);
40759             this.failureType = Roo.form.Action.SERVER_INVALID;
40760         }
40761         this.form.afterAction(this, false);
40762     },
40763
40764     handleResponse : function(response){
40765         if(this.form.errorReader){
40766             var rs = this.form.errorReader.read(response);
40767             var errors = [];
40768             if(rs.records){
40769                 for(var i = 0, len = rs.records.length; i < len; i++) {
40770                     var r = rs.records[i];
40771                     errors[i] = r.data;
40772                 }
40773             }
40774             if(errors.length < 1){
40775                 errors = null;
40776             }
40777             return {
40778                 success : rs.success,
40779                 errors : errors
40780             };
40781         }
40782         var ret = false;
40783         try {
40784             ret = Roo.decode(response.responseText);
40785         } catch (e) {
40786             ret = {
40787                 success: false,
40788                 errorMsg: "Failed to read server message: " + response.responseText,
40789                 errors : []
40790             };
40791         }
40792         return ret;
40793         
40794     }
40795 });
40796
40797
40798 Roo.form.Action.Load = function(form, options){
40799     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40800     this.reader = this.form.reader;
40801 };
40802
40803 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40804     type : 'load',
40805
40806     run : function(){
40807         Roo.Ajax.request(Roo.apply(
40808                 this.createCallback(), {
40809                     method:this.getMethod(),
40810                     url:this.getUrl(false),
40811                     params:this.getParams()
40812         }));
40813     },
40814
40815     success : function(response){
40816         var result = this.processResponse(response);
40817         if(result === true || !result.success || !result.data){
40818             this.failureType = Roo.form.Action.LOAD_FAILURE;
40819             this.form.afterAction(this, false);
40820             return;
40821         }
40822         this.form.clearInvalid();
40823         this.form.setValues(result.data);
40824         this.form.afterAction(this, true);
40825     },
40826
40827     handleResponse : function(response){
40828         if(this.form.reader){
40829             var rs = this.form.reader.read(response);
40830             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40831             return {
40832                 success : rs.success,
40833                 data : data
40834             };
40835         }
40836         return Roo.decode(response.responseText);
40837     }
40838 });
40839
40840 Roo.form.Action.ACTION_TYPES = {
40841     'load' : Roo.form.Action.Load,
40842     'submit' : Roo.form.Action.Submit
40843 };/*
40844  * Based on:
40845  * Ext JS Library 1.1.1
40846  * Copyright(c) 2006-2007, Ext JS, LLC.
40847  *
40848  * Originally Released Under LGPL - original licence link has changed is not relivant.
40849  *
40850  * Fork - LGPL
40851  * <script type="text/javascript">
40852  */
40853  
40854 /**
40855  * @class Roo.form.Layout
40856  * @extends Roo.Component
40857  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40858  * @constructor
40859  * @param {Object} config Configuration options
40860  */
40861 Roo.form.Layout = function(config){
40862     var xitems = [];
40863     if (config.items) {
40864         xitems = config.items;
40865         delete config.items;
40866     }
40867     Roo.form.Layout.superclass.constructor.call(this, config);
40868     this.stack = [];
40869     Roo.each(xitems, this.addxtype, this);
40870      
40871 };
40872
40873 Roo.extend(Roo.form.Layout, Roo.Component, {
40874     /**
40875      * @cfg {String/Object} autoCreate
40876      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40877      */
40878     /**
40879      * @cfg {String/Object/Function} style
40880      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40881      * a function which returns such a specification.
40882      */
40883     /**
40884      * @cfg {String} labelAlign
40885      * Valid values are "left," "top" and "right" (defaults to "left")
40886      */
40887     /**
40888      * @cfg {Number} labelWidth
40889      * Fixed width in pixels of all field labels (defaults to undefined)
40890      */
40891     /**
40892      * @cfg {Boolean} clear
40893      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40894      */
40895     clear : true,
40896     /**
40897      * @cfg {String} labelSeparator
40898      * The separator to use after field labels (defaults to ':')
40899      */
40900     labelSeparator : ':',
40901     /**
40902      * @cfg {Boolean} hideLabels
40903      * True to suppress the display of field labels in this layout (defaults to false)
40904      */
40905     hideLabels : false,
40906
40907     // private
40908     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40909     
40910     isLayout : true,
40911     
40912     // private
40913     onRender : function(ct, position){
40914         if(this.el){ // from markup
40915             this.el = Roo.get(this.el);
40916         }else {  // generate
40917             var cfg = this.getAutoCreate();
40918             this.el = ct.createChild(cfg, position);
40919         }
40920         if(this.style){
40921             this.el.applyStyles(this.style);
40922         }
40923         if(this.labelAlign){
40924             this.el.addClass('x-form-label-'+this.labelAlign);
40925         }
40926         if(this.hideLabels){
40927             this.labelStyle = "display:none";
40928             this.elementStyle = "padding-left:0;";
40929         }else{
40930             if(typeof this.labelWidth == 'number'){
40931                 this.labelStyle = "width:"+this.labelWidth+"px;";
40932                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40933             }
40934             if(this.labelAlign == 'top'){
40935                 this.labelStyle = "width:auto;";
40936                 this.elementStyle = "padding-left:0;";
40937             }
40938         }
40939         var stack = this.stack;
40940         var slen = stack.length;
40941         if(slen > 0){
40942             if(!this.fieldTpl){
40943                 var t = new Roo.Template(
40944                     '<div class="x-form-item {5}">',
40945                         '<label for="{0}" style="{2}">{1}{4}</label>',
40946                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40947                         '</div>',
40948                     '</div><div class="x-form-clear-left"></div>'
40949                 );
40950                 t.disableFormats = true;
40951                 t.compile();
40952                 Roo.form.Layout.prototype.fieldTpl = t;
40953             }
40954             for(var i = 0; i < slen; i++) {
40955                 if(stack[i].isFormField){
40956                     this.renderField(stack[i]);
40957                 }else{
40958                     this.renderComponent(stack[i]);
40959                 }
40960             }
40961         }
40962         if(this.clear){
40963             this.el.createChild({cls:'x-form-clear'});
40964         }
40965     },
40966
40967     // private
40968     renderField : function(f){
40969         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40970                f.id, //0
40971                f.fieldLabel, //1
40972                f.labelStyle||this.labelStyle||'', //2
40973                this.elementStyle||'', //3
40974                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40975                f.itemCls||this.itemCls||''  //5
40976        ], true).getPrevSibling());
40977     },
40978
40979     // private
40980     renderComponent : function(c){
40981         c.render(c.isLayout ? this.el : this.el.createChild());    
40982     },
40983     /**
40984      * Adds a object form elements (using the xtype property as the factory method.)
40985      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40986      * @param {Object} config 
40987      */
40988     addxtype : function(o)
40989     {
40990         // create the lement.
40991         o.form = this.form;
40992         var fe = Roo.factory(o, Roo.form);
40993         this.form.allItems.push(fe);
40994         this.stack.push(fe);
40995         
40996         if (fe.isFormField) {
40997             this.form.items.add(fe);
40998         }
40999          
41000         return fe;
41001     }
41002 });
41003
41004 /**
41005  * @class Roo.form.Column
41006  * @extends Roo.form.Layout
41007  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41008  * @constructor
41009  * @param {Object} config Configuration options
41010  */
41011 Roo.form.Column = function(config){
41012     Roo.form.Column.superclass.constructor.call(this, config);
41013 };
41014
41015 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41016     /**
41017      * @cfg {Number/String} width
41018      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41019      */
41020     /**
41021      * @cfg {String/Object} autoCreate
41022      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41023      */
41024
41025     // private
41026     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41027
41028     // private
41029     onRender : function(ct, position){
41030         Roo.form.Column.superclass.onRender.call(this, ct, position);
41031         if(this.width){
41032             this.el.setWidth(this.width);
41033         }
41034     }
41035 });
41036
41037
41038 /**
41039  * @class Roo.form.Row
41040  * @extends Roo.form.Layout
41041  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41042  * @constructor
41043  * @param {Object} config Configuration options
41044  */
41045
41046  
41047 Roo.form.Row = function(config){
41048     Roo.form.Row.superclass.constructor.call(this, config);
41049 };
41050  
41051 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41052       /**
41053      * @cfg {Number/String} width
41054      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41055      */
41056     /**
41057      * @cfg {Number/String} height
41058      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41059      */
41060     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41061     
41062     padWidth : 20,
41063     // private
41064     onRender : function(ct, position){
41065         //console.log('row render');
41066         if(!this.rowTpl){
41067             var t = new Roo.Template(
41068                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41069                     '<label for="{0}" style="{2}">{1}{4}</label>',
41070                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41071                     '</div>',
41072                 '</div>'
41073             );
41074             t.disableFormats = true;
41075             t.compile();
41076             Roo.form.Layout.prototype.rowTpl = t;
41077         }
41078         this.fieldTpl = this.rowTpl;
41079         
41080         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41081         var labelWidth = 100;
41082         
41083         if ((this.labelAlign != 'top')) {
41084             if (typeof this.labelWidth == 'number') {
41085                 labelWidth = this.labelWidth
41086             }
41087             this.padWidth =  20 + labelWidth;
41088             
41089         }
41090         
41091         Roo.form.Column.superclass.onRender.call(this, ct, position);
41092         if(this.width){
41093             this.el.setWidth(this.width);
41094         }
41095         if(this.height){
41096             this.el.setHeight(this.height);
41097         }
41098     },
41099     
41100     // private
41101     renderField : function(f){
41102         f.fieldEl = this.fieldTpl.append(this.el, [
41103                f.id, f.fieldLabel,
41104                f.labelStyle||this.labelStyle||'',
41105                this.elementStyle||'',
41106                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41107                f.itemCls||this.itemCls||'',
41108                f.width ? f.width + this.padWidth : 160 + this.padWidth
41109        ],true);
41110     }
41111 });
41112  
41113
41114 /**
41115  * @class Roo.form.FieldSet
41116  * @extends Roo.form.Layout
41117  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41118  * @constructor
41119  * @param {Object} config Configuration options
41120  */
41121 Roo.form.FieldSet = function(config){
41122     Roo.form.FieldSet.superclass.constructor.call(this, config);
41123 };
41124
41125 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41126     /**
41127      * @cfg {String} legend
41128      * The text to display as the legend for the FieldSet (defaults to '')
41129      */
41130     /**
41131      * @cfg {String/Object} autoCreate
41132      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41133      */
41134
41135     // private
41136     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41137
41138     // private
41139     onRender : function(ct, position){
41140         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41141         if(this.legend){
41142             this.setLegend(this.legend);
41143         }
41144     },
41145
41146     // private
41147     setLegend : function(text){
41148         if(this.rendered){
41149             this.el.child('legend').update(text);
41150         }
41151     }
41152 });/*
41153  * Based on:
41154  * Ext JS Library 1.1.1
41155  * Copyright(c) 2006-2007, Ext JS, LLC.
41156  *
41157  * Originally Released Under LGPL - original licence link has changed is not relivant.
41158  *
41159  * Fork - LGPL
41160  * <script type="text/javascript">
41161  */
41162 /**
41163  * @class Roo.form.VTypes
41164  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41165  * @singleton
41166  */
41167 Roo.form.VTypes = function(){
41168     // closure these in so they are only created once.
41169     var alpha = /^[a-zA-Z_]+$/;
41170     var alphanum = /^[a-zA-Z0-9_]+$/;
41171     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41172     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41173
41174     // All these messages and functions are configurable
41175     return {
41176         /**
41177          * The function used to validate email addresses
41178          * @param {String} value The email address
41179          */
41180         'email' : function(v){
41181             return email.test(v);
41182         },
41183         /**
41184          * The error text to display when the email validation function returns false
41185          * @type String
41186          */
41187         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41188         /**
41189          * The keystroke filter mask to be applied on email input
41190          * @type RegExp
41191          */
41192         'emailMask' : /[a-z0-9_\.\-@]/i,
41193
41194         /**
41195          * The function used to validate URLs
41196          * @param {String} value The URL
41197          */
41198         'url' : function(v){
41199             return url.test(v);
41200         },
41201         /**
41202          * The error text to display when the url validation function returns false
41203          * @type String
41204          */
41205         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41206         
41207         /**
41208          * The function used to validate alpha values
41209          * @param {String} value The value
41210          */
41211         'alpha' : function(v){
41212             return alpha.test(v);
41213         },
41214         /**
41215          * The error text to display when the alpha validation function returns false
41216          * @type String
41217          */
41218         'alphaText' : 'This field should only contain letters and _',
41219         /**
41220          * The keystroke filter mask to be applied on alpha input
41221          * @type RegExp
41222          */
41223         'alphaMask' : /[a-z_]/i,
41224
41225         /**
41226          * The function used to validate alphanumeric values
41227          * @param {String} value The value
41228          */
41229         'alphanum' : function(v){
41230             return alphanum.test(v);
41231         },
41232         /**
41233          * The error text to display when the alphanumeric validation function returns false
41234          * @type String
41235          */
41236         'alphanumText' : 'This field should only contain letters, numbers and _',
41237         /**
41238          * The keystroke filter mask to be applied on alphanumeric input
41239          * @type RegExp
41240          */
41241         'alphanumMask' : /[a-z0-9_]/i
41242     };
41243 }();//<script type="text/javascript">
41244
41245 /**
41246  * @class Roo.form.FCKeditor
41247  * @extends Roo.form.TextArea
41248  * Wrapper around the FCKEditor http://www.fckeditor.net
41249  * @constructor
41250  * Creates a new FCKeditor
41251  * @param {Object} config Configuration options
41252  */
41253 Roo.form.FCKeditor = function(config){
41254     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41255     this.addEvents({
41256          /**
41257          * @event editorinit
41258          * Fired when the editor is initialized - you can add extra handlers here..
41259          * @param {FCKeditor} this
41260          * @param {Object} the FCK object.
41261          */
41262         editorinit : true
41263     });
41264     
41265     
41266 };
41267 Roo.form.FCKeditor.editors = { };
41268 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41269 {
41270     //defaultAutoCreate : {
41271     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41272     //},
41273     // private
41274     /**
41275      * @cfg {Object} fck options - see fck manual for details.
41276      */
41277     fckconfig : false,
41278     
41279     /**
41280      * @cfg {Object} fck toolbar set (Basic or Default)
41281      */
41282     toolbarSet : 'Basic',
41283     /**
41284      * @cfg {Object} fck BasePath
41285      */ 
41286     basePath : '/fckeditor/',
41287     
41288     
41289     frame : false,
41290     
41291     value : '',
41292     
41293    
41294     onRender : function(ct, position)
41295     {
41296         if(!this.el){
41297             this.defaultAutoCreate = {
41298                 tag: "textarea",
41299                 style:"width:300px;height:60px;",
41300                 autocomplete: "off"
41301             };
41302         }
41303         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41304         /*
41305         if(this.grow){
41306             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41307             if(this.preventScrollbars){
41308                 this.el.setStyle("overflow", "hidden");
41309             }
41310             this.el.setHeight(this.growMin);
41311         }
41312         */
41313         //console.log('onrender' + this.getId() );
41314         Roo.form.FCKeditor.editors[this.getId()] = this;
41315          
41316
41317         this.replaceTextarea() ;
41318         
41319     },
41320     
41321     getEditor : function() {
41322         return this.fckEditor;
41323     },
41324     /**
41325      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41326      * @param {Mixed} value The value to set
41327      */
41328     
41329     
41330     setValue : function(value)
41331     {
41332         //console.log('setValue: ' + value);
41333         
41334         if(typeof(value) == 'undefined') { // not sure why this is happending...
41335             return;
41336         }
41337         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41338         
41339         //if(!this.el || !this.getEditor()) {
41340         //    this.value = value;
41341             //this.setValue.defer(100,this,[value]);    
41342         //    return;
41343         //} 
41344         
41345         if(!this.getEditor()) {
41346             return;
41347         }
41348         
41349         this.getEditor().SetData(value);
41350         
41351         //
41352
41353     },
41354
41355     /**
41356      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41357      * @return {Mixed} value The field value
41358      */
41359     getValue : function()
41360     {
41361         
41362         if (this.frame && this.frame.dom.style.display == 'none') {
41363             return Roo.form.FCKeditor.superclass.getValue.call(this);
41364         }
41365         
41366         if(!this.el || !this.getEditor()) {
41367            
41368            // this.getValue.defer(100,this); 
41369             return this.value;
41370         }
41371        
41372         
41373         var value=this.getEditor().GetData();
41374         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41375         return Roo.form.FCKeditor.superclass.getValue.call(this);
41376         
41377
41378     },
41379
41380     /**
41381      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41382      * @return {Mixed} value The field value
41383      */
41384     getRawValue : function()
41385     {
41386         if (this.frame && this.frame.dom.style.display == 'none') {
41387             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41388         }
41389         
41390         if(!this.el || !this.getEditor()) {
41391             //this.getRawValue.defer(100,this); 
41392             return this.value;
41393             return;
41394         }
41395         
41396         
41397         
41398         var value=this.getEditor().GetData();
41399         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41400         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41401          
41402     },
41403     
41404     setSize : function(w,h) {
41405         
41406         
41407         
41408         //if (this.frame && this.frame.dom.style.display == 'none') {
41409         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41410         //    return;
41411         //}
41412         //if(!this.el || !this.getEditor()) {
41413         //    this.setSize.defer(100,this, [w,h]); 
41414         //    return;
41415         //}
41416         
41417         
41418         
41419         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41420         
41421         this.frame.dom.setAttribute('width', w);
41422         this.frame.dom.setAttribute('height', h);
41423         this.frame.setSize(w,h);
41424         
41425     },
41426     
41427     toggleSourceEdit : function(value) {
41428         
41429       
41430          
41431         this.el.dom.style.display = value ? '' : 'none';
41432         this.frame.dom.style.display = value ?  'none' : '';
41433         
41434     },
41435     
41436     
41437     focus: function(tag)
41438     {
41439         if (this.frame.dom.style.display == 'none') {
41440             return Roo.form.FCKeditor.superclass.focus.call(this);
41441         }
41442         if(!this.el || !this.getEditor()) {
41443             this.focus.defer(100,this, [tag]); 
41444             return;
41445         }
41446         
41447         
41448         
41449         
41450         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41451         this.getEditor().Focus();
41452         if (tgs.length) {
41453             if (!this.getEditor().Selection.GetSelection()) {
41454                 this.focus.defer(100,this, [tag]); 
41455                 return;
41456             }
41457             
41458             
41459             var r = this.getEditor().EditorDocument.createRange();
41460             r.setStart(tgs[0],0);
41461             r.setEnd(tgs[0],0);
41462             this.getEditor().Selection.GetSelection().removeAllRanges();
41463             this.getEditor().Selection.GetSelection().addRange(r);
41464             this.getEditor().Focus();
41465         }
41466         
41467     },
41468     
41469     
41470     
41471     replaceTextarea : function()
41472     {
41473         if ( document.getElementById( this.getId() + '___Frame' ) )
41474             return ;
41475         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41476         //{
41477             // We must check the elements firstly using the Id and then the name.
41478         var oTextarea = document.getElementById( this.getId() );
41479         
41480         var colElementsByName = document.getElementsByName( this.getId() ) ;
41481          
41482         oTextarea.style.display = 'none' ;
41483
41484         if ( oTextarea.tabIndex ) {            
41485             this.TabIndex = oTextarea.tabIndex ;
41486         }
41487         
41488         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41489         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41490         this.frame = Roo.get(this.getId() + '___Frame')
41491     },
41492     
41493     _getConfigHtml : function()
41494     {
41495         var sConfig = '' ;
41496
41497         for ( var o in this.fckconfig ) {
41498             sConfig += sConfig.length > 0  ? '&amp;' : '';
41499             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41500         }
41501
41502         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41503     },
41504     
41505     
41506     _getIFrameHtml : function()
41507     {
41508         var sFile = 'fckeditor.html' ;
41509         /* no idea what this is about..
41510         try
41511         {
41512             if ( (/fcksource=true/i).test( window.top.location.search ) )
41513                 sFile = 'fckeditor.original.html' ;
41514         }
41515         catch (e) { 
41516         */
41517
41518         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41519         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41520         
41521         
41522         var html = '<iframe id="' + this.getId() +
41523             '___Frame" src="' + sLink +
41524             '" width="' + this.width +
41525             '" height="' + this.height + '"' +
41526             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41527             ' frameborder="0" scrolling="no"></iframe>' ;
41528
41529         return html ;
41530     },
41531     
41532     _insertHtmlBefore : function( html, element )
41533     {
41534         if ( element.insertAdjacentHTML )       {
41535             // IE
41536             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41537         } else { // Gecko
41538             var oRange = document.createRange() ;
41539             oRange.setStartBefore( element ) ;
41540             var oFragment = oRange.createContextualFragment( html );
41541             element.parentNode.insertBefore( oFragment, element ) ;
41542         }
41543     }
41544     
41545     
41546   
41547     
41548     
41549     
41550     
41551
41552 });
41553
41554 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41555
41556 function FCKeditor_OnComplete(editorInstance){
41557     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41558     f.fckEditor = editorInstance;
41559     //console.log("loaded");
41560     f.fireEvent('editorinit', f, editorInstance);
41561
41562   
41563
41564  
41565
41566
41567
41568
41569
41570
41571
41572
41573
41574
41575
41576
41577
41578
41579
41580 //<script type="text/javascript">
41581 /**
41582  * @class Roo.form.GridField
41583  * @extends Roo.form.Field
41584  * Embed a grid (or editable grid into a form)
41585  * STATUS ALPHA
41586  * @constructor
41587  * Creates a new GridField
41588  * @param {Object} config Configuration options
41589  */
41590 Roo.form.GridField = function(config){
41591     Roo.form.GridField.superclass.constructor.call(this, config);
41592      
41593 };
41594
41595 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41596     /**
41597      * @cfg {Number} width  - used to restrict width of grid..
41598      */
41599     width : 100,
41600     /**
41601      * @cfg {Number} height - used to restrict height of grid..
41602      */
41603     height : 50,
41604      /**
41605      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41606      */
41607     xgrid : false, 
41608     /**
41609      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41610      * {tag: "input", type: "checkbox", autocomplete: "off"})
41611      */
41612    // defaultAutoCreate : { tag: 'div' },
41613     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41614     /**
41615      * @cfg {String} addTitle Text to include for adding a title.
41616      */
41617     addTitle : false,
41618     //
41619     onResize : function(){
41620         Roo.form.Field.superclass.onResize.apply(this, arguments);
41621     },
41622
41623     initEvents : function(){
41624         // Roo.form.Checkbox.superclass.initEvents.call(this);
41625         // has no events...
41626        
41627     },
41628
41629
41630     getResizeEl : function(){
41631         return this.wrap;
41632     },
41633
41634     getPositionEl : function(){
41635         return this.wrap;
41636     },
41637
41638     // private
41639     onRender : function(ct, position){
41640         
41641         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41642         var style = this.style;
41643         delete this.style;
41644         
41645         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41646         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41647         this.viewEl = this.wrap.createChild({ tag: 'div' });
41648         if (style) {
41649             this.viewEl.applyStyles(style);
41650         }
41651         if (this.width) {
41652             this.viewEl.setWidth(this.width);
41653         }
41654         if (this.height) {
41655             this.viewEl.setHeight(this.height);
41656         }
41657         //if(this.inputValue !== undefined){
41658         //this.setValue(this.value);
41659         
41660         
41661         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41662         
41663         
41664         this.grid.render();
41665         this.grid.getDataSource().on('remove', this.refreshValue, this);
41666         this.grid.getDataSource().on('update', this.refreshValue, this);
41667         this.grid.on('afteredit', this.refreshValue, this);
41668  
41669     },
41670      
41671     
41672     /**
41673      * Sets the value of the item. 
41674      * @param {String} either an object  or a string..
41675      */
41676     setValue : function(v){
41677         //this.value = v;
41678         v = v || []; // empty set..
41679         // this does not seem smart - it really only affects memoryproxy grids..
41680         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41681             var ds = this.grid.getDataSource();
41682             // assumes a json reader..
41683             var data = {}
41684             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41685             ds.loadData( data);
41686         }
41687         Roo.form.GridField.superclass.setValue.call(this, v);
41688         this.refreshValue();
41689         // should load data in the grid really....
41690     },
41691     
41692     // private
41693     refreshValue: function() {
41694          var val = [];
41695         this.grid.getDataSource().each(function(r) {
41696             val.push(r.data);
41697         });
41698         this.el.dom.value = Roo.encode(val);
41699     }
41700     
41701      
41702     
41703     
41704 });//<script type="text/javasscript">
41705  
41706
41707 /**
41708  * @class Roo.DDView
41709  * A DnD enabled version of Roo.View.
41710  * @param {Element/String} container The Element in which to create the View.
41711  * @param {String} tpl The template string used to create the markup for each element of the View
41712  * @param {Object} config The configuration properties. These include all the config options of
41713  * {@link Roo.View} plus some specific to this class.<br>
41714  * <p>
41715  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41716  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41717  * <p>
41718  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41719 .x-view-drag-insert-above {
41720         border-top:1px dotted #3366cc;
41721 }
41722 .x-view-drag-insert-below {
41723         border-bottom:1px dotted #3366cc;
41724 }
41725 </code></pre>
41726  * 
41727  */
41728  
41729 Roo.DDView = function(container, tpl, config) {
41730     Roo.DDView.superclass.constructor.apply(this, arguments);
41731     this.getEl().setStyle("outline", "0px none");
41732     this.getEl().unselectable();
41733     if (this.dragGroup) {
41734                 this.setDraggable(this.dragGroup.split(","));
41735     }
41736     if (this.dropGroup) {
41737                 this.setDroppable(this.dropGroup.split(","));
41738     }
41739     if (this.deletable) {
41740         this.setDeletable();
41741     }
41742     this.isDirtyFlag = false;
41743         this.addEvents({
41744                 "drop" : true
41745         });
41746 };
41747
41748 Roo.extend(Roo.DDView, Roo.View, {
41749 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41750 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41751 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41752 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41753
41754         isFormField: true,
41755
41756         reset: Roo.emptyFn,
41757         
41758         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41759
41760         validate: function() {
41761                 return true;
41762         },
41763         
41764         destroy: function() {
41765                 this.purgeListeners();
41766                 this.getEl.removeAllListeners();
41767                 this.getEl().remove();
41768                 if (this.dragZone) {
41769                         if (this.dragZone.destroy) {
41770                                 this.dragZone.destroy();
41771                         }
41772                 }
41773                 if (this.dropZone) {
41774                         if (this.dropZone.destroy) {
41775                                 this.dropZone.destroy();
41776                         }
41777                 }
41778         },
41779
41780 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41781         getName: function() {
41782                 return this.name;
41783         },
41784
41785 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41786         setValue: function(v) {
41787                 if (!this.store) {
41788                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41789                 }
41790                 var data = {};
41791                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41792                 this.store.proxy = new Roo.data.MemoryProxy(data);
41793                 this.store.load();
41794         },
41795
41796 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41797         getValue: function() {
41798                 var result = '(';
41799                 this.store.each(function(rec) {
41800                         result += rec.id + ',';
41801                 });
41802                 return result.substr(0, result.length - 1) + ')';
41803         },
41804         
41805         getIds: function() {
41806                 var i = 0, result = new Array(this.store.getCount());
41807                 this.store.each(function(rec) {
41808                         result[i++] = rec.id;
41809                 });
41810                 return result;
41811         },
41812         
41813         isDirty: function() {
41814                 return this.isDirtyFlag;
41815         },
41816
41817 /**
41818  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41819  *      whole Element becomes the target, and this causes the drop gesture to append.
41820  */
41821     getTargetFromEvent : function(e) {
41822                 var target = e.getTarget();
41823                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41824                 target = target.parentNode;
41825                 }
41826                 if (!target) {
41827                         target = this.el.dom.lastChild || this.el.dom;
41828                 }
41829                 return target;
41830     },
41831
41832 /**
41833  *      Create the drag data which consists of an object which has the property "ddel" as
41834  *      the drag proxy element. 
41835  */
41836     getDragData : function(e) {
41837         var target = this.findItemFromChild(e.getTarget());
41838                 if(target) {
41839                         this.handleSelection(e);
41840                         var selNodes = this.getSelectedNodes();
41841             var dragData = {
41842                 source: this,
41843                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41844                 nodes: selNodes,
41845                 records: []
41846                         };
41847                         var selectedIndices = this.getSelectedIndexes();
41848                         for (var i = 0; i < selectedIndices.length; i++) {
41849                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41850                         }
41851                         if (selNodes.length == 1) {
41852                                 dragData.ddel = target.cloneNode(true); // the div element
41853                         } else {
41854                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41855                                 div.className = 'multi-proxy';
41856                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41857                                         div.appendChild(selNodes[i].cloneNode(true));
41858                                 }
41859                                 dragData.ddel = div;
41860                         }
41861             //console.log(dragData)
41862             //console.log(dragData.ddel.innerHTML)
41863                         return dragData;
41864                 }
41865         //console.log('nodragData')
41866                 return false;
41867     },
41868     
41869 /**     Specify to which ddGroup items in this DDView may be dragged. */
41870     setDraggable: function(ddGroup) {
41871         if (ddGroup instanceof Array) {
41872                 Roo.each(ddGroup, this.setDraggable, this);
41873                 return;
41874         }
41875         if (this.dragZone) {
41876                 this.dragZone.addToGroup(ddGroup);
41877         } else {
41878                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41879                                 containerScroll: true,
41880                                 ddGroup: ddGroup 
41881
41882                         });
41883 //                      Draggability implies selection. DragZone's mousedown selects the element.
41884                         if (!this.multiSelect) { this.singleSelect = true; }
41885
41886 //                      Wire the DragZone's handlers up to methods in *this*
41887                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41888                 }
41889     },
41890
41891 /**     Specify from which ddGroup this DDView accepts drops. */
41892     setDroppable: function(ddGroup) {
41893         if (ddGroup instanceof Array) {
41894                 Roo.each(ddGroup, this.setDroppable, this);
41895                 return;
41896         }
41897         if (this.dropZone) {
41898                 this.dropZone.addToGroup(ddGroup);
41899         } else {
41900                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41901                                 containerScroll: true,
41902                                 ddGroup: ddGroup
41903                         });
41904
41905 //                      Wire the DropZone's handlers up to methods in *this*
41906                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41907                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41908                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41909                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41910                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41911                 }
41912     },
41913
41914 /**     Decide whether to drop above or below a View node. */
41915     getDropPoint : function(e, n, dd){
41916         if (n == this.el.dom) { return "above"; }
41917                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41918                 var c = t + (b - t) / 2;
41919                 var y = Roo.lib.Event.getPageY(e);
41920                 if(y <= c) {
41921                         return "above";
41922                 }else{
41923                         return "below";
41924                 }
41925     },
41926
41927     onNodeEnter : function(n, dd, e, data){
41928                 return false;
41929     },
41930     
41931     onNodeOver : function(n, dd, e, data){
41932                 var pt = this.getDropPoint(e, n, dd);
41933                 // set the insert point style on the target node
41934                 var dragElClass = this.dropNotAllowed;
41935                 if (pt) {
41936                         var targetElClass;
41937                         if (pt == "above"){
41938                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41939                                 targetElClass = "x-view-drag-insert-above";
41940                         } else {
41941                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41942                                 targetElClass = "x-view-drag-insert-below";
41943                         }
41944                         if (this.lastInsertClass != targetElClass){
41945                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41946                                 this.lastInsertClass = targetElClass;
41947                         }
41948                 }
41949                 return dragElClass;
41950         },
41951
41952     onNodeOut : function(n, dd, e, data){
41953                 this.removeDropIndicators(n);
41954     },
41955
41956     onNodeDrop : function(n, dd, e, data){
41957         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41958                 return false;
41959         }
41960         var pt = this.getDropPoint(e, n, dd);
41961                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41962                 if (pt == "below") { insertAt++; }
41963                 for (var i = 0; i < data.records.length; i++) {
41964                         var r = data.records[i];
41965                         var dup = this.store.getById(r.id);
41966                         if (dup && (dd != this.dragZone)) {
41967                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41968                         } else {
41969                                 if (data.copy) {
41970                                         this.store.insert(insertAt++, r.copy());
41971                                 } else {
41972                                         data.source.isDirtyFlag = true;
41973                                         r.store.remove(r);
41974                                         this.store.insert(insertAt++, r);
41975                                 }
41976                                 this.isDirtyFlag = true;
41977                         }
41978                 }
41979                 this.dragZone.cachedTarget = null;
41980                 return true;
41981     },
41982
41983     removeDropIndicators : function(n){
41984                 if(n){
41985                         Roo.fly(n).removeClass([
41986                                 "x-view-drag-insert-above",
41987                                 "x-view-drag-insert-below"]);
41988                         this.lastInsertClass = "_noclass";
41989                 }
41990     },
41991
41992 /**
41993  *      Utility method. Add a delete option to the DDView's context menu.
41994  *      @param {String} imageUrl The URL of the "delete" icon image.
41995  */
41996         setDeletable: function(imageUrl) {
41997                 if (!this.singleSelect && !this.multiSelect) {
41998                         this.singleSelect = true;
41999                 }
42000                 var c = this.getContextMenu();
42001                 this.contextMenu.on("itemclick", function(item) {
42002                         switch (item.id) {
42003                                 case "delete":
42004                                         this.remove(this.getSelectedIndexes());
42005                                         break;
42006                         }
42007                 }, this);
42008                 this.contextMenu.add({
42009                         icon: imageUrl,
42010                         id: "delete",
42011                         text: 'Delete'
42012                 });
42013         },
42014         
42015 /**     Return the context menu for this DDView. */
42016         getContextMenu: function() {
42017                 if (!this.contextMenu) {
42018 //                      Create the View's context menu
42019                         this.contextMenu = new Roo.menu.Menu({
42020                                 id: this.id + "-contextmenu"
42021                         });
42022                         this.el.on("contextmenu", this.showContextMenu, this);
42023                 }
42024                 return this.contextMenu;
42025         },
42026         
42027         disableContextMenu: function() {
42028                 if (this.contextMenu) {
42029                         this.el.un("contextmenu", this.showContextMenu, this);
42030                 }
42031         },
42032
42033         showContextMenu: function(e, item) {
42034         item = this.findItemFromChild(e.getTarget());
42035                 if (item) {
42036                         e.stopEvent();
42037                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42038                         this.contextMenu.showAt(e.getXY());
42039             }
42040     },
42041
42042 /**
42043  *      Remove {@link Roo.data.Record}s at the specified indices.
42044  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42045  */
42046     remove: function(selectedIndices) {
42047                 selectedIndices = [].concat(selectedIndices);
42048                 for (var i = 0; i < selectedIndices.length; i++) {
42049                         var rec = this.store.getAt(selectedIndices[i]);
42050                         this.store.remove(rec);
42051                 }
42052     },
42053
42054 /**
42055  *      Double click fires the event, but also, if this is draggable, and there is only one other
42056  *      related DropZone, it transfers the selected node.
42057  */
42058     onDblClick : function(e){
42059         var item = this.findItemFromChild(e.getTarget());
42060         if(item){
42061             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42062                 return false;
42063             }
42064             if (this.dragGroup) {
42065                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42066                     while (targets.indexOf(this.dropZone) > -1) {
42067                             targets.remove(this.dropZone);
42068                                 }
42069                     if (targets.length == 1) {
42070                                         this.dragZone.cachedTarget = null;
42071                         var el = Roo.get(targets[0].getEl());
42072                         var box = el.getBox(true);
42073                         targets[0].onNodeDrop(el.dom, {
42074                                 target: el.dom,
42075                                 xy: [box.x, box.y + box.height - 1]
42076                         }, null, this.getDragData(e));
42077                     }
42078                 }
42079         }
42080     },
42081     
42082     handleSelection: function(e) {
42083                 this.dragZone.cachedTarget = null;
42084         var item = this.findItemFromChild(e.getTarget());
42085         if (!item) {
42086                 this.clearSelections(true);
42087                 return;
42088         }
42089                 if (item && (this.multiSelect || this.singleSelect)){
42090                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42091                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42092                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42093                                 this.unselect(item);
42094                         } else {
42095                                 this.select(item, this.multiSelect && e.ctrlKey);
42096                                 this.lastSelection = item;
42097                         }
42098                 }
42099     },
42100
42101     onItemClick : function(item, index, e){
42102                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42103                         return false;
42104                 }
42105                 return true;
42106     },
42107
42108     unselect : function(nodeInfo, suppressEvent){
42109                 var node = this.getNode(nodeInfo);
42110                 if(node && this.isSelected(node)){
42111                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42112                                 Roo.fly(node).removeClass(this.selectedClass);
42113                                 this.selections.remove(node);
42114                                 if(!suppressEvent){
42115                                         this.fireEvent("selectionchange", this, this.selections);
42116                                 }
42117                         }
42118                 }
42119     }
42120 });
42121 /*
42122  * Based on:
42123  * Ext JS Library 1.1.1
42124  * Copyright(c) 2006-2007, Ext JS, LLC.
42125  *
42126  * Originally Released Under LGPL - original licence link has changed is not relivant.
42127  *
42128  * Fork - LGPL
42129  * <script type="text/javascript">
42130  */
42131  
42132 /**
42133  * @class Roo.LayoutManager
42134  * @extends Roo.util.Observable
42135  * Base class for layout managers.
42136  */
42137 Roo.LayoutManager = function(container, config){
42138     Roo.LayoutManager.superclass.constructor.call(this);
42139     this.el = Roo.get(container);
42140     // ie scrollbar fix
42141     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42142         document.body.scroll = "no";
42143     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42144         this.el.position('relative');
42145     }
42146     this.id = this.el.id;
42147     this.el.addClass("x-layout-container");
42148     /** false to disable window resize monitoring @type Boolean */
42149     this.monitorWindowResize = true;
42150     this.regions = {};
42151     this.addEvents({
42152         /**
42153          * @event layout
42154          * Fires when a layout is performed. 
42155          * @param {Roo.LayoutManager} this
42156          */
42157         "layout" : true,
42158         /**
42159          * @event regionresized
42160          * Fires when the user resizes a region. 
42161          * @param {Roo.LayoutRegion} region The resized region
42162          * @param {Number} newSize The new size (width for east/west, height for north/south)
42163          */
42164         "regionresized" : true,
42165         /**
42166          * @event regioncollapsed
42167          * Fires when a region is collapsed. 
42168          * @param {Roo.LayoutRegion} region The collapsed region
42169          */
42170         "regioncollapsed" : true,
42171         /**
42172          * @event regionexpanded
42173          * Fires when a region is expanded.  
42174          * @param {Roo.LayoutRegion} region The expanded region
42175          */
42176         "regionexpanded" : true
42177     });
42178     this.updating = false;
42179     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42180 };
42181
42182 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42183     /**
42184      * Returns true if this layout is currently being updated
42185      * @return {Boolean}
42186      */
42187     isUpdating : function(){
42188         return this.updating; 
42189     },
42190     
42191     /**
42192      * Suspend the LayoutManager from doing auto-layouts while
42193      * making multiple add or remove calls
42194      */
42195     beginUpdate : function(){
42196         this.updating = true;    
42197     },
42198     
42199     /**
42200      * Restore auto-layouts and optionally disable the manager from performing a layout
42201      * @param {Boolean} noLayout true to disable a layout update 
42202      */
42203     endUpdate : function(noLayout){
42204         this.updating = false;
42205         if(!noLayout){
42206             this.layout();
42207         }    
42208     },
42209     
42210     layout: function(){
42211         
42212     },
42213     
42214     onRegionResized : function(region, newSize){
42215         this.fireEvent("regionresized", region, newSize);
42216         this.layout();
42217     },
42218     
42219     onRegionCollapsed : function(region){
42220         this.fireEvent("regioncollapsed", region);
42221     },
42222     
42223     onRegionExpanded : function(region){
42224         this.fireEvent("regionexpanded", region);
42225     },
42226         
42227     /**
42228      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42229      * performs box-model adjustments.
42230      * @return {Object} The size as an object {width: (the width), height: (the height)}
42231      */
42232     getViewSize : function(){
42233         var size;
42234         if(this.el.dom != document.body){
42235             size = this.el.getSize();
42236         }else{
42237             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42238         }
42239         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42240         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42241         return size;
42242     },
42243     
42244     /**
42245      * Returns the Element this layout is bound to.
42246      * @return {Roo.Element}
42247      */
42248     getEl : function(){
42249         return this.el;
42250     },
42251     
42252     /**
42253      * Returns the specified region.
42254      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42255      * @return {Roo.LayoutRegion}
42256      */
42257     getRegion : function(target){
42258         return this.regions[target.toLowerCase()];
42259     },
42260     
42261     onWindowResize : function(){
42262         if(this.monitorWindowResize){
42263             this.layout();
42264         }
42265     }
42266 });/*
42267  * Based on:
42268  * Ext JS Library 1.1.1
42269  * Copyright(c) 2006-2007, Ext JS, LLC.
42270  *
42271  * Originally Released Under LGPL - original licence link has changed is not relivant.
42272  *
42273  * Fork - LGPL
42274  * <script type="text/javascript">
42275  */
42276 /**
42277  * @class Roo.BorderLayout
42278  * @extends Roo.LayoutManager
42279  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42280  * please see: <br><br>
42281  * <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>
42282  * <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>
42283  * Example:
42284  <pre><code>
42285  var layout = new Roo.BorderLayout(document.body, {
42286     north: {
42287         initialSize: 25,
42288         titlebar: false
42289     },
42290     west: {
42291         split:true,
42292         initialSize: 200,
42293         minSize: 175,
42294         maxSize: 400,
42295         titlebar: true,
42296         collapsible: true
42297     },
42298     east: {
42299         split:true,
42300         initialSize: 202,
42301         minSize: 175,
42302         maxSize: 400,
42303         titlebar: true,
42304         collapsible: true
42305     },
42306     south: {
42307         split:true,
42308         initialSize: 100,
42309         minSize: 100,
42310         maxSize: 200,
42311         titlebar: true,
42312         collapsible: true
42313     },
42314     center: {
42315         titlebar: true,
42316         autoScroll:true,
42317         resizeTabs: true,
42318         minTabWidth: 50,
42319         preferredTabWidth: 150
42320     }
42321 });
42322
42323 // shorthand
42324 var CP = Roo.ContentPanel;
42325
42326 layout.beginUpdate();
42327 layout.add("north", new CP("north", "North"));
42328 layout.add("south", new CP("south", {title: "South", closable: true}));
42329 layout.add("west", new CP("west", {title: "West"}));
42330 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42331 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42332 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42333 layout.getRegion("center").showPanel("center1");
42334 layout.endUpdate();
42335 </code></pre>
42336
42337 <b>The container the layout is rendered into can be either the body element or any other element.
42338 If it is not the body element, the container needs to either be an absolute positioned element,
42339 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42340 the container size if it is not the body element.</b>
42341
42342 * @constructor
42343 * Create a new BorderLayout
42344 * @param {String/HTMLElement/Element} container The container this layout is bound to
42345 * @param {Object} config Configuration options
42346  */
42347 Roo.BorderLayout = function(container, config){
42348     config = config || {};
42349     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42350     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42351     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42352         var target = this.factory.validRegions[i];
42353         if(config[target]){
42354             this.addRegion(target, config[target]);
42355         }
42356     }
42357 };
42358
42359 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42360     /**
42361      * Creates and adds a new region if it doesn't already exist.
42362      * @param {String} target The target region key (north, south, east, west or center).
42363      * @param {Object} config The regions config object
42364      * @return {BorderLayoutRegion} The new region
42365      */
42366     addRegion : function(target, config){
42367         if(!this.regions[target]){
42368             var r = this.factory.create(target, this, config);
42369             this.bindRegion(target, r);
42370         }
42371         return this.regions[target];
42372     },
42373
42374     // private (kinda)
42375     bindRegion : function(name, r){
42376         this.regions[name] = r;
42377         r.on("visibilitychange", this.layout, this);
42378         r.on("paneladded", this.layout, this);
42379         r.on("panelremoved", this.layout, this);
42380         r.on("invalidated", this.layout, this);
42381         r.on("resized", this.onRegionResized, this);
42382         r.on("collapsed", this.onRegionCollapsed, this);
42383         r.on("expanded", this.onRegionExpanded, this);
42384     },
42385
42386     /**
42387      * Performs a layout update.
42388      */
42389     layout : function(){
42390         if(this.updating) return;
42391         var size = this.getViewSize();
42392         var w = size.width;
42393         var h = size.height;
42394         var centerW = w;
42395         var centerH = h;
42396         var centerY = 0;
42397         var centerX = 0;
42398         //var x = 0, y = 0;
42399
42400         var rs = this.regions;
42401         var north = rs["north"];
42402         var south = rs["south"]; 
42403         var west = rs["west"];
42404         var east = rs["east"];
42405         var center = rs["center"];
42406         //if(this.hideOnLayout){ // not supported anymore
42407             //c.el.setStyle("display", "none");
42408         //}
42409         if(north && north.isVisible()){
42410             var b = north.getBox();
42411             var m = north.getMargins();
42412             b.width = w - (m.left+m.right);
42413             b.x = m.left;
42414             b.y = m.top;
42415             centerY = b.height + b.y + m.bottom;
42416             centerH -= centerY;
42417             north.updateBox(this.safeBox(b));
42418         }
42419         if(south && south.isVisible()){
42420             var b = south.getBox();
42421             var m = south.getMargins();
42422             b.width = w - (m.left+m.right);
42423             b.x = m.left;
42424             var totalHeight = (b.height + m.top + m.bottom);
42425             b.y = h - totalHeight + m.top;
42426             centerH -= totalHeight;
42427             south.updateBox(this.safeBox(b));
42428         }
42429         if(west && west.isVisible()){
42430             var b = west.getBox();
42431             var m = west.getMargins();
42432             b.height = centerH - (m.top+m.bottom);
42433             b.x = m.left;
42434             b.y = centerY + m.top;
42435             var totalWidth = (b.width + m.left + m.right);
42436             centerX += totalWidth;
42437             centerW -= totalWidth;
42438             west.updateBox(this.safeBox(b));
42439         }
42440         if(east && east.isVisible()){
42441             var b = east.getBox();
42442             var m = east.getMargins();
42443             b.height = centerH - (m.top+m.bottom);
42444             var totalWidth = (b.width + m.left + m.right);
42445             b.x = w - totalWidth + m.left;
42446             b.y = centerY + m.top;
42447             centerW -= totalWidth;
42448             east.updateBox(this.safeBox(b));
42449         }
42450         if(center){
42451             var m = center.getMargins();
42452             var centerBox = {
42453                 x: centerX + m.left,
42454                 y: centerY + m.top,
42455                 width: centerW - (m.left+m.right),
42456                 height: centerH - (m.top+m.bottom)
42457             };
42458             //if(this.hideOnLayout){
42459                 //center.el.setStyle("display", "block");
42460             //}
42461             center.updateBox(this.safeBox(centerBox));
42462         }
42463         this.el.repaint();
42464         this.fireEvent("layout", this);
42465     },
42466
42467     // private
42468     safeBox : function(box){
42469         box.width = Math.max(0, box.width);
42470         box.height = Math.max(0, box.height);
42471         return box;
42472     },
42473
42474     /**
42475      * Adds a ContentPanel (or subclass) to this layout.
42476      * @param {String} target The target region key (north, south, east, west or center).
42477      * @param {Roo.ContentPanel} panel The panel to add
42478      * @return {Roo.ContentPanel} The added panel
42479      */
42480     add : function(target, panel){
42481          
42482         target = target.toLowerCase();
42483         return this.regions[target].add(panel);
42484     },
42485
42486     /**
42487      * Remove a ContentPanel (or subclass) to this layout.
42488      * @param {String} target The target region key (north, south, east, west or center).
42489      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42490      * @return {Roo.ContentPanel} The removed panel
42491      */
42492     remove : function(target, panel){
42493         target = target.toLowerCase();
42494         return this.regions[target].remove(panel);
42495     },
42496
42497     /**
42498      * Searches all regions for a panel with the specified id
42499      * @param {String} panelId
42500      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42501      */
42502     findPanel : function(panelId){
42503         var rs = this.regions;
42504         for(var target in rs){
42505             if(typeof rs[target] != "function"){
42506                 var p = rs[target].getPanel(panelId);
42507                 if(p){
42508                     return p;
42509                 }
42510             }
42511         }
42512         return null;
42513     },
42514
42515     /**
42516      * Searches all regions for a panel with the specified id and activates (shows) it.
42517      * @param {String/ContentPanel} panelId The panels id or the panel itself
42518      * @return {Roo.ContentPanel} The shown panel or null
42519      */
42520     showPanel : function(panelId) {
42521       var rs = this.regions;
42522       for(var target in rs){
42523          var r = rs[target];
42524          if(typeof r != "function"){
42525             if(r.hasPanel(panelId)){
42526                return r.showPanel(panelId);
42527             }
42528          }
42529       }
42530       return null;
42531    },
42532
42533    /**
42534      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42535      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42536      */
42537     restoreState : function(provider){
42538         if(!provider){
42539             provider = Roo.state.Manager;
42540         }
42541         var sm = new Roo.LayoutStateManager();
42542         sm.init(this, provider);
42543     },
42544
42545     /**
42546      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42547      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42548      * a valid ContentPanel config object.  Example:
42549      * <pre><code>
42550 // Create the main layout
42551 var layout = new Roo.BorderLayout('main-ct', {
42552     west: {
42553         split:true,
42554         minSize: 175,
42555         titlebar: true
42556     },
42557     center: {
42558         title:'Components'
42559     }
42560 }, 'main-ct');
42561
42562 // Create and add multiple ContentPanels at once via configs
42563 layout.batchAdd({
42564    west: {
42565        id: 'source-files',
42566        autoCreate:true,
42567        title:'Ext Source Files',
42568        autoScroll:true,
42569        fitToFrame:true
42570    },
42571    center : {
42572        el: cview,
42573        autoScroll:true,
42574        fitToFrame:true,
42575        toolbar: tb,
42576        resizeEl:'cbody'
42577    }
42578 });
42579 </code></pre>
42580      * @param {Object} regions An object containing ContentPanel configs by region name
42581      */
42582     batchAdd : function(regions){
42583         this.beginUpdate();
42584         for(var rname in regions){
42585             var lr = this.regions[rname];
42586             if(lr){
42587                 this.addTypedPanels(lr, regions[rname]);
42588             }
42589         }
42590         this.endUpdate();
42591     },
42592
42593     // private
42594     addTypedPanels : function(lr, ps){
42595         if(typeof ps == 'string'){
42596             lr.add(new Roo.ContentPanel(ps));
42597         }
42598         else if(ps instanceof Array){
42599             for(var i =0, len = ps.length; i < len; i++){
42600                 this.addTypedPanels(lr, ps[i]);
42601             }
42602         }
42603         else if(!ps.events){ // raw config?
42604             var el = ps.el;
42605             delete ps.el; // prevent conflict
42606             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42607         }
42608         else {  // panel object assumed!
42609             lr.add(ps);
42610         }
42611     },
42612     /**
42613      * Adds a xtype elements to the layout.
42614      * <pre><code>
42615
42616 layout.addxtype({
42617        xtype : 'ContentPanel',
42618        region: 'west',
42619        items: [ .... ]
42620    }
42621 );
42622
42623 layout.addxtype({
42624         xtype : 'NestedLayoutPanel',
42625         region: 'west',
42626         layout: {
42627            center: { },
42628            west: { }   
42629         },
42630         items : [ ... list of content panels or nested layout panels.. ]
42631    }
42632 );
42633 </code></pre>
42634      * @param {Object} cfg Xtype definition of item to add.
42635      */
42636     addxtype : function(cfg)
42637     {
42638         // basically accepts a pannel...
42639         // can accept a layout region..!?!?
42640        // console.log('BorderLayout add ' + cfg.xtype)
42641         
42642         if (!cfg.xtype.match(/Panel$/)) {
42643             return false;
42644         }
42645         var ret = false;
42646         var region = cfg.region;
42647         delete cfg.region;
42648         
42649           
42650         var xitems = [];
42651         if (cfg.items) {
42652             xitems = cfg.items;
42653             delete cfg.items;
42654         }
42655         
42656         
42657         switch(cfg.xtype) 
42658         {
42659             case 'ContentPanel':  // ContentPanel (el, cfg)
42660                 if(cfg.autoCreate) {
42661                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42662                 } else {
42663                     var el = this.el.createChild();
42664                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42665                 }
42666                 
42667                 this.add(region, ret);
42668                 break;
42669             
42670             
42671             case 'TreePanel': // our new panel!
42672                 cfg.el = this.el.createChild();
42673                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42674                 this.add(region, ret);
42675                 break;
42676             
42677             case 'NestedLayoutPanel': 
42678                 // create a new Layout (which is  a Border Layout...
42679                 var el = this.el.createChild();
42680                 var clayout = cfg.layout;
42681                 delete cfg.layout;
42682                 clayout.items   = clayout.items  || [];
42683                 // replace this exitems with the clayout ones..
42684                 xitems = clayout.items;
42685                  
42686                 
42687                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42688                     cfg.background = false;
42689                 }
42690                 var layout = new Roo.BorderLayout(el, clayout);
42691                 
42692                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42693                 //console.log('adding nested layout panel '  + cfg.toSource());
42694                 this.add(region, ret);
42695                 
42696                 break;
42697                 
42698             case 'GridPanel': 
42699             
42700                 // needs grid and region
42701                 
42702                 //var el = this.getRegion(region).el.createChild();
42703                 var el = this.el.createChild();
42704                 // create the grid first...
42705                 
42706                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42707                 delete cfg.grid;
42708                 if (region == 'center' && this.active ) {
42709                     cfg.background = false;
42710                 }
42711                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42712                 
42713                 this.add(region, ret);
42714                 if (cfg.background) {
42715                     ret.on('activate', function(gp) {
42716                         if (!gp.grid.rendered) {
42717                             gp.grid.render();
42718                         }
42719                     });
42720                 } else {
42721                     grid.render();
42722                 }
42723                 break;
42724            
42725                
42726                 
42727                 
42728             default: 
42729                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42730                 return;
42731              // GridPanel (grid, cfg)
42732             
42733         }
42734         this.beginUpdate();
42735         // add children..
42736         Roo.each(xitems, function(i)  {
42737             ret.addxtype(i);
42738         });
42739         this.endUpdate();
42740         return ret;
42741         
42742     }
42743 });
42744
42745 /**
42746  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42747  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42748  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42749  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42750  * <pre><code>
42751 // shorthand
42752 var CP = Roo.ContentPanel;
42753
42754 var layout = Roo.BorderLayout.create({
42755     north: {
42756         initialSize: 25,
42757         titlebar: false,
42758         panels: [new CP("north", "North")]
42759     },
42760     west: {
42761         split:true,
42762         initialSize: 200,
42763         minSize: 175,
42764         maxSize: 400,
42765         titlebar: true,
42766         collapsible: true,
42767         panels: [new CP("west", {title: "West"})]
42768     },
42769     east: {
42770         split:true,
42771         initialSize: 202,
42772         minSize: 175,
42773         maxSize: 400,
42774         titlebar: true,
42775         collapsible: true,
42776         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42777     },
42778     south: {
42779         split:true,
42780         initialSize: 100,
42781         minSize: 100,
42782         maxSize: 200,
42783         titlebar: true,
42784         collapsible: true,
42785         panels: [new CP("south", {title: "South", closable: true})]
42786     },
42787     center: {
42788         titlebar: true,
42789         autoScroll:true,
42790         resizeTabs: true,
42791         minTabWidth: 50,
42792         preferredTabWidth: 150,
42793         panels: [
42794             new CP("center1", {title: "Close Me", closable: true}),
42795             new CP("center2", {title: "Center Panel", closable: false})
42796         ]
42797     }
42798 }, document.body);
42799
42800 layout.getRegion("center").showPanel("center1");
42801 </code></pre>
42802  * @param config
42803  * @param targetEl
42804  */
42805 Roo.BorderLayout.create = function(config, targetEl){
42806     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42807     layout.beginUpdate();
42808     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42809     for(var j = 0, jlen = regions.length; j < jlen; j++){
42810         var lr = regions[j];
42811         if(layout.regions[lr] && config[lr].panels){
42812             var r = layout.regions[lr];
42813             var ps = config[lr].panels;
42814             layout.addTypedPanels(r, ps);
42815         }
42816     }
42817     layout.endUpdate();
42818     return layout;
42819 };
42820
42821 // private
42822 Roo.BorderLayout.RegionFactory = {
42823     // private
42824     validRegions : ["north","south","east","west","center"],
42825
42826     // private
42827     create : function(target, mgr, config){
42828         target = target.toLowerCase();
42829         if(config.lightweight || config.basic){
42830             return new Roo.BasicLayoutRegion(mgr, config, target);
42831         }
42832         switch(target){
42833             case "north":
42834                 return new Roo.NorthLayoutRegion(mgr, config);
42835             case "south":
42836                 return new Roo.SouthLayoutRegion(mgr, config);
42837             case "east":
42838                 return new Roo.EastLayoutRegion(mgr, config);
42839             case "west":
42840                 return new Roo.WestLayoutRegion(mgr, config);
42841             case "center":
42842                 return new Roo.CenterLayoutRegion(mgr, config);
42843         }
42844         throw 'Layout region "'+target+'" not supported.';
42845     }
42846 };/*
42847  * Based on:
42848  * Ext JS Library 1.1.1
42849  * Copyright(c) 2006-2007, Ext JS, LLC.
42850  *
42851  * Originally Released Under LGPL - original licence link has changed is not relivant.
42852  *
42853  * Fork - LGPL
42854  * <script type="text/javascript">
42855  */
42856  
42857 /**
42858  * @class Roo.BasicLayoutRegion
42859  * @extends Roo.util.Observable
42860  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42861  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42862  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42863  */
42864 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42865     this.mgr = mgr;
42866     this.position  = pos;
42867     this.events = {
42868         /**
42869          * @scope Roo.BasicLayoutRegion
42870          */
42871         
42872         /**
42873          * @event beforeremove
42874          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42875          * @param {Roo.LayoutRegion} this
42876          * @param {Roo.ContentPanel} panel The panel
42877          * @param {Object} e The cancel event object
42878          */
42879         "beforeremove" : true,
42880         /**
42881          * @event invalidated
42882          * Fires when the layout for this region is changed.
42883          * @param {Roo.LayoutRegion} this
42884          */
42885         "invalidated" : true,
42886         /**
42887          * @event visibilitychange
42888          * Fires when this region is shown or hidden 
42889          * @param {Roo.LayoutRegion} this
42890          * @param {Boolean} visibility true or false
42891          */
42892         "visibilitychange" : true,
42893         /**
42894          * @event paneladded
42895          * Fires when a panel is added. 
42896          * @param {Roo.LayoutRegion} this
42897          * @param {Roo.ContentPanel} panel The panel
42898          */
42899         "paneladded" : true,
42900         /**
42901          * @event panelremoved
42902          * Fires when a panel is removed. 
42903          * @param {Roo.LayoutRegion} this
42904          * @param {Roo.ContentPanel} panel The panel
42905          */
42906         "panelremoved" : true,
42907         /**
42908          * @event collapsed
42909          * Fires when this region is collapsed.
42910          * @param {Roo.LayoutRegion} this
42911          */
42912         "collapsed" : true,
42913         /**
42914          * @event expanded
42915          * Fires when this region is expanded.
42916          * @param {Roo.LayoutRegion} this
42917          */
42918         "expanded" : true,
42919         /**
42920          * @event slideshow
42921          * Fires when this region is slid into view.
42922          * @param {Roo.LayoutRegion} this
42923          */
42924         "slideshow" : true,
42925         /**
42926          * @event slidehide
42927          * Fires when this region slides out of view. 
42928          * @param {Roo.LayoutRegion} this
42929          */
42930         "slidehide" : true,
42931         /**
42932          * @event panelactivated
42933          * Fires when a panel is activated. 
42934          * @param {Roo.LayoutRegion} this
42935          * @param {Roo.ContentPanel} panel The activated panel
42936          */
42937         "panelactivated" : true,
42938         /**
42939          * @event resized
42940          * Fires when the user resizes this region. 
42941          * @param {Roo.LayoutRegion} this
42942          * @param {Number} newSize The new size (width for east/west, height for north/south)
42943          */
42944         "resized" : true
42945     };
42946     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42947     this.panels = new Roo.util.MixedCollection();
42948     this.panels.getKey = this.getPanelId.createDelegate(this);
42949     this.box = null;
42950     this.activePanel = null;
42951     // ensure listeners are added...
42952     
42953     if (config.listeners || config.events) {
42954         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42955             listeners : config.listeners || {},
42956             events : config.events || {}
42957         });
42958     }
42959     
42960     if(skipConfig !== true){
42961         this.applyConfig(config);
42962     }
42963 };
42964
42965 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42966     getPanelId : function(p){
42967         return p.getId();
42968     },
42969     
42970     applyConfig : function(config){
42971         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42972         this.config = config;
42973         
42974     },
42975     
42976     /**
42977      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42978      * the width, for horizontal (north, south) the height.
42979      * @param {Number} newSize The new width or height
42980      */
42981     resizeTo : function(newSize){
42982         var el = this.el ? this.el :
42983                  (this.activePanel ? this.activePanel.getEl() : null);
42984         if(el){
42985             switch(this.position){
42986                 case "east":
42987                 case "west":
42988                     el.setWidth(newSize);
42989                     this.fireEvent("resized", this, newSize);
42990                 break;
42991                 case "north":
42992                 case "south":
42993                     el.setHeight(newSize);
42994                     this.fireEvent("resized", this, newSize);
42995                 break;                
42996             }
42997         }
42998     },
42999     
43000     getBox : function(){
43001         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43002     },
43003     
43004     getMargins : function(){
43005         return this.margins;
43006     },
43007     
43008     updateBox : function(box){
43009         this.box = box;
43010         var el = this.activePanel.getEl();
43011         el.dom.style.left = box.x + "px";
43012         el.dom.style.top = box.y + "px";
43013         this.activePanel.setSize(box.width, box.height);
43014     },
43015     
43016     /**
43017      * Returns the container element for this region.
43018      * @return {Roo.Element}
43019      */
43020     getEl : function(){
43021         return this.activePanel;
43022     },
43023     
43024     /**
43025      * Returns true if this region is currently visible.
43026      * @return {Boolean}
43027      */
43028     isVisible : function(){
43029         return this.activePanel ? true : false;
43030     },
43031     
43032     setActivePanel : function(panel){
43033         panel = this.getPanel(panel);
43034         if(this.activePanel && this.activePanel != panel){
43035             this.activePanel.setActiveState(false);
43036             this.activePanel.getEl().setLeftTop(-10000,-10000);
43037         }
43038         this.activePanel = panel;
43039         panel.setActiveState(true);
43040         if(this.box){
43041             panel.setSize(this.box.width, this.box.height);
43042         }
43043         this.fireEvent("panelactivated", this, panel);
43044         this.fireEvent("invalidated");
43045     },
43046     
43047     /**
43048      * Show the specified panel.
43049      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43050      * @return {Roo.ContentPanel} The shown panel or null
43051      */
43052     showPanel : function(panel){
43053         if(panel = this.getPanel(panel)){
43054             this.setActivePanel(panel);
43055         }
43056         return panel;
43057     },
43058     
43059     /**
43060      * Get the active panel for this region.
43061      * @return {Roo.ContentPanel} The active panel or null
43062      */
43063     getActivePanel : function(){
43064         return this.activePanel;
43065     },
43066     
43067     /**
43068      * Add the passed ContentPanel(s)
43069      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43070      * @return {Roo.ContentPanel} The panel added (if only one was added)
43071      */
43072     add : function(panel){
43073         if(arguments.length > 1){
43074             for(var i = 0, len = arguments.length; i < len; i++) {
43075                 this.add(arguments[i]);
43076             }
43077             return null;
43078         }
43079         if(this.hasPanel(panel)){
43080             this.showPanel(panel);
43081             return panel;
43082         }
43083         var el = panel.getEl();
43084         if(el.dom.parentNode != this.mgr.el.dom){
43085             this.mgr.el.dom.appendChild(el.dom);
43086         }
43087         if(panel.setRegion){
43088             panel.setRegion(this);
43089         }
43090         this.panels.add(panel);
43091         el.setStyle("position", "absolute");
43092         if(!panel.background){
43093             this.setActivePanel(panel);
43094             if(this.config.initialSize && this.panels.getCount()==1){
43095                 this.resizeTo(this.config.initialSize);
43096             }
43097         }
43098         this.fireEvent("paneladded", this, panel);
43099         return panel;
43100     },
43101     
43102     /**
43103      * Returns true if the panel is in this region.
43104      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43105      * @return {Boolean}
43106      */
43107     hasPanel : function(panel){
43108         if(typeof panel == "object"){ // must be panel obj
43109             panel = panel.getId();
43110         }
43111         return this.getPanel(panel) ? true : false;
43112     },
43113     
43114     /**
43115      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43116      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43117      * @param {Boolean} preservePanel Overrides the config preservePanel option
43118      * @return {Roo.ContentPanel} The panel that was removed
43119      */
43120     remove : function(panel, preservePanel){
43121         panel = this.getPanel(panel);
43122         if(!panel){
43123             return null;
43124         }
43125         var e = {};
43126         this.fireEvent("beforeremove", this, panel, e);
43127         if(e.cancel === true){
43128             return null;
43129         }
43130         var panelId = panel.getId();
43131         this.panels.removeKey(panelId);
43132         return panel;
43133     },
43134     
43135     /**
43136      * Returns the panel specified or null if it's not in this region.
43137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43138      * @return {Roo.ContentPanel}
43139      */
43140     getPanel : function(id){
43141         if(typeof id == "object"){ // must be panel obj
43142             return id;
43143         }
43144         return this.panels.get(id);
43145     },
43146     
43147     /**
43148      * Returns this regions position (north/south/east/west/center).
43149      * @return {String} 
43150      */
43151     getPosition: function(){
43152         return this.position;    
43153     }
43154 });/*
43155  * Based on:
43156  * Ext JS Library 1.1.1
43157  * Copyright(c) 2006-2007, Ext JS, LLC.
43158  *
43159  * Originally Released Under LGPL - original licence link has changed is not relivant.
43160  *
43161  * Fork - LGPL
43162  * <script type="text/javascript">
43163  */
43164  
43165 /**
43166  * @class Roo.LayoutRegion
43167  * @extends Roo.BasicLayoutRegion
43168  * This class represents a region in a layout manager.
43169  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43170  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43171  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43172  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43173  * @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})
43174  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43175  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43176  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43177  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43178  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43179  * @cfg {String} title The title for the region (overrides panel titles)
43180  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43181  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43182  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43183  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43184  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43185  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43186  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43187  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43188  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43189  * @cfg {Boolean} showPin True to show a pin button
43190 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43191 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43192 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43193 * @cfg {Number} width  For East/West panels
43194 * @cfg {Number} height For North/South panels
43195 * @cfg {Boolean} split To show the splitter
43196  */
43197 Roo.LayoutRegion = function(mgr, config, pos){
43198     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43199     var dh = Roo.DomHelper;
43200     /** This region's container element 
43201     * @type Roo.Element */
43202     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43203     /** This region's title element 
43204     * @type Roo.Element */
43205
43206     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43207         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43208         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43209     ]}, true);
43210     this.titleEl.enableDisplayMode();
43211     /** This region's title text element 
43212     * @type HTMLElement */
43213     this.titleTextEl = this.titleEl.dom.firstChild;
43214     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43215     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43216     this.closeBtn.enableDisplayMode();
43217     this.closeBtn.on("click", this.closeClicked, this);
43218     this.closeBtn.hide();
43219
43220     this.createBody(config);
43221     this.visible = true;
43222     this.collapsed = false;
43223
43224     if(config.hideWhenEmpty){
43225         this.hide();
43226         this.on("paneladded", this.validateVisibility, this);
43227         this.on("panelremoved", this.validateVisibility, this);
43228     }
43229     this.applyConfig(config);
43230 };
43231
43232 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43233
43234     createBody : function(){
43235         /** This region's body element 
43236         * @type Roo.Element */
43237         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43238     },
43239
43240     applyConfig : function(c){
43241         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43242             var dh = Roo.DomHelper;
43243             if(c.titlebar !== false){
43244                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43245                 this.collapseBtn.on("click", this.collapse, this);
43246                 this.collapseBtn.enableDisplayMode();
43247
43248                 if(c.showPin === true || this.showPin){
43249                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43250                     this.stickBtn.enableDisplayMode();
43251                     this.stickBtn.on("click", this.expand, this);
43252                     this.stickBtn.hide();
43253                 }
43254             }
43255             /** This region's collapsed element
43256             * @type Roo.Element */
43257             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43258                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43259             ]}, true);
43260             if(c.floatable !== false){
43261                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43262                this.collapsedEl.on("click", this.collapseClick, this);
43263             }
43264
43265             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43266                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43267                    id: "message", unselectable: "on", style:{"float":"left"}});
43268                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43269              }
43270             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43271             this.expandBtn.on("click", this.expand, this);
43272         }
43273         if(this.collapseBtn){
43274             this.collapseBtn.setVisible(c.collapsible == true);
43275         }
43276         this.cmargins = c.cmargins || this.cmargins ||
43277                          (this.position == "west" || this.position == "east" ?
43278                              {top: 0, left: 2, right:2, bottom: 0} :
43279                              {top: 2, left: 0, right:0, bottom: 2});
43280         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43281         this.bottomTabs = c.tabPosition != "top";
43282         this.autoScroll = c.autoScroll || false;
43283         if(this.autoScroll){
43284             this.bodyEl.setStyle("overflow", "auto");
43285         }else{
43286             this.bodyEl.setStyle("overflow", "hidden");
43287         }
43288         //if(c.titlebar !== false){
43289             if((!c.titlebar && !c.title) || c.titlebar === false){
43290                 this.titleEl.hide();
43291             }else{
43292                 this.titleEl.show();
43293                 if(c.title){
43294                     this.titleTextEl.innerHTML = c.title;
43295                 }
43296             }
43297         //}
43298         this.duration = c.duration || .30;
43299         this.slideDuration = c.slideDuration || .45;
43300         this.config = c;
43301         if(c.collapsed){
43302             this.collapse(true);
43303         }
43304         if(c.hidden){
43305             this.hide();
43306         }
43307     },
43308     /**
43309      * Returns true if this region is currently visible.
43310      * @return {Boolean}
43311      */
43312     isVisible : function(){
43313         return this.visible;
43314     },
43315
43316     /**
43317      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43318      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43319      */
43320     setCollapsedTitle : function(title){
43321         title = title || "&#160;";
43322         if(this.collapsedTitleTextEl){
43323             this.collapsedTitleTextEl.innerHTML = title;
43324         }
43325     },
43326
43327     getBox : function(){
43328         var b;
43329         if(!this.collapsed){
43330             b = this.el.getBox(false, true);
43331         }else{
43332             b = this.collapsedEl.getBox(false, true);
43333         }
43334         return b;
43335     },
43336
43337     getMargins : function(){
43338         return this.collapsed ? this.cmargins : this.margins;
43339     },
43340
43341     highlight : function(){
43342         this.el.addClass("x-layout-panel-dragover");
43343     },
43344
43345     unhighlight : function(){
43346         this.el.removeClass("x-layout-panel-dragover");
43347     },
43348
43349     updateBox : function(box){
43350         this.box = box;
43351         if(!this.collapsed){
43352             this.el.dom.style.left = box.x + "px";
43353             this.el.dom.style.top = box.y + "px";
43354             this.updateBody(box.width, box.height);
43355         }else{
43356             this.collapsedEl.dom.style.left = box.x + "px";
43357             this.collapsedEl.dom.style.top = box.y + "px";
43358             this.collapsedEl.setSize(box.width, box.height);
43359         }
43360         if(this.tabs){
43361             this.tabs.autoSizeTabs();
43362         }
43363     },
43364
43365     updateBody : function(w, h){
43366         if(w !== null){
43367             this.el.setWidth(w);
43368             w -= this.el.getBorderWidth("rl");
43369             if(this.config.adjustments){
43370                 w += this.config.adjustments[0];
43371             }
43372         }
43373         if(h !== null){
43374             this.el.setHeight(h);
43375             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43376             h -= this.el.getBorderWidth("tb");
43377             if(this.config.adjustments){
43378                 h += this.config.adjustments[1];
43379             }
43380             this.bodyEl.setHeight(h);
43381             if(this.tabs){
43382                 h = this.tabs.syncHeight(h);
43383             }
43384         }
43385         if(this.panelSize){
43386             w = w !== null ? w : this.panelSize.width;
43387             h = h !== null ? h : this.panelSize.height;
43388         }
43389         if(this.activePanel){
43390             var el = this.activePanel.getEl();
43391             w = w !== null ? w : el.getWidth();
43392             h = h !== null ? h : el.getHeight();
43393             this.panelSize = {width: w, height: h};
43394             this.activePanel.setSize(w, h);
43395         }
43396         if(Roo.isIE && this.tabs){
43397             this.tabs.el.repaint();
43398         }
43399     },
43400
43401     /**
43402      * Returns the container element for this region.
43403      * @return {Roo.Element}
43404      */
43405     getEl : function(){
43406         return this.el;
43407     },
43408
43409     /**
43410      * Hides this region.
43411      */
43412     hide : function(){
43413         if(!this.collapsed){
43414             this.el.dom.style.left = "-2000px";
43415             this.el.hide();
43416         }else{
43417             this.collapsedEl.dom.style.left = "-2000px";
43418             this.collapsedEl.hide();
43419         }
43420         this.visible = false;
43421         this.fireEvent("visibilitychange", this, false);
43422     },
43423
43424     /**
43425      * Shows this region if it was previously hidden.
43426      */
43427     show : function(){
43428         if(!this.collapsed){
43429             this.el.show();
43430         }else{
43431             this.collapsedEl.show();
43432         }
43433         this.visible = true;
43434         this.fireEvent("visibilitychange", this, true);
43435     },
43436
43437     closeClicked : function(){
43438         if(this.activePanel){
43439             this.remove(this.activePanel);
43440         }
43441     },
43442
43443     collapseClick : function(e){
43444         if(this.isSlid){
43445            e.stopPropagation();
43446            this.slideIn();
43447         }else{
43448            e.stopPropagation();
43449            this.slideOut();
43450         }
43451     },
43452
43453     /**
43454      * Collapses this region.
43455      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43456      */
43457     collapse : function(skipAnim){
43458         if(this.collapsed) return;
43459         this.collapsed = true;
43460         if(this.split){
43461             this.split.el.hide();
43462         }
43463         if(this.config.animate && skipAnim !== true){
43464             this.fireEvent("invalidated", this);
43465             this.animateCollapse();
43466         }else{
43467             this.el.setLocation(-20000,-20000);
43468             this.el.hide();
43469             this.collapsedEl.show();
43470             this.fireEvent("collapsed", this);
43471             this.fireEvent("invalidated", this);
43472         }
43473     },
43474
43475     animateCollapse : function(){
43476         // overridden
43477     },
43478
43479     /**
43480      * Expands this region if it was previously collapsed.
43481      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43482      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43483      */
43484     expand : function(e, skipAnim){
43485         if(e) e.stopPropagation();
43486         if(!this.collapsed || this.el.hasActiveFx()) return;
43487         if(this.isSlid){
43488             this.afterSlideIn();
43489             skipAnim = true;
43490         }
43491         this.collapsed = false;
43492         if(this.config.animate && skipAnim !== true){
43493             this.animateExpand();
43494         }else{
43495             this.el.show();
43496             if(this.split){
43497                 this.split.el.show();
43498             }
43499             this.collapsedEl.setLocation(-2000,-2000);
43500             this.collapsedEl.hide();
43501             this.fireEvent("invalidated", this);
43502             this.fireEvent("expanded", this);
43503         }
43504     },
43505
43506     animateExpand : function(){
43507         // overridden
43508     },
43509
43510     initTabs : function(){
43511         this.bodyEl.setStyle("overflow", "hidden");
43512         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43513             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43514             disableTooltips: this.config.disableTabTips
43515         });
43516         if(this.config.hideTabs){
43517             ts.stripWrap.setDisplayed(false);
43518         }
43519         this.tabs = ts;
43520         ts.resizeTabs = this.config.resizeTabs === true;
43521         ts.minTabWidth = this.config.minTabWidth || 40;
43522         ts.maxTabWidth = this.config.maxTabWidth || 250;
43523         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43524         ts.monitorResize = false;
43525         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43526         ts.bodyEl.addClass('x-layout-tabs-body');
43527         this.panels.each(this.initPanelAsTab, this);
43528     },
43529
43530     initPanelAsTab : function(panel){
43531         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43532                     this.config.closeOnTab && panel.isClosable());
43533         if(panel.tabTip !== undefined){
43534             ti.setTooltip(panel.tabTip);
43535         }
43536         ti.on("activate", function(){
43537               this.setActivePanel(panel);
43538         }, this);
43539         if(this.config.closeOnTab){
43540             ti.on("beforeclose", function(t, e){
43541                 e.cancel = true;
43542                 this.remove(panel);
43543             }, this);
43544         }
43545         return ti;
43546     },
43547
43548     updatePanelTitle : function(panel, title){
43549         if(this.activePanel == panel){
43550             this.updateTitle(title);
43551         }
43552         if(this.tabs){
43553             var ti = this.tabs.getTab(panel.getEl().id);
43554             ti.setText(title);
43555             if(panel.tabTip !== undefined){
43556                 ti.setTooltip(panel.tabTip);
43557             }
43558         }
43559     },
43560
43561     updateTitle : function(title){
43562         if(this.titleTextEl && !this.config.title){
43563             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43564         }
43565     },
43566
43567     setActivePanel : function(panel){
43568         panel = this.getPanel(panel);
43569         if(this.activePanel && this.activePanel != panel){
43570             this.activePanel.setActiveState(false);
43571         }
43572         this.activePanel = panel;
43573         panel.setActiveState(true);
43574         if(this.panelSize){
43575             panel.setSize(this.panelSize.width, this.panelSize.height);
43576         }
43577         if(this.closeBtn){
43578             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43579         }
43580         this.updateTitle(panel.getTitle());
43581         if(this.tabs){
43582             this.fireEvent("invalidated", this);
43583         }
43584         this.fireEvent("panelactivated", this, panel);
43585     },
43586
43587     /**
43588      * Shows the specified panel.
43589      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43590      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43591      */
43592     showPanel : function(panel){
43593         if(panel = this.getPanel(panel)){
43594             if(this.tabs){
43595                 var tab = this.tabs.getTab(panel.getEl().id);
43596                 if(tab.isHidden()){
43597                     this.tabs.unhideTab(tab.id);
43598                 }
43599                 tab.activate();
43600             }else{
43601                 this.setActivePanel(panel);
43602             }
43603         }
43604         return panel;
43605     },
43606
43607     /**
43608      * Get the active panel for this region.
43609      * @return {Roo.ContentPanel} The active panel or null
43610      */
43611     getActivePanel : function(){
43612         return this.activePanel;
43613     },
43614
43615     validateVisibility : function(){
43616         if(this.panels.getCount() < 1){
43617             this.updateTitle("&#160;");
43618             this.closeBtn.hide();
43619             this.hide();
43620         }else{
43621             if(!this.isVisible()){
43622                 this.show();
43623             }
43624         }
43625     },
43626
43627     /**
43628      * Adds the passed ContentPanel(s) to this region.
43629      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43630      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43631      */
43632     add : function(panel){
43633         if(arguments.length > 1){
43634             for(var i = 0, len = arguments.length; i < len; i++) {
43635                 this.add(arguments[i]);
43636             }
43637             return null;
43638         }
43639         if(this.hasPanel(panel)){
43640             this.showPanel(panel);
43641             return panel;
43642         }
43643         panel.setRegion(this);
43644         this.panels.add(panel);
43645         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43646             this.bodyEl.dom.appendChild(panel.getEl().dom);
43647             if(panel.background !== true){
43648                 this.setActivePanel(panel);
43649             }
43650             this.fireEvent("paneladded", this, panel);
43651             return panel;
43652         }
43653         if(!this.tabs){
43654             this.initTabs();
43655         }else{
43656             this.initPanelAsTab(panel);
43657         }
43658         if(panel.background !== true){
43659             this.tabs.activate(panel.getEl().id);
43660         }
43661         this.fireEvent("paneladded", this, panel);
43662         return panel;
43663     },
43664
43665     /**
43666      * Hides the tab for the specified panel.
43667      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43668      */
43669     hidePanel : function(panel){
43670         if(this.tabs && (panel = this.getPanel(panel))){
43671             this.tabs.hideTab(panel.getEl().id);
43672         }
43673     },
43674
43675     /**
43676      * Unhides the tab for a previously hidden panel.
43677      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43678      */
43679     unhidePanel : function(panel){
43680         if(this.tabs && (panel = this.getPanel(panel))){
43681             this.tabs.unhideTab(panel.getEl().id);
43682         }
43683     },
43684
43685     clearPanels : function(){
43686         while(this.panels.getCount() > 0){
43687              this.remove(this.panels.first());
43688         }
43689     },
43690
43691     /**
43692      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43693      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43694      * @param {Boolean} preservePanel Overrides the config preservePanel option
43695      * @return {Roo.ContentPanel} The panel that was removed
43696      */
43697     remove : function(panel, preservePanel){
43698         panel = this.getPanel(panel);
43699         if(!panel){
43700             return null;
43701         }
43702         var e = {};
43703         this.fireEvent("beforeremove", this, panel, e);
43704         if(e.cancel === true){
43705             return null;
43706         }
43707         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43708         var panelId = panel.getId();
43709         this.panels.removeKey(panelId);
43710         if(preservePanel){
43711             document.body.appendChild(panel.getEl().dom);
43712         }
43713         if(this.tabs){
43714             this.tabs.removeTab(panel.getEl().id);
43715         }else if (!preservePanel){
43716             this.bodyEl.dom.removeChild(panel.getEl().dom);
43717         }
43718         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43719             var p = this.panels.first();
43720             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43721             tempEl.appendChild(p.getEl().dom);
43722             this.bodyEl.update("");
43723             this.bodyEl.dom.appendChild(p.getEl().dom);
43724             tempEl = null;
43725             this.updateTitle(p.getTitle());
43726             this.tabs = null;
43727             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43728             this.setActivePanel(p);
43729         }
43730         panel.setRegion(null);
43731         if(this.activePanel == panel){
43732             this.activePanel = null;
43733         }
43734         if(this.config.autoDestroy !== false && preservePanel !== true){
43735             try{panel.destroy();}catch(e){}
43736         }
43737         this.fireEvent("panelremoved", this, panel);
43738         return panel;
43739     },
43740
43741     /**
43742      * Returns the TabPanel component used by this region
43743      * @return {Roo.TabPanel}
43744      */
43745     getTabs : function(){
43746         return this.tabs;
43747     },
43748
43749     createTool : function(parentEl, className){
43750         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43751             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43752         btn.addClassOnOver("x-layout-tools-button-over");
43753         return btn;
43754     }
43755 });/*
43756  * Based on:
43757  * Ext JS Library 1.1.1
43758  * Copyright(c) 2006-2007, Ext JS, LLC.
43759  *
43760  * Originally Released Under LGPL - original licence link has changed is not relivant.
43761  *
43762  * Fork - LGPL
43763  * <script type="text/javascript">
43764  */
43765  
43766
43767
43768 /**
43769  * @class Roo.SplitLayoutRegion
43770  * @extends Roo.LayoutRegion
43771  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43772  */
43773 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43774     this.cursor = cursor;
43775     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43776 };
43777
43778 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43779     splitTip : "Drag to resize.",
43780     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43781     useSplitTips : false,
43782
43783     applyConfig : function(config){
43784         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43785         if(config.split){
43786             if(!this.split){
43787                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43788                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43789                 /** The SplitBar for this region 
43790                 * @type Roo.SplitBar */
43791                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43792                 this.split.on("moved", this.onSplitMove, this);
43793                 this.split.useShim = config.useShim === true;
43794                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43795                 if(this.useSplitTips){
43796                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43797                 }
43798                 if(config.collapsible){
43799                     this.split.el.on("dblclick", this.collapse,  this);
43800                 }
43801             }
43802             if(typeof config.minSize != "undefined"){
43803                 this.split.minSize = config.minSize;
43804             }
43805             if(typeof config.maxSize != "undefined"){
43806                 this.split.maxSize = config.maxSize;
43807             }
43808             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43809                 this.hideSplitter();
43810             }
43811         }
43812     },
43813
43814     getHMaxSize : function(){
43815          var cmax = this.config.maxSize || 10000;
43816          var center = this.mgr.getRegion("center");
43817          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43818     },
43819
43820     getVMaxSize : function(){
43821          var cmax = this.config.maxSize || 10000;
43822          var center = this.mgr.getRegion("center");
43823          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43824     },
43825
43826     onSplitMove : function(split, newSize){
43827         this.fireEvent("resized", this, newSize);
43828     },
43829     
43830     /** 
43831      * Returns the {@link Roo.SplitBar} for this region.
43832      * @return {Roo.SplitBar}
43833      */
43834     getSplitBar : function(){
43835         return this.split;
43836     },
43837     
43838     hide : function(){
43839         this.hideSplitter();
43840         Roo.SplitLayoutRegion.superclass.hide.call(this);
43841     },
43842
43843     hideSplitter : function(){
43844         if(this.split){
43845             this.split.el.setLocation(-2000,-2000);
43846             this.split.el.hide();
43847         }
43848     },
43849
43850     show : function(){
43851         if(this.split){
43852             this.split.el.show();
43853         }
43854         Roo.SplitLayoutRegion.superclass.show.call(this);
43855     },
43856     
43857     beforeSlide: function(){
43858         if(Roo.isGecko){// firefox overflow auto bug workaround
43859             this.bodyEl.clip();
43860             if(this.tabs) this.tabs.bodyEl.clip();
43861             if(this.activePanel){
43862                 this.activePanel.getEl().clip();
43863                 
43864                 if(this.activePanel.beforeSlide){
43865                     this.activePanel.beforeSlide();
43866                 }
43867             }
43868         }
43869     },
43870     
43871     afterSlide : function(){
43872         if(Roo.isGecko){// firefox overflow auto bug workaround
43873             this.bodyEl.unclip();
43874             if(this.tabs) this.tabs.bodyEl.unclip();
43875             if(this.activePanel){
43876                 this.activePanel.getEl().unclip();
43877                 if(this.activePanel.afterSlide){
43878                     this.activePanel.afterSlide();
43879                 }
43880             }
43881         }
43882     },
43883
43884     initAutoHide : function(){
43885         if(this.autoHide !== false){
43886             if(!this.autoHideHd){
43887                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43888                 this.autoHideHd = {
43889                     "mouseout": function(e){
43890                         if(!e.within(this.el, true)){
43891                             st.delay(500);
43892                         }
43893                     },
43894                     "mouseover" : function(e){
43895                         st.cancel();
43896                     },
43897                     scope : this
43898                 };
43899             }
43900             this.el.on(this.autoHideHd);
43901         }
43902     },
43903
43904     clearAutoHide : function(){
43905         if(this.autoHide !== false){
43906             this.el.un("mouseout", this.autoHideHd.mouseout);
43907             this.el.un("mouseover", this.autoHideHd.mouseover);
43908         }
43909     },
43910
43911     clearMonitor : function(){
43912         Roo.get(document).un("click", this.slideInIf, this);
43913     },
43914
43915     // these names are backwards but not changed for compat
43916     slideOut : function(){
43917         if(this.isSlid || this.el.hasActiveFx()){
43918             return;
43919         }
43920         this.isSlid = true;
43921         if(this.collapseBtn){
43922             this.collapseBtn.hide();
43923         }
43924         this.closeBtnState = this.closeBtn.getStyle('display');
43925         this.closeBtn.hide();
43926         if(this.stickBtn){
43927             this.stickBtn.show();
43928         }
43929         this.el.show();
43930         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43931         this.beforeSlide();
43932         this.el.setStyle("z-index", 10001);
43933         this.el.slideIn(this.getSlideAnchor(), {
43934             callback: function(){
43935                 this.afterSlide();
43936                 this.initAutoHide();
43937                 Roo.get(document).on("click", this.slideInIf, this);
43938                 this.fireEvent("slideshow", this);
43939             },
43940             scope: this,
43941             block: true
43942         });
43943     },
43944
43945     afterSlideIn : function(){
43946         this.clearAutoHide();
43947         this.isSlid = false;
43948         this.clearMonitor();
43949         this.el.setStyle("z-index", "");
43950         if(this.collapseBtn){
43951             this.collapseBtn.show();
43952         }
43953         this.closeBtn.setStyle('display', this.closeBtnState);
43954         if(this.stickBtn){
43955             this.stickBtn.hide();
43956         }
43957         this.fireEvent("slidehide", this);
43958     },
43959
43960     slideIn : function(cb){
43961         if(!this.isSlid || this.el.hasActiveFx()){
43962             Roo.callback(cb);
43963             return;
43964         }
43965         this.isSlid = false;
43966         this.beforeSlide();
43967         this.el.slideOut(this.getSlideAnchor(), {
43968             callback: function(){
43969                 this.el.setLeftTop(-10000, -10000);
43970                 this.afterSlide();
43971                 this.afterSlideIn();
43972                 Roo.callback(cb);
43973             },
43974             scope: this,
43975             block: true
43976         });
43977     },
43978     
43979     slideInIf : function(e){
43980         if(!e.within(this.el)){
43981             this.slideIn();
43982         }
43983     },
43984
43985     animateCollapse : function(){
43986         this.beforeSlide();
43987         this.el.setStyle("z-index", 20000);
43988         var anchor = this.getSlideAnchor();
43989         this.el.slideOut(anchor, {
43990             callback : function(){
43991                 this.el.setStyle("z-index", "");
43992                 this.collapsedEl.slideIn(anchor, {duration:.3});
43993                 this.afterSlide();
43994                 this.el.setLocation(-10000,-10000);
43995                 this.el.hide();
43996                 this.fireEvent("collapsed", this);
43997             },
43998             scope: this,
43999             block: true
44000         });
44001     },
44002
44003     animateExpand : function(){
44004         this.beforeSlide();
44005         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44006         this.el.setStyle("z-index", 20000);
44007         this.collapsedEl.hide({
44008             duration:.1
44009         });
44010         this.el.slideIn(this.getSlideAnchor(), {
44011             callback : function(){
44012                 this.el.setStyle("z-index", "");
44013                 this.afterSlide();
44014                 if(this.split){
44015                     this.split.el.show();
44016                 }
44017                 this.fireEvent("invalidated", this);
44018                 this.fireEvent("expanded", this);
44019             },
44020             scope: this,
44021             block: true
44022         });
44023     },
44024
44025     anchors : {
44026         "west" : "left",
44027         "east" : "right",
44028         "north" : "top",
44029         "south" : "bottom"
44030     },
44031
44032     sanchors : {
44033         "west" : "l",
44034         "east" : "r",
44035         "north" : "t",
44036         "south" : "b"
44037     },
44038
44039     canchors : {
44040         "west" : "tl-tr",
44041         "east" : "tr-tl",
44042         "north" : "tl-bl",
44043         "south" : "bl-tl"
44044     },
44045
44046     getAnchor : function(){
44047         return this.anchors[this.position];
44048     },
44049
44050     getCollapseAnchor : function(){
44051         return this.canchors[this.position];
44052     },
44053
44054     getSlideAnchor : function(){
44055         return this.sanchors[this.position];
44056     },
44057
44058     getAlignAdj : function(){
44059         var cm = this.cmargins;
44060         switch(this.position){
44061             case "west":
44062                 return [0, 0];
44063             break;
44064             case "east":
44065                 return [0, 0];
44066             break;
44067             case "north":
44068                 return [0, 0];
44069             break;
44070             case "south":
44071                 return [0, 0];
44072             break;
44073         }
44074     },
44075
44076     getExpandAdj : function(){
44077         var c = this.collapsedEl, cm = this.cmargins;
44078         switch(this.position){
44079             case "west":
44080                 return [-(cm.right+c.getWidth()+cm.left), 0];
44081             break;
44082             case "east":
44083                 return [cm.right+c.getWidth()+cm.left, 0];
44084             break;
44085             case "north":
44086                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44087             break;
44088             case "south":
44089                 return [0, cm.top+cm.bottom+c.getHeight()];
44090             break;
44091         }
44092     }
44093 });/*
44094  * Based on:
44095  * Ext JS Library 1.1.1
44096  * Copyright(c) 2006-2007, Ext JS, LLC.
44097  *
44098  * Originally Released Under LGPL - original licence link has changed is not relivant.
44099  *
44100  * Fork - LGPL
44101  * <script type="text/javascript">
44102  */
44103 /*
44104  * These classes are private internal classes
44105  */
44106 Roo.CenterLayoutRegion = function(mgr, config){
44107     Roo.LayoutRegion.call(this, mgr, config, "center");
44108     this.visible = true;
44109     this.minWidth = config.minWidth || 20;
44110     this.minHeight = config.minHeight || 20;
44111 };
44112
44113 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44114     hide : function(){
44115         // center panel can't be hidden
44116     },
44117     
44118     show : function(){
44119         // center panel can't be hidden
44120     },
44121     
44122     getMinWidth: function(){
44123         return this.minWidth;
44124     },
44125     
44126     getMinHeight: function(){
44127         return this.minHeight;
44128     }
44129 });
44130
44131
44132 Roo.NorthLayoutRegion = function(mgr, config){
44133     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44134     if(this.split){
44135         this.split.placement = Roo.SplitBar.TOP;
44136         this.split.orientation = Roo.SplitBar.VERTICAL;
44137         this.split.el.addClass("x-layout-split-v");
44138     }
44139     var size = config.initialSize || config.height;
44140     if(typeof size != "undefined"){
44141         this.el.setHeight(size);
44142     }
44143 };
44144 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44145     orientation: Roo.SplitBar.VERTICAL,
44146     getBox : function(){
44147         if(this.collapsed){
44148             return this.collapsedEl.getBox();
44149         }
44150         var box = this.el.getBox();
44151         if(this.split){
44152             box.height += this.split.el.getHeight();
44153         }
44154         return box;
44155     },
44156     
44157     updateBox : function(box){
44158         if(this.split && !this.collapsed){
44159             box.height -= this.split.el.getHeight();
44160             this.split.el.setLeft(box.x);
44161             this.split.el.setTop(box.y+box.height);
44162             this.split.el.setWidth(box.width);
44163         }
44164         if(this.collapsed){
44165             this.updateBody(box.width, null);
44166         }
44167         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44168     }
44169 });
44170
44171 Roo.SouthLayoutRegion = function(mgr, config){
44172     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44173     if(this.split){
44174         this.split.placement = Roo.SplitBar.BOTTOM;
44175         this.split.orientation = Roo.SplitBar.VERTICAL;
44176         this.split.el.addClass("x-layout-split-v");
44177     }
44178     var size = config.initialSize || config.height;
44179     if(typeof size != "undefined"){
44180         this.el.setHeight(size);
44181     }
44182 };
44183 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44184     orientation: Roo.SplitBar.VERTICAL,
44185     getBox : function(){
44186         if(this.collapsed){
44187             return this.collapsedEl.getBox();
44188         }
44189         var box = this.el.getBox();
44190         if(this.split){
44191             var sh = this.split.el.getHeight();
44192             box.height += sh;
44193             box.y -= sh;
44194         }
44195         return box;
44196     },
44197     
44198     updateBox : function(box){
44199         if(this.split && !this.collapsed){
44200             var sh = this.split.el.getHeight();
44201             box.height -= sh;
44202             box.y += sh;
44203             this.split.el.setLeft(box.x);
44204             this.split.el.setTop(box.y-sh);
44205             this.split.el.setWidth(box.width);
44206         }
44207         if(this.collapsed){
44208             this.updateBody(box.width, null);
44209         }
44210         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44211     }
44212 });
44213
44214 Roo.EastLayoutRegion = function(mgr, config){
44215     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44216     if(this.split){
44217         this.split.placement = Roo.SplitBar.RIGHT;
44218         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44219         this.split.el.addClass("x-layout-split-h");
44220     }
44221     var size = config.initialSize || config.width;
44222     if(typeof size != "undefined"){
44223         this.el.setWidth(size);
44224     }
44225 };
44226 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44227     orientation: Roo.SplitBar.HORIZONTAL,
44228     getBox : function(){
44229         if(this.collapsed){
44230             return this.collapsedEl.getBox();
44231         }
44232         var box = this.el.getBox();
44233         if(this.split){
44234             var sw = this.split.el.getWidth();
44235             box.width += sw;
44236             box.x -= sw;
44237         }
44238         return box;
44239     },
44240
44241     updateBox : function(box){
44242         if(this.split && !this.collapsed){
44243             var sw = this.split.el.getWidth();
44244             box.width -= sw;
44245             this.split.el.setLeft(box.x);
44246             this.split.el.setTop(box.y);
44247             this.split.el.setHeight(box.height);
44248             box.x += sw;
44249         }
44250         if(this.collapsed){
44251             this.updateBody(null, box.height);
44252         }
44253         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44254     }
44255 });
44256
44257 Roo.WestLayoutRegion = function(mgr, config){
44258     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44259     if(this.split){
44260         this.split.placement = Roo.SplitBar.LEFT;
44261         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44262         this.split.el.addClass("x-layout-split-h");
44263     }
44264     var size = config.initialSize || config.width;
44265     if(typeof size != "undefined"){
44266         this.el.setWidth(size);
44267     }
44268 };
44269 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44270     orientation: Roo.SplitBar.HORIZONTAL,
44271     getBox : function(){
44272         if(this.collapsed){
44273             return this.collapsedEl.getBox();
44274         }
44275         var box = this.el.getBox();
44276         if(this.split){
44277             box.width += this.split.el.getWidth();
44278         }
44279         return box;
44280     },
44281     
44282     updateBox : function(box){
44283         if(this.split && !this.collapsed){
44284             var sw = this.split.el.getWidth();
44285             box.width -= sw;
44286             this.split.el.setLeft(box.x+box.width);
44287             this.split.el.setTop(box.y);
44288             this.split.el.setHeight(box.height);
44289         }
44290         if(this.collapsed){
44291             this.updateBody(null, box.height);
44292         }
44293         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44294     }
44295 });
44296 /*
44297  * Based on:
44298  * Ext JS Library 1.1.1
44299  * Copyright(c) 2006-2007, Ext JS, LLC.
44300  *
44301  * Originally Released Under LGPL - original licence link has changed is not relivant.
44302  *
44303  * Fork - LGPL
44304  * <script type="text/javascript">
44305  */
44306  
44307  
44308 /*
44309  * Private internal class for reading and applying state
44310  */
44311 Roo.LayoutStateManager = function(layout){
44312      // default empty state
44313      this.state = {
44314         north: {},
44315         south: {},
44316         east: {},
44317         west: {}       
44318     };
44319 };
44320
44321 Roo.LayoutStateManager.prototype = {
44322     init : function(layout, provider){
44323         this.provider = provider;
44324         var state = provider.get(layout.id+"-layout-state");
44325         if(state){
44326             var wasUpdating = layout.isUpdating();
44327             if(!wasUpdating){
44328                 layout.beginUpdate();
44329             }
44330             for(var key in state){
44331                 if(typeof state[key] != "function"){
44332                     var rstate = state[key];
44333                     var r = layout.getRegion(key);
44334                     if(r && rstate){
44335                         if(rstate.size){
44336                             r.resizeTo(rstate.size);
44337                         }
44338                         if(rstate.collapsed == true){
44339                             r.collapse(true);
44340                         }else{
44341                             r.expand(null, true);
44342                         }
44343                     }
44344                 }
44345             }
44346             if(!wasUpdating){
44347                 layout.endUpdate();
44348             }
44349             this.state = state; 
44350         }
44351         this.layout = layout;
44352         layout.on("regionresized", this.onRegionResized, this);
44353         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44354         layout.on("regionexpanded", this.onRegionExpanded, this);
44355     },
44356     
44357     storeState : function(){
44358         this.provider.set(this.layout.id+"-layout-state", this.state);
44359     },
44360     
44361     onRegionResized : function(region, newSize){
44362         this.state[region.getPosition()].size = newSize;
44363         this.storeState();
44364     },
44365     
44366     onRegionCollapsed : function(region){
44367         this.state[region.getPosition()].collapsed = true;
44368         this.storeState();
44369     },
44370     
44371     onRegionExpanded : function(region){
44372         this.state[region.getPosition()].collapsed = false;
44373         this.storeState();
44374     }
44375 };/*
44376  * Based on:
44377  * Ext JS Library 1.1.1
44378  * Copyright(c) 2006-2007, Ext JS, LLC.
44379  *
44380  * Originally Released Under LGPL - original licence link has changed is not relivant.
44381  *
44382  * Fork - LGPL
44383  * <script type="text/javascript">
44384  */
44385 /**
44386  * @class Roo.ContentPanel
44387  * @extends Roo.util.Observable
44388  * A basic ContentPanel element.
44389  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44390  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44391  * @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
44392  * @cfg {Boolean} closable True if the panel can be closed/removed
44393  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44394  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44395  * @cfg {Toolbar} toolbar A toolbar for this panel
44396  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44397  * @cfg {String} title The title for this panel
44398  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44399  * @cfg {String} url Calls {@link #setUrl} with this value
44400  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44401  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44402  * @constructor
44403  * Create a new ContentPanel.
44404  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44405  * @param {String/Object} config A string to set only the title or a config object
44406  * @param {String} content (optional) Set the HTML content for this panel
44407  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44408  */
44409 Roo.ContentPanel = function(el, config, content){
44410     
44411      
44412     /*
44413     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44414         config = el;
44415         el = Roo.id();
44416     }
44417     if (config && config.parentLayout) { 
44418         el = config.parentLayout.el.createChild(); 
44419     }
44420     */
44421     if(el.autoCreate){ // xtype is available if this is called from factory
44422         config = el;
44423         el = Roo.id();
44424     }
44425     this.el = Roo.get(el);
44426     if(!this.el && config && config.autoCreate){
44427         if(typeof config.autoCreate == "object"){
44428             if(!config.autoCreate.id){
44429                 config.autoCreate.id = config.id||el;
44430             }
44431             this.el = Roo.DomHelper.append(document.body,
44432                         config.autoCreate, true);
44433         }else{
44434             this.el = Roo.DomHelper.append(document.body,
44435                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44436         }
44437     }
44438     this.closable = false;
44439     this.loaded = false;
44440     this.active = false;
44441     if(typeof config == "string"){
44442         this.title = config;
44443     }else{
44444         Roo.apply(this, config);
44445     }
44446     
44447     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44448         this.wrapEl = this.el.wrap();    
44449         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44450         
44451     }
44452     
44453     
44454     
44455     if(this.resizeEl){
44456         this.resizeEl = Roo.get(this.resizeEl, true);
44457     }else{
44458         this.resizeEl = this.el;
44459     }
44460     this.addEvents({
44461         /**
44462          * @event activate
44463          * Fires when this panel is activated. 
44464          * @param {Roo.ContentPanel} this
44465          */
44466         "activate" : true,
44467         /**
44468          * @event deactivate
44469          * Fires when this panel is activated. 
44470          * @param {Roo.ContentPanel} this
44471          */
44472         "deactivate" : true,
44473
44474         /**
44475          * @event resize
44476          * Fires when this panel is resized if fitToFrame is true.
44477          * @param {Roo.ContentPanel} this
44478          * @param {Number} width The width after any component adjustments
44479          * @param {Number} height The height after any component adjustments
44480          */
44481         "resize" : true
44482     });
44483     if(this.autoScroll){
44484         this.resizeEl.setStyle("overflow", "auto");
44485     }
44486     content = content || this.content;
44487     if(content){
44488         this.setContent(content);
44489     }
44490     if(config && config.url){
44491         this.setUrl(this.url, this.params, this.loadOnce);
44492     }
44493     
44494     
44495     
44496     Roo.ContentPanel.superclass.constructor.call(this);
44497 };
44498
44499 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44500     tabTip:'',
44501     setRegion : function(region){
44502         this.region = region;
44503         if(region){
44504            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44505         }else{
44506            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44507         } 
44508     },
44509     
44510     /**
44511      * Returns the toolbar for this Panel if one was configured. 
44512      * @return {Roo.Toolbar} 
44513      */
44514     getToolbar : function(){
44515         return this.toolbar;
44516     },
44517     
44518     setActiveState : function(active){
44519         this.active = active;
44520         if(!active){
44521             this.fireEvent("deactivate", this);
44522         }else{
44523             this.fireEvent("activate", this);
44524         }
44525     },
44526     /**
44527      * Updates this panel's element
44528      * @param {String} content The new content
44529      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44530     */
44531     setContent : function(content, loadScripts){
44532         this.el.update(content, loadScripts);
44533     },
44534
44535     ignoreResize : function(w, h){
44536         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44537             return true;
44538         }else{
44539             this.lastSize = {width: w, height: h};
44540             return false;
44541         }
44542     },
44543     /**
44544      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44545      * @return {Roo.UpdateManager} The UpdateManager
44546      */
44547     getUpdateManager : function(){
44548         return this.el.getUpdateManager();
44549     },
44550      /**
44551      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44552      * @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:
44553 <pre><code>
44554 panel.load({
44555     url: "your-url.php",
44556     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44557     callback: yourFunction,
44558     scope: yourObject, //(optional scope)
44559     discardUrl: false,
44560     nocache: false,
44561     text: "Loading...",
44562     timeout: 30,
44563     scripts: false
44564 });
44565 </code></pre>
44566      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44567      * 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.
44568      * @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}
44569      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44570      * @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.
44571      * @return {Roo.ContentPanel} this
44572      */
44573     load : function(){
44574         var um = this.el.getUpdateManager();
44575         um.update.apply(um, arguments);
44576         return this;
44577     },
44578
44579
44580     /**
44581      * 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.
44582      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44583      * @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)
44584      * @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)
44585      * @return {Roo.UpdateManager} The UpdateManager
44586      */
44587     setUrl : function(url, params, loadOnce){
44588         if(this.refreshDelegate){
44589             this.removeListener("activate", this.refreshDelegate);
44590         }
44591         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44592         this.on("activate", this.refreshDelegate);
44593         return this.el.getUpdateManager();
44594     },
44595     
44596     _handleRefresh : function(url, params, loadOnce){
44597         if(!loadOnce || !this.loaded){
44598             var updater = this.el.getUpdateManager();
44599             updater.update(url, params, this._setLoaded.createDelegate(this));
44600         }
44601     },
44602     
44603     _setLoaded : function(){
44604         this.loaded = true;
44605     }, 
44606     
44607     /**
44608      * Returns this panel's id
44609      * @return {String} 
44610      */
44611     getId : function(){
44612         return this.el.id;
44613     },
44614     
44615     /** 
44616      * Returns this panel's element - used by regiosn to add.
44617      * @return {Roo.Element} 
44618      */
44619     getEl : function(){
44620         return this.wrapEl || this.el;
44621     },
44622     
44623     adjustForComponents : function(width, height){
44624         if(this.resizeEl != this.el){
44625             width -= this.el.getFrameWidth('lr');
44626             height -= this.el.getFrameWidth('tb');
44627         }
44628         if(this.toolbar){
44629             var te = this.toolbar.getEl();
44630             height -= te.getHeight();
44631             te.setWidth(width);
44632         }
44633         if(this.adjustments){
44634             width += this.adjustments[0];
44635             height += this.adjustments[1];
44636         }
44637         return {"width": width, "height": height};
44638     },
44639     
44640     setSize : function(width, height){
44641         if(this.fitToFrame && !this.ignoreResize(width, height)){
44642             if(this.fitContainer && this.resizeEl != this.el){
44643                 this.el.setSize(width, height);
44644             }
44645             var size = this.adjustForComponents(width, height);
44646             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44647             this.fireEvent('resize', this, size.width, size.height);
44648         }
44649     },
44650     
44651     /**
44652      * Returns this panel's title
44653      * @return {String} 
44654      */
44655     getTitle : function(){
44656         return this.title;
44657     },
44658     
44659     /**
44660      * Set this panel's title
44661      * @param {String} title
44662      */
44663     setTitle : function(title){
44664         this.title = title;
44665         if(this.region){
44666             this.region.updatePanelTitle(this, title);
44667         }
44668     },
44669     
44670     /**
44671      * Returns true is this panel was configured to be closable
44672      * @return {Boolean} 
44673      */
44674     isClosable : function(){
44675         return this.closable;
44676     },
44677     
44678     beforeSlide : function(){
44679         this.el.clip();
44680         this.resizeEl.clip();
44681     },
44682     
44683     afterSlide : function(){
44684         this.el.unclip();
44685         this.resizeEl.unclip();
44686     },
44687     
44688     /**
44689      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44690      *   Will fail silently if the {@link #setUrl} method has not been called.
44691      *   This does not activate the panel, just updates its content.
44692      */
44693     refresh : function(){
44694         if(this.refreshDelegate){
44695            this.loaded = false;
44696            this.refreshDelegate();
44697         }
44698     },
44699     
44700     /**
44701      * Destroys this panel
44702      */
44703     destroy : function(){
44704         this.el.removeAllListeners();
44705         var tempEl = document.createElement("span");
44706         tempEl.appendChild(this.el.dom);
44707         tempEl.innerHTML = "";
44708         this.el.remove();
44709         this.el = null;
44710     },
44711     
44712       /**
44713      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44714      * <pre><code>
44715
44716 layout.addxtype({
44717        xtype : 'Form',
44718        items: [ .... ]
44719    }
44720 );
44721
44722 </code></pre>
44723      * @param {Object} cfg Xtype definition of item to add.
44724      */
44725     
44726     addxtype : function(cfg) {
44727         // add form..
44728         if (cfg.xtype.match(/^Form$/)) {
44729             var el = this.el.createChild();
44730
44731             this.form = new  Roo.form.Form(cfg);
44732             
44733             
44734             if ( this.form.allItems.length) this.form.render(el.dom);
44735             return this.form;
44736         }
44737         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
44738             // views..
44739             cfg.el = this.el;
44740             // factory?
44741             var ret = new Roo[cfg.xtype](cfg);
44742             ret.render(false, ''); // render blank..
44743             return ret;
44744             
44745         }
44746         return false;
44747         
44748     }
44749 });
44750
44751 /**
44752  * @class Roo.GridPanel
44753  * @extends Roo.ContentPanel
44754  * @constructor
44755  * Create a new GridPanel.
44756  * @param {Roo.grid.Grid} grid The grid for this panel
44757  * @param {String/Object} config A string to set only the panel's title, or a config object
44758  */
44759 Roo.GridPanel = function(grid, config){
44760     
44761   
44762     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44763         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44764         
44765     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44766     
44767     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44768     
44769     if(this.toolbar){
44770         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44771     }
44772     // xtype created footer. - not sure if will work as we normally have to render first..
44773     if (this.footer && !this.footer.el && this.footer.xtype) {
44774         
44775         this.footer.container = this.grid.getView().getFooterPanel(true);
44776         this.footer.dataSource = this.grid.dataSource;
44777         this.footer = Roo.factory(this.footer, Roo);
44778         
44779     }
44780     
44781     grid.monitorWindowResize = false; // turn off autosizing
44782     grid.autoHeight = false;
44783     grid.autoWidth = false;
44784     this.grid = grid;
44785     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44786 };
44787
44788 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44789     getId : function(){
44790         return this.grid.id;
44791     },
44792     
44793     /**
44794      * Returns the grid for this panel
44795      * @return {Roo.grid.Grid} 
44796      */
44797     getGrid : function(){
44798         return this.grid;    
44799     },
44800     
44801     setSize : function(width, height){
44802         if(!this.ignoreResize(width, height)){
44803             var grid = this.grid;
44804             var size = this.adjustForComponents(width, height);
44805             grid.getGridEl().setSize(size.width, size.height);
44806             grid.autoSize();
44807         }
44808     },
44809     
44810     beforeSlide : function(){
44811         this.grid.getView().scroller.clip();
44812     },
44813     
44814     afterSlide : function(){
44815         this.grid.getView().scroller.unclip();
44816     },
44817     
44818     destroy : function(){
44819         this.grid.destroy();
44820         delete this.grid;
44821         Roo.GridPanel.superclass.destroy.call(this); 
44822     }
44823 });
44824
44825
44826 /**
44827  * @class Roo.NestedLayoutPanel
44828  * @extends Roo.ContentPanel
44829  * @constructor
44830  * Create a new NestedLayoutPanel.
44831  * 
44832  * 
44833  * @param {Roo.BorderLayout} layout The layout for this panel
44834  * @param {String/Object} config A string to set only the title or a config object
44835  */
44836 Roo.NestedLayoutPanel = function(layout, config)
44837 {
44838     // construct with only one argument..
44839     /* FIXME - implement nicer consturctors
44840     if (layout.layout) {
44841         config = layout;
44842         layout = config.layout;
44843         delete config.layout;
44844     }
44845     if (layout.xtype && !layout.getEl) {
44846         // then layout needs constructing..
44847         layout = Roo.factory(layout, Roo);
44848     }
44849     */
44850     
44851     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44852     
44853     layout.monitorWindowResize = false; // turn off autosizing
44854     this.layout = layout;
44855     this.layout.getEl().addClass("x-layout-nested-layout");
44856     
44857     
44858     
44859 };
44860
44861 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44862
44863     setSize : function(width, height){
44864         if(!this.ignoreResize(width, height)){
44865             var size = this.adjustForComponents(width, height);
44866             var el = this.layout.getEl();
44867             el.setSize(size.width, size.height);
44868             var touch = el.dom.offsetWidth;
44869             this.layout.layout();
44870             // ie requires a double layout on the first pass
44871             if(Roo.isIE && !this.initialized){
44872                 this.initialized = true;
44873                 this.layout.layout();
44874             }
44875         }
44876     },
44877     
44878     // activate all subpanels if not currently active..
44879     
44880     setActiveState : function(active){
44881         this.active = active;
44882         if(!active){
44883             this.fireEvent("deactivate", this);
44884             return;
44885         }
44886         
44887         this.fireEvent("activate", this);
44888         // not sure if this should happen before or after..
44889         if (!this.layout) {
44890             return; // should not happen..
44891         }
44892         var reg = false;
44893         for (var r in this.layout.regions) {
44894             reg = this.layout.getRegion(r);
44895             if (reg.getActivePanel()) {
44896                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44897                 reg.setActivePanel(reg.getActivePanel());
44898                 continue;
44899             }
44900             if (!reg.panels.length) {
44901                 continue;
44902             }
44903             reg.showPanel(reg.getPanel(0));
44904         }
44905         
44906         
44907         
44908         
44909     },
44910     
44911     /**
44912      * Returns the nested BorderLayout for this panel
44913      * @return {Roo.BorderLayout} 
44914      */
44915     getLayout : function(){
44916         return this.layout;
44917     },
44918     
44919      /**
44920      * Adds a xtype elements to the layout of the nested panel
44921      * <pre><code>
44922
44923 panel.addxtype({
44924        xtype : 'ContentPanel',
44925        region: 'west',
44926        items: [ .... ]
44927    }
44928 );
44929
44930 panel.addxtype({
44931         xtype : 'NestedLayoutPanel',
44932         region: 'west',
44933         layout: {
44934            center: { },
44935            west: { }   
44936         },
44937         items : [ ... list of content panels or nested layout panels.. ]
44938    }
44939 );
44940 </code></pre>
44941      * @param {Object} cfg Xtype definition of item to add.
44942      */
44943     addxtype : function(cfg) {
44944         return this.layout.addxtype(cfg);
44945     
44946     }
44947 });
44948
44949 Roo.ScrollPanel = function(el, config, content){
44950     config = config || {};
44951     config.fitToFrame = true;
44952     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44953     
44954     this.el.dom.style.overflow = "hidden";
44955     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44956     this.el.removeClass("x-layout-inactive-content");
44957     this.el.on("mousewheel", this.onWheel, this);
44958
44959     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44960     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44961     up.unselectable(); down.unselectable();
44962     up.on("click", this.scrollUp, this);
44963     down.on("click", this.scrollDown, this);
44964     up.addClassOnOver("x-scroller-btn-over");
44965     down.addClassOnOver("x-scroller-btn-over");
44966     up.addClassOnClick("x-scroller-btn-click");
44967     down.addClassOnClick("x-scroller-btn-click");
44968     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44969
44970     this.resizeEl = this.el;
44971     this.el = wrap; this.up = up; this.down = down;
44972 };
44973
44974 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44975     increment : 100,
44976     wheelIncrement : 5,
44977     scrollUp : function(){
44978         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44979     },
44980
44981     scrollDown : function(){
44982         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44983     },
44984
44985     afterScroll : function(){
44986         var el = this.resizeEl;
44987         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44988         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44989         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44990     },
44991
44992     setSize : function(){
44993         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44994         this.afterScroll();
44995     },
44996
44997     onWheel : function(e){
44998         var d = e.getWheelDelta();
44999         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45000         this.afterScroll();
45001         e.stopEvent();
45002     },
45003
45004     setContent : function(content, loadScripts){
45005         this.resizeEl.update(content, loadScripts);
45006     }
45007
45008 });
45009
45010
45011
45012
45013
45014
45015
45016
45017
45018 /**
45019  * @class Roo.TreePanel
45020  * @extends Roo.ContentPanel
45021  * @constructor
45022  * Create a new TreePanel.
45023  * @param {String/Object} config A string to set only the panel's title, or a config object
45024  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45025  */
45026 Roo.TreePanel = function(config){
45027     var el = config.el;
45028     var tree = config.tree;
45029     delete config.tree; 
45030     delete config.el; // hopefull!
45031     Roo.TreePanel.superclass.constructor.call(this, el, config);
45032     var treeEl = el.createChild();
45033     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45034     //console.log(tree);
45035     this.on('activate', function()
45036     {
45037         if (this.tree.rendered) {
45038             return;
45039         }
45040         //console.log('render tree');
45041         this.tree.render();
45042     });
45043     
45044     this.on('resize',  function (cp, w, h) {
45045             this.tree.innerCt.setWidth(w);
45046             this.tree.innerCt.setHeight(h);
45047             this.tree.innerCt.setStyle('overflow-y', 'auto');
45048     });
45049
45050         
45051     
45052 };
45053
45054 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
45055
45056
45057
45058
45059
45060
45061
45062
45063
45064
45065
45066 /*
45067  * Based on:
45068  * Ext JS Library 1.1.1
45069  * Copyright(c) 2006-2007, Ext JS, LLC.
45070  *
45071  * Originally Released Under LGPL - original licence link has changed is not relivant.
45072  *
45073  * Fork - LGPL
45074  * <script type="text/javascript">
45075  */
45076  
45077
45078 /**
45079  * @class Roo.ReaderLayout
45080  * @extends Roo.BorderLayout
45081  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45082  * center region containing two nested regions (a top one for a list view and one for item preview below),
45083  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45084  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45085  * expedites the setup of the overall layout and regions for this common application style.
45086  * Example:
45087  <pre><code>
45088 var reader = new Roo.ReaderLayout();
45089 var CP = Roo.ContentPanel;  // shortcut for adding
45090
45091 reader.beginUpdate();
45092 reader.add("north", new CP("north", "North"));
45093 reader.add("west", new CP("west", {title: "West"}));
45094 reader.add("east", new CP("east", {title: "East"}));
45095
45096 reader.regions.listView.add(new CP("listView", "List"));
45097 reader.regions.preview.add(new CP("preview", "Preview"));
45098 reader.endUpdate();
45099 </code></pre>
45100 * @constructor
45101 * Create a new ReaderLayout
45102 * @param {Object} config Configuration options
45103 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45104 * document.body if omitted)
45105 */
45106 Roo.ReaderLayout = function(config, renderTo){
45107     var c = config || {size:{}};
45108     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45109         north: c.north !== false ? Roo.apply({
45110             split:false,
45111             initialSize: 32,
45112             titlebar: false
45113         }, c.north) : false,
45114         west: c.west !== false ? Roo.apply({
45115             split:true,
45116             initialSize: 200,
45117             minSize: 175,
45118             maxSize: 400,
45119             titlebar: true,
45120             collapsible: true,
45121             animate: true,
45122             margins:{left:5,right:0,bottom:5,top:5},
45123             cmargins:{left:5,right:5,bottom:5,top:5}
45124         }, c.west) : false,
45125         east: c.east !== false ? Roo.apply({
45126             split:true,
45127             initialSize: 200,
45128             minSize: 175,
45129             maxSize: 400,
45130             titlebar: true,
45131             collapsible: true,
45132             animate: true,
45133             margins:{left:0,right:5,bottom:5,top:5},
45134             cmargins:{left:5,right:5,bottom:5,top:5}
45135         }, c.east) : false,
45136         center: Roo.apply({
45137             tabPosition: 'top',
45138             autoScroll:false,
45139             closeOnTab: true,
45140             titlebar:false,
45141             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45142         }, c.center)
45143     });
45144
45145     this.el.addClass('x-reader');
45146
45147     this.beginUpdate();
45148
45149     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45150         south: c.preview !== false ? Roo.apply({
45151             split:true,
45152             initialSize: 200,
45153             minSize: 100,
45154             autoScroll:true,
45155             collapsible:true,
45156             titlebar: true,
45157             cmargins:{top:5,left:0, right:0, bottom:0}
45158         }, c.preview) : false,
45159         center: Roo.apply({
45160             autoScroll:false,
45161             titlebar:false,
45162             minHeight:200
45163         }, c.listView)
45164     });
45165     this.add('center', new Roo.NestedLayoutPanel(inner,
45166             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45167
45168     this.endUpdate();
45169
45170     this.regions.preview = inner.getRegion('south');
45171     this.regions.listView = inner.getRegion('center');
45172 };
45173
45174 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45175  * Based on:
45176  * Ext JS Library 1.1.1
45177  * Copyright(c) 2006-2007, Ext JS, LLC.
45178  *
45179  * Originally Released Under LGPL - original licence link has changed is not relivant.
45180  *
45181  * Fork - LGPL
45182  * <script type="text/javascript">
45183  */
45184  
45185 /**
45186  * @class Roo.grid.Grid
45187  * @extends Roo.util.Observable
45188  * This class represents the primary interface of a component based grid control.
45189  * <br><br>Usage:<pre><code>
45190  var grid = new Roo.grid.Grid("my-container-id", {
45191      ds: myDataStore,
45192      cm: myColModel,
45193      selModel: mySelectionModel,
45194      autoSizeColumns: true,
45195      monitorWindowResize: false,
45196      trackMouseOver: true
45197  });
45198  // set any options
45199  grid.render();
45200  * </code></pre>
45201  * <b>Common Problems:</b><br/>
45202  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45203  * element will correct this<br/>
45204  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45205  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45206  * are unpredictable.<br/>
45207  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45208  * grid to calculate dimensions/offsets.<br/>
45209   * @constructor
45210  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45211  * The container MUST have some type of size defined for the grid to fill. The container will be
45212  * automatically set to position relative if it isn't already.
45213  * @param {Object} config A config object that sets properties on this grid.
45214  */
45215 Roo.grid.Grid = function(container, config){
45216         // initialize the container
45217         this.container = Roo.get(container);
45218         this.container.update("");
45219         this.container.setStyle("overflow", "hidden");
45220     this.container.addClass('x-grid-container');
45221
45222     this.id = this.container.id;
45223
45224     Roo.apply(this, config);
45225     // check and correct shorthanded configs
45226     if(this.ds){
45227         this.dataSource = this.ds;
45228         delete this.ds;
45229     }
45230     if(this.cm){
45231         this.colModel = this.cm;
45232         delete this.cm;
45233     }
45234     if(this.sm){
45235         this.selModel = this.sm;
45236         delete this.sm;
45237     }
45238
45239     if (this.selModel) {
45240         this.selModel = Roo.factory(this.selModel, Roo.grid);
45241         this.sm = this.selModel;
45242         this.sm.xmodule = this.xmodule || false;
45243     }
45244     if (typeof(this.colModel.config) == 'undefined') {
45245         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45246         this.cm = this.colModel;
45247         this.cm.xmodule = this.xmodule || false;
45248     }
45249     if (this.dataSource) {
45250         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45251         this.ds = this.dataSource;
45252         this.ds.xmodule = this.xmodule || false;
45253         
45254     }
45255     
45256     
45257     
45258     if(this.width){
45259         this.container.setWidth(this.width);
45260     }
45261
45262     if(this.height){
45263         this.container.setHeight(this.height);
45264     }
45265     /** @private */
45266         this.addEvents({
45267             // raw events
45268             /**
45269              * @event click
45270              * The raw click event for the entire grid.
45271              * @param {Roo.EventObject} e
45272              */
45273             "click" : true,
45274             /**
45275              * @event dblclick
45276              * The raw dblclick event for the entire grid.
45277              * @param {Roo.EventObject} e
45278              */
45279             "dblclick" : true,
45280             /**
45281              * @event contextmenu
45282              * The raw contextmenu event for the entire grid.
45283              * @param {Roo.EventObject} e
45284              */
45285             "contextmenu" : true,
45286             /**
45287              * @event mousedown
45288              * The raw mousedown event for the entire grid.
45289              * @param {Roo.EventObject} e
45290              */
45291             "mousedown" : true,
45292             /**
45293              * @event mouseup
45294              * The raw mouseup event for the entire grid.
45295              * @param {Roo.EventObject} e
45296              */
45297             "mouseup" : true,
45298             /**
45299              * @event mouseover
45300              * The raw mouseover event for the entire grid.
45301              * @param {Roo.EventObject} e
45302              */
45303             "mouseover" : true,
45304             /**
45305              * @event mouseout
45306              * The raw mouseout event for the entire grid.
45307              * @param {Roo.EventObject} e
45308              */
45309             "mouseout" : true,
45310             /**
45311              * @event keypress
45312              * The raw keypress event for the entire grid.
45313              * @param {Roo.EventObject} e
45314              */
45315             "keypress" : true,
45316             /**
45317              * @event keydown
45318              * The raw keydown event for the entire grid.
45319              * @param {Roo.EventObject} e
45320              */
45321             "keydown" : true,
45322
45323             // custom events
45324
45325             /**
45326              * @event cellclick
45327              * Fires when a cell is clicked
45328              * @param {Grid} this
45329              * @param {Number} rowIndex
45330              * @param {Number} columnIndex
45331              * @param {Roo.EventObject} e
45332              */
45333             "cellclick" : true,
45334             /**
45335              * @event celldblclick
45336              * Fires when a cell is double clicked
45337              * @param {Grid} this
45338              * @param {Number} rowIndex
45339              * @param {Number} columnIndex
45340              * @param {Roo.EventObject} e
45341              */
45342             "celldblclick" : true,
45343             /**
45344              * @event rowclick
45345              * Fires when a row is clicked
45346              * @param {Grid} this
45347              * @param {Number} rowIndex
45348              * @param {Roo.EventObject} e
45349              */
45350             "rowclick" : true,
45351             /**
45352              * @event rowdblclick
45353              * Fires when a row is double clicked
45354              * @param {Grid} this
45355              * @param {Number} rowIndex
45356              * @param {Roo.EventObject} e
45357              */
45358             "rowdblclick" : true,
45359             /**
45360              * @event headerclick
45361              * Fires when a header is clicked
45362              * @param {Grid} this
45363              * @param {Number} columnIndex
45364              * @param {Roo.EventObject} e
45365              */
45366             "headerclick" : true,
45367             /**
45368              * @event headerdblclick
45369              * Fires when a header cell is double clicked
45370              * @param {Grid} this
45371              * @param {Number} columnIndex
45372              * @param {Roo.EventObject} e
45373              */
45374             "headerdblclick" : true,
45375             /**
45376              * @event rowcontextmenu
45377              * Fires when a row is right clicked
45378              * @param {Grid} this
45379              * @param {Number} rowIndex
45380              * @param {Roo.EventObject} e
45381              */
45382             "rowcontextmenu" : true,
45383             /**
45384          * @event cellcontextmenu
45385          * Fires when a cell is right clicked
45386          * @param {Grid} this
45387          * @param {Number} rowIndex
45388          * @param {Number} cellIndex
45389          * @param {Roo.EventObject} e
45390          */
45391          "cellcontextmenu" : true,
45392             /**
45393              * @event headercontextmenu
45394              * Fires when a header is right clicked
45395              * @param {Grid} this
45396              * @param {Number} columnIndex
45397              * @param {Roo.EventObject} e
45398              */
45399             "headercontextmenu" : true,
45400             /**
45401              * @event bodyscroll
45402              * Fires when the body element is scrolled
45403              * @param {Number} scrollLeft
45404              * @param {Number} scrollTop
45405              */
45406             "bodyscroll" : true,
45407             /**
45408              * @event columnresize
45409              * Fires when the user resizes a column
45410              * @param {Number} columnIndex
45411              * @param {Number} newSize
45412              */
45413             "columnresize" : true,
45414             /**
45415              * @event columnmove
45416              * Fires when the user moves a column
45417              * @param {Number} oldIndex
45418              * @param {Number} newIndex
45419              */
45420             "columnmove" : true,
45421             /**
45422              * @event startdrag
45423              * Fires when row(s) start being dragged
45424              * @param {Grid} this
45425              * @param {Roo.GridDD} dd The drag drop object
45426              * @param {event} e The raw browser event
45427              */
45428             "startdrag" : true,
45429             /**
45430              * @event enddrag
45431              * Fires when a drag operation is complete
45432              * @param {Grid} this
45433              * @param {Roo.GridDD} dd The drag drop object
45434              * @param {event} e The raw browser event
45435              */
45436             "enddrag" : true,
45437             /**
45438              * @event dragdrop
45439              * Fires when dragged row(s) are dropped on a valid DD target
45440              * @param {Grid} this
45441              * @param {Roo.GridDD} dd The drag drop object
45442              * @param {String} targetId The target drag drop object
45443              * @param {event} e The raw browser event
45444              */
45445             "dragdrop" : true,
45446             /**
45447              * @event dragover
45448              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45449              * @param {Grid} this
45450              * @param {Roo.GridDD} dd The drag drop object
45451              * @param {String} targetId The target drag drop object
45452              * @param {event} e The raw browser event
45453              */
45454             "dragover" : true,
45455             /**
45456              * @event dragenter
45457              *  Fires when the dragged row(s) first cross another DD target while being dragged
45458              * @param {Grid} this
45459              * @param {Roo.GridDD} dd The drag drop object
45460              * @param {String} targetId The target drag drop object
45461              * @param {event} e The raw browser event
45462              */
45463             "dragenter" : true,
45464             /**
45465              * @event dragout
45466              * Fires when the dragged row(s) leave another DD target while being dragged
45467              * @param {Grid} this
45468              * @param {Roo.GridDD} dd The drag drop object
45469              * @param {String} targetId The target drag drop object
45470              * @param {event} e The raw browser event
45471              */
45472             "dragout" : true,
45473         /**
45474          * @event render
45475          * Fires when the grid is rendered
45476          * @param {Grid} grid
45477          */
45478         render : true
45479     });
45480
45481     Roo.grid.Grid.superclass.constructor.call(this);
45482 };
45483 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45484     /**
45485      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45486          */
45487         minColumnWidth : 25,
45488
45489     /**
45490          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45491          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45492          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45493          */
45494         autoSizeColumns : false,
45495
45496         /**
45497          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45498          */
45499         autoSizeHeaders : true,
45500
45501         /**
45502          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45503          */
45504         monitorWindowResize : true,
45505
45506         /**
45507          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45508          * rows measured to get a columns size. Default is 0 (all rows).
45509          */
45510         maxRowsToMeasure : 0,
45511
45512         /**
45513          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45514          */
45515         trackMouseOver : true,
45516
45517         /**
45518          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45519          */
45520         enableDragDrop : false,
45521
45522         /**
45523          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45524          */
45525         enableColumnMove : true,
45526
45527         /**
45528          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45529          */
45530         enableColumnHide : true,
45531
45532         /**
45533          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45534          */
45535         enableRowHeightSync : false,
45536
45537         /**
45538          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45539          */
45540         stripeRows : true,
45541
45542         /**
45543          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45544          */
45545         autoHeight : false,
45546
45547     /**
45548      * @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.
45549      */
45550     autoExpandColumn : false,
45551
45552     /**
45553     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45554     * Default is 50.
45555     */
45556     autoExpandMin : 50,
45557
45558     /**
45559     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45560     */
45561     autoExpandMax : 1000,
45562
45563     /**
45564          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45565          */
45566         view : null,
45567
45568         /**
45569      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45570          */
45571         loadMask : false,
45572
45573     // private
45574     rendered : false,
45575
45576     /**
45577     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45578     * of a fixed width. Default is false.
45579     */
45580     /**
45581     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45582     */
45583     /**
45584      * Called once after all setup has been completed and the grid is ready to be rendered.
45585      * @return {Roo.grid.Grid} this
45586      */
45587     render : function(){
45588         var c = this.container;
45589         // try to detect autoHeight/width mode
45590         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45591             this.autoHeight = true;
45592         }
45593         var view = this.getView();
45594         view.init(this);
45595
45596         c.on("click", this.onClick, this);
45597         c.on("dblclick", this.onDblClick, this);
45598         c.on("contextmenu", this.onContextMenu, this);
45599         c.on("keydown", this.onKeyDown, this);
45600
45601         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45602
45603         this.getSelectionModel().init(this);
45604
45605         view.render();
45606
45607         if(this.loadMask){
45608             this.loadMask = new Roo.LoadMask(this.container,
45609                     Roo.apply({store:this.dataSource}, this.loadMask));
45610         }
45611         
45612         
45613         if (this.toolbar && this.toolbar.xtype) {
45614             this.toolbar.container = this.getView().getHeaderPanel(true);
45615             this.toolbar = new Ext.Toolbar(this.toolbar);
45616         }
45617         if (this.footer && this.footer.xtype) {
45618             this.footer.dataSource = this.getDataSource();
45619             this.footer.container = this.getView().getFooterPanel(true);
45620             this.footer = Roo.factory(this.footer, Roo);
45621         }
45622         this.rendered = true;
45623         this.fireEvent('render', this);
45624         return this;
45625     },
45626
45627         /**
45628          * Reconfigures the grid to use a different Store and Column Model.
45629          * The View will be bound to the new objects and refreshed.
45630          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45631          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45632          */
45633     reconfigure : function(dataSource, colModel){
45634         if(this.loadMask){
45635             this.loadMask.destroy();
45636             this.loadMask = new Roo.LoadMask(this.container,
45637                     Roo.apply({store:dataSource}, this.loadMask));
45638         }
45639         this.view.bind(dataSource, colModel);
45640         this.dataSource = dataSource;
45641         this.colModel = colModel;
45642         this.view.refresh(true);
45643     },
45644
45645     // private
45646     onKeyDown : function(e){
45647         this.fireEvent("keydown", e);
45648     },
45649
45650     /**
45651      * Destroy this grid.
45652      * @param {Boolean} removeEl True to remove the element
45653      */
45654     destroy : function(removeEl, keepListeners){
45655         if(this.loadMask){
45656             this.loadMask.destroy();
45657         }
45658         var c = this.container;
45659         c.removeAllListeners();
45660         this.view.destroy();
45661         this.colModel.purgeListeners();
45662         if(!keepListeners){
45663             this.purgeListeners();
45664         }
45665         c.update("");
45666         if(removeEl === true){
45667             c.remove();
45668         }
45669     },
45670
45671     // private
45672     processEvent : function(name, e){
45673         this.fireEvent(name, e);
45674         var t = e.getTarget();
45675         var v = this.view;
45676         var header = v.findHeaderIndex(t);
45677         if(header !== false){
45678             this.fireEvent("header" + name, this, header, e);
45679         }else{
45680             var row = v.findRowIndex(t);
45681             var cell = v.findCellIndex(t);
45682             if(row !== false){
45683                 this.fireEvent("row" + name, this, row, e);
45684                 if(cell !== false){
45685                     this.fireEvent("cell" + name, this, row, cell, e);
45686                 }
45687             }
45688         }
45689     },
45690
45691     // private
45692     onClick : function(e){
45693         this.processEvent("click", e);
45694     },
45695
45696     // private
45697     onContextMenu : function(e, t){
45698         this.processEvent("contextmenu", e);
45699     },
45700
45701     // private
45702     onDblClick : function(e){
45703         this.processEvent("dblclick", e);
45704     },
45705
45706     // private
45707     walkCells : function(row, col, step, fn, scope){
45708         var cm = this.colModel, clen = cm.getColumnCount();
45709         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45710         if(step < 0){
45711             if(col < 0){
45712                 row--;
45713                 first = false;
45714             }
45715             while(row >= 0){
45716                 if(!first){
45717                     col = clen-1;
45718                 }
45719                 first = false;
45720                 while(col >= 0){
45721                     if(fn.call(scope || this, row, col, cm) === true){
45722                         return [row, col];
45723                     }
45724                     col--;
45725                 }
45726                 row--;
45727             }
45728         } else {
45729             if(col >= clen){
45730                 row++;
45731                 first = false;
45732             }
45733             while(row < rlen){
45734                 if(!first){
45735                     col = 0;
45736                 }
45737                 first = false;
45738                 while(col < clen){
45739                     if(fn.call(scope || this, row, col, cm) === true){
45740                         return [row, col];
45741                     }
45742                     col++;
45743                 }
45744                 row++;
45745             }
45746         }
45747         return null;
45748     },
45749
45750     // private
45751     getSelections : function(){
45752         return this.selModel.getSelections();
45753     },
45754
45755     /**
45756      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45757      * but if manual update is required this method will initiate it.
45758      */
45759     autoSize : function(){
45760         if(this.rendered){
45761             this.view.layout();
45762             if(this.view.adjustForScroll){
45763                 this.view.adjustForScroll();
45764             }
45765         }
45766     },
45767
45768     /**
45769      * Returns the grid's underlying element.
45770      * @return {Element} The element
45771      */
45772     getGridEl : function(){
45773         return this.container;
45774     },
45775
45776     // private for compatibility, overridden by editor grid
45777     stopEditing : function(){},
45778
45779     /**
45780      * Returns the grid's SelectionModel.
45781      * @return {SelectionModel}
45782      */
45783     getSelectionModel : function(){
45784         if(!this.selModel){
45785             this.selModel = new Roo.grid.RowSelectionModel();
45786         }
45787         return this.selModel;
45788     },
45789
45790     /**
45791      * Returns the grid's DataSource.
45792      * @return {DataSource}
45793      */
45794     getDataSource : function(){
45795         return this.dataSource;
45796     },
45797
45798     /**
45799      * Returns the grid's ColumnModel.
45800      * @return {ColumnModel}
45801      */
45802     getColumnModel : function(){
45803         return this.colModel;
45804     },
45805
45806     /**
45807      * Returns the grid's GridView object.
45808      * @return {GridView}
45809      */
45810     getView : function(){
45811         if(!this.view){
45812             this.view = new Roo.grid.GridView(this.viewConfig);
45813         }
45814         return this.view;
45815     },
45816     /**
45817      * Called to get grid's drag proxy text, by default returns this.ddText.
45818      * @return {String}
45819      */
45820     getDragDropText : function(){
45821         var count = this.selModel.getCount();
45822         return String.format(this.ddText, count, count == 1 ? '' : 's');
45823     }
45824 });
45825 /**
45826  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45827  * %0 is replaced with the number of selected rows.
45828  * @type String
45829  */
45830 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45831  * Based on:
45832  * Ext JS Library 1.1.1
45833  * Copyright(c) 2006-2007, Ext JS, LLC.
45834  *
45835  * Originally Released Under LGPL - original licence link has changed is not relivant.
45836  *
45837  * Fork - LGPL
45838  * <script type="text/javascript">
45839  */
45840  
45841 Roo.grid.AbstractGridView = function(){
45842         this.grid = null;
45843         
45844         this.events = {
45845             "beforerowremoved" : true,
45846             "beforerowsinserted" : true,
45847             "beforerefresh" : true,
45848             "rowremoved" : true,
45849             "rowsinserted" : true,
45850             "rowupdated" : true,
45851             "refresh" : true
45852         };
45853     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45854 };
45855
45856 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45857     rowClass : "x-grid-row",
45858     cellClass : "x-grid-cell",
45859     tdClass : "x-grid-td",
45860     hdClass : "x-grid-hd",
45861     splitClass : "x-grid-hd-split",
45862     
45863         init: function(grid){
45864         this.grid = grid;
45865                 var cid = this.grid.getGridEl().id;
45866         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45867         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45868         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45869         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45870         },
45871         
45872         getColumnRenderers : function(){
45873         var renderers = [];
45874         var cm = this.grid.colModel;
45875         var colCount = cm.getColumnCount();
45876         for(var i = 0; i < colCount; i++){
45877             renderers[i] = cm.getRenderer(i);
45878         }
45879         return renderers;
45880     },
45881     
45882     getColumnIds : function(){
45883         var ids = [];
45884         var cm = this.grid.colModel;
45885         var colCount = cm.getColumnCount();
45886         for(var i = 0; i < colCount; i++){
45887             ids[i] = cm.getColumnId(i);
45888         }
45889         return ids;
45890     },
45891     
45892     getDataIndexes : function(){
45893         if(!this.indexMap){
45894             this.indexMap = this.buildIndexMap();
45895         }
45896         return this.indexMap.colToData;
45897     },
45898     
45899     getColumnIndexByDataIndex : function(dataIndex){
45900         if(!this.indexMap){
45901             this.indexMap = this.buildIndexMap();
45902         }
45903         return this.indexMap.dataToCol[dataIndex];
45904     },
45905     
45906     /**
45907      * Set a css style for a column dynamically. 
45908      * @param {Number} colIndex The index of the column
45909      * @param {String} name The css property name
45910      * @param {String} value The css value
45911      */
45912     setCSSStyle : function(colIndex, name, value){
45913         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45914         Roo.util.CSS.updateRule(selector, name, value);
45915     },
45916     
45917     generateRules : function(cm){
45918         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45919         Roo.util.CSS.removeStyleSheet(rulesId);
45920         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45921             var cid = cm.getColumnId(i);
45922             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45923                          this.tdSelector, cid, " {\n}\n",
45924                          this.hdSelector, cid, " {\n}\n",
45925                          this.splitSelector, cid, " {\n}\n");
45926         }
45927         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45928     }
45929 });/*
45930  * Based on:
45931  * Ext JS Library 1.1.1
45932  * Copyright(c) 2006-2007, Ext JS, LLC.
45933  *
45934  * Originally Released Under LGPL - original licence link has changed is not relivant.
45935  *
45936  * Fork - LGPL
45937  * <script type="text/javascript">
45938  */
45939
45940 // private
45941 // This is a support class used internally by the Grid components
45942 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45943     this.grid = grid;
45944     this.view = grid.getView();
45945     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45946     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45947     if(hd2){
45948         this.setHandleElId(Roo.id(hd));
45949         this.setOuterHandleElId(Roo.id(hd2));
45950     }
45951     this.scroll = false;
45952 };
45953 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45954     maxDragWidth: 120,
45955     getDragData : function(e){
45956         var t = Roo.lib.Event.getTarget(e);
45957         var h = this.view.findHeaderCell(t);
45958         if(h){
45959             return {ddel: h.firstChild, header:h};
45960         }
45961         return false;
45962     },
45963
45964     onInitDrag : function(e){
45965         this.view.headersDisabled = true;
45966         var clone = this.dragData.ddel.cloneNode(true);
45967         clone.id = Roo.id();
45968         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45969         this.proxy.update(clone);
45970         return true;
45971     },
45972
45973     afterValidDrop : function(){
45974         var v = this.view;
45975         setTimeout(function(){
45976             v.headersDisabled = false;
45977         }, 50);
45978     },
45979
45980     afterInvalidDrop : function(){
45981         var v = this.view;
45982         setTimeout(function(){
45983             v.headersDisabled = false;
45984         }, 50);
45985     }
45986 });
45987 /*
45988  * Based on:
45989  * Ext JS Library 1.1.1
45990  * Copyright(c) 2006-2007, Ext JS, LLC.
45991  *
45992  * Originally Released Under LGPL - original licence link has changed is not relivant.
45993  *
45994  * Fork - LGPL
45995  * <script type="text/javascript">
45996  */
45997 // private
45998 // This is a support class used internally by the Grid components
45999 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46000     this.grid = grid;
46001     this.view = grid.getView();
46002     // split the proxies so they don't interfere with mouse events
46003     this.proxyTop = Roo.DomHelper.append(document.body, {
46004         cls:"col-move-top", html:"&#160;"
46005     }, true);
46006     this.proxyBottom = Roo.DomHelper.append(document.body, {
46007         cls:"col-move-bottom", html:"&#160;"
46008     }, true);
46009     this.proxyTop.hide = this.proxyBottom.hide = function(){
46010         this.setLeftTop(-100,-100);
46011         this.setStyle("visibility", "hidden");
46012     };
46013     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46014     // temporarily disabled
46015     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46016     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46017 };
46018 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46019     proxyOffsets : [-4, -9],
46020     fly: Roo.Element.fly,
46021
46022     getTargetFromEvent : function(e){
46023         var t = Roo.lib.Event.getTarget(e);
46024         var cindex = this.view.findCellIndex(t);
46025         if(cindex !== false){
46026             return this.view.getHeaderCell(cindex);
46027         }
46028     },
46029
46030     nextVisible : function(h){
46031         var v = this.view, cm = this.grid.colModel;
46032         h = h.nextSibling;
46033         while(h){
46034             if(!cm.isHidden(v.getCellIndex(h))){
46035                 return h;
46036             }
46037             h = h.nextSibling;
46038         }
46039         return null;
46040     },
46041
46042     prevVisible : function(h){
46043         var v = this.view, cm = this.grid.colModel;
46044         h = h.prevSibling;
46045         while(h){
46046             if(!cm.isHidden(v.getCellIndex(h))){
46047                 return h;
46048             }
46049             h = h.prevSibling;
46050         }
46051         return null;
46052     },
46053
46054     positionIndicator : function(h, n, e){
46055         var x = Roo.lib.Event.getPageX(e);
46056         var r = Roo.lib.Dom.getRegion(n.firstChild);
46057         var px, pt, py = r.top + this.proxyOffsets[1];
46058         if((r.right - x) <= (r.right-r.left)/2){
46059             px = r.right+this.view.borderWidth;
46060             pt = "after";
46061         }else{
46062             px = r.left;
46063             pt = "before";
46064         }
46065         var oldIndex = this.view.getCellIndex(h);
46066         var newIndex = this.view.getCellIndex(n);
46067
46068         if(this.grid.colModel.isFixed(newIndex)){
46069             return false;
46070         }
46071
46072         var locked = this.grid.colModel.isLocked(newIndex);
46073
46074         if(pt == "after"){
46075             newIndex++;
46076         }
46077         if(oldIndex < newIndex){
46078             newIndex--;
46079         }
46080         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46081             return false;
46082         }
46083         px +=  this.proxyOffsets[0];
46084         this.proxyTop.setLeftTop(px, py);
46085         this.proxyTop.show();
46086         if(!this.bottomOffset){
46087             this.bottomOffset = this.view.mainHd.getHeight();
46088         }
46089         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46090         this.proxyBottom.show();
46091         return pt;
46092     },
46093
46094     onNodeEnter : function(n, dd, e, data){
46095         if(data.header != n){
46096             this.positionIndicator(data.header, n, e);
46097         }
46098     },
46099
46100     onNodeOver : function(n, dd, e, data){
46101         var result = false;
46102         if(data.header != n){
46103             result = this.positionIndicator(data.header, n, e);
46104         }
46105         if(!result){
46106             this.proxyTop.hide();
46107             this.proxyBottom.hide();
46108         }
46109         return result ? this.dropAllowed : this.dropNotAllowed;
46110     },
46111
46112     onNodeOut : function(n, dd, e, data){
46113         this.proxyTop.hide();
46114         this.proxyBottom.hide();
46115     },
46116
46117     onNodeDrop : function(n, dd, e, data){
46118         var h = data.header;
46119         if(h != n){
46120             var cm = this.grid.colModel;
46121             var x = Roo.lib.Event.getPageX(e);
46122             var r = Roo.lib.Dom.getRegion(n.firstChild);
46123             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46124             var oldIndex = this.view.getCellIndex(h);
46125             var newIndex = this.view.getCellIndex(n);
46126             var locked = cm.isLocked(newIndex);
46127             if(pt == "after"){
46128                 newIndex++;
46129             }
46130             if(oldIndex < newIndex){
46131                 newIndex--;
46132             }
46133             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46134                 return false;
46135             }
46136             cm.setLocked(oldIndex, locked, true);
46137             cm.moveColumn(oldIndex, newIndex);
46138             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46139             return true;
46140         }
46141         return false;
46142     }
46143 });
46144 /*
46145  * Based on:
46146  * Ext JS Library 1.1.1
46147  * Copyright(c) 2006-2007, Ext JS, LLC.
46148  *
46149  * Originally Released Under LGPL - original licence link has changed is not relivant.
46150  *
46151  * Fork - LGPL
46152  * <script type="text/javascript">
46153  */
46154   
46155 /**
46156  * @class Roo.grid.GridView
46157  * @extends Roo.util.Observable
46158  *
46159  * @constructor
46160  * @param {Object} config
46161  */
46162 Roo.grid.GridView = function(config){
46163     Roo.grid.GridView.superclass.constructor.call(this);
46164     this.el = null;
46165
46166     Roo.apply(this, config);
46167 };
46168
46169 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46170
46171     /**
46172      * Override this function to apply custom css classes to rows during rendering
46173      * @param {Record} record The record
46174      * @param {Number} index
46175      * @method getRowClass
46176      */
46177     rowClass : "x-grid-row",
46178
46179     cellClass : "x-grid-col",
46180
46181     tdClass : "x-grid-td",
46182
46183     hdClass : "x-grid-hd",
46184
46185     splitClass : "x-grid-split",
46186
46187     sortClasses : ["sort-asc", "sort-desc"],
46188
46189     enableMoveAnim : false,
46190
46191     hlColor: "C3DAF9",
46192
46193     dh : Roo.DomHelper,
46194
46195     fly : Roo.Element.fly,
46196
46197     css : Roo.util.CSS,
46198
46199     borderWidth: 1,
46200
46201     splitOffset: 3,
46202
46203     scrollIncrement : 22,
46204
46205     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46206
46207     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46208
46209     bind : function(ds, cm){
46210         if(this.ds){
46211             this.ds.un("load", this.onLoad, this);
46212             this.ds.un("datachanged", this.onDataChange, this);
46213             this.ds.un("add", this.onAdd, this);
46214             this.ds.un("remove", this.onRemove, this);
46215             this.ds.un("update", this.onUpdate, this);
46216             this.ds.un("clear", this.onClear, this);
46217         }
46218         if(ds){
46219             ds.on("load", this.onLoad, this);
46220             ds.on("datachanged", this.onDataChange, this);
46221             ds.on("add", this.onAdd, this);
46222             ds.on("remove", this.onRemove, this);
46223             ds.on("update", this.onUpdate, this);
46224             ds.on("clear", this.onClear, this);
46225         }
46226         this.ds = ds;
46227
46228         if(this.cm){
46229             this.cm.un("widthchange", this.onColWidthChange, this);
46230             this.cm.un("headerchange", this.onHeaderChange, this);
46231             this.cm.un("hiddenchange", this.onHiddenChange, this);
46232             this.cm.un("columnmoved", this.onColumnMove, this);
46233             this.cm.un("columnlockchange", this.onColumnLock, this);
46234         }
46235         if(cm){
46236             this.generateRules(cm);
46237             cm.on("widthchange", this.onColWidthChange, this);
46238             cm.on("headerchange", this.onHeaderChange, this);
46239             cm.on("hiddenchange", this.onHiddenChange, this);
46240             cm.on("columnmoved", this.onColumnMove, this);
46241             cm.on("columnlockchange", this.onColumnLock, this);
46242         }
46243         this.cm = cm;
46244     },
46245
46246     init: function(grid){
46247                 Roo.grid.GridView.superclass.init.call(this, grid);
46248
46249                 this.bind(grid.dataSource, grid.colModel);
46250
46251             grid.on("headerclick", this.handleHeaderClick, this);
46252
46253         if(grid.trackMouseOver){
46254             grid.on("mouseover", this.onRowOver, this);
46255                 grid.on("mouseout", this.onRowOut, this);
46256             }
46257             grid.cancelTextSelection = function(){};
46258                 this.gridId = grid.id;
46259
46260                 var tpls = this.templates || {};
46261
46262                 if(!tpls.master){
46263                     tpls.master = new Roo.Template(
46264                        '<div class="x-grid" hidefocus="true">',
46265                           '<div class="x-grid-topbar"></div>',
46266                           '<div class="x-grid-scroller"><div></div></div>',
46267                           '<div class="x-grid-locked">',
46268                               '<div class="x-grid-header">{lockedHeader}</div>',
46269                               '<div class="x-grid-body">{lockedBody}</div>',
46270                           "</div>",
46271                           '<div class="x-grid-viewport">',
46272                               '<div class="x-grid-header">{header}</div>',
46273                               '<div class="x-grid-body">{body}</div>',
46274                           "</div>",
46275                           '<div class="x-grid-bottombar"></div>',
46276                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46277                           '<div class="x-grid-resize-proxy">&#160;</div>',
46278                        "</div>"
46279                     );
46280                     tpls.master.disableformats = true;
46281                 }
46282
46283                 if(!tpls.header){
46284                     tpls.header = new Roo.Template(
46285                        '<table border="0" cellspacing="0" cellpadding="0">',
46286                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46287                        "</table>{splits}"
46288                     );
46289                     tpls.header.disableformats = true;
46290                 }
46291                 tpls.header.compile();
46292
46293                 if(!tpls.hcell){
46294                     tpls.hcell = new Roo.Template(
46295                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46296                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46297                         "</div></td>"
46298                      );
46299                      tpls.hcell.disableFormats = true;
46300                 }
46301                 tpls.hcell.compile();
46302
46303                 if(!tpls.hsplit){
46304                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46305                     tpls.hsplit.disableFormats = true;
46306                 }
46307                 tpls.hsplit.compile();
46308
46309                 if(!tpls.body){
46310                     tpls.body = new Roo.Template(
46311                        '<table border="0" cellspacing="0" cellpadding="0">',
46312                        "<tbody>{rows}</tbody>",
46313                        "</table>"
46314                     );
46315                     tpls.body.disableFormats = true;
46316                 }
46317                 tpls.body.compile();
46318
46319                 if(!tpls.row){
46320                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46321                     tpls.row.disableFormats = true;
46322                 }
46323                 tpls.row.compile();
46324
46325                 if(!tpls.cell){
46326                     tpls.cell = new Roo.Template(
46327                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46328                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46329                         "</td>"
46330                     );
46331             tpls.cell.disableFormats = true;
46332         }
46333                 tpls.cell.compile();
46334
46335                 this.templates = tpls;
46336         },
46337
46338         // remap these for backwards compat
46339     onColWidthChange : function(){
46340         this.updateColumns.apply(this, arguments);
46341     },
46342     onHeaderChange : function(){
46343         this.updateHeaders.apply(this, arguments);
46344     }, 
46345     onHiddenChange : function(){
46346         this.handleHiddenChange.apply(this, arguments);
46347     },
46348     onColumnMove : function(){
46349         this.handleColumnMove.apply(this, arguments);
46350     },
46351     onColumnLock : function(){
46352         this.handleLockChange.apply(this, arguments);
46353     },
46354
46355     onDataChange : function(){
46356         this.refresh();
46357         this.updateHeaderSortState();
46358     },
46359
46360         onClear : function(){
46361         this.refresh();
46362     },
46363
46364         onUpdate : function(ds, record){
46365         this.refreshRow(record);
46366     },
46367
46368     refreshRow : function(record){
46369         var ds = this.ds, index;
46370         if(typeof record == 'number'){
46371             index = record;
46372             record = ds.getAt(index);
46373         }else{
46374             index = ds.indexOf(record);
46375         }
46376         this.insertRows(ds, index, index, true);
46377         this.onRemove(ds, record, index+1, true);
46378         this.syncRowHeights(index, index);
46379         this.layout();
46380         this.fireEvent("rowupdated", this, index, record);
46381     },
46382
46383     onAdd : function(ds, records, index){
46384         this.insertRows(ds, index, index + (records.length-1));
46385     },
46386
46387     onRemove : function(ds, record, index, isUpdate){
46388         if(isUpdate !== true){
46389             this.fireEvent("beforerowremoved", this, index, record);
46390         }
46391         var bt = this.getBodyTable(), lt = this.getLockedTable();
46392         if(bt.rows[index]){
46393             bt.firstChild.removeChild(bt.rows[index]);
46394         }
46395         if(lt.rows[index]){
46396             lt.firstChild.removeChild(lt.rows[index]);
46397         }
46398         if(isUpdate !== true){
46399             this.stripeRows(index);
46400             this.syncRowHeights(index, index);
46401             this.layout();
46402             this.fireEvent("rowremoved", this, index, record);
46403         }
46404     },
46405
46406     onLoad : function(){
46407         this.scrollToTop();
46408     },
46409
46410     /**
46411      * Scrolls the grid to the top
46412      */
46413     scrollToTop : function(){
46414         if(this.scroller){
46415             this.scroller.dom.scrollTop = 0;
46416             this.syncScroll();
46417         }
46418     },
46419
46420     /**
46421      * Gets a panel in the header of the grid that can be used for toolbars etc.
46422      * After modifying the contents of this panel a call to grid.autoSize() may be
46423      * required to register any changes in size.
46424      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46425      * @return Roo.Element
46426      */
46427     getHeaderPanel : function(doShow){
46428         if(doShow){
46429             this.headerPanel.show();
46430         }
46431         return this.headerPanel;
46432         },
46433
46434         /**
46435      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46436      * After modifying the contents of this panel a call to grid.autoSize() may be
46437      * required to register any changes in size.
46438      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46439      * @return Roo.Element
46440      */
46441     getFooterPanel : function(doShow){
46442         if(doShow){
46443             this.footerPanel.show();
46444         }
46445         return this.footerPanel;
46446         },
46447
46448         initElements : function(){
46449             var E = Roo.Element;
46450             var el = this.grid.getGridEl().dom.firstChild;
46451             var cs = el.childNodes;
46452
46453             this.el = new E(el);
46454             this.headerPanel = new E(el.firstChild);
46455             this.headerPanel.enableDisplayMode("block");
46456
46457         this.scroller = new E(cs[1]);
46458             this.scrollSizer = new E(this.scroller.dom.firstChild);
46459
46460             this.lockedWrap = new E(cs[2]);
46461             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46462             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46463
46464             this.mainWrap = new E(cs[3]);
46465             this.mainHd = new E(this.mainWrap.dom.firstChild);
46466             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46467
46468             this.footerPanel = new E(cs[4]);
46469             this.footerPanel.enableDisplayMode("block");
46470
46471         this.focusEl = new E(cs[5]);
46472         this.focusEl.swallowEvent("click", true);
46473         this.resizeProxy = new E(cs[6]);
46474
46475             this.headerSelector = String.format(
46476                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46477                this.lockedHd.id, this.mainHd.id
46478             );
46479
46480             this.splitterSelector = String.format(
46481                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46482                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46483             );
46484     },
46485     idToCssName : function(s)
46486     {
46487         return s.replace(/[^a-z0-9]+/ig, '-');
46488     },
46489
46490         getHeaderCell : function(index){
46491             return Roo.DomQuery.select(this.headerSelector)[index];
46492         },
46493
46494         getHeaderCellMeasure : function(index){
46495             return this.getHeaderCell(index).firstChild;
46496         },
46497
46498         getHeaderCellText : function(index){
46499             return this.getHeaderCell(index).firstChild.firstChild;
46500         },
46501
46502         getLockedTable : function(){
46503             return this.lockedBody.dom.firstChild;
46504         },
46505
46506         getBodyTable : function(){
46507             return this.mainBody.dom.firstChild;
46508         },
46509
46510         getLockedRow : function(index){
46511             return this.getLockedTable().rows[index];
46512         },
46513
46514         getRow : function(index){
46515             return this.getBodyTable().rows[index];
46516         },
46517
46518         getRowComposite : function(index){
46519             if(!this.rowEl){
46520                 this.rowEl = new Roo.CompositeElementLite();
46521             }
46522         var els = [], lrow, mrow;
46523         if(lrow = this.getLockedRow(index)){
46524             els.push(lrow);
46525         }
46526         if(mrow = this.getRow(index)){
46527             els.push(mrow);
46528         }
46529         this.rowEl.elements = els;
46530             return this.rowEl;
46531         },
46532
46533         getCell : function(rowIndex, colIndex){
46534             var locked = this.cm.getLockedCount();
46535             var source;
46536             if(colIndex < locked){
46537                 source = this.lockedBody.dom.firstChild;
46538             }else{
46539                 source = this.mainBody.dom.firstChild;
46540                 colIndex -= locked;
46541             }
46542         return source.rows[rowIndex].childNodes[colIndex];
46543         },
46544
46545         getCellText : function(rowIndex, colIndex){
46546             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46547         },
46548
46549         getCellBox : function(cell){
46550             var b = this.fly(cell).getBox();
46551         if(Roo.isOpera){ // opera fails to report the Y
46552             b.y = cell.offsetTop + this.mainBody.getY();
46553         }
46554         return b;
46555     },
46556
46557     getCellIndex : function(cell){
46558         var id = String(cell.className).match(this.cellRE);
46559         if(id){
46560             return parseInt(id[1], 10);
46561         }
46562         return 0;
46563     },
46564
46565     findHeaderIndex : function(n){
46566         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46567         return r ? this.getCellIndex(r) : false;
46568     },
46569
46570     findHeaderCell : function(n){
46571         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46572         return r ? r : false;
46573     },
46574
46575     findRowIndex : function(n){
46576         if(!n){
46577             return false;
46578         }
46579         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46580         return r ? r.rowIndex : false;
46581     },
46582
46583     findCellIndex : function(node){
46584         var stop = this.el.dom;
46585         while(node && node != stop){
46586             if(this.findRE.test(node.className)){
46587                 return this.getCellIndex(node);
46588             }
46589             node = node.parentNode;
46590         }
46591         return false;
46592     },
46593
46594     getColumnId : function(index){
46595             return this.cm.getColumnId(index);
46596         },
46597
46598         getSplitters : function(){
46599             if(this.splitterSelector){
46600                return Roo.DomQuery.select(this.splitterSelector);
46601             }else{
46602                 return null;
46603             }
46604         },
46605
46606         getSplitter : function(index){
46607             return this.getSplitters()[index];
46608         },
46609
46610     onRowOver : function(e, t){
46611         var row;
46612         if((row = this.findRowIndex(t)) !== false){
46613             this.getRowComposite(row).addClass("x-grid-row-over");
46614         }
46615     },
46616
46617     onRowOut : function(e, t){
46618         var row;
46619         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46620             this.getRowComposite(row).removeClass("x-grid-row-over");
46621         }
46622     },
46623
46624     renderHeaders : function(){
46625             var cm = this.cm;
46626         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46627         var cb = [], lb = [], sb = [], lsb = [], p = {};
46628         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46629             p.cellId = "x-grid-hd-0-" + i;
46630             p.splitId = "x-grid-csplit-0-" + i;
46631             p.id = cm.getColumnId(i);
46632             p.title = cm.getColumnTooltip(i) || "";
46633             p.value = cm.getColumnHeader(i) || "";
46634             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46635             if(!cm.isLocked(i)){
46636                 cb[cb.length] = ct.apply(p);
46637                 sb[sb.length] = st.apply(p);
46638             }else{
46639                 lb[lb.length] = ct.apply(p);
46640                 lsb[lsb.length] = st.apply(p);
46641             }
46642         }
46643         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46644                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46645         },
46646
46647         updateHeaders : function(){
46648         var html = this.renderHeaders();
46649         this.lockedHd.update(html[0]);
46650         this.mainHd.update(html[1]);
46651     },
46652
46653     /**
46654      * Focuses the specified row.
46655      * @param {Number} row The row index
46656      */
46657     focusRow : function(row){
46658         var x = this.scroller.dom.scrollLeft;
46659         this.focusCell(row, 0, false);
46660         this.scroller.dom.scrollLeft = x;
46661     },
46662
46663     /**
46664      * Focuses the specified cell.
46665      * @param {Number} row The row index
46666      * @param {Number} col The column index
46667      * @param {Boolean} hscroll false to disable horizontal scrolling
46668      */
46669     focusCell : function(row, col, hscroll){
46670         var el = this.ensureVisible(row, col, hscroll);
46671         this.focusEl.alignTo(el, "tl-tl");
46672         if(Roo.isGecko){
46673             this.focusEl.focus();
46674         }else{
46675             this.focusEl.focus.defer(1, this.focusEl);
46676         }
46677     },
46678
46679     /**
46680      * Scrolls the specified cell into view
46681      * @param {Number} row The row index
46682      * @param {Number} col The column index
46683      * @param {Boolean} hscroll false to disable horizontal scrolling
46684      */
46685     ensureVisible : function(row, col, hscroll){
46686         if(typeof row != "number"){
46687             row = row.rowIndex;
46688         }
46689         if(row < 0 && row >= this.ds.getCount()){
46690             return;
46691         }
46692         col = (col !== undefined ? col : 0);
46693         var cm = this.grid.colModel;
46694         while(cm.isHidden(col)){
46695             col++;
46696         }
46697
46698         var el = this.getCell(row, col);
46699         if(!el){
46700             return;
46701         }
46702         var c = this.scroller.dom;
46703
46704         var ctop = parseInt(el.offsetTop, 10);
46705         var cleft = parseInt(el.offsetLeft, 10);
46706         var cbot = ctop + el.offsetHeight;
46707         var cright = cleft + el.offsetWidth;
46708
46709         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46710         var stop = parseInt(c.scrollTop, 10);
46711         var sleft = parseInt(c.scrollLeft, 10);
46712         var sbot = stop + ch;
46713         var sright = sleft + c.clientWidth;
46714
46715         if(ctop < stop){
46716                 c.scrollTop = ctop;
46717         }else if(cbot > sbot){
46718             c.scrollTop = cbot-ch;
46719         }
46720
46721         if(hscroll !== false){
46722             if(cleft < sleft){
46723                 c.scrollLeft = cleft;
46724             }else if(cright > sright){
46725                 c.scrollLeft = cright-c.clientWidth;
46726             }
46727         }
46728         return el;
46729     },
46730
46731     updateColumns : function(){
46732         this.grid.stopEditing();
46733         var cm = this.grid.colModel, colIds = this.getColumnIds();
46734         //var totalWidth = cm.getTotalWidth();
46735         var pos = 0;
46736         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46737             //if(cm.isHidden(i)) continue;
46738             var w = cm.getColumnWidth(i);
46739             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46740             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46741         }
46742         this.updateSplitters();
46743     },
46744
46745     generateRules : function(cm){
46746         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46747         Roo.util.CSS.removeStyleSheet(rulesId);
46748         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46749             var cid = cm.getColumnId(i);
46750             var align = '';
46751             if(cm.config[i].align){
46752                 align = 'text-align:'+cm.config[i].align+';';
46753             }
46754             var hidden = '';
46755             if(cm.isHidden(i)){
46756                 hidden = 'display:none;';
46757             }
46758             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46759             ruleBuf.push(
46760                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46761                     this.hdSelector, cid, " {\n", align, width, "}\n",
46762                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46763                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46764         }
46765         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46766     },
46767
46768     updateSplitters : function(){
46769         var cm = this.cm, s = this.getSplitters();
46770         if(s){ // splitters not created yet
46771             var pos = 0, locked = true;
46772             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46773                 if(cm.isHidden(i)) continue;
46774                 var w = cm.getColumnWidth(i);
46775                 if(!cm.isLocked(i) && locked){
46776                     pos = 0;
46777                     locked = false;
46778                 }
46779                 pos += w;
46780                 s[i].style.left = (pos-this.splitOffset) + "px";
46781             }
46782         }
46783     },
46784
46785     handleHiddenChange : function(colModel, colIndex, hidden){
46786         if(hidden){
46787             this.hideColumn(colIndex);
46788         }else{
46789             this.unhideColumn(colIndex);
46790         }
46791     },
46792
46793     hideColumn : function(colIndex){
46794         var cid = this.getColumnId(colIndex);
46795         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46796         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46797         if(Roo.isSafari){
46798             this.updateHeaders();
46799         }
46800         this.updateSplitters();
46801         this.layout();
46802     },
46803
46804     unhideColumn : function(colIndex){
46805         var cid = this.getColumnId(colIndex);
46806         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46807         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46808
46809         if(Roo.isSafari){
46810             this.updateHeaders();
46811         }
46812         this.updateSplitters();
46813         this.layout();
46814     },
46815
46816     insertRows : function(dm, firstRow, lastRow, isUpdate){
46817         if(firstRow == 0 && lastRow == dm.getCount()-1){
46818             this.refresh();
46819         }else{
46820             if(!isUpdate){
46821                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46822             }
46823             var s = this.getScrollState();
46824             var markup = this.renderRows(firstRow, lastRow);
46825             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46826             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46827             this.restoreScroll(s);
46828             if(!isUpdate){
46829                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46830                 this.syncRowHeights(firstRow, lastRow);
46831                 this.stripeRows(firstRow);
46832                 this.layout();
46833             }
46834         }
46835     },
46836
46837     bufferRows : function(markup, target, index){
46838         var before = null, trows = target.rows, tbody = target.tBodies[0];
46839         if(index < trows.length){
46840             before = trows[index];
46841         }
46842         var b = document.createElement("div");
46843         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46844         var rows = b.firstChild.rows;
46845         for(var i = 0, len = rows.length; i < len; i++){
46846             if(before){
46847                 tbody.insertBefore(rows[0], before);
46848             }else{
46849                 tbody.appendChild(rows[0]);
46850             }
46851         }
46852         b.innerHTML = "";
46853         b = null;
46854     },
46855
46856     deleteRows : function(dm, firstRow, lastRow){
46857         if(dm.getRowCount()<1){
46858             this.fireEvent("beforerefresh", this);
46859             this.mainBody.update("");
46860             this.lockedBody.update("");
46861             this.fireEvent("refresh", this);
46862         }else{
46863             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46864             var bt = this.getBodyTable();
46865             var tbody = bt.firstChild;
46866             var rows = bt.rows;
46867             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46868                 tbody.removeChild(rows[firstRow]);
46869             }
46870             this.stripeRows(firstRow);
46871             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46872         }
46873     },
46874
46875     updateRows : function(dataSource, firstRow, lastRow){
46876         var s = this.getScrollState();
46877         this.refresh();
46878         this.restoreScroll(s);
46879     },
46880
46881     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46882         if(!noRefresh){
46883            this.refresh();
46884         }
46885         this.updateHeaderSortState();
46886     },
46887
46888     getScrollState : function(){
46889         var sb = this.scroller.dom;
46890         return {left: sb.scrollLeft, top: sb.scrollTop};
46891     },
46892
46893     stripeRows : function(startRow){
46894         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46895             return;
46896         }
46897         startRow = startRow || 0;
46898         var rows = this.getBodyTable().rows;
46899         var lrows = this.getLockedTable().rows;
46900         var cls = ' x-grid-row-alt ';
46901         for(var i = startRow, len = rows.length; i < len; i++){
46902             var row = rows[i], lrow = lrows[i];
46903             var isAlt = ((i+1) % 2 == 0);
46904             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46905             if(isAlt == hasAlt){
46906                 continue;
46907             }
46908             if(isAlt){
46909                 row.className += " x-grid-row-alt";
46910             }else{
46911                 row.className = row.className.replace("x-grid-row-alt", "");
46912             }
46913             if(lrow){
46914                 lrow.className = row.className;
46915             }
46916         }
46917     },
46918
46919     restoreScroll : function(state){
46920         var sb = this.scroller.dom;
46921         sb.scrollLeft = state.left;
46922         sb.scrollTop = state.top;
46923         this.syncScroll();
46924     },
46925
46926     syncScroll : function(){
46927         var sb = this.scroller.dom;
46928         var sh = this.mainHd.dom;
46929         var bs = this.mainBody.dom;
46930         var lv = this.lockedBody.dom;
46931         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46932         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46933     },
46934
46935     handleScroll : function(e){
46936         this.syncScroll();
46937         var sb = this.scroller.dom;
46938         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46939         e.stopEvent();
46940     },
46941
46942     handleWheel : function(e){
46943         var d = e.getWheelDelta();
46944         this.scroller.dom.scrollTop -= d*22;
46945         // set this here to prevent jumpy scrolling on large tables
46946         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46947         e.stopEvent();
46948     },
46949
46950     renderRows : function(startRow, endRow){
46951         // pull in all the crap needed to render rows
46952         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46953         var colCount = cm.getColumnCount();
46954
46955         if(ds.getCount() < 1){
46956             return ["", ""];
46957         }
46958
46959         // build a map for all the columns
46960         var cs = [];
46961         for(var i = 0; i < colCount; i++){
46962             var name = cm.getDataIndex(i);
46963             cs[i] = {
46964                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46965                 renderer : cm.getRenderer(i),
46966                 id : cm.getColumnId(i),
46967                 locked : cm.isLocked(i)
46968             };
46969         }
46970
46971         startRow = startRow || 0;
46972         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46973
46974         // records to render
46975         var rs = ds.getRange(startRow, endRow);
46976
46977         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46978     },
46979
46980     // As much as I hate to duplicate code, this was branched because FireFox really hates
46981     // [].join("") on strings. The performance difference was substantial enough to
46982     // branch this function
46983     doRender : Roo.isGecko ?
46984             function(cs, rs, ds, startRow, colCount, stripe){
46985                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46986                 // buffers
46987                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46988                 for(var j = 0, len = rs.length; j < len; j++){
46989                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46990                     for(var i = 0; i < colCount; i++){
46991                         c = cs[i];
46992                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46993                         p.id = c.id;
46994                         p.css = p.attr = "";
46995                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46996                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46997                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46998                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46999                         }
47000                         var markup = ct.apply(p);
47001                         if(!c.locked){
47002                             cb+= markup;
47003                         }else{
47004                             lcb+= markup;
47005                         }
47006                     }
47007                     var alt = [];
47008                     if(stripe && ((rowIndex+1) % 2 == 0)){
47009                         alt[0] = "x-grid-row-alt";
47010                     }
47011                     if(r.dirty){
47012                         alt[1] = " x-grid-dirty-row";
47013                     }
47014                     rp.cells = lcb;
47015                     if(this.getRowClass){
47016                         alt[2] = this.getRowClass(r, rowIndex);
47017                     }
47018                     rp.alt = alt.join(" ");
47019                     lbuf+= rt.apply(rp);
47020                     rp.cells = cb;
47021                     buf+=  rt.apply(rp);
47022                 }
47023                 return [lbuf, buf];
47024             } :
47025             function(cs, rs, ds, startRow, colCount, stripe){
47026                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47027                 // buffers
47028                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47029                 for(var j = 0, len = rs.length; j < len; j++){
47030                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47031                     for(var i = 0; i < colCount; i++){
47032                         c = cs[i];
47033                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47034                         p.id = c.id;
47035                         p.css = p.attr = "";
47036                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47037                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47038                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47039                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47040                         }
47041                         var markup = ct.apply(p);
47042                         if(!c.locked){
47043                             cb[cb.length] = markup;
47044                         }else{
47045                             lcb[lcb.length] = markup;
47046                         }
47047                     }
47048                     var alt = [];
47049                     if(stripe && ((rowIndex+1) % 2 == 0)){
47050                         alt[0] = "x-grid-row-alt";
47051                     }
47052                     if(r.dirty){
47053                         alt[1] = " x-grid-dirty-row";
47054                     }
47055                     rp.cells = lcb;
47056                     if(this.getRowClass){
47057                         alt[2] = this.getRowClass(r, rowIndex);
47058                     }
47059                     rp.alt = alt.join(" ");
47060                     rp.cells = lcb.join("");
47061                     lbuf[lbuf.length] = rt.apply(rp);
47062                     rp.cells = cb.join("");
47063                     buf[buf.length] =  rt.apply(rp);
47064                 }
47065                 return [lbuf.join(""), buf.join("")];
47066             },
47067
47068     renderBody : function(){
47069         var markup = this.renderRows();
47070         var bt = this.templates.body;
47071         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47072     },
47073
47074     /**
47075      * Refreshes the grid
47076      * @param {Boolean} headersToo
47077      */
47078     refresh : function(headersToo){
47079         this.fireEvent("beforerefresh", this);
47080         this.grid.stopEditing();
47081         var result = this.renderBody();
47082         this.lockedBody.update(result[0]);
47083         this.mainBody.update(result[1]);
47084         if(headersToo === true){
47085             this.updateHeaders();
47086             this.updateColumns();
47087             this.updateSplitters();
47088             this.updateHeaderSortState();
47089         }
47090         this.syncRowHeights();
47091         this.layout();
47092         this.fireEvent("refresh", this);
47093     },
47094
47095     handleColumnMove : function(cm, oldIndex, newIndex){
47096         this.indexMap = null;
47097         var s = this.getScrollState();
47098         this.refresh(true);
47099         this.restoreScroll(s);
47100         this.afterMove(newIndex);
47101     },
47102
47103     afterMove : function(colIndex){
47104         if(this.enableMoveAnim && Roo.enableFx){
47105             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47106         }
47107     },
47108
47109     updateCell : function(dm, rowIndex, dataIndex){
47110         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47111         if(typeof colIndex == "undefined"){ // not present in grid
47112             return;
47113         }
47114         var cm = this.grid.colModel;
47115         var cell = this.getCell(rowIndex, colIndex);
47116         var cellText = this.getCellText(rowIndex, colIndex);
47117
47118         var p = {
47119             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47120             id : cm.getColumnId(colIndex),
47121             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47122         };
47123         var renderer = cm.getRenderer(colIndex);
47124         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47125         if(typeof val == "undefined" || val === "") val = "&#160;";
47126         cellText.innerHTML = val;
47127         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47128         this.syncRowHeights(rowIndex, rowIndex);
47129     },
47130
47131     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47132         var maxWidth = 0;
47133         if(this.grid.autoSizeHeaders){
47134             var h = this.getHeaderCellMeasure(colIndex);
47135             maxWidth = Math.max(maxWidth, h.scrollWidth);
47136         }
47137         var tb, index;
47138         if(this.cm.isLocked(colIndex)){
47139             tb = this.getLockedTable();
47140             index = colIndex;
47141         }else{
47142             tb = this.getBodyTable();
47143             index = colIndex - this.cm.getLockedCount();
47144         }
47145         if(tb && tb.rows){
47146             var rows = tb.rows;
47147             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47148             for(var i = 0; i < stopIndex; i++){
47149                 var cell = rows[i].childNodes[index].firstChild;
47150                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47151             }
47152         }
47153         return maxWidth + /*margin for error in IE*/ 5;
47154     },
47155     /**
47156      * Autofit a column to its content.
47157      * @param {Number} colIndex
47158      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47159      */
47160      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47161          if(this.cm.isHidden(colIndex)){
47162              return; // can't calc a hidden column
47163          }
47164         if(forceMinSize){
47165             var cid = this.cm.getColumnId(colIndex);
47166             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47167            if(this.grid.autoSizeHeaders){
47168                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47169            }
47170         }
47171         var newWidth = this.calcColumnWidth(colIndex);
47172         this.cm.setColumnWidth(colIndex,
47173             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47174         if(!suppressEvent){
47175             this.grid.fireEvent("columnresize", colIndex, newWidth);
47176         }
47177     },
47178
47179     /**
47180      * Autofits all columns to their content and then expands to fit any extra space in the grid
47181      */
47182      autoSizeColumns : function(){
47183         var cm = this.grid.colModel;
47184         var colCount = cm.getColumnCount();
47185         for(var i = 0; i < colCount; i++){
47186             this.autoSizeColumn(i, true, true);
47187         }
47188         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47189             this.fitColumns();
47190         }else{
47191             this.updateColumns();
47192             this.layout();
47193         }
47194     },
47195
47196     /**
47197      * Autofits all columns to the grid's width proportionate with their current size
47198      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47199      */
47200     fitColumns : function(reserveScrollSpace){
47201         var cm = this.grid.colModel;
47202         var colCount = cm.getColumnCount();
47203         var cols = [];
47204         var width = 0;
47205         var i, w;
47206         for (i = 0; i < colCount; i++){
47207             if(!cm.isHidden(i) && !cm.isFixed(i)){
47208                 w = cm.getColumnWidth(i);
47209                 cols.push(i);
47210                 cols.push(w);
47211                 width += w;
47212             }
47213         }
47214         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47215         if(reserveScrollSpace){
47216             avail -= 17;
47217         }
47218         var frac = (avail - cm.getTotalWidth())/width;
47219         while (cols.length){
47220             w = cols.pop();
47221             i = cols.pop();
47222             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47223         }
47224         this.updateColumns();
47225         this.layout();
47226     },
47227
47228     onRowSelect : function(rowIndex){
47229         var row = this.getRowComposite(rowIndex);
47230         row.addClass("x-grid-row-selected");
47231     },
47232
47233     onRowDeselect : function(rowIndex){
47234         var row = this.getRowComposite(rowIndex);
47235         row.removeClass("x-grid-row-selected");
47236     },
47237
47238     onCellSelect : function(row, col){
47239         var cell = this.getCell(row, col);
47240         if(cell){
47241             Roo.fly(cell).addClass("x-grid-cell-selected");
47242         }
47243     },
47244
47245     onCellDeselect : function(row, col){
47246         var cell = this.getCell(row, col);
47247         if(cell){
47248             Roo.fly(cell).removeClass("x-grid-cell-selected");
47249         }
47250     },
47251
47252     updateHeaderSortState : function(){
47253         var state = this.ds.getSortState();
47254         if(!state){
47255             return;
47256         }
47257         this.sortState = state;
47258         var sortColumn = this.cm.findColumnIndex(state.field);
47259         if(sortColumn != -1){
47260             var sortDir = state.direction;
47261             var sc = this.sortClasses;
47262             var hds = this.el.select(this.headerSelector).removeClass(sc);
47263             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47264         }
47265     },
47266
47267     handleHeaderClick : function(g, index){
47268         if(this.headersDisabled){
47269             return;
47270         }
47271         var dm = g.dataSource, cm = g.colModel;
47272             if(!cm.isSortable(index)){
47273             return;
47274         }
47275             g.stopEditing();
47276         dm.sort(cm.getDataIndex(index));
47277     },
47278
47279
47280     destroy : function(){
47281         if(this.colMenu){
47282             this.colMenu.removeAll();
47283             Roo.menu.MenuMgr.unregister(this.colMenu);
47284             this.colMenu.getEl().remove();
47285             delete this.colMenu;
47286         }
47287         if(this.hmenu){
47288             this.hmenu.removeAll();
47289             Roo.menu.MenuMgr.unregister(this.hmenu);
47290             this.hmenu.getEl().remove();
47291             delete this.hmenu;
47292         }
47293         if(this.grid.enableColumnMove){
47294             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47295             if(dds){
47296                 for(var dd in dds){
47297                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47298                         var elid = dds[dd].dragElId;
47299                         dds[dd].unreg();
47300                         Roo.get(elid).remove();
47301                     } else if(dds[dd].config.isTarget){
47302                         dds[dd].proxyTop.remove();
47303                         dds[dd].proxyBottom.remove();
47304                         dds[dd].unreg();
47305                     }
47306                     if(Roo.dd.DDM.locationCache[dd]){
47307                         delete Roo.dd.DDM.locationCache[dd];
47308                     }
47309                 }
47310                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47311             }
47312         }
47313         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47314         this.bind(null, null);
47315         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47316     },
47317
47318     handleLockChange : function(){
47319         this.refresh(true);
47320     },
47321
47322     onDenyColumnLock : function(){
47323
47324     },
47325
47326     onDenyColumnHide : function(){
47327
47328     },
47329
47330     handleHdMenuClick : function(item){
47331         var index = this.hdCtxIndex;
47332         var cm = this.cm, ds = this.ds;
47333         switch(item.id){
47334             case "asc":
47335                 ds.sort(cm.getDataIndex(index), "ASC");
47336                 break;
47337             case "desc":
47338                 ds.sort(cm.getDataIndex(index), "DESC");
47339                 break;
47340             case "lock":
47341                 var lc = cm.getLockedCount();
47342                 if(cm.getColumnCount(true) <= lc+1){
47343                     this.onDenyColumnLock();
47344                     return;
47345                 }
47346                 if(lc != index){
47347                     cm.setLocked(index, true, true);
47348                     cm.moveColumn(index, lc);
47349                     this.grid.fireEvent("columnmove", index, lc);
47350                 }else{
47351                     cm.setLocked(index, true);
47352                 }
47353             break;
47354             case "unlock":
47355                 var lc = cm.getLockedCount();
47356                 if((lc-1) != index){
47357                     cm.setLocked(index, false, true);
47358                     cm.moveColumn(index, lc-1);
47359                     this.grid.fireEvent("columnmove", index, lc-1);
47360                 }else{
47361                     cm.setLocked(index, false);
47362                 }
47363             break;
47364             default:
47365                 index = cm.getIndexById(item.id.substr(4));
47366                 if(index != -1){
47367                     if(item.checked && cm.getColumnCount(true) <= 1){
47368                         this.onDenyColumnHide();
47369                         return false;
47370                     }
47371                     cm.setHidden(index, item.checked);
47372                 }
47373         }
47374         return true;
47375     },
47376
47377     beforeColMenuShow : function(){
47378         var cm = this.cm,  colCount = cm.getColumnCount();
47379         this.colMenu.removeAll();
47380         for(var i = 0; i < colCount; i++){
47381             this.colMenu.add(new Roo.menu.CheckItem({
47382                 id: "col-"+cm.getColumnId(i),
47383                 text: cm.getColumnHeader(i),
47384                 checked: !cm.isHidden(i),
47385                 hideOnClick:false
47386             }));
47387         }
47388     },
47389
47390     handleHdCtx : function(g, index, e){
47391         e.stopEvent();
47392         var hd = this.getHeaderCell(index);
47393         this.hdCtxIndex = index;
47394         var ms = this.hmenu.items, cm = this.cm;
47395         ms.get("asc").setDisabled(!cm.isSortable(index));
47396         ms.get("desc").setDisabled(!cm.isSortable(index));
47397         if(this.grid.enableColLock !== false){
47398             ms.get("lock").setDisabled(cm.isLocked(index));
47399             ms.get("unlock").setDisabled(!cm.isLocked(index));
47400         }
47401         this.hmenu.show(hd, "tl-bl");
47402     },
47403
47404     handleHdOver : function(e){
47405         var hd = this.findHeaderCell(e.getTarget());
47406         if(hd && !this.headersDisabled){
47407             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47408                this.fly(hd).addClass("x-grid-hd-over");
47409             }
47410         }
47411     },
47412
47413     handleHdOut : function(e){
47414         var hd = this.findHeaderCell(e.getTarget());
47415         if(hd){
47416             this.fly(hd).removeClass("x-grid-hd-over");
47417         }
47418     },
47419
47420     handleSplitDblClick : function(e, t){
47421         var i = this.getCellIndex(t);
47422         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47423             this.autoSizeColumn(i, true);
47424             this.layout();
47425         }
47426     },
47427
47428     render : function(){
47429
47430         var cm = this.cm;
47431         var colCount = cm.getColumnCount();
47432
47433         if(this.grid.monitorWindowResize === true){
47434             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47435         }
47436         var header = this.renderHeaders();
47437         var body = this.templates.body.apply({rows:""});
47438         var html = this.templates.master.apply({
47439             lockedBody: body,
47440             body: body,
47441             lockedHeader: header[0],
47442             header: header[1]
47443         });
47444
47445         //this.updateColumns();
47446
47447         this.grid.getGridEl().dom.innerHTML = html;
47448
47449         this.initElements();
47450
47451         this.scroller.on("scroll", this.handleScroll, this);
47452         this.lockedBody.on("mousewheel", this.handleWheel, this);
47453         this.mainBody.on("mousewheel", this.handleWheel, this);
47454
47455         this.mainHd.on("mouseover", this.handleHdOver, this);
47456         this.mainHd.on("mouseout", this.handleHdOut, this);
47457         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47458                 {delegate: "."+this.splitClass});
47459
47460         this.lockedHd.on("mouseover", this.handleHdOver, this);
47461         this.lockedHd.on("mouseout", this.handleHdOut, this);
47462         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47463                 {delegate: "."+this.splitClass});
47464
47465         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47466             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47467         }
47468
47469         this.updateSplitters();
47470
47471         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47472             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47473             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47474         }
47475
47476         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47477             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47478             this.hmenu.add(
47479                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47480                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47481             );
47482             if(this.grid.enableColLock !== false){
47483                 this.hmenu.add('-',
47484                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47485                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47486                 );
47487             }
47488             if(this.grid.enableColumnHide !== false){
47489
47490                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47491                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47492                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47493
47494                 this.hmenu.add('-',
47495                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47496                 );
47497             }
47498             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47499
47500             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47501         }
47502
47503         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47504             this.dd = new Roo.grid.GridDragZone(this.grid, {
47505                 ddGroup : this.grid.ddGroup || 'GridDD'
47506             });
47507         }
47508
47509         /*
47510         for(var i = 0; i < colCount; i++){
47511             if(cm.isHidden(i)){
47512                 this.hideColumn(i);
47513             }
47514             if(cm.config[i].align){
47515                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47516                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47517             }
47518         }*/
47519         
47520         this.updateHeaderSortState();
47521
47522         this.beforeInitialResize();
47523         this.layout(true);
47524
47525         // two part rendering gives faster view to the user
47526         this.renderPhase2.defer(1, this);
47527     },
47528
47529     renderPhase2 : function(){
47530         // render the rows now
47531         this.refresh();
47532         if(this.grid.autoSizeColumns){
47533             this.autoSizeColumns();
47534         }
47535     },
47536
47537     beforeInitialResize : function(){
47538
47539     },
47540
47541     onColumnSplitterMoved : function(i, w){
47542         this.userResized = true;
47543         var cm = this.grid.colModel;
47544         cm.setColumnWidth(i, w, true);
47545         var cid = cm.getColumnId(i);
47546         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47547         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47548         this.updateSplitters();
47549         this.layout();
47550         this.grid.fireEvent("columnresize", i, w);
47551     },
47552
47553     syncRowHeights : function(startIndex, endIndex){
47554         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47555             startIndex = startIndex || 0;
47556             var mrows = this.getBodyTable().rows;
47557             var lrows = this.getLockedTable().rows;
47558             var len = mrows.length-1;
47559             endIndex = Math.min(endIndex || len, len);
47560             for(var i = startIndex; i <= endIndex; i++){
47561                 var m = mrows[i], l = lrows[i];
47562                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47563                 m.style.height = l.style.height = h + "px";
47564             }
47565         }
47566     },
47567
47568     layout : function(initialRender, is2ndPass){
47569         var g = this.grid;
47570         var auto = g.autoHeight;
47571         var scrollOffset = 16;
47572         var c = g.getGridEl(), cm = this.cm,
47573                 expandCol = g.autoExpandColumn,
47574                 gv = this;
47575         //c.beginMeasure();
47576
47577         if(!c.dom.offsetWidth){ // display:none?
47578             if(initialRender){
47579                 this.lockedWrap.show();
47580                 this.mainWrap.show();
47581             }
47582             return;
47583         }
47584
47585         var hasLock = this.cm.isLocked(0);
47586
47587         var tbh = this.headerPanel.getHeight();
47588         var bbh = this.footerPanel.getHeight();
47589
47590         if(auto){
47591             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47592             var newHeight = ch + c.getBorderWidth("tb");
47593             if(g.maxHeight){
47594                 newHeight = Math.min(g.maxHeight, newHeight);
47595             }
47596             c.setHeight(newHeight);
47597         }
47598
47599         if(g.autoWidth){
47600             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47601         }
47602
47603         var s = this.scroller;
47604
47605         var csize = c.getSize(true);
47606
47607         this.el.setSize(csize.width, csize.height);
47608
47609         this.headerPanel.setWidth(csize.width);
47610         this.footerPanel.setWidth(csize.width);
47611
47612         var hdHeight = this.mainHd.getHeight();
47613         var vw = csize.width;
47614         var vh = csize.height - (tbh + bbh);
47615
47616         s.setSize(vw, vh);
47617
47618         var bt = this.getBodyTable();
47619         var ltWidth = hasLock ?
47620                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47621
47622         var scrollHeight = bt.offsetHeight;
47623         var scrollWidth = ltWidth + bt.offsetWidth;
47624         var vscroll = false, hscroll = false;
47625
47626         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47627
47628         var lw = this.lockedWrap, mw = this.mainWrap;
47629         var lb = this.lockedBody, mb = this.mainBody;
47630
47631         setTimeout(function(){
47632             var t = s.dom.offsetTop;
47633             var w = s.dom.clientWidth,
47634                 h = s.dom.clientHeight;
47635
47636             lw.setTop(t);
47637             lw.setSize(ltWidth, h);
47638
47639             mw.setLeftTop(ltWidth, t);
47640             mw.setSize(w-ltWidth, h);
47641
47642             lb.setHeight(h-hdHeight);
47643             mb.setHeight(h-hdHeight);
47644
47645             if(is2ndPass !== true && !gv.userResized && expandCol){
47646                 // high speed resize without full column calculation
47647                 
47648                 var ci = cm.getIndexById(expandCol);
47649                 if (ci < 0) {
47650                     ci = cm.findColumnIndex(expandCol);
47651                 }
47652                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47653                 var expandId = cm.getColumnId(ci);
47654                 var  tw = cm.getTotalWidth(false);
47655                 var currentWidth = cm.getColumnWidth(ci);
47656                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47657                 if(currentWidth != cw){
47658                     cm.setColumnWidth(ci, cw, true);
47659                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47660                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47661                     gv.updateSplitters();
47662                     gv.layout(false, true);
47663                 }
47664             }
47665
47666             if(initialRender){
47667                 lw.show();
47668                 mw.show();
47669             }
47670             //c.endMeasure();
47671         }, 10);
47672     },
47673
47674     onWindowResize : function(){
47675         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47676             return;
47677         }
47678         this.layout();
47679     },
47680
47681     appendFooter : function(parentEl){
47682         return null;
47683     },
47684
47685     sortAscText : "Sort Ascending",
47686     sortDescText : "Sort Descending",
47687     lockText : "Lock Column",
47688     unlockText : "Unlock Column",
47689     columnsText : "Columns"
47690 });
47691
47692
47693 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47694     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47695     this.proxy.el.addClass('x-grid3-col-dd');
47696 };
47697
47698 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47699     handleMouseDown : function(e){
47700
47701     },
47702
47703     callHandleMouseDown : function(e){
47704         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47705     }
47706 });
47707 /*
47708  * Based on:
47709  * Ext JS Library 1.1.1
47710  * Copyright(c) 2006-2007, Ext JS, LLC.
47711  *
47712  * Originally Released Under LGPL - original licence link has changed is not relivant.
47713  *
47714  * Fork - LGPL
47715  * <script type="text/javascript">
47716  */
47717  
47718 // private
47719 // This is a support class used internally by the Grid components
47720 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47721     this.grid = grid;
47722     this.view = grid.getView();
47723     this.proxy = this.view.resizeProxy;
47724     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47725         "gridSplitters" + this.grid.getGridEl().id, {
47726         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47727     });
47728     this.setHandleElId(Roo.id(hd));
47729     this.setOuterHandleElId(Roo.id(hd2));
47730     this.scroll = false;
47731 };
47732 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47733     fly: Roo.Element.fly,
47734
47735     b4StartDrag : function(x, y){
47736         this.view.headersDisabled = true;
47737         this.proxy.setHeight(this.view.mainWrap.getHeight());
47738         var w = this.cm.getColumnWidth(this.cellIndex);
47739         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47740         this.resetConstraints();
47741         this.setXConstraint(minw, 1000);
47742         this.setYConstraint(0, 0);
47743         this.minX = x - minw;
47744         this.maxX = x + 1000;
47745         this.startPos = x;
47746         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47747     },
47748
47749
47750     handleMouseDown : function(e){
47751         ev = Roo.EventObject.setEvent(e);
47752         var t = this.fly(ev.getTarget());
47753         if(t.hasClass("x-grid-split")){
47754             this.cellIndex = this.view.getCellIndex(t.dom);
47755             this.split = t.dom;
47756             this.cm = this.grid.colModel;
47757             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47758                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47759             }
47760         }
47761     },
47762
47763     endDrag : function(e){
47764         this.view.headersDisabled = false;
47765         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47766         var diff = endX - this.startPos;
47767         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47768     },
47769
47770     autoOffset : function(){
47771         this.setDelta(0,0);
47772     }
47773 });/*
47774  * Based on:
47775  * Ext JS Library 1.1.1
47776  * Copyright(c) 2006-2007, Ext JS, LLC.
47777  *
47778  * Originally Released Under LGPL - original licence link has changed is not relivant.
47779  *
47780  * Fork - LGPL
47781  * <script type="text/javascript">
47782  */
47783  
47784 // private
47785 // This is a support class used internally by the Grid components
47786 Roo.grid.GridDragZone = function(grid, config){
47787     this.view = grid.getView();
47788     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47789     if(this.view.lockedBody){
47790         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47791         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47792     }
47793     this.scroll = false;
47794     this.grid = grid;
47795     this.ddel = document.createElement('div');
47796     this.ddel.className = 'x-grid-dd-wrap';
47797 };
47798
47799 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47800     ddGroup : "GridDD",
47801
47802     getDragData : function(e){
47803         var t = Roo.lib.Event.getTarget(e);
47804         var rowIndex = this.view.findRowIndex(t);
47805         if(rowIndex !== false){
47806             var sm = this.grid.selModel;
47807             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47808               //  sm.mouseDown(e, t);
47809             //}
47810             if (e.hasModifier()){
47811                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47812             }
47813             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47814         }
47815         return false;
47816     },
47817
47818     onInitDrag : function(e){
47819         var data = this.dragData;
47820         this.ddel.innerHTML = this.grid.getDragDropText();
47821         this.proxy.update(this.ddel);
47822         // fire start drag?
47823     },
47824
47825     afterRepair : function(){
47826         this.dragging = false;
47827     },
47828
47829     getRepairXY : function(e, data){
47830         return false;
47831     },
47832
47833     onEndDrag : function(data, e){
47834         // fire end drag?
47835     },
47836
47837     onValidDrop : function(dd, e, id){
47838         // fire drag drop?
47839         this.hideProxy();
47840     },
47841
47842     beforeInvalidDrop : function(e, id){
47843
47844     }
47845 });/*
47846  * Based on:
47847  * Ext JS Library 1.1.1
47848  * Copyright(c) 2006-2007, Ext JS, LLC.
47849  *
47850  * Originally Released Under LGPL - original licence link has changed is not relivant.
47851  *
47852  * Fork - LGPL
47853  * <script type="text/javascript">
47854  */
47855  
47856
47857 /**
47858  * @class Roo.grid.ColumnModel
47859  * @extends Roo.util.Observable
47860  * This is the default implementation of a ColumnModel used by the Grid. It defines
47861  * the columns in the grid.
47862  * <br>Usage:<br>
47863  <pre><code>
47864  var colModel = new Roo.grid.ColumnModel([
47865         {header: "Ticker", width: 60, sortable: true, locked: true},
47866         {header: "Company Name", width: 150, sortable: true},
47867         {header: "Market Cap.", width: 100, sortable: true},
47868         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47869         {header: "Employees", width: 100, sortable: true, resizable: false}
47870  ]);
47871  </code></pre>
47872  * <p>
47873  
47874  * The config options listed for this class are options which may appear in each
47875  * individual column definition.
47876  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47877  * @constructor
47878  * @param {Object} config An Array of column config objects. See this class's
47879  * config objects for details.
47880 */
47881 Roo.grid.ColumnModel = function(config){
47882         /**
47883      * The config passed into the constructor
47884      */
47885     this.config = config;
47886     this.lookup = {};
47887
47888     // if no id, create one
47889     // if the column does not have a dataIndex mapping,
47890     // map it to the order it is in the config
47891     for(var i = 0, len = config.length; i < len; i++){
47892         var c = config[i];
47893         if(typeof c.dataIndex == "undefined"){
47894             c.dataIndex = i;
47895         }
47896         if(typeof c.renderer == "string"){
47897             c.renderer = Roo.util.Format[c.renderer];
47898         }
47899         if(typeof c.id == "undefined"){
47900             c.id = Roo.id();
47901         }
47902         if(c.editor && c.editor.xtype){
47903             c.editor  = Roo.factory(c.editor, Roo.grid);
47904         }
47905         if(c.editor && c.editor.isFormField){
47906             c.editor = new Roo.grid.GridEditor(c.editor);
47907         }
47908         this.lookup[c.id] = c;
47909     }
47910
47911     /**
47912      * The width of columns which have no width specified (defaults to 100)
47913      * @type Number
47914      */
47915     this.defaultWidth = 100;
47916
47917     /**
47918      * Default sortable of columns which have no sortable specified (defaults to false)
47919      * @type Boolean
47920      */
47921     this.defaultSortable = false;
47922
47923     this.addEvents({
47924         /**
47925              * @event widthchange
47926              * Fires when the width of a column changes.
47927              * @param {ColumnModel} this
47928              * @param {Number} columnIndex The column index
47929              * @param {Number} newWidth The new width
47930              */
47931             "widthchange": true,
47932         /**
47933              * @event headerchange
47934              * Fires when the text of a header changes.
47935              * @param {ColumnModel} this
47936              * @param {Number} columnIndex The column index
47937              * @param {Number} newText The new header text
47938              */
47939             "headerchange": true,
47940         /**
47941              * @event hiddenchange
47942              * Fires when a column is hidden or "unhidden".
47943              * @param {ColumnModel} this
47944              * @param {Number} columnIndex The column index
47945              * @param {Boolean} hidden true if hidden, false otherwise
47946              */
47947             "hiddenchange": true,
47948             /**
47949          * @event columnmoved
47950          * Fires when a column is moved.
47951          * @param {ColumnModel} this
47952          * @param {Number} oldIndex
47953          * @param {Number} newIndex
47954          */
47955         "columnmoved" : true,
47956         /**
47957          * @event columlockchange
47958          * Fires when a column's locked state is changed
47959          * @param {ColumnModel} this
47960          * @param {Number} colIndex
47961          * @param {Boolean} locked true if locked
47962          */
47963         "columnlockchange" : true
47964     });
47965     Roo.grid.ColumnModel.superclass.constructor.call(this);
47966 };
47967 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47968     /**
47969      * @cfg {String} header The header text to display in the Grid view.
47970      */
47971     /**
47972      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47973      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47974      * specified, the column's index is used as an index into the Record's data Array.
47975      */
47976     /**
47977      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47978      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47979      */
47980     /**
47981      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47982      * Defaults to the value of the {@link #defaultSortable} property.
47983      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47984      */
47985     /**
47986      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47987      */
47988     /**
47989      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47990      */
47991     /**
47992      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47993      */
47994     /**
47995      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47996      */
47997     /**
47998      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47999      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48000      * default renderer uses the raw data value.
48001      */
48002        /**
48003      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48004      */
48005     /**
48006      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48007      */
48008
48009     /**
48010      * Returns the id of the column at the specified index.
48011      * @param {Number} index The column index
48012      * @return {String} the id
48013      */
48014     getColumnId : function(index){
48015         return this.config[index].id;
48016     },
48017
48018     /**
48019      * Returns the column for a specified id.
48020      * @param {String} id The column id
48021      * @return {Object} the column
48022      */
48023     getColumnById : function(id){
48024         return this.lookup[id];
48025     },
48026
48027     /**
48028      * Returns the index for a specified column id.
48029      * @param {String} id The column id
48030      * @return {Number} the index, or -1 if not found
48031      */
48032     getIndexById : function(id){
48033         for(var i = 0, len = this.config.length; i < len; i++){
48034             if(this.config[i].id == id){
48035                 return i;
48036             }
48037         }
48038         return -1;
48039     },
48040     /**
48041      * Returns the index for a specified column dataIndex.
48042      * @param {String} dataIndex The column dataIndex
48043      * @return {Number} the index, or -1 if not found
48044      */
48045     
48046     findColumnIndex : function(dataIndex){
48047         for(var i = 0, len = this.config.length; i < len; i++){
48048             if(this.config[i].dataIndex == dataIndex){
48049                 return i;
48050             }
48051         }
48052         return -1;
48053     },
48054     
48055     
48056     moveColumn : function(oldIndex, newIndex){
48057         var c = this.config[oldIndex];
48058         this.config.splice(oldIndex, 1);
48059         this.config.splice(newIndex, 0, c);
48060         this.dataMap = null;
48061         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48062     },
48063
48064     isLocked : function(colIndex){
48065         return this.config[colIndex].locked === true;
48066     },
48067
48068     setLocked : function(colIndex, value, suppressEvent){
48069         if(this.isLocked(colIndex) == value){
48070             return;
48071         }
48072         this.config[colIndex].locked = value;
48073         if(!suppressEvent){
48074             this.fireEvent("columnlockchange", this, colIndex, value);
48075         }
48076     },
48077
48078     getTotalLockedWidth : function(){
48079         var totalWidth = 0;
48080         for(var i = 0; i < this.config.length; i++){
48081             if(this.isLocked(i) && !this.isHidden(i)){
48082                 this.totalWidth += this.getColumnWidth(i);
48083             }
48084         }
48085         return totalWidth;
48086     },
48087
48088     getLockedCount : function(){
48089         for(var i = 0, len = this.config.length; i < len; i++){
48090             if(!this.isLocked(i)){
48091                 return i;
48092             }
48093         }
48094     },
48095
48096     /**
48097      * Returns the number of columns.
48098      * @return {Number}
48099      */
48100     getColumnCount : function(visibleOnly){
48101         if(visibleOnly === true){
48102             var c = 0;
48103             for(var i = 0, len = this.config.length; i < len; i++){
48104                 if(!this.isHidden(i)){
48105                     c++;
48106                 }
48107             }
48108             return c;
48109         }
48110         return this.config.length;
48111     },
48112
48113     /**
48114      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48115      * @param {Function} fn
48116      * @param {Object} scope (optional)
48117      * @return {Array} result
48118      */
48119     getColumnsBy : function(fn, scope){
48120         var r = [];
48121         for(var i = 0, len = this.config.length; i < len; i++){
48122             var c = this.config[i];
48123             if(fn.call(scope||this, c, i) === true){
48124                 r[r.length] = c;
48125             }
48126         }
48127         return r;
48128     },
48129
48130     /**
48131      * Returns true if the specified column is sortable.
48132      * @param {Number} col The column index
48133      * @return {Boolean}
48134      */
48135     isSortable : function(col){
48136         if(typeof this.config[col].sortable == "undefined"){
48137             return this.defaultSortable;
48138         }
48139         return this.config[col].sortable;
48140     },
48141
48142     /**
48143      * Returns the rendering (formatting) function defined for the column.
48144      * @param {Number} col The column index.
48145      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48146      */
48147     getRenderer : function(col){
48148         if(!this.config[col].renderer){
48149             return Roo.grid.ColumnModel.defaultRenderer;
48150         }
48151         return this.config[col].renderer;
48152     },
48153
48154     /**
48155      * Sets the rendering (formatting) function for a column.
48156      * @param {Number} col The column index
48157      * @param {Function} fn The function to use to process the cell's raw data
48158      * to return HTML markup for the grid view. The render function is called with
48159      * the following parameters:<ul>
48160      * <li>Data value.</li>
48161      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48162      * <li>css A CSS style string to apply to the table cell.</li>
48163      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48164      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48165      * <li>Row index</li>
48166      * <li>Column index</li>
48167      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48168      */
48169     setRenderer : function(col, fn){
48170         this.config[col].renderer = fn;
48171     },
48172
48173     /**
48174      * Returns the width for the specified column.
48175      * @param {Number} col The column index
48176      * @return {Number}
48177      */
48178     getColumnWidth : function(col){
48179         return this.config[col].width || this.defaultWidth;
48180     },
48181
48182     /**
48183      * Sets the width for a column.
48184      * @param {Number} col The column index
48185      * @param {Number} width The new width
48186      */
48187     setColumnWidth : function(col, width, suppressEvent){
48188         this.config[col].width = width;
48189         this.totalWidth = null;
48190         if(!suppressEvent){
48191              this.fireEvent("widthchange", this, col, width);
48192         }
48193     },
48194
48195     /**
48196      * Returns the total width of all columns.
48197      * @param {Boolean} includeHidden True to include hidden column widths
48198      * @return {Number}
48199      */
48200     getTotalWidth : function(includeHidden){
48201         if(!this.totalWidth){
48202             this.totalWidth = 0;
48203             for(var i = 0, len = this.config.length; i < len; i++){
48204                 if(includeHidden || !this.isHidden(i)){
48205                     this.totalWidth += this.getColumnWidth(i);
48206                 }
48207             }
48208         }
48209         return this.totalWidth;
48210     },
48211
48212     /**
48213      * Returns the header for the specified column.
48214      * @param {Number} col The column index
48215      * @return {String}
48216      */
48217     getColumnHeader : function(col){
48218         return this.config[col].header;
48219     },
48220
48221     /**
48222      * Sets the header for a column.
48223      * @param {Number} col The column index
48224      * @param {String} header The new header
48225      */
48226     setColumnHeader : function(col, header){
48227         this.config[col].header = header;
48228         this.fireEvent("headerchange", this, col, header);
48229     },
48230
48231     /**
48232      * Returns the tooltip for the specified column.
48233      * @param {Number} col The column index
48234      * @return {String}
48235      */
48236     getColumnTooltip : function(col){
48237             return this.config[col].tooltip;
48238     },
48239     /**
48240      * Sets the tooltip for a column.
48241      * @param {Number} col The column index
48242      * @param {String} tooltip The new tooltip
48243      */
48244     setColumnTooltip : function(col, tooltip){
48245             this.config[col].tooltip = tooltip;
48246     },
48247
48248     /**
48249      * Returns the dataIndex for the specified column.
48250      * @param {Number} col The column index
48251      * @return {Number}
48252      */
48253     getDataIndex : function(col){
48254         return this.config[col].dataIndex;
48255     },
48256
48257     /**
48258      * Sets the dataIndex for a column.
48259      * @param {Number} col The column index
48260      * @param {Number} dataIndex The new dataIndex
48261      */
48262     setDataIndex : function(col, dataIndex){
48263         this.config[col].dataIndex = dataIndex;
48264     },
48265
48266     
48267     
48268     /**
48269      * Returns true if the cell is editable.
48270      * @param {Number} colIndex The column index
48271      * @param {Number} rowIndex The row index
48272      * @return {Boolean}
48273      */
48274     isCellEditable : function(colIndex, rowIndex){
48275         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48276     },
48277
48278     /**
48279      * Returns the editor defined for the cell/column.
48280      * return false or null to disable editing.
48281      * @param {Number} colIndex The column index
48282      * @param {Number} rowIndex The row index
48283      * @return {Object}
48284      */
48285     getCellEditor : function(colIndex, rowIndex){
48286         return this.config[colIndex].editor;
48287     },
48288
48289     /**
48290      * Sets if a column is editable.
48291      * @param {Number} col The column index
48292      * @param {Boolean} editable True if the column is editable
48293      */
48294     setEditable : function(col, editable){
48295         this.config[col].editable = editable;
48296     },
48297
48298
48299     /**
48300      * Returns true if the column is hidden.
48301      * @param {Number} colIndex The column index
48302      * @return {Boolean}
48303      */
48304     isHidden : function(colIndex){
48305         return this.config[colIndex].hidden;
48306     },
48307
48308
48309     /**
48310      * Returns true if the column width cannot be changed
48311      */
48312     isFixed : function(colIndex){
48313         return this.config[colIndex].fixed;
48314     },
48315
48316     /**
48317      * Returns true if the column can be resized
48318      * @return {Boolean}
48319      */
48320     isResizable : function(colIndex){
48321         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48322     },
48323     /**
48324      * Sets if a column is hidden.
48325      * @param {Number} colIndex The column index
48326      * @param {Boolean} hidden True if the column is hidden
48327      */
48328     setHidden : function(colIndex, hidden){
48329         this.config[colIndex].hidden = hidden;
48330         this.totalWidth = null;
48331         this.fireEvent("hiddenchange", this, colIndex, hidden);
48332     },
48333
48334     /**
48335      * Sets the editor for a column.
48336      * @param {Number} col The column index
48337      * @param {Object} editor The editor object
48338      */
48339     setEditor : function(col, editor){
48340         this.config[col].editor = editor;
48341     }
48342 });
48343
48344 Roo.grid.ColumnModel.defaultRenderer = function(value){
48345         if(typeof value == "string" && value.length < 1){
48346             return "&#160;";
48347         }
48348         return value;
48349 };
48350
48351 // Alias for backwards compatibility
48352 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48353 /*
48354  * Based on:
48355  * Ext JS Library 1.1.1
48356  * Copyright(c) 2006-2007, Ext JS, LLC.
48357  *
48358  * Originally Released Under LGPL - original licence link has changed is not relivant.
48359  *
48360  * Fork - LGPL
48361  * <script type="text/javascript">
48362  */
48363
48364 /**
48365  * @class Roo.grid.AbstractSelectionModel
48366  * @extends Roo.util.Observable
48367  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48368  * implemented by descendant classes.  This class should not be directly instantiated.
48369  * @constructor
48370  */
48371 Roo.grid.AbstractSelectionModel = function(){
48372     this.locked = false;
48373     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48374 };
48375
48376 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48377     /** @ignore Called by the grid automatically. Do not call directly. */
48378     init : function(grid){
48379         this.grid = grid;
48380         this.initEvents();
48381     },
48382
48383     /**
48384      * Locks the selections.
48385      */
48386     lock : function(){
48387         this.locked = true;
48388     },
48389
48390     /**
48391      * Unlocks the selections.
48392      */
48393     unlock : function(){
48394         this.locked = false;
48395     },
48396
48397     /**
48398      * Returns true if the selections are locked.
48399      * @return {Boolean}
48400      */
48401     isLocked : function(){
48402         return this.locked;
48403     }
48404 });/*
48405  * Based on:
48406  * Ext JS Library 1.1.1
48407  * Copyright(c) 2006-2007, Ext JS, LLC.
48408  *
48409  * Originally Released Under LGPL - original licence link has changed is not relivant.
48410  *
48411  * Fork - LGPL
48412  * <script type="text/javascript">
48413  */
48414 /**
48415  * @extends Roo.grid.AbstractSelectionModel
48416  * @class Roo.grid.RowSelectionModel
48417  * The default SelectionModel used by {@link Roo.grid.Grid}.
48418  * It supports multiple selections and keyboard selection/navigation. 
48419  * @constructor
48420  * @param {Object} config
48421  */
48422 Roo.grid.RowSelectionModel = function(config){
48423     Roo.apply(this, config);
48424     this.selections = new Roo.util.MixedCollection(false, function(o){
48425         return o.id;
48426     });
48427
48428     this.last = false;
48429     this.lastActive = false;
48430
48431     this.addEvents({
48432         /**
48433              * @event selectionchange
48434              * Fires when the selection changes
48435              * @param {SelectionModel} this
48436              */
48437             "selectionchange" : true,
48438         /**
48439              * @event afterselectionchange
48440              * Fires after the selection changes (eg. by key press or clicking)
48441              * @param {SelectionModel} this
48442              */
48443             "afterselectionchange" : true,
48444         /**
48445              * @event beforerowselect
48446              * Fires when a row is selected being selected, return false to cancel.
48447              * @param {SelectionModel} this
48448              * @param {Number} rowIndex The selected index
48449              * @param {Boolean} keepExisting False if other selections will be cleared
48450              */
48451             "beforerowselect" : true,
48452         /**
48453              * @event rowselect
48454              * Fires when a row is selected.
48455              * @param {SelectionModel} this
48456              * @param {Number} rowIndex The selected index
48457              * @param {Roo.data.Record} r The record
48458              */
48459             "rowselect" : true,
48460         /**
48461              * @event rowdeselect
48462              * Fires when a row is deselected.
48463              * @param {SelectionModel} this
48464              * @param {Number} rowIndex The selected index
48465              */
48466         "rowdeselect" : true
48467     });
48468     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48469     this.locked = false;
48470 };
48471
48472 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48473     /**
48474      * @cfg {Boolean} singleSelect
48475      * True to allow selection of only one row at a time (defaults to false)
48476      */
48477     singleSelect : false,
48478
48479     // private
48480     initEvents : function(){
48481
48482         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48483             this.grid.on("mousedown", this.handleMouseDown, this);
48484         }else{ // allow click to work like normal
48485             this.grid.on("rowclick", this.handleDragableRowClick, this);
48486         }
48487
48488         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48489             "up" : function(e){
48490                 if(!e.shiftKey){
48491                     this.selectPrevious(e.shiftKey);
48492                 }else if(this.last !== false && this.lastActive !== false){
48493                     var last = this.last;
48494                     this.selectRange(this.last,  this.lastActive-1);
48495                     this.grid.getView().focusRow(this.lastActive);
48496                     if(last !== false){
48497                         this.last = last;
48498                     }
48499                 }else{
48500                     this.selectFirstRow();
48501                 }
48502                 this.fireEvent("afterselectionchange", this);
48503             },
48504             "down" : function(e){
48505                 if(!e.shiftKey){
48506                     this.selectNext(e.shiftKey);
48507                 }else if(this.last !== false && this.lastActive !== false){
48508                     var last = this.last;
48509                     this.selectRange(this.last,  this.lastActive+1);
48510                     this.grid.getView().focusRow(this.lastActive);
48511                     if(last !== false){
48512                         this.last = last;
48513                     }
48514                 }else{
48515                     this.selectFirstRow();
48516                 }
48517                 this.fireEvent("afterselectionchange", this);
48518             },
48519             scope: this
48520         });
48521
48522         var view = this.grid.view;
48523         view.on("refresh", this.onRefresh, this);
48524         view.on("rowupdated", this.onRowUpdated, this);
48525         view.on("rowremoved", this.onRemove, this);
48526     },
48527
48528     // private
48529     onRefresh : function(){
48530         var ds = this.grid.dataSource, i, v = this.grid.view;
48531         var s = this.selections;
48532         s.each(function(r){
48533             if((i = ds.indexOfId(r.id)) != -1){
48534                 v.onRowSelect(i);
48535             }else{
48536                 s.remove(r);
48537             }
48538         });
48539     },
48540
48541     // private
48542     onRemove : function(v, index, r){
48543         this.selections.remove(r);
48544     },
48545
48546     // private
48547     onRowUpdated : function(v, index, r){
48548         if(this.isSelected(r)){
48549             v.onRowSelect(index);
48550         }
48551     },
48552
48553     /**
48554      * Select records.
48555      * @param {Array} records The records to select
48556      * @param {Boolean} keepExisting (optional) True to keep existing selections
48557      */
48558     selectRecords : function(records, keepExisting){
48559         if(!keepExisting){
48560             this.clearSelections();
48561         }
48562         var ds = this.grid.dataSource;
48563         for(var i = 0, len = records.length; i < len; i++){
48564             this.selectRow(ds.indexOf(records[i]), true);
48565         }
48566     },
48567
48568     /**
48569      * Gets the number of selected rows.
48570      * @return {Number}
48571      */
48572     getCount : function(){
48573         return this.selections.length;
48574     },
48575
48576     /**
48577      * Selects the first row in the grid.
48578      */
48579     selectFirstRow : function(){
48580         this.selectRow(0);
48581     },
48582
48583     /**
48584      * Select the last row.
48585      * @param {Boolean} keepExisting (optional) True to keep existing selections
48586      */
48587     selectLastRow : function(keepExisting){
48588         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48589     },
48590
48591     /**
48592      * Selects the row immediately following the last selected row.
48593      * @param {Boolean} keepExisting (optional) True to keep existing selections
48594      */
48595     selectNext : function(keepExisting){
48596         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48597             this.selectRow(this.last+1, keepExisting);
48598             this.grid.getView().focusRow(this.last);
48599         }
48600     },
48601
48602     /**
48603      * Selects the row that precedes the last selected row.
48604      * @param {Boolean} keepExisting (optional) True to keep existing selections
48605      */
48606     selectPrevious : function(keepExisting){
48607         if(this.last){
48608             this.selectRow(this.last-1, keepExisting);
48609             this.grid.getView().focusRow(this.last);
48610         }
48611     },
48612
48613     /**
48614      * Returns the selected records
48615      * @return {Array} Array of selected records
48616      */
48617     getSelections : function(){
48618         return [].concat(this.selections.items);
48619     },
48620
48621     /**
48622      * Returns the first selected record.
48623      * @return {Record}
48624      */
48625     getSelected : function(){
48626         return this.selections.itemAt(0);
48627     },
48628
48629
48630     /**
48631      * Clears all selections.
48632      */
48633     clearSelections : function(fast){
48634         if(this.locked) return;
48635         if(fast !== true){
48636             var ds = this.grid.dataSource;
48637             var s = this.selections;
48638             s.each(function(r){
48639                 this.deselectRow(ds.indexOfId(r.id));
48640             }, this);
48641             s.clear();
48642         }else{
48643             this.selections.clear();
48644         }
48645         this.last = false;
48646     },
48647
48648
48649     /**
48650      * Selects all rows.
48651      */
48652     selectAll : function(){
48653         if(this.locked) return;
48654         this.selections.clear();
48655         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48656             this.selectRow(i, true);
48657         }
48658     },
48659
48660     /**
48661      * Returns True if there is a selection.
48662      * @return {Boolean}
48663      */
48664     hasSelection : function(){
48665         return this.selections.length > 0;
48666     },
48667
48668     /**
48669      * Returns True if the specified row is selected.
48670      * @param {Number/Record} record The record or index of the record to check
48671      * @return {Boolean}
48672      */
48673     isSelected : function(index){
48674         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48675         return (r && this.selections.key(r.id) ? true : false);
48676     },
48677
48678     /**
48679      * Returns True if the specified record id is selected.
48680      * @param {String} id The id of record to check
48681      * @return {Boolean}
48682      */
48683     isIdSelected : function(id){
48684         return (this.selections.key(id) ? true : false);
48685     },
48686
48687     // private
48688     handleMouseDown : function(e, t){
48689         var view = this.grid.getView(), rowIndex;
48690         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48691             return;
48692         };
48693         if(e.shiftKey && this.last !== false){
48694             var last = this.last;
48695             this.selectRange(last, rowIndex, e.ctrlKey);
48696             this.last = last; // reset the last
48697             view.focusRow(rowIndex);
48698         }else{
48699             var isSelected = this.isSelected(rowIndex);
48700             if(e.button !== 0 && isSelected){
48701                 view.focusRow(rowIndex);
48702             }else if(e.ctrlKey && isSelected){
48703                 this.deselectRow(rowIndex);
48704             }else if(!isSelected){
48705                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48706                 view.focusRow(rowIndex);
48707             }
48708         }
48709         this.fireEvent("afterselectionchange", this);
48710     },
48711     // private
48712     handleDragableRowClick :  function(grid, rowIndex, e) 
48713     {
48714         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48715             this.selectRow(rowIndex, false);
48716             grid.view.focusRow(rowIndex);
48717              this.fireEvent("afterselectionchange", this);
48718         }
48719     },
48720     
48721     /**
48722      * Selects multiple rows.
48723      * @param {Array} rows Array of the indexes of the row to select
48724      * @param {Boolean} keepExisting (optional) True to keep existing selections
48725      */
48726     selectRows : function(rows, keepExisting){
48727         if(!keepExisting){
48728             this.clearSelections();
48729         }
48730         for(var i = 0, len = rows.length; i < len; i++){
48731             this.selectRow(rows[i], true);
48732         }
48733     },
48734
48735     /**
48736      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48737      * @param {Number} startRow The index of the first row in the range
48738      * @param {Number} endRow The index of the last row in the range
48739      * @param {Boolean} keepExisting (optional) True to retain existing selections
48740      */
48741     selectRange : function(startRow, endRow, keepExisting){
48742         if(this.locked) return;
48743         if(!keepExisting){
48744             this.clearSelections();
48745         }
48746         if(startRow <= endRow){
48747             for(var i = startRow; i <= endRow; i++){
48748                 this.selectRow(i, true);
48749             }
48750         }else{
48751             for(var i = startRow; i >= endRow; i--){
48752                 this.selectRow(i, true);
48753             }
48754         }
48755     },
48756
48757     /**
48758      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48759      * @param {Number} startRow The index of the first row in the range
48760      * @param {Number} endRow The index of the last row in the range
48761      */
48762     deselectRange : function(startRow, endRow, preventViewNotify){
48763         if(this.locked) return;
48764         for(var i = startRow; i <= endRow; i++){
48765             this.deselectRow(i, preventViewNotify);
48766         }
48767     },
48768
48769     /**
48770      * Selects a row.
48771      * @param {Number} row The index of the row to select
48772      * @param {Boolean} keepExisting (optional) True to keep existing selections
48773      */
48774     selectRow : function(index, keepExisting, preventViewNotify){
48775         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48776         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48777             if(!keepExisting || this.singleSelect){
48778                 this.clearSelections();
48779             }
48780             var r = this.grid.dataSource.getAt(index);
48781             this.selections.add(r);
48782             this.last = this.lastActive = index;
48783             if(!preventViewNotify){
48784                 this.grid.getView().onRowSelect(index);
48785             }
48786             this.fireEvent("rowselect", this, index, r);
48787             this.fireEvent("selectionchange", this);
48788         }
48789     },
48790
48791     /**
48792      * Deselects a row.
48793      * @param {Number} row The index of the row to deselect
48794      */
48795     deselectRow : function(index, preventViewNotify){
48796         if(this.locked) return;
48797         if(this.last == index){
48798             this.last = false;
48799         }
48800         if(this.lastActive == index){
48801             this.lastActive = false;
48802         }
48803         var r = this.grid.dataSource.getAt(index);
48804         this.selections.remove(r);
48805         if(!preventViewNotify){
48806             this.grid.getView().onRowDeselect(index);
48807         }
48808         this.fireEvent("rowdeselect", this, index);
48809         this.fireEvent("selectionchange", this);
48810     },
48811
48812     // private
48813     restoreLast : function(){
48814         if(this._last){
48815             this.last = this._last;
48816         }
48817     },
48818
48819     // private
48820     acceptsNav : function(row, col, cm){
48821         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48822     },
48823
48824     // private
48825     onEditorKey : function(field, e){
48826         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48827         if(k == e.TAB){
48828             e.stopEvent();
48829             ed.completeEdit();
48830             if(e.shiftKey){
48831                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48832             }else{
48833                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48834             }
48835         }else if(k == e.ENTER && !e.ctrlKey){
48836             e.stopEvent();
48837             ed.completeEdit();
48838             if(e.shiftKey){
48839                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48840             }else{
48841                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48842             }
48843         }else if(k == e.ESC){
48844             ed.cancelEdit();
48845         }
48846         if(newCell){
48847             g.startEditing(newCell[0], newCell[1]);
48848         }
48849     }
48850 });/*
48851  * Based on:
48852  * Ext JS Library 1.1.1
48853  * Copyright(c) 2006-2007, Ext JS, LLC.
48854  *
48855  * Originally Released Under LGPL - original licence link has changed is not relivant.
48856  *
48857  * Fork - LGPL
48858  * <script type="text/javascript">
48859  */
48860 /**
48861  * @class Roo.grid.CellSelectionModel
48862  * @extends Roo.grid.AbstractSelectionModel
48863  * This class provides the basic implementation for cell selection in a grid.
48864  * @constructor
48865  * @param {Object} config The object containing the configuration of this model.
48866  */
48867 Roo.grid.CellSelectionModel = function(config){
48868     Roo.apply(this, config);
48869
48870     this.selection = null;
48871
48872     this.addEvents({
48873         /**
48874              * @event beforerowselect
48875              * Fires before a cell is selected.
48876              * @param {SelectionModel} this
48877              * @param {Number} rowIndex The selected row index
48878              * @param {Number} colIndex The selected cell index
48879              */
48880             "beforecellselect" : true,
48881         /**
48882              * @event cellselect
48883              * Fires when a cell is selected.
48884              * @param {SelectionModel} this
48885              * @param {Number} rowIndex The selected row index
48886              * @param {Number} colIndex The selected cell index
48887              */
48888             "cellselect" : true,
48889         /**
48890              * @event selectionchange
48891              * Fires when the active selection changes.
48892              * @param {SelectionModel} this
48893              * @param {Object} selection null for no selection or an object (o) with two properties
48894                 <ul>
48895                 <li>o.record: the record object for the row the selection is in</li>
48896                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48897                 </ul>
48898              */
48899             "selectionchange" : true
48900     });
48901     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48902 };
48903
48904 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48905
48906     /** @ignore */
48907     initEvents : function(){
48908         this.grid.on("mousedown", this.handleMouseDown, this);
48909         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48910         var view = this.grid.view;
48911         view.on("refresh", this.onViewChange, this);
48912         view.on("rowupdated", this.onRowUpdated, this);
48913         view.on("beforerowremoved", this.clearSelections, this);
48914         view.on("beforerowsinserted", this.clearSelections, this);
48915         if(this.grid.isEditor){
48916             this.grid.on("beforeedit", this.beforeEdit,  this);
48917         }
48918     },
48919
48920         //private
48921     beforeEdit : function(e){
48922         this.select(e.row, e.column, false, true, e.record);
48923     },
48924
48925         //private
48926     onRowUpdated : function(v, index, r){
48927         if(this.selection && this.selection.record == r){
48928             v.onCellSelect(index, this.selection.cell[1]);
48929         }
48930     },
48931
48932         //private
48933     onViewChange : function(){
48934         this.clearSelections(true);
48935     },
48936
48937         /**
48938          * Returns the currently selected cell,.
48939          * @return {Array} The selected cell (row, column) or null if none selected.
48940          */
48941     getSelectedCell : function(){
48942         return this.selection ? this.selection.cell : null;
48943     },
48944
48945     /**
48946      * Clears all selections.
48947      * @param {Boolean} true to prevent the gridview from being notified about the change.
48948      */
48949     clearSelections : function(preventNotify){
48950         var s = this.selection;
48951         if(s){
48952             if(preventNotify !== true){
48953                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48954             }
48955             this.selection = null;
48956             this.fireEvent("selectionchange", this, null);
48957         }
48958     },
48959
48960     /**
48961      * Returns true if there is a selection.
48962      * @return {Boolean}
48963      */
48964     hasSelection : function(){
48965         return this.selection ? true : false;
48966     },
48967
48968     /** @ignore */
48969     handleMouseDown : function(e, t){
48970         var v = this.grid.getView();
48971         if(this.isLocked()){
48972             return;
48973         };
48974         var row = v.findRowIndex(t);
48975         var cell = v.findCellIndex(t);
48976         if(row !== false && cell !== false){
48977             this.select(row, cell);
48978         }
48979     },
48980
48981     /**
48982      * Selects a cell.
48983      * @param {Number} rowIndex
48984      * @param {Number} collIndex
48985      */
48986     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48987         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48988             this.clearSelections();
48989             r = r || this.grid.dataSource.getAt(rowIndex);
48990             this.selection = {
48991                 record : r,
48992                 cell : [rowIndex, colIndex]
48993             };
48994             if(!preventViewNotify){
48995                 var v = this.grid.getView();
48996                 v.onCellSelect(rowIndex, colIndex);
48997                 if(preventFocus !== true){
48998                     v.focusCell(rowIndex, colIndex);
48999                 }
49000             }
49001             this.fireEvent("cellselect", this, rowIndex, colIndex);
49002             this.fireEvent("selectionchange", this, this.selection);
49003         }
49004     },
49005
49006         //private
49007     isSelectable : function(rowIndex, colIndex, cm){
49008         return !cm.isHidden(colIndex);
49009     },
49010
49011     /** @ignore */
49012     handleKeyDown : function(e){
49013         if(!e.isNavKeyPress()){
49014             return;
49015         }
49016         var g = this.grid, s = this.selection;
49017         if(!s){
49018             e.stopEvent();
49019             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49020             if(cell){
49021                 this.select(cell[0], cell[1]);
49022             }
49023             return;
49024         }
49025         var sm = this;
49026         var walk = function(row, col, step){
49027             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49028         };
49029         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49030         var newCell;
49031
49032         switch(k){
49033              case e.TAB:
49034                  if(e.shiftKey){
49035                      newCell = walk(r, c-1, -1);
49036                  }else{
49037                      newCell = walk(r, c+1, 1);
49038                  }
49039              break;
49040              case e.DOWN:
49041                  newCell = walk(r+1, c, 1);
49042              break;
49043              case e.UP:
49044                  newCell = walk(r-1, c, -1);
49045              break;
49046              case e.RIGHT:
49047                  newCell = walk(r, c+1, 1);
49048              break;
49049              case e.LEFT:
49050                  newCell = walk(r, c-1, -1);
49051              break;
49052              case e.ENTER:
49053                  if(g.isEditor && !g.editing){
49054                     g.startEditing(r, c);
49055                     e.stopEvent();
49056                     return;
49057                 }
49058              break;
49059         };
49060         if(newCell){
49061             this.select(newCell[0], newCell[1]);
49062             e.stopEvent();
49063         }
49064     },
49065
49066     acceptsNav : function(row, col, cm){
49067         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49068     },
49069
49070     onEditorKey : function(field, e){
49071         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49072         if(k == e.TAB){
49073             if(e.shiftKey){
49074                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49075             }else{
49076                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49077             }
49078             e.stopEvent();
49079         }else if(k == e.ENTER && !e.ctrlKey){
49080             ed.completeEdit();
49081             e.stopEvent();
49082         }else if(k == e.ESC){
49083             ed.cancelEdit();
49084         }
49085         if(newCell){
49086             g.startEditing(newCell[0], newCell[1]);
49087         }
49088     }
49089 });/*
49090  * Based on:
49091  * Ext JS Library 1.1.1
49092  * Copyright(c) 2006-2007, Ext JS, LLC.
49093  *
49094  * Originally Released Under LGPL - original licence link has changed is not relivant.
49095  *
49096  * Fork - LGPL
49097  * <script type="text/javascript">
49098  */
49099  
49100 /**
49101  * @class Roo.grid.EditorGrid
49102  * @extends Roo.grid.Grid
49103  * Class for creating and editable grid.
49104  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49105  * The container MUST have some type of size defined for the grid to fill. The container will be 
49106  * automatically set to position relative if it isn't already.
49107  * @param {Object} dataSource The data model to bind to
49108  * @param {Object} colModel The column model with info about this grid's columns
49109  */
49110 Roo.grid.EditorGrid = function(container, config){
49111     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49112     this.getGridEl().addClass("xedit-grid");
49113
49114     if(!this.selModel){
49115         this.selModel = new Roo.grid.CellSelectionModel();
49116     }
49117
49118     this.activeEditor = null;
49119
49120         this.addEvents({
49121             /**
49122              * @event beforeedit
49123              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49124              * <ul style="padding:5px;padding-left:16px;">
49125              * <li>grid - This grid</li>
49126              * <li>record - The record being edited</li>
49127              * <li>field - The field name being edited</li>
49128              * <li>value - The value for the field being edited.</li>
49129              * <li>row - The grid row index</li>
49130              * <li>column - The grid column index</li>
49131              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49132              * </ul>
49133              * @param {Object} e An edit event (see above for description)
49134              */
49135             "beforeedit" : true,
49136             /**
49137              * @event afteredit
49138              * Fires after a cell is edited. <br />
49139              * <ul style="padding:5px;padding-left:16px;">
49140              * <li>grid - This grid</li>
49141              * <li>record - The record being edited</li>
49142              * <li>field - The field name being edited</li>
49143              * <li>value - The value being set</li>
49144              * <li>originalValue - The original value for the field, before the edit.</li>
49145              * <li>row - The grid row index</li>
49146              * <li>column - The grid column index</li>
49147              * </ul>
49148              * @param {Object} e An edit event (see above for description)
49149              */
49150             "afteredit" : true,
49151             /**
49152              * @event validateedit
49153              * Fires after a cell is edited, but before the value is set in the record. 
49154          * You can use this to modify the value being set in the field, Return false
49155              * to cancel the change. The edit event object has the following properties <br />
49156              * <ul style="padding:5px;padding-left:16px;">
49157          * <li>editor - This editor</li>
49158              * <li>grid - This grid</li>
49159              * <li>record - The record being edited</li>
49160              * <li>field - The field name being edited</li>
49161              * <li>value - The value being set</li>
49162              * <li>originalValue - The original value for the field, before the edit.</li>
49163              * <li>row - The grid row index</li>
49164              * <li>column - The grid column index</li>
49165              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49166              * </ul>
49167              * @param {Object} e An edit event (see above for description)
49168              */
49169             "validateedit" : true
49170         });
49171     this.on("bodyscroll", this.stopEditing,  this);
49172     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49173 };
49174
49175 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49176     /**
49177      * @cfg {Number} clicksToEdit
49178      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49179      */
49180     clicksToEdit: 2,
49181
49182     // private
49183     isEditor : true,
49184     // private
49185     trackMouseOver: false, // causes very odd FF errors
49186
49187     onCellDblClick : function(g, row, col){
49188         this.startEditing(row, col);
49189     },
49190
49191     onEditComplete : function(ed, value, startValue){
49192         this.editing = false;
49193         this.activeEditor = null;
49194         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49195         var r = ed.record;
49196         var field = this.colModel.getDataIndex(ed.col);
49197         var e = {
49198             grid: this,
49199             record: r,
49200             field: field,
49201             originalValue: startValue,
49202             value: value,
49203             row: ed.row,
49204             column: ed.col,
49205             cancel:false,
49206             editor: ed
49207         };
49208         if(String(value) !== String(startValue)){
49209             
49210             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49211                 r.set(field, e.value);
49212                 delete e.cancel; //?? why!!!
49213                 this.fireEvent("afteredit", e);
49214             }
49215         } else {
49216             this.fireEvent("afteredit", e); // always fir it!
49217         }
49218         this.view.focusCell(ed.row, ed.col);
49219     },
49220
49221     /**
49222      * Starts editing the specified for the specified row/column
49223      * @param {Number} rowIndex
49224      * @param {Number} colIndex
49225      */
49226     startEditing : function(row, col){
49227         this.stopEditing();
49228         if(this.colModel.isCellEditable(col, row)){
49229             this.view.ensureVisible(row, col, true);
49230             var r = this.dataSource.getAt(row);
49231             var field = this.colModel.getDataIndex(col);
49232             var e = {
49233                 grid: this,
49234                 record: r,
49235                 field: field,
49236                 value: r.data[field],
49237                 row: row,
49238                 column: col,
49239                 cancel:false
49240             };
49241             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49242                 this.editing = true;
49243                 var ed = this.colModel.getCellEditor(col, row);
49244                 
49245                 if (!ed) {
49246                     return;
49247                 }
49248                 if(!ed.rendered){
49249                     ed.render(ed.parentEl || document.body);
49250                 }
49251                 ed.field.reset();
49252                 (function(){ // complex but required for focus issues in safari, ie and opera
49253                     ed.row = row;
49254                     ed.col = col;
49255                     ed.record = r;
49256                     ed.on("complete", this.onEditComplete, this, {single: true});
49257                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49258                     this.activeEditor = ed;
49259                     var v = r.data[field];
49260                     ed.startEdit(this.view.getCell(row, col), v);
49261                 }).defer(50, this);
49262             }
49263         }
49264     },
49265         
49266     /**
49267      * Stops any active editing
49268      */
49269     stopEditing : function(){
49270         if(this.activeEditor){
49271             this.activeEditor.completeEdit();
49272         }
49273         this.activeEditor = null;
49274     }
49275 });/*
49276  * Based on:
49277  * Ext JS Library 1.1.1
49278  * Copyright(c) 2006-2007, Ext JS, LLC.
49279  *
49280  * Originally Released Under LGPL - original licence link has changed is not relivant.
49281  *
49282  * Fork - LGPL
49283  * <script type="text/javascript">
49284  */
49285
49286 // private - not really -- you end up using it !
49287 // This is a support class used internally by the Grid components
49288
49289 /**
49290  * @class Roo.grid.GridEditor
49291  * @extends Roo.Editor
49292  * Class for creating and editable grid elements.
49293  * @param {Object} config any settings (must include field)
49294  */
49295 Roo.grid.GridEditor = function(field, config){
49296     if (!config && field.field) {
49297         config = field;
49298         field = Roo.factory(config.field, Roo.form);
49299     }
49300     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49301     field.monitorTab = false;
49302 };
49303
49304 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49305     
49306     /**
49307      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49308      */
49309     
49310     alignment: "tl-tl",
49311     autoSize: "width",
49312     hideEl : false,
49313     cls: "x-small-editor x-grid-editor",
49314     shim:false,
49315     shadow:"frame"
49316 });/*
49317  * Based on:
49318  * Ext JS Library 1.1.1
49319  * Copyright(c) 2006-2007, Ext JS, LLC.
49320  *
49321  * Originally Released Under LGPL - original licence link has changed is not relivant.
49322  *
49323  * Fork - LGPL
49324  * <script type="text/javascript">
49325  */
49326   
49327
49328   
49329 Roo.grid.PropertyRecord = Roo.data.Record.create([
49330     {name:'name',type:'string'},  'value'
49331 ]);
49332
49333
49334 Roo.grid.PropertyStore = function(grid, source){
49335     this.grid = grid;
49336     this.store = new Roo.data.Store({
49337         recordType : Roo.grid.PropertyRecord
49338     });
49339     this.store.on('update', this.onUpdate,  this);
49340     if(source){
49341         this.setSource(source);
49342     }
49343     Roo.grid.PropertyStore.superclass.constructor.call(this);
49344 };
49345
49346
49347
49348 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49349     setSource : function(o){
49350         this.source = o;
49351         this.store.removeAll();
49352         var data = [];
49353         for(var k in o){
49354             if(this.isEditableValue(o[k])){
49355                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49356             }
49357         }
49358         this.store.loadRecords({records: data}, {}, true);
49359     },
49360
49361     onUpdate : function(ds, record, type){
49362         if(type == Roo.data.Record.EDIT){
49363             var v = record.data['value'];
49364             var oldValue = record.modified['value'];
49365             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49366                 this.source[record.id] = v;
49367                 record.commit();
49368                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49369             }else{
49370                 record.reject();
49371             }
49372         }
49373     },
49374
49375     getProperty : function(row){
49376        return this.store.getAt(row);
49377     },
49378
49379     isEditableValue: function(val){
49380         if(val && val instanceof Date){
49381             return true;
49382         }else if(typeof val == 'object' || typeof val == 'function'){
49383             return false;
49384         }
49385         return true;
49386     },
49387
49388     setValue : function(prop, value){
49389         this.source[prop] = value;
49390         this.store.getById(prop).set('value', value);
49391     },
49392
49393     getSource : function(){
49394         return this.source;
49395     }
49396 });
49397
49398 Roo.grid.PropertyColumnModel = function(grid, store){
49399     this.grid = grid;
49400     var g = Roo.grid;
49401     g.PropertyColumnModel.superclass.constructor.call(this, [
49402         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49403         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49404     ]);
49405     this.store = store;
49406     this.bselect = Roo.DomHelper.append(document.body, {
49407         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49408             {tag: 'option', value: 'true', html: 'true'},
49409             {tag: 'option', value: 'false', html: 'false'}
49410         ]
49411     });
49412     Roo.id(this.bselect);
49413     var f = Roo.form;
49414     this.editors = {
49415         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49416         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49417         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49418         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49419         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49420     };
49421     this.renderCellDelegate = this.renderCell.createDelegate(this);
49422     this.renderPropDelegate = this.renderProp.createDelegate(this);
49423 };
49424
49425 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49426     
49427     
49428     nameText : 'Name',
49429     valueText : 'Value',
49430     
49431     dateFormat : 'm/j/Y',
49432     
49433     
49434     renderDate : function(dateVal){
49435         return dateVal.dateFormat(this.dateFormat);
49436     },
49437
49438     renderBool : function(bVal){
49439         return bVal ? 'true' : 'false';
49440     },
49441
49442     isCellEditable : function(colIndex, rowIndex){
49443         return colIndex == 1;
49444     },
49445
49446     getRenderer : function(col){
49447         return col == 1 ?
49448             this.renderCellDelegate : this.renderPropDelegate;
49449     },
49450
49451     renderProp : function(v){
49452         return this.getPropertyName(v);
49453     },
49454
49455     renderCell : function(val){
49456         var rv = val;
49457         if(val instanceof Date){
49458             rv = this.renderDate(val);
49459         }else if(typeof val == 'boolean'){
49460             rv = this.renderBool(val);
49461         }
49462         return Roo.util.Format.htmlEncode(rv);
49463     },
49464
49465     getPropertyName : function(name){
49466         var pn = this.grid.propertyNames;
49467         return pn && pn[name] ? pn[name] : name;
49468     },
49469
49470     getCellEditor : function(colIndex, rowIndex){
49471         var p = this.store.getProperty(rowIndex);
49472         var n = p.data['name'], val = p.data['value'];
49473         
49474         if(typeof(this.grid.customEditors[n]) == 'string'){
49475             return this.editors[this.grid.customEditors[n]];
49476         }
49477         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49478             return this.grid.customEditors[n];
49479         }
49480         if(val instanceof Date){
49481             return this.editors['date'];
49482         }else if(typeof val == 'number'){
49483             return this.editors['number'];
49484         }else if(typeof val == 'boolean'){
49485             return this.editors['boolean'];
49486         }else{
49487             return this.editors['string'];
49488         }
49489     }
49490 });
49491
49492 /**
49493  * @class Roo.grid.PropertyGrid
49494  * @extends Roo.grid.EditorGrid
49495  * This class represents the  interface of a component based property grid control.
49496  * <br><br>Usage:<pre><code>
49497  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49498       
49499  });
49500  // set any options
49501  grid.render();
49502  * </code></pre>
49503   
49504  * @constructor
49505  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49506  * The container MUST have some type of size defined for the grid to fill. The container will be
49507  * automatically set to position relative if it isn't already.
49508  * @param {Object} config A config object that sets properties on this grid.
49509  */
49510 Roo.grid.PropertyGrid = function(container, config){
49511     config = config || {};
49512     var store = new Roo.grid.PropertyStore(this);
49513     this.store = store;
49514     var cm = new Roo.grid.PropertyColumnModel(this, store);
49515     store.store.sort('name', 'ASC');
49516     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49517         ds: store.store,
49518         cm: cm,
49519         enableColLock:false,
49520         enableColumnMove:false,
49521         stripeRows:false,
49522         trackMouseOver: false,
49523         clicksToEdit:1
49524     }, config));
49525     this.getGridEl().addClass('x-props-grid');
49526     this.lastEditRow = null;
49527     this.on('columnresize', this.onColumnResize, this);
49528     this.addEvents({
49529          /**
49530              * @event beforepropertychange
49531              * Fires before a property changes (return false to stop?)
49532              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49533              * @param {String} id Record Id
49534              * @param {String} newval New Value
49535          * @param {String} oldval Old Value
49536              */
49537         "beforepropertychange": true,
49538         /**
49539              * @event propertychange
49540              * Fires after a property changes
49541              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49542              * @param {String} id Record Id
49543              * @param {String} newval New Value
49544          * @param {String} oldval Old Value
49545              */
49546         "propertychange": true
49547     });
49548     this.customEditors = this.customEditors || {};
49549 };
49550 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49551     
49552      /**
49553      * @cfg {Object} customEditors map of colnames=> custom editors.
49554      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49555      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49556      * false disables editing of the field.
49557          */
49558     
49559       /**
49560      * @cfg {Object} propertyNames map of property Names to their displayed value
49561          */
49562     
49563     render : function(){
49564         Roo.grid.PropertyGrid.superclass.render.call(this);
49565         this.autoSize.defer(100, this);
49566     },
49567
49568     autoSize : function(){
49569         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49570         if(this.view){
49571             this.view.fitColumns();
49572         }
49573     },
49574
49575     onColumnResize : function(){
49576         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49577         this.autoSize();
49578     },
49579     /**
49580      * Sets the data for the Grid
49581      * accepts a Key => Value object of all the elements avaiable.
49582      * @param {Object} data  to appear in grid.
49583      */
49584     setSource : function(source){
49585         this.store.setSource(source);
49586         //this.autoSize();
49587     },
49588     /**
49589      * Gets all the data from the grid.
49590      * @return {Object} data  data stored in grid
49591      */
49592     getSource : function(){
49593         return this.store.getSource();
49594     }
49595 });/*
49596  * Based on:
49597  * Ext JS Library 1.1.1
49598  * Copyright(c) 2006-2007, Ext JS, LLC.
49599  *
49600  * Originally Released Under LGPL - original licence link has changed is not relivant.
49601  *
49602  * Fork - LGPL
49603  * <script type="text/javascript">
49604  */
49605  
49606 /**
49607  * @class Roo.LoadMask
49608  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49609  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49610  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49611  * element's UpdateManager load indicator and will be destroyed after the initial load.
49612  * @constructor
49613  * Create a new LoadMask
49614  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49615  * @param {Object} config The config object
49616  */
49617 Roo.LoadMask = function(el, config){
49618     this.el = Roo.get(el);
49619     Roo.apply(this, config);
49620     if(this.store){
49621         this.store.on('beforeload', this.onBeforeLoad, this);
49622         this.store.on('load', this.onLoad, this);
49623         this.store.on('loadexception', this.onLoad, this);
49624         this.removeMask = false;
49625     }else{
49626         var um = this.el.getUpdateManager();
49627         um.showLoadIndicator = false; // disable the default indicator
49628         um.on('beforeupdate', this.onBeforeLoad, this);
49629         um.on('update', this.onLoad, this);
49630         um.on('failure', this.onLoad, this);
49631         this.removeMask = true;
49632     }
49633 };
49634
49635 Roo.LoadMask.prototype = {
49636     /**
49637      * @cfg {Boolean} removeMask
49638      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49639      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49640      */
49641     /**
49642      * @cfg {String} msg
49643      * The text to display in a centered loading message box (defaults to 'Loading...')
49644      */
49645     msg : 'Loading...',
49646     /**
49647      * @cfg {String} msgCls
49648      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49649      */
49650     msgCls : 'x-mask-loading',
49651
49652     /**
49653      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49654      * @type Boolean
49655      */
49656     disabled: false,
49657
49658     /**
49659      * Disables the mask to prevent it from being displayed
49660      */
49661     disable : function(){
49662        this.disabled = true;
49663     },
49664
49665     /**
49666      * Enables the mask so that it can be displayed
49667      */
49668     enable : function(){
49669         this.disabled = false;
49670     },
49671
49672     // private
49673     onLoad : function(){
49674         this.el.unmask(this.removeMask);
49675     },
49676
49677     // private
49678     onBeforeLoad : function(){
49679         if(!this.disabled){
49680             this.el.mask(this.msg, this.msgCls);
49681         }
49682     },
49683
49684     // private
49685     destroy : function(){
49686         if(this.store){
49687             this.store.un('beforeload', this.onBeforeLoad, this);
49688             this.store.un('load', this.onLoad, this);
49689             this.store.un('loadexception', this.onLoad, this);
49690         }else{
49691             var um = this.el.getUpdateManager();
49692             um.un('beforeupdate', this.onBeforeLoad, this);
49693             um.un('update', this.onLoad, this);
49694             um.un('failure', this.onLoad, this);
49695         }
49696     }
49697 };/*
49698  * Based on:
49699  * Ext JS Library 1.1.1
49700  * Copyright(c) 2006-2007, Ext JS, LLC.
49701  *
49702  * Originally Released Under LGPL - original licence link has changed is not relivant.
49703  *
49704  * Fork - LGPL
49705  * <script type="text/javascript">
49706  */
49707 Roo.XTemplate = function(){
49708     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49709     var s = this.html;
49710
49711     s = ['<tpl>', s, '</tpl>'].join('');
49712
49713     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49714
49715     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49716     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49717     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49718     var m, id = 0;
49719     var tpls = [];
49720
49721     while(m = s.match(re)){
49722        var m2 = m[0].match(nameRe);
49723        var m3 = m[0].match(ifRe);
49724        var m4 = m[0].match(execRe);
49725        var exp = null, fn = null, exec = null;
49726        var name = m2 && m2[1] ? m2[1] : '';
49727        if(m3){
49728            exp = m3 && m3[1] ? m3[1] : null;
49729            if(exp){
49730                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49731            }
49732        }
49733        if(m4){
49734            exp = m4 && m4[1] ? m4[1] : null;
49735            if(exp){
49736                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49737            }
49738        }
49739        if(name){
49740            switch(name){
49741                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49742                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49743                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49744            }
49745        }
49746        tpls.push({
49747             id: id,
49748             target: name,
49749             exec: exec,
49750             test: fn,
49751             body: m[1]||''
49752         });
49753        s = s.replace(m[0], '{xtpl'+ id + '}');
49754        ++id;
49755     }
49756     for(var i = tpls.length-1; i >= 0; --i){
49757         this.compileTpl(tpls[i]);
49758     }
49759     this.master = tpls[tpls.length-1];
49760     this.tpls = tpls;
49761 };
49762 Roo.extend(Roo.XTemplate, Roo.Template, {
49763
49764     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49765
49766     applySubTemplate : function(id, values, parent){
49767         var t = this.tpls[id];
49768         if(t.test && !t.test.call(this, values, parent)){
49769             return '';
49770         }
49771         if(t.exec && t.exec.call(this, values, parent)){
49772             return '';
49773         }
49774         var vs = t.target ? t.target.call(this, values, parent) : values;
49775         parent = t.target ? values : parent;
49776         if(t.target && vs instanceof Array){
49777             var buf = [];
49778             for(var i = 0, len = vs.length; i < len; i++){
49779                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49780             }
49781             return buf.join('');
49782         }
49783         return t.compiled.call(this, vs, parent);
49784     },
49785
49786     compileTpl : function(tpl){
49787         var fm = Roo.util.Format;
49788         var useF = this.disableFormats !== true;
49789         var sep = Roo.isGecko ? "+" : ",";
49790         var fn = function(m, name, format, args){
49791             if(name.substr(0, 4) == 'xtpl'){
49792                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49793             }
49794             var v;
49795             if(name.indexOf('.') != -1){
49796                 v = name;
49797             }else{
49798                 v = "values['" + name + "']";
49799             }
49800             if(format && useF){
49801                 args = args ? ',' + args : "";
49802                 if(format.substr(0, 5) != "this."){
49803                     format = "fm." + format + '(';
49804                 }else{
49805                     format = 'this.call("'+ format.substr(5) + '", ';
49806                     args = ", values";
49807                 }
49808             }else{
49809                 args= ''; format = "("+v+" === undefined ? '' : ";
49810             }
49811             return "'"+ sep + format + v + args + ")"+sep+"'";
49812         };
49813         var body;
49814         // branched to use + in gecko and [].join() in others
49815         if(Roo.isGecko){
49816             body = "tpl.compiled = function(values, parent){ return '" +
49817                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49818                     "';};";
49819         }else{
49820             body = ["tpl.compiled = function(values, parent){ return ['"];
49821             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49822             body.push("'].join('');};");
49823             body = body.join('');
49824         }
49825         /** eval:var:zzzzzzz */
49826         eval(body);
49827         return this;
49828     },
49829
49830     applyTemplate : function(values){
49831         return this.master.compiled.call(this, values, {});
49832         var s = this.subs;
49833     },
49834
49835     apply : function(){
49836         return this.applyTemplate.apply(this, arguments);
49837     },
49838
49839     compile : function(){return this;}
49840 });
49841
49842 Roo.XTemplate.from = function(el){
49843     el = Roo.getDom(el);
49844     return new Roo.XTemplate(el.value || el.innerHTML);
49845 };/*
49846  * Original code for Roojs - LGPL
49847  * <script type="text/javascript">
49848  */
49849  
49850 /**
49851  * @class Roo.XComponent
49852  * A delayed Element creator...
49853  * 
49854  * Mypart.xyx = new Roo.XComponent({
49855
49856     parent : 'Mypart.xyz', // empty == document.element.!!
49857     order : '001',
49858     name : 'xxxx'
49859     region : 'xxxx'
49860     disabled : function() {} 
49861      
49862     tree : function() { // return an tree of xtype declared components
49863         var MODULE = this;
49864         return 
49865         {
49866             xtype : 'NestedLayoutPanel',
49867             // technicall
49868         }
49869      ]
49870  *})
49871  * @extends Roo.util.Observable
49872  * @constructor
49873  * @param cfg {Object} configuration of component
49874  * 
49875  */
49876 Roo.XComponent = function(cfg) {
49877     Roo.apply(this, cfg);
49878     this.addEvents({ 
49879         /**
49880              * @event built
49881              * Fires when this the componnt is built
49882              * @param {Roo.XComponent} c the component
49883              */
49884         'built' : true,
49885         /**
49886              * @event buildcomplete
49887              * Fires on the top level element when all elements have been built
49888              * @param {Roo.XComponent} c the top level component.
49889          */
49890         'buildcomplete' : true,
49891         
49892     });
49893     
49894     Roo.XComponent.register(this);
49895     this.modules = false;
49896     this.el = false; // where the layout goes..
49897     
49898     
49899 }
49900 Roo.extend(Roo.XComponent, Roo.util.Observable, {
49901     /**
49902      * @property el
49903      * The created element (with Roo.factory())
49904      * @type {Roo.Layout}
49905      */
49906     el  : false,
49907     
49908     /**
49909      * @property el
49910      * for BC  - use el in new code
49911      * @type {Roo.Layout}
49912      */
49913     panel : false,
49914     
49915     /**
49916      * @property layout
49917      * for BC  - use el in new code
49918      * @type {Roo.Layout}
49919      */
49920     layout : false,
49921     
49922      /**
49923      * @cfg {Function|boolean} disabled
49924      * If this module is disabled by some rule, return true from the funtion
49925      */
49926     disabled : false,
49927     
49928     /**
49929      * @cfg {String} parent 
49930      * Name of parent element which it get xtype added to..
49931      */
49932     parent: false,
49933     
49934     /**
49935      * @cfg {String} order
49936      * Used to set the order in which elements are created (usefull for multiple tabs)
49937      */
49938     
49939     order : false,
49940     /**
49941      * @cfg {String} name
49942      * String to display while loading.
49943      */
49944     name : false,
49945     /**
49946      * @cfg {Array} items
49947      * A single item array - the first element is the root of the tree..
49948      * It's done this way to stay compatible with the Xtype system...
49949      */
49950     items : false,
49951      
49952      
49953     
49954 });
49955
49956 Roo.apply(Roo.XComponent, {
49957     
49958     /**
49959      * @property  buildCompleted
49960      * True when the builder has completed building the interface.
49961      * @type Boolean
49962      */
49963     buildCompleted : false,
49964      
49965     /**
49966      * @property  topModule
49967      * the upper most module - uses document.element as it's constructor.
49968      * @type Object
49969      */
49970      
49971     topModule  : false,
49972       
49973     /**
49974      * @property  modules
49975      * array of modules to be created by registration system.
49976      * @type Roo.XComponent
49977      */
49978     
49979     modules : [],
49980       
49981     
49982     /**
49983      * Register components to be built later.
49984      *
49985      * This solves the following issues
49986      * - Building is not done on page load, but after an authentication process has occured.
49987      * - Interface elements are registered on page load
49988      * - Parent Interface elements may not be loaded before child, so this handles that..
49989      * 
49990      *
49991      * example:
49992      * 
49993      * MyApp.register({
49994           order : '000001',
49995           module : 'Pman.Tab.projectMgr',
49996           region : 'center',
49997           parent : 'Pman.layout',
49998           disabled : false,  // or use a function..
49999         })
50000      
50001      * * @param {Object} details about module
50002      */
50003     register : function(obj) {
50004         this.modules.push(obj);
50005          
50006     },
50007     /**
50008      * convert a string to an object..
50009      * 
50010      */
50011     
50012     toObject : function(str)
50013     {
50014         if (!str || typeof(str) == 'object') {
50015             return str;
50016         }
50017         var ar = str.split('.');
50018         var rt, o;
50019         rt = ar.shift();
50020             /** eval:var:o */
50021         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50022         if (o === false) {
50023             throw "Module not found : " + str;
50024         }
50025         Roo.each(ar, function(e) {
50026             if (typeof(o[e]) == 'undefined') {
50027                 throw "Module not found : " + str;
50028             }
50029             o = o[e];
50030         });
50031         return o;
50032         
50033     },
50034     
50035     
50036     /**
50037      * move modules into their correct place in the tree..
50038      * 
50039      */
50040     preBuild : function ()
50041     {
50042         
50043         Roo.each(this.modules , function (obj)
50044         {
50045             obj.parent = this.toObject(obj.parent);
50046             
50047             if (!obj.parent) {
50048                 this.topModule = obj;
50049                 return;
50050             }
50051             
50052             if (!obj.parent.modules) {
50053                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50054                     function(o) { return o.order + '' }
50055                 );
50056             }
50057             
50058             obj.parent.modules.add(obj);
50059         }, this);
50060     },
50061     
50062      /**
50063      * make a list of modules to build.
50064      * @return {Array} list of modules. 
50065      */ 
50066     
50067     buildOrder : function()
50068     {
50069         var _this = this;
50070         var cmp = function(a,b) {   
50071             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50072         };
50073         
50074         if (!this.topModule || !this.topModule.modules) {
50075             throw "No top level modules to build";
50076         }
50077        
50078         // make a flat list in order of modules to build.
50079         var mods = [ this.topModule ];
50080         
50081         
50082         // add modules to their parents..
50083         var addMod = function(m) {
50084            // console.log(m.modKey);
50085             
50086             mods.push(m);
50087             if (m.modules) {
50088                 m.modules.keySort('ASC',  cmp );
50089                 m.modules.each(addMod);
50090             }
50091             // not sure if this is used any more..
50092             if (m.finalize) {
50093                 m.finalize.name = m.name + " (clean up) ";
50094                 mods.push(m.finalize);
50095             }
50096             
50097         }
50098         this.topModule.modules.keySort('ASC',  cmp );
50099         this.topModule.modules.each(addMod);
50100         return mods;
50101     },
50102     
50103      /**
50104      * Build the registered modules.
50105      * @param {Object} parent element.
50106      * @param {Function} optional method to call after module has been added.
50107      * 
50108      */ 
50109    
50110     build : function() 
50111     {
50112         
50113         this.preBuild();
50114         var mods = this.buildOrder();
50115       
50116         //this.allmods = mods;
50117         //console.log(mods);
50118         //return;
50119         if (!mods.length) { // should not happen
50120             throw "NO modules!!!";
50121         }
50122         
50123         
50124         
50125         // flash it up as modal - so we store the mask!?
50126         Roo.MessageBox.show({ title: 'loading' });
50127         Roo.MessageBox.show({
50128            title: "Please wait...",
50129            msg: "Building Interface...",
50130            width:450,
50131            progress:true,
50132            closable:false,
50133            modal: false
50134           
50135         });
50136         var total = mods.length;
50137         
50138         var _this = this;
50139         var progressRun = function() {
50140             if (!mods.length) {
50141                 console.log('hide?');
50142                 Roo.MessageBox.hide();
50143                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50144                 return;    
50145             }
50146             
50147             var m = mods.shift();
50148             console.log(m);
50149             if (typeof(m) == 'function') { // not sure if this is supported any more..
50150                 m.call(this);
50151                 return progressRun.defer(10, _this);
50152             } 
50153             
50154             Roo.MessageBox.updateProgress(
50155                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50156                     " of " + total + 
50157                     (m.name ? (' - ' + m.name) : '')
50158                     );
50159             
50160          
50161             
50162             var disabled = (typeof(m.disabled) == 'function') ?
50163                 m.disabled.call(m.module.disabled) : m.disabled;    
50164             
50165             
50166             if (disabled) {
50167                 return progressRun(); // we do not update the display!
50168             }
50169             
50170             if (!m.parent) {
50171                 // it's a top level one..
50172                 var layoutbase = new Ext.BorderLayout(document.body, {
50173                
50174                     center: {
50175                          titlebar: false,
50176                          autoScroll:false,
50177                          closeOnTab: true,
50178                          tabPosition: 'top',
50179                          //resizeTabs: true,
50180                          alwaysShowTabs: true,
50181                          minTabWidth: 140
50182                     }
50183                 });
50184                 var tree = m.tree();
50185                 tree.region = 'center';
50186                 m.el = layoutbase.addxtype(tree);
50187                 m.panel = m.el;
50188                 m.layout = m.panel.layout;    
50189                 return progressRun.defer(10, _this);
50190             }
50191             
50192             var tree = m.tree();
50193             tree.region = tree.region || m.region;
50194             m.el = m.parent.el.addxtype(tree);
50195             m.fireEvent('built', m);
50196             m.panel = m.el;
50197             m.layout = m.panel.layout;    
50198             progressRun.defer(10, _this); 
50199             
50200         }
50201         progressRun.defer(1, _this);
50202      
50203         
50204         
50205     }
50206      
50207    
50208     
50209     
50210 });
50211  //<script type="text/javascript">
50212
50213
50214 /**
50215  * @class Roo.Login
50216  * @extends Roo.LayoutDialog
50217  * A generic Login Dialog..... - only one needed in theory!?!?
50218  *
50219  * Fires XComponent builder on success...
50220  * 
50221  * Sends 
50222  *    username,password, lang = for login actions.
50223  *    check = 1 for periodic checking that sesion is valid.
50224  *    passwordRequest = email request password
50225  *    logout = 1 = to logout
50226  * 
50227  * Affects: (this id="????" elements)
50228  *   loading  (removed) (used to indicate application is loading)
50229  *   loading-mask (hides) (used to hide application when it's building loading)
50230  *   
50231  * 
50232  * Usage: 
50233  *    
50234  * 
50235  * Myapp.login = Roo.Login({
50236      url: xxxx,
50237    
50238      realm : 'Myapp', 
50239      
50240      
50241      method : 'POST',
50242      
50243      
50244      * 
50245  })
50246  * 
50247  * 
50248  * 
50249  **/
50250  
50251 Roo.Login = function(cfg)
50252 {
50253     this.addEvents({
50254         'refreshed' : true,
50255     });
50256     
50257     Roo.apply(this,cfg);
50258     
50259     Roo.onReady(function() {
50260         this.onLoad();
50261     }, this);
50262     // call parent..
50263     
50264    
50265     Roo.Login.superclass.constructor.call(this, this);
50266     //this.addxtype(this.items[0]);
50267     
50268     
50269 }
50270
50271
50272 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50273     
50274     /**
50275      * @cfg {String} method
50276      * Method used to query for login details.
50277      */
50278     
50279     method : 'POST',
50280     /**
50281      * @cfg {String} url
50282      * URL to query login data. - eg. baseURL + '/Login.php'
50283      */
50284     url : '',
50285     
50286     /**
50287      * @property user
50288      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50289      * @type {Object} 
50290      */
50291     user : false,
50292     /**
50293      * @property checkFails
50294      * Number of times we have attempted to get authentication check, and failed.
50295      * @type {Number} 
50296      */
50297     checkFails : 0,
50298       /**
50299      * @property intervalID
50300      * The window interval that does the constant login checking.
50301      * @type {Number} 
50302      */
50303     intervalID : 0,
50304     
50305     
50306     onLoad : function() // called on page load...
50307     {
50308         // load 
50309          
50310         if (Roo.get('loading')) { // clear any loading indicator..
50311             Roo.get('loading').remove();
50312         }
50313         
50314         //this.switchLang('en'); // set the language to english..
50315        
50316         this.check({
50317             success:  function(response, opts)  {  // check successfull...
50318             
50319                 var res = this.processResponse(response);
50320                 this.checkFails =0;
50321                 if (!res.success) { // error!
50322                     this.checkFails = 5;
50323                     //console.log('call failure');
50324                     return this.failure(response,opts);
50325                 }
50326                 
50327                 if (!res.data.id) { // id=0 == login failure.
50328                     return this.show();
50329                 }
50330                 
50331                               
50332                         //console.log(success);
50333                 this.fillAuth(res.data);   
50334                 this.checkFails =0;
50335                 Roo.XComponent.build();
50336             },
50337             failure : this.show
50338         });
50339         
50340     }, 
50341     
50342     
50343     check: function(cfg) // called every so often to refresh cookie etc..
50344     {
50345         if (cfg.again) { // could be undefined..
50346             this.checkFails++;
50347         } else {
50348             this.checkFails = 0;
50349         }
50350         var _this = this;
50351         if (this.sending) {
50352             if ( this.checkFails > 4) {
50353                 Roo.MessageBox.alert("Error",  
50354                     "Error getting authentication status. - try reloading, or wait a while", function() {
50355                         _this.sending = false;
50356                     }); 
50357                 return;
50358             }
50359             cfg.again = true;
50360             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50361             return;
50362         }
50363         this.sending = true;
50364         
50365         Roo.Ajax.request({  
50366             url: this.url,
50367             params: {
50368                 getAuthUser: true
50369             },  
50370             method: this.method,
50371             success:  cfg.success || this.success,
50372             failure : cfg.failure || this.failure,
50373             scope : this,
50374             callCfg : cfg
50375               
50376         });  
50377     }, 
50378     
50379     
50380     logout: function()
50381     {
50382         window.onbeforeunload = function() { }; // false does not work for IE..
50383         this.user = false;
50384         var _this = this;
50385         
50386         Roo.Ajax.request({  
50387             url: this.url,
50388             params: {
50389                 logout: 1
50390             },  
50391             method: 'GET',
50392             failure : function() {
50393                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50394                     document.location = document.location.toString() + '?ts=' + Math.random();
50395                 });
50396                 
50397             },
50398             success : function() {
50399                 _this.user = false;
50400                 this.checkFails =0;
50401                 // fixme..
50402                 document.location = document.location.toString() + '?ts=' + Math.random();
50403             }
50404               
50405               
50406         }); 
50407     },
50408     
50409     processResponse : function (response)
50410     {
50411         var res = '';
50412         try {
50413             res = Roo.decode(response.responseText);
50414             // oops...
50415             if (typeof(res) != 'object') {
50416                 res = { success : false, errorMsg : res, errors : true };
50417             }
50418             if (typeof(res.success) == 'undefined') {
50419                 res.success = false;
50420             }
50421             
50422         } catch(e) {
50423             res = { success : false,  errorMsg : response.responseText, errors : true };
50424         }
50425         return res;
50426     },
50427     
50428     success : function(response, opts)  // check successfull...
50429     {  
50430         this.sending = false;
50431         var res = this.processResponse(response);
50432         if (!res.success) {
50433             return this.failure(response, opts);
50434         }
50435         if (!res.data || !res.data.id) {
50436             return this.failure(response,opts);
50437         }
50438         //console.log(res);
50439         this.fillAuth(res.data);
50440         
50441         this.checkFails =0;
50442         
50443     },
50444     
50445     
50446     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50447     {
50448         this.authUser = -1;
50449         this.sending = false;
50450         var res = this.processResponse(response);
50451         //console.log(res);
50452         if ( this.checkFails > 2) {
50453         
50454             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50455                 "Error getting authentication status. - try reloading"); 
50456             return;
50457         }
50458         opts.callCfg.again = true;
50459         this.check.defer(1000, this, [ opts.callCfg ]);
50460         return;  
50461     },
50462     
50463     
50464     
50465     fillAuth: function(au) {
50466         this.startAuthCheck();
50467         this.authUserId = au.id;
50468         this.authUser = au;
50469         this.lastChecked = new Date();
50470         this.fireEvent('refreshed', au);
50471         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50472         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50473         au.lang = au.lang || 'en';
50474         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50475         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50476         this.switchLang(au.lang );
50477         
50478      
50479         // open system... - -on setyp..
50480         if (this.authUserId  < 0) {
50481             Roo.MessageBox.alert("Warning", 
50482                 "This is an open system - please set up a admin user with a password.");  
50483         }
50484          
50485         //Pman.onload(); // which should do nothing if it's a re-auth result...
50486         
50487              
50488     },
50489     
50490     startAuthCheck : function() // starter for timeout checking..
50491     {
50492         if (this.intervalID) { // timer already in place...
50493             return false;
50494         }
50495         var _this = this;
50496         this.intervalID =  window.setInterval(function() {
50497               _this.check(false);
50498             }, 120000); // every 120 secs = 2mins..
50499         
50500         
50501     },
50502          
50503     
50504     switchLang : function (lang) 
50505     {
50506         _T = typeof(_T) == 'undefined' ? false : _T;
50507           if (!_T || !lang.length) {
50508             return;
50509         }
50510         
50511         if (!_T && lang != 'en') {
50512             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50513             return;
50514         }
50515         
50516         if (typeof(_T.en) == 'undefined') {
50517             _T.en = {};
50518             Roo.apply(_T.en, _T);
50519         }
50520         
50521         if (typeof(_T[lang]) == 'undefined') {
50522             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50523             return;
50524         }
50525         
50526         
50527         Roo.apply(_T, _T[lang]);
50528         // just need to set the text values for everything...
50529         var _this = this;
50530         /* this will not work ...
50531         if (this.form) { 
50532             
50533                
50534             function formLabel(name, val) {
50535                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50536             }
50537             
50538             formLabel('password', "Password"+':');
50539             formLabel('username', "Email Address"+':');
50540             formLabel('lang', "Language"+':');
50541             this.dialog.setTitle("Login");
50542             this.dialog.buttons[0].setText("Forgot Password");
50543             this.dialog.buttons[1].setText("Login");
50544         }
50545         */
50546         
50547         
50548     },
50549     
50550     
50551     title: "Login",
50552     modal: true,
50553     width:  350,
50554     //height: 230,
50555     height: 180,
50556     shadow: true,
50557     minWidth:200,
50558     minHeight:180,
50559     //proxyDrag: true,
50560     closable: false,
50561     draggable: false,
50562     collapsible: false,
50563     resizable: false,
50564     center: {  // needed??
50565         autoScroll:false,
50566         titlebar: false,
50567        // tabPosition: 'top',
50568         hideTabs: true,
50569         closeOnTab: true,
50570         alwaysShowTabs: false
50571     } ,
50572     listeners : {
50573         
50574         show  : function(dlg)
50575         {
50576             //console.log(this);
50577             this.form = this.layout.getRegion('center').activePanel.form;
50578             this.form.dialog = dlg;
50579             this.buttons[0].form = this.form;
50580             this.buttons[0].dialog = dlg
50581             this.buttons[1].form = this.form;
50582             this.buttons[1].dialog = dlg;
50583            
50584            //this.resizeToLogo.defer(1000,this);
50585             // this is all related to resizing for logos..
50586             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50587            //// if (!sz) {
50588              //   this.resizeToLogo.defer(1000,this);
50589              //   return;
50590            // }
50591             //var w = Ext.lib.Dom.getViewWidth() - 100;
50592             //var h = Ext.lib.Dom.getViewHeight() - 100;
50593             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50594             //this.center();
50595             if (this.disabled) {
50596                 this.hide();
50597                 return;
50598             }
50599             
50600             if (this.user.id < 0) { // used for inital setup situations.
50601                 return;
50602             }
50603             
50604             if (this.intervalID) {
50605                 // remove the timer
50606                 window.clearInterval(this.intervalID);
50607                 this.intervalID = false;
50608             }
50609             
50610             
50611             if (Roo.get('loading')) {
50612                 Roo.get('loading').remove();
50613             }
50614             if (Roo.get('loading-mask')) {
50615                 Roo.get('loading-mask').hide();
50616             }
50617             
50618             //incomming._node = tnode;
50619             this.form.reset();
50620             //this.dialog.modal = !modal;
50621             //this.dialog.show();
50622             this.el.unmask(); 
50623             
50624             
50625             this.form.setValues({
50626                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50627                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50628             });
50629             
50630             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50631             if (this.form.findField('username').getValue().length > 0 ){
50632                 this.form.findField('password').focus();
50633             } else {
50634                this.form.findField('username').focus();
50635             }
50636     
50637         }
50638     },
50639     items : [
50640          {
50641        
50642             xtype : 'ContentPanel',
50643             xns : Roo,
50644             region: 'center',
50645             fitToFrame : true,
50646             
50647             items : [
50648     
50649                 {
50650                
50651                     xtype : 'Form',
50652                     xns : Roo.form,
50653                     labelWidth: 100,
50654                     style : 'margin: 10px;',
50655                     
50656                     listeners : {
50657                         actionfailed : function(f, act) {
50658                             // form can return { errors: .... }
50659                                 
50660                             //act.result.errors // invalid form element list...
50661                             //act.result.errorMsg// invalid form element list...
50662                             
50663                             this.dialog.el.unmask();
50664                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50665                                         "Login failed - communication error - try again.");
50666                                       
50667                         },
50668                         actioncomplete: function(re, act) {
50669                              
50670                             Roo.state.Manager.set(
50671                                 this.dialog.realm + '.username',  
50672                                     this.findField('username').getValue()
50673                             );
50674                             Roo.state.Manager.set(
50675                                 this.dialog.realm + '.lang',  
50676                                 this.findField('lang').getValue() 
50677                             );
50678                             
50679                             this.dialog.fillAuth(act.result.data);
50680                               
50681                             this.dialog.hide();
50682                             
50683                             if (Roo.get('loading-mask')) {
50684                                 Roo.get('loading-mask').show();
50685                             }
50686                             Roo.XComponent.build();
50687                             
50688                              
50689                             
50690                         }
50691                     },
50692                     items : [
50693                         {
50694                             xtype : 'TextField',
50695                             xns : Roo.form,
50696                             fieldLabel: "Email Address",
50697                             name: 'username',
50698                             width:200,
50699                             autoCreate : {tag: "input", type: "text", size: "20"}
50700                         },
50701                         {
50702                             xtype : 'TextField',
50703                             xns : Roo.form,
50704                             fieldLabel: "Password",
50705                             inputType: 'password',
50706                             name: 'password',
50707                             width:200,
50708                             autoCreate : {tag: "input", type: "text", size: "20"},
50709                             listeners : {
50710                                 specialkey : function(e,ev) {
50711                                     if (ev.keyCode == 13) {
50712                                         this.form.dialog.el.mask("Logging in");
50713                                         this.form.doAction('submit', {
50714                                             url: this.form.dialog.url,
50715                                             method: this.form.dialog.method,
50716                                         });
50717                                     }
50718                                 }
50719                             }  
50720                         },
50721                         {
50722                             xtype : 'ComboBox',
50723                             xns : Roo.form,
50724                             fieldLabel: "Language",
50725                             name : 'langdisp',
50726                             store: {
50727                                 xtype : 'SimpleStore',
50728                                 fields: ['lang', 'ldisp'],
50729                                 data : [
50730                                     [ 'en', 'English' ],
50731                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50732                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50733                                 ]
50734                             },
50735                             
50736                             valueField : 'lang',
50737                             hiddenName:  'lang',
50738                             width: 200,
50739                             displayField:'ldisp',
50740                             typeAhead: false,
50741                             editable: false,
50742                             mode: 'local',
50743                             triggerAction: 'all',
50744                             emptyText:'Select a Language...',
50745                             selectOnFocus:true,
50746                             listeners : {
50747                                 select :  function(cb, rec, ix) {
50748                                     this.form.switchLang(rec.data.lang);
50749                                 }
50750                             }
50751                         
50752                         }
50753                     ]
50754                 }
50755                   
50756                 
50757             ]
50758         }
50759     ],
50760     buttons : [
50761         {
50762             xtype : 'Button',
50763             xns : 'Roo',
50764             text : "Forgot Password",
50765             listeners : {
50766                 click : function() {
50767                     //console.log(this);
50768                     var n = this.form.findField('username').getValue();
50769                     if (!n.length) {
50770                         Roo.MessageBox.alert("Error", "Fill in your email address");
50771                         return;
50772                     }
50773                     Roo.Ajax.request({
50774                         url: this.dialog.url,
50775                         params: {
50776                             passwordRequest: n
50777                         },
50778                         method: this.dialog.method,
50779                         success:  function(response, opts)  {  // check successfull...
50780                         
50781                             var res = this.dialog.processResponse(response);
50782                             if (!res.success) { // error!
50783                                Roo.MessageBox.alert("Error" ,
50784                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50785                                return;
50786                             }
50787                             Roo.MessageBox.alert("Notice" ,
50788                                 "Please check you email for the Password Reset message");
50789                         },
50790                         failure : function() {
50791                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50792                         }
50793                         
50794                     });
50795                 }
50796             }
50797         },
50798         {
50799             xtype : 'Button',
50800             xns : 'Roo',
50801             text : "Login",
50802             listeners : {
50803                 
50804                 click : function () {
50805                         
50806                     this.dialog.el.mask("Logging in");
50807                     this.form.doAction('submit', {
50808                             url: this.dialog.url,
50809                             method: this.dialog.method
50810                     });
50811                 }
50812             }
50813         }
50814     ]
50815   
50816   
50817 })
50818  
50819
50820
50821