images/ux/lightbox/nextlabel.gif
[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     html :  '&lt;div name="{id}"&gt;' + 
4405         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4406         '&lt;/div&gt;',
4407     myformat: function (value, allValues) {
4408         return 'XX' + value;
4409     }
4410 });
4411 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4412 </code></pre>
4413 * 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>. 
4414 * @constructor
4415 * @param {Object} cfg - Configuration object.
4416 */
4417 Roo.Template = function(cfg){
4418     // BC!
4419     if(cfg instanceof Array){
4420         cfg = cfg.join("");
4421     }else if(arguments.length > 1){
4422         cfg = Array.prototype.join.call(arguments, "");
4423     }
4424     
4425     
4426     if (typeof(cfg) == 'object') {
4427         Roo.apply(this,cfg)
4428     } else {
4429         // bc
4430         this.html = cfg;
4431     }
4432     
4433     
4434 };
4435 Roo.Template.prototype = {
4436     
4437     /**
4438      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4439      */
4440     html : '',
4441     /**
4442      * Returns an HTML fragment of this template with the specified values applied.
4443      * @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'})
4444      * @return {String} The HTML fragment
4445      */
4446     applyTemplate : function(values){
4447         try {
4448             
4449             if(this.compiled){
4450                 return this.compiled(values);
4451             }
4452             var useF = this.disableFormats !== true;
4453             var fm = Roo.util.Format, tpl = this;
4454             var fn = function(m, name, format, args){
4455                 if(format && useF){
4456                     if(format.substr(0, 5) == "this."){
4457                         return tpl.call(format.substr(5), values[name], values);
4458                     }else{
4459                         if(args){
4460                             // quoted values are required for strings in compiled templates, 
4461                             // but for non compiled we need to strip them
4462                             // quoted reversed for jsmin
4463                             var re = /^\s*['"](.*)["']\s*$/;
4464                             args = args.split(',');
4465                             for(var i = 0, len = args.length; i < len; i++){
4466                                 args[i] = args[i].replace(re, "$1");
4467                             }
4468                             args = [values[name]].concat(args);
4469                         }else{
4470                             args = [values[name]];
4471                         }
4472                         return fm[format].apply(fm, args);
4473                     }
4474                 }else{
4475                     return values[name] !== undefined ? values[name] : "";
4476                 }
4477             };
4478             return this.html.replace(this.re, fn);
4479         } catch (e) {
4480             Roo.log(e);
4481             throw e;
4482         }
4483          
4484     },
4485     
4486     /**
4487      * Sets the HTML used as the template and optionally compiles it.
4488      * @param {String} html
4489      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4490      * @return {Roo.Template} this
4491      */
4492     set : function(html, compile){
4493         this.html = html;
4494         this.compiled = null;
4495         if(compile){
4496             this.compile();
4497         }
4498         return this;
4499     },
4500     
4501     /**
4502      * True to disable format functions (defaults to false)
4503      * @type Boolean
4504      */
4505     disableFormats : false,
4506     
4507     /**
4508     * The regular expression used to match template variables 
4509     * @type RegExp
4510     * @property 
4511     */
4512     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4513     
4514     /**
4515      * Compiles the template into an internal function, eliminating the RegEx overhead.
4516      * @return {Roo.Template} this
4517      */
4518     compile : function(){
4519         var fm = Roo.util.Format;
4520         var useF = this.disableFormats !== true;
4521         var sep = Roo.isGecko ? "+" : ",";
4522         var fn = function(m, name, format, args){
4523             if(format && useF){
4524                 args = args ? ',' + args : "";
4525                 if(format.substr(0, 5) != "this."){
4526                     format = "fm." + format + '(';
4527                 }else{
4528                     format = 'this.call("'+ format.substr(5) + '", ';
4529                     args = ", values";
4530                 }
4531             }else{
4532                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4533             }
4534             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4535         };
4536         var body;
4537         // branched to use + in gecko and [].join() in others
4538         if(Roo.isGecko){
4539             body = "this.compiled = function(values){ return '" +
4540                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4541                     "';};";
4542         }else{
4543             body = ["this.compiled = function(values){ return ['"];
4544             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4545             body.push("'].join('');};");
4546             body = body.join('');
4547         }
4548         /**
4549          * eval:var:values
4550          * eval:var:fm
4551          */
4552         eval(body);
4553         return this;
4554     },
4555     
4556     // private function used to call members
4557     call : function(fnName, value, allValues){
4558         return this[fnName](value, allValues);
4559     },
4560     
4561     /**
4562      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4563      * @param {String/HTMLElement/Roo.Element} el The context element
4564      * @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'})
4565      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4566      * @return {HTMLElement/Roo.Element} The new node or Element
4567      */
4568     insertFirst: function(el, values, returnElement){
4569         return this.doInsert('afterBegin', el, values, returnElement);
4570     },
4571
4572     /**
4573      * Applies the supplied values to the template and inserts the new node(s) before el.
4574      * @param {String/HTMLElement/Roo.Element} el The context element
4575      * @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'})
4576      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4577      * @return {HTMLElement/Roo.Element} The new node or Element
4578      */
4579     insertBefore: function(el, values, returnElement){
4580         return this.doInsert('beforeBegin', el, values, returnElement);
4581     },
4582
4583     /**
4584      * Applies the supplied values to the template and inserts the new node(s) after el.
4585      * @param {String/HTMLElement/Roo.Element} el The context element
4586      * @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'})
4587      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4588      * @return {HTMLElement/Roo.Element} The new node or Element
4589      */
4590     insertAfter : function(el, values, returnElement){
4591         return this.doInsert('afterEnd', el, values, returnElement);
4592     },
4593     
4594     /**
4595      * Applies the supplied values to the template and appends the new node(s) to el.
4596      * @param {String/HTMLElement/Roo.Element} el The context element
4597      * @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'})
4598      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4599      * @return {HTMLElement/Roo.Element} The new node or Element
4600      */
4601     append : function(el, values, returnElement){
4602         return this.doInsert('beforeEnd', el, values, returnElement);
4603     },
4604
4605     doInsert : function(where, el, values, returnEl){
4606         el = Roo.getDom(el);
4607         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4608         return returnEl ? Roo.get(newNode, true) : newNode;
4609     },
4610
4611     /**
4612      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4613      * @param {String/HTMLElement/Roo.Element} el The context element
4614      * @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'})
4615      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4616      * @return {HTMLElement/Roo.Element} The new node or Element
4617      */
4618     overwrite : function(el, values, returnElement){
4619         el = Roo.getDom(el);
4620         el.innerHTML = this.applyTemplate(values);
4621         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4622     }
4623 };
4624 /**
4625  * Alias for {@link #applyTemplate}
4626  * @method
4627  */
4628 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4629
4630 // backwards compat
4631 Roo.DomHelper.Template = Roo.Template;
4632
4633 /**
4634  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4635  * @param {String/HTMLElement} el A DOM element or its id
4636  * @returns {Roo.Template} The created template
4637  * @static
4638  */
4639 Roo.Template.from = function(el){
4640     el = Roo.getDom(el);
4641     return new Roo.Template(el.value || el.innerHTML);
4642 };/*
4643  * Based on:
4644  * Ext JS Library 1.1.1
4645  * Copyright(c) 2006-2007, Ext JS, LLC.
4646  *
4647  * Originally Released Under LGPL - original licence link has changed is not relivant.
4648  *
4649  * Fork - LGPL
4650  * <script type="text/javascript">
4651  */
4652  
4653
4654 /*
4655  * This is code is also distributed under MIT license for use
4656  * with jQuery and prototype JavaScript libraries.
4657  */
4658 /**
4659  * @class Roo.DomQuery
4660 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).
4661 <p>
4662 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>
4663
4664 <p>
4665 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.
4666 </p>
4667 <h4>Element Selectors:</h4>
4668 <ul class="list">
4669     <li> <b>*</b> any element</li>
4670     <li> <b>E</b> an element with the tag E</li>
4671     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4672     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4673     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4674     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4675 </ul>
4676 <h4>Attribute Selectors:</h4>
4677 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4678 <ul class="list">
4679     <li> <b>E[foo]</b> has an attribute "foo"</li>
4680     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4681     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4682     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4683     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4684     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4685     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4686 </ul>
4687 <h4>Pseudo Classes:</h4>
4688 <ul class="list">
4689     <li> <b>E:first-child</b> E is the first child of its parent</li>
4690     <li> <b>E:last-child</b> E is the last child of its parent</li>
4691     <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>
4692     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4693     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4694     <li> <b>E:only-child</b> E is the only child of its parent</li>
4695     <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>
4696     <li> <b>E:first</b> the first E in the resultset</li>
4697     <li> <b>E:last</b> the last E in the resultset</li>
4698     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4699     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4700     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4701     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4702     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4703     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4704     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4705     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4706     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4707 </ul>
4708 <h4>CSS Value Selectors:</h4>
4709 <ul class="list">
4710     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4711     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4712     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4713     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4714     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4715     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4716 </ul>
4717  * @singleton
4718  */
4719 Roo.DomQuery = function(){
4720     var cache = {}, simpleCache = {}, valueCache = {};
4721     var nonSpace = /\S/;
4722     var trimRe = /^\s+|\s+$/g;
4723     var tplRe = /\{(\d+)\}/g;
4724     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4725     var tagTokenRe = /^(#)?([\w-\*]+)/;
4726     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4727
4728     function child(p, index){
4729         var i = 0;
4730         var n = p.firstChild;
4731         while(n){
4732             if(n.nodeType == 1){
4733                if(++i == index){
4734                    return n;
4735                }
4736             }
4737             n = n.nextSibling;
4738         }
4739         return null;
4740     };
4741
4742     function next(n){
4743         while((n = n.nextSibling) && n.nodeType != 1);
4744         return n;
4745     };
4746
4747     function prev(n){
4748         while((n = n.previousSibling) && n.nodeType != 1);
4749         return n;
4750     };
4751
4752     function children(d){
4753         var n = d.firstChild, ni = -1;
4754             while(n){
4755                 var nx = n.nextSibling;
4756                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4757                     d.removeChild(n);
4758                 }else{
4759                     n.nodeIndex = ++ni;
4760                 }
4761                 n = nx;
4762             }
4763             return this;
4764         };
4765
4766     function byClassName(c, a, v){
4767         if(!v){
4768             return c;
4769         }
4770         var r = [], ri = -1, cn;
4771         for(var i = 0, ci; ci = c[i]; i++){
4772             if((' '+ci.className+' ').indexOf(v) != -1){
4773                 r[++ri] = ci;
4774             }
4775         }
4776         return r;
4777     };
4778
4779     function attrValue(n, attr){
4780         if(!n.tagName && typeof n.length != "undefined"){
4781             n = n[0];
4782         }
4783         if(!n){
4784             return null;
4785         }
4786         if(attr == "for"){
4787             return n.htmlFor;
4788         }
4789         if(attr == "class" || attr == "className"){
4790             return n.className;
4791         }
4792         return n.getAttribute(attr) || n[attr];
4793
4794     };
4795
4796     function getNodes(ns, mode, tagName){
4797         var result = [], ri = -1, cs;
4798         if(!ns){
4799             return result;
4800         }
4801         tagName = tagName || "*";
4802         if(typeof ns.getElementsByTagName != "undefined"){
4803             ns = [ns];
4804         }
4805         if(!mode){
4806             for(var i = 0, ni; ni = ns[i]; i++){
4807                 cs = ni.getElementsByTagName(tagName);
4808                 for(var j = 0, ci; ci = cs[j]; j++){
4809                     result[++ri] = ci;
4810                 }
4811             }
4812         }else if(mode == "/" || mode == ">"){
4813             var utag = tagName.toUpperCase();
4814             for(var i = 0, ni, cn; ni = ns[i]; i++){
4815                 cn = ni.children || ni.childNodes;
4816                 for(var j = 0, cj; cj = cn[j]; j++){
4817                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4818                         result[++ri] = cj;
4819                     }
4820                 }
4821             }
4822         }else if(mode == "+"){
4823             var utag = tagName.toUpperCase();
4824             for(var i = 0, n; n = ns[i]; i++){
4825                 while((n = n.nextSibling) && n.nodeType != 1);
4826                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4827                     result[++ri] = n;
4828                 }
4829             }
4830         }else if(mode == "~"){
4831             for(var i = 0, n; n = ns[i]; i++){
4832                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4833                 if(n){
4834                     result[++ri] = n;
4835                 }
4836             }
4837         }
4838         return result;
4839     };
4840
4841     function concat(a, b){
4842         if(b.slice){
4843             return a.concat(b);
4844         }
4845         for(var i = 0, l = b.length; i < l; i++){
4846             a[a.length] = b[i];
4847         }
4848         return a;
4849     }
4850
4851     function byTag(cs, tagName){
4852         if(cs.tagName || cs == document){
4853             cs = [cs];
4854         }
4855         if(!tagName){
4856             return cs;
4857         }
4858         var r = [], ri = -1;
4859         tagName = tagName.toLowerCase();
4860         for(var i = 0, ci; ci = cs[i]; i++){
4861             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4862                 r[++ri] = ci;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function byId(cs, attr, id){
4869         if(cs.tagName || cs == document){
4870             cs = [cs];
4871         }
4872         if(!id){
4873             return cs;
4874         }
4875         var r = [], ri = -1;
4876         for(var i = 0,ci; ci = cs[i]; i++){
4877             if(ci && ci.id == id){
4878                 r[++ri] = ci;
4879                 return r;
4880             }
4881         }
4882         return r;
4883     };
4884
4885     function byAttribute(cs, attr, value, op, custom){
4886         var r = [], ri = -1, st = custom=="{";
4887         var f = Roo.DomQuery.operators[op];
4888         for(var i = 0, ci; ci = cs[i]; i++){
4889             var a;
4890             if(st){
4891                 a = Roo.DomQuery.getStyle(ci, attr);
4892             }
4893             else if(attr == "class" || attr == "className"){
4894                 a = ci.className;
4895             }else if(attr == "for"){
4896                 a = ci.htmlFor;
4897             }else if(attr == "href"){
4898                 a = ci.getAttribute("href", 2);
4899             }else{
4900                 a = ci.getAttribute(attr);
4901             }
4902             if((f && f(a, value)) || (!f && a)){
4903                 r[++ri] = ci;
4904             }
4905         }
4906         return r;
4907     };
4908
4909     function byPseudo(cs, name, value){
4910         return Roo.DomQuery.pseudos[name](cs, value);
4911     };
4912
4913     // This is for IE MSXML which does not support expandos.
4914     // IE runs the same speed using setAttribute, however FF slows way down
4915     // and Safari completely fails so they need to continue to use expandos.
4916     var isIE = window.ActiveXObject ? true : false;
4917
4918     // this eval is stop the compressor from
4919     // renaming the variable to something shorter
4920     
4921     /** eval:var:batch */
4922     var batch = 30803; 
4923
4924     var key = 30803;
4925
4926     function nodupIEXml(cs){
4927         var d = ++key;
4928         cs[0].setAttribute("_nodup", d);
4929         var r = [cs[0]];
4930         for(var i = 1, len = cs.length; i < len; i++){
4931             var c = cs[i];
4932             if(!c.getAttribute("_nodup") != d){
4933                 c.setAttribute("_nodup", d);
4934                 r[r.length] = c;
4935             }
4936         }
4937         for(var i = 0, len = cs.length; i < len; i++){
4938             cs[i].removeAttribute("_nodup");
4939         }
4940         return r;
4941     }
4942
4943     function nodup(cs){
4944         if(!cs){
4945             return [];
4946         }
4947         var len = cs.length, c, i, r = cs, cj, ri = -1;
4948         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4949             return cs;
4950         }
4951         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4952             return nodupIEXml(cs);
4953         }
4954         var d = ++key;
4955         cs[0]._nodup = d;
4956         for(i = 1; c = cs[i]; i++){
4957             if(c._nodup != d){
4958                 c._nodup = d;
4959             }else{
4960                 r = [];
4961                 for(var j = 0; j < i; j++){
4962                     r[++ri] = cs[j];
4963                 }
4964                 for(j = i+1; cj = cs[j]; j++){
4965                     if(cj._nodup != d){
4966                         cj._nodup = d;
4967                         r[++ri] = cj;
4968                     }
4969                 }
4970                 return r;
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickDiffIEXml(c1, c2){
4977         var d = ++key;
4978         for(var i = 0, len = c1.length; i < len; i++){
4979             c1[i].setAttribute("_qdiff", d);
4980         }
4981         var r = [];
4982         for(var i = 0, len = c2.length; i < len; i++){
4983             if(c2[i].getAttribute("_qdiff") != d){
4984                 r[r.length] = c2[i];
4985             }
4986         }
4987         for(var i = 0, len = c1.length; i < len; i++){
4988            c1[i].removeAttribute("_qdiff");
4989         }
4990         return r;
4991     }
4992
4993     function quickDiff(c1, c2){
4994         var len1 = c1.length;
4995         if(!len1){
4996             return c2;
4997         }
4998         if(isIE && c1[0].selectSingleNode){
4999             return quickDiffIEXml(c1, c2);
5000         }
5001         var d = ++key;
5002         for(var i = 0; i < len1; i++){
5003             c1[i]._qdiff = d;
5004         }
5005         var r = [];
5006         for(var i = 0, len = c2.length; i < len; i++){
5007             if(c2[i]._qdiff != d){
5008                 r[r.length] = c2[i];
5009             }
5010         }
5011         return r;
5012     }
5013
5014     function quickId(ns, mode, root, id){
5015         if(ns == root){
5016            var d = root.ownerDocument || root;
5017            return d.getElementById(id);
5018         }
5019         ns = getNodes(ns, mode, "*");
5020         return byId(ns, null, id);
5021     }
5022
5023     return {
5024         getStyle : function(el, name){
5025             return Roo.fly(el).getStyle(name);
5026         },
5027         /**
5028          * Compiles a selector/xpath query into a reusable function. The returned function
5029          * takes one parameter "root" (optional), which is the context node from where the query should start.
5030          * @param {String} selector The selector/xpath query
5031          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5032          * @return {Function}
5033          */
5034         compile : function(path, type){
5035             type = type || "select";
5036             
5037             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5038             var q = path, mode, lq;
5039             var tk = Roo.DomQuery.matchers;
5040             var tklen = tk.length;
5041             var mm;
5042
5043             // accept leading mode switch
5044             var lmode = q.match(modeRe);
5045             if(lmode && lmode[1]){
5046                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5047                 q = q.replace(lmode[1], "");
5048             }
5049             // strip leading slashes
5050             while(path.substr(0, 1)=="/"){
5051                 path = path.substr(1);
5052             }
5053
5054             while(q && lq != q){
5055                 lq = q;
5056                 var tm = q.match(tagTokenRe);
5057                 if(type == "select"){
5058                     if(tm){
5059                         if(tm[1] == "#"){
5060                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5061                         }else{
5062                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5063                         }
5064                         q = q.replace(tm[0], "");
5065                     }else if(q.substr(0, 1) != '@'){
5066                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5067                     }
5068                 }else{
5069                     if(tm){
5070                         if(tm[1] == "#"){
5071                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5072                         }else{
5073                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5074                         }
5075                         q = q.replace(tm[0], "");
5076                     }
5077                 }
5078                 while(!(mm = q.match(modeRe))){
5079                     var matched = false;
5080                     for(var j = 0; j < tklen; j++){
5081                         var t = tk[j];
5082                         var m = q.match(t.re);
5083                         if(m){
5084                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5085                                                     return m[i];
5086                                                 });
5087                             q = q.replace(m[0], "");
5088                             matched = true;
5089                             break;
5090                         }
5091                     }
5092                     // prevent infinite loop on bad selector
5093                     if(!matched){
5094                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5095                     }
5096                 }
5097                 if(mm[1]){
5098                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5099                     q = q.replace(mm[1], "");
5100                 }
5101             }
5102             fn[fn.length] = "return nodup(n);\n}";
5103             
5104              /** 
5105               * list of variables that need from compression as they are used by eval.
5106              *  eval:var:batch 
5107              *  eval:var:nodup
5108              *  eval:var:byTag
5109              *  eval:var:ById
5110              *  eval:var:getNodes
5111              *  eval:var:quickId
5112              *  eval:var:mode
5113              *  eval:var:root
5114              *  eval:var:n
5115              *  eval:var:byClassName
5116              *  eval:var:byPseudo
5117              *  eval:var:byAttribute
5118              *  eval:var:attrValue
5119              * 
5120              **/ 
5121             eval(fn.join(""));
5122             return f;
5123         },
5124
5125         /**
5126          * Selects a group of elements.
5127          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5128          * @param {Node} root (optional) The start of the query (defaults to document).
5129          * @return {Array}
5130          */
5131         select : function(path, root, type){
5132             if(!root || root == document){
5133                 root = document;
5134             }
5135             if(typeof root == "string"){
5136                 root = document.getElementById(root);
5137             }
5138             var paths = path.split(",");
5139             var results = [];
5140             for(var i = 0, len = paths.length; i < len; i++){
5141                 var p = paths[i].replace(trimRe, "");
5142                 if(!cache[p]){
5143                     cache[p] = Roo.DomQuery.compile(p);
5144                     if(!cache[p]){
5145                         throw p + " is not a valid selector";
5146                     }
5147                 }
5148                 var result = cache[p](root);
5149                 if(result && result != document){
5150                     results = results.concat(result);
5151                 }
5152             }
5153             if(paths.length > 1){
5154                 return nodup(results);
5155             }
5156             return results;
5157         },
5158
5159         /**
5160          * Selects a single element.
5161          * @param {String} selector The selector/xpath query
5162          * @param {Node} root (optional) The start of the query (defaults to document).
5163          * @return {Element}
5164          */
5165         selectNode : function(path, root){
5166             return Roo.DomQuery.select(path, root)[0];
5167         },
5168
5169         /**
5170          * Selects the value of a node, optionally replacing null with the defaultValue.
5171          * @param {String} selector The selector/xpath query
5172          * @param {Node} root (optional) The start of the query (defaults to document).
5173          * @param {String} defaultValue
5174          */
5175         selectValue : function(path, root, defaultValue){
5176             path = path.replace(trimRe, "");
5177             if(!valueCache[path]){
5178                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5179             }
5180             var n = valueCache[path](root);
5181             n = n[0] ? n[0] : n;
5182             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5183             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5184         },
5185
5186         /**
5187          * Selects the value of a node, parsing integers and floats.
5188          * @param {String} selector The selector/xpath query
5189          * @param {Node} root (optional) The start of the query (defaults to document).
5190          * @param {Number} defaultValue
5191          * @return {Number}
5192          */
5193         selectNumber : function(path, root, defaultValue){
5194             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5195             return parseFloat(v);
5196         },
5197
5198         /**
5199          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5200          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5201          * @param {String} selector The simple selector to test
5202          * @return {Boolean}
5203          */
5204         is : function(el, ss){
5205             if(typeof el == "string"){
5206                 el = document.getElementById(el);
5207             }
5208             var isArray = (el instanceof Array);
5209             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5210             return isArray ? (result.length == el.length) : (result.length > 0);
5211         },
5212
5213         /**
5214          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5215          * @param {Array} el An array of elements to filter
5216          * @param {String} selector The simple selector to test
5217          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5218          * the selector instead of the ones that match
5219          * @return {Array}
5220          */
5221         filter : function(els, ss, nonMatches){
5222             ss = ss.replace(trimRe, "");
5223             if(!simpleCache[ss]){
5224                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5225             }
5226             var result = simpleCache[ss](els);
5227             return nonMatches ? quickDiff(result, els) : result;
5228         },
5229
5230         /**
5231          * Collection of matching regular expressions and code snippets.
5232          */
5233         matchers : [{
5234                 re: /^\.([\w-]+)/,
5235                 select: 'n = byClassName(n, null, " {1} ");'
5236             }, {
5237                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5238                 select: 'n = byPseudo(n, "{1}", "{2}");'
5239             },{
5240                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5241                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5242             }, {
5243                 re: /^#([\w-]+)/,
5244                 select: 'n = byId(n, null, "{1}");'
5245             },{
5246                 re: /^@([\w-]+)/,
5247                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5248             }
5249         ],
5250
5251         /**
5252          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5253          * 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;.
5254          */
5255         operators : {
5256             "=" : function(a, v){
5257                 return a == v;
5258             },
5259             "!=" : function(a, v){
5260                 return a != v;
5261             },
5262             "^=" : function(a, v){
5263                 return a && a.substr(0, v.length) == v;
5264             },
5265             "$=" : function(a, v){
5266                 return a && a.substr(a.length-v.length) == v;
5267             },
5268             "*=" : function(a, v){
5269                 return a && a.indexOf(v) !== -1;
5270             },
5271             "%=" : function(a, v){
5272                 return (a % v) == 0;
5273             },
5274             "|=" : function(a, v){
5275                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5276             },
5277             "~=" : function(a, v){
5278                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5279             }
5280         },
5281
5282         /**
5283          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5284          * and the argument (if any) supplied in the selector.
5285          */
5286         pseudos : {
5287             "first-child" : function(c){
5288                 var r = [], ri = -1, n;
5289                 for(var i = 0, ci; ci = n = c[i]; i++){
5290                     while((n = n.previousSibling) && n.nodeType != 1);
5291                     if(!n){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "last-child" : function(c){
5299                 var r = [], ri = -1, n;
5300                 for(var i = 0, ci; ci = n = c[i]; i++){
5301                     while((n = n.nextSibling) && n.nodeType != 1);
5302                     if(!n){
5303                         r[++ri] = ci;
5304                     }
5305                 }
5306                 return r;
5307             },
5308
5309             "nth-child" : function(c, a) {
5310                 var r = [], ri = -1;
5311                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5312                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5313                 for(var i = 0, n; n = c[i]; i++){
5314                     var pn = n.parentNode;
5315                     if (batch != pn._batch) {
5316                         var j = 0;
5317                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5318                             if(cn.nodeType == 1){
5319                                cn.nodeIndex = ++j;
5320                             }
5321                         }
5322                         pn._batch = batch;
5323                     }
5324                     if (f == 1) {
5325                         if (l == 0 || n.nodeIndex == l){
5326                             r[++ri] = n;
5327                         }
5328                     } else if ((n.nodeIndex + l) % f == 0){
5329                         r[++ri] = n;
5330                     }
5331                 }
5332
5333                 return r;
5334             },
5335
5336             "only-child" : function(c){
5337                 var r = [], ri = -1;;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(!prev(ci) && !next(ci)){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "empty" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     var cns = ci.childNodes, j = 0, cn, empty = true;
5350                     while(cn = cns[j]){
5351                         ++j;
5352                         if(cn.nodeType == 1 || cn.nodeType == 3){
5353                             empty = false;
5354                             break;
5355                         }
5356                     }
5357                     if(empty){
5358                         r[++ri] = ci;
5359                     }
5360                 }
5361                 return r;
5362             },
5363
5364             "contains" : function(c, v){
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "nodeValue" : function(c, v){
5375                 var r = [], ri = -1;
5376                 for(var i = 0, ci; ci = c[i]; i++){
5377                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5378                         r[++ri] = ci;
5379                     }
5380                 }
5381                 return r;
5382             },
5383
5384             "checked" : function(c){
5385                 var r = [], ri = -1;
5386                 for(var i = 0, ci; ci = c[i]; i++){
5387                     if(ci.checked == true){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "not" : function(c, ss){
5395                 return Roo.DomQuery.filter(c, ss, true);
5396             },
5397
5398             "odd" : function(c){
5399                 return this["nth-child"](c, "odd");
5400             },
5401
5402             "even" : function(c){
5403                 return this["nth-child"](c, "even");
5404             },
5405
5406             "nth" : function(c, a){
5407                 return c[a-1] || [];
5408             },
5409
5410             "first" : function(c){
5411                 return c[0] || [];
5412             },
5413
5414             "last" : function(c){
5415                 return c[c.length-1] || [];
5416             },
5417
5418             "has" : function(c, ss){
5419                 var s = Roo.DomQuery.select;
5420                 var r = [], ri = -1;
5421                 for(var i = 0, ci; ci = c[i]; i++){
5422                     if(s(ss, ci).length > 0){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "next" : function(c, ss){
5430                 var is = Roo.DomQuery.is;
5431                 var r = [], ri = -1;
5432                 for(var i = 0, ci; ci = c[i]; i++){
5433                     var n = next(ci);
5434                     if(n && is(n, ss)){
5435                         r[++ri] = ci;
5436                     }
5437                 }
5438                 return r;
5439             },
5440
5441             "prev" : function(c, ss){
5442                 var is = Roo.DomQuery.is;
5443                 var r = [], ri = -1;
5444                 for(var i = 0, ci; ci = c[i]; i++){
5445                     var n = prev(ci);
5446                     if(n && is(n, ss)){
5447                         r[++ri] = ci;
5448                     }
5449                 }
5450                 return r;
5451             }
5452         }
5453     };
5454 }();
5455
5456 /**
5457  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5458  * @param {String} path The selector/xpath query
5459  * @param {Node} root (optional) The start of the query (defaults to document).
5460  * @return {Array}
5461  * @member Roo
5462  * @method query
5463  */
5464 Roo.query = Roo.DomQuery.select;
5465 /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475
5476 /**
5477  * @class Roo.util.Observable
5478  * Base class that provides a common interface for publishing events. Subclasses are expected to
5479  * to have a property "events" with all the events defined.<br>
5480  * For example:
5481  * <pre><code>
5482  Employee = function(name){
5483     this.name = name;
5484     this.addEvents({
5485         "fired" : true,
5486         "quit" : true
5487     });
5488  }
5489  Roo.extend(Employee, Roo.util.Observable);
5490 </code></pre>
5491  * @param {Object} config properties to use (incuding events / listeners)
5492  */
5493
5494 Roo.util.Observable = function(cfg){
5495     
5496     cfg = cfg|| {};
5497     this.addEvents(cfg.events || {});
5498     if (cfg.events) {
5499         delete cfg.events; // make sure
5500     }
5501      
5502     Roo.apply(this, cfg);
5503     
5504     if(this.listeners){
5505         this.on(this.listeners);
5506         delete this.listeners;
5507     }
5508 };
5509 Roo.util.Observable.prototype = {
5510     /** 
5511  * @cfg {Object} listeners  list of events and functions to call for this object, 
5512  * For example :
5513  * <pre><code>
5514     listeners :  { 
5515        'click' : function(e) {
5516            ..... 
5517         } ,
5518         .... 
5519     } 
5520   </code></pre>
5521  */
5522     
5523     
5524     /**
5525      * Fires the specified event with the passed parameters (minus the event name).
5526      * @param {String} eventName
5527      * @param {Object...} args Variable number of parameters are passed to handlers
5528      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5529      */
5530     fireEvent : function(){
5531         var ce = this.events[arguments[0].toLowerCase()];
5532         if(typeof ce == "object"){
5533             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5534         }else{
5535             return true;
5536         }
5537     },
5538
5539     // private
5540     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5541
5542     /**
5543      * Appends an event handler to this component
5544      * @param {String}   eventName The type of event to listen for
5545      * @param {Function} handler The method the event invokes
5546      * @param {Object}   scope (optional) The scope in which to execute the handler
5547      * function. The handler function's "this" context.
5548      * @param {Object}   options (optional) An object containing handler configuration
5549      * properties. This may contain any of the following properties:<ul>
5550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5551      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5552      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5553      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5554      * by the specified number of milliseconds. If the event fires again within that time, the original
5555      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5556      * </ul><br>
5557      * <p>
5558      * <b>Combining Options</b><br>
5559      * Using the options argument, it is possible to combine different types of listeners:<br>
5560      * <br>
5561      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5562                 <pre><code>
5563                 el.on('click', this.onClick, this, {
5564                         single: true,
5565                 delay: 100,
5566                 forumId: 4
5567                 });
5568                 </code></pre>
5569      * <p>
5570      * <b>Attaching multiple handlers in 1 call</b><br>
5571      * The method also allows for a single argument to be passed which is a config object containing properties
5572      * which specify multiple handlers.
5573      * <pre><code>
5574                 el.on({
5575                         'click': {
5576                         fn: this.onClick,
5577                         scope: this,
5578                         delay: 100
5579                 }, 
5580                 'mouseover': {
5581                         fn: this.onMouseOver,
5582                         scope: this
5583                 },
5584                 'mouseout': {
5585                         fn: this.onMouseOut,
5586                         scope: this
5587                 }
5588                 });
5589                 </code></pre>
5590      * <p>
5591      * Or a shorthand syntax which passes the same scope object to all handlers:
5592         <pre><code>
5593                 el.on({
5594                         'click': this.onClick,
5595                 'mouseover': this.onMouseOver,
5596                 'mouseout': this.onMouseOut,
5597                 scope: this
5598                 });
5599                 </code></pre>
5600      */
5601     addListener : function(eventName, fn, scope, o){
5602         if(typeof eventName == "object"){
5603             o = eventName;
5604             for(var e in o){
5605                 if(this.filterOptRe.test(e)){
5606                     continue;
5607                 }
5608                 if(typeof o[e] == "function"){
5609                     // shared options
5610                     this.addListener(e, o[e], o.scope,  o);
5611                 }else{
5612                     // individual options
5613                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5614                 }
5615             }
5616             return;
5617         }
5618         o = (!o || typeof o == "boolean") ? {} : o;
5619         eventName = eventName.toLowerCase();
5620         var ce = this.events[eventName] || true;
5621         if(typeof ce == "boolean"){
5622             ce = new Roo.util.Event(this, eventName);
5623             this.events[eventName] = ce;
5624         }
5625         ce.addListener(fn, scope, o);
5626     },
5627
5628     /**
5629      * Removes a listener
5630      * @param {String}   eventName     The type of event to listen for
5631      * @param {Function} handler        The handler to remove
5632      * @param {Object}   scope  (optional) The scope (this object) for the handler
5633      */
5634     removeListener : function(eventName, fn, scope){
5635         var ce = this.events[eventName.toLowerCase()];
5636         if(typeof ce == "object"){
5637             ce.removeListener(fn, scope);
5638         }
5639     },
5640
5641     /**
5642      * Removes all listeners for this object
5643      */
5644     purgeListeners : function(){
5645         for(var evt in this.events){
5646             if(typeof this.events[evt] == "object"){
5647                  this.events[evt].clearListeners();
5648             }
5649         }
5650     },
5651
5652     relayEvents : function(o, events){
5653         var createHandler = function(ename){
5654             return function(){
5655                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5656             };
5657         };
5658         for(var i = 0, len = events.length; i < len; i++){
5659             var ename = events[i];
5660             if(!this.events[ename]){ this.events[ename] = true; };
5661             o.on(ename, createHandler(ename), this);
5662         }
5663     },
5664
5665     /**
5666      * Used to define events on this Observable
5667      * @param {Object} object The object with the events defined
5668      */
5669     addEvents : function(o){
5670         if(!this.events){
5671             this.events = {};
5672         }
5673         Roo.applyIf(this.events, o);
5674     },
5675
5676     /**
5677      * Checks to see if this object has any listeners for a specified event
5678      * @param {String} eventName The name of the event to check for
5679      * @return {Boolean} True if the event is being listened for, else false
5680      */
5681     hasListener : function(eventName){
5682         var e = this.events[eventName];
5683         return typeof e == "object" && e.listeners.length > 0;
5684     }
5685 };
5686 /**
5687  * Appends an event handler to this element (shorthand for addListener)
5688  * @param {String}   eventName     The type of event to listen for
5689  * @param {Function} handler        The method the event invokes
5690  * @param {Object}   scope (optional) The scope in which to execute the handler
5691  * function. The handler function's "this" context.
5692  * @param {Object}   options  (optional)
5693  * @method
5694  */
5695 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5696 /**
5697  * Removes a listener (shorthand for removeListener)
5698  * @param {String}   eventName     The type of event to listen for
5699  * @param {Function} handler        The handler to remove
5700  * @param {Object}   scope  (optional) The scope (this object) for the handler
5701  * @method
5702  */
5703 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5704
5705 /**
5706  * Starts capture on the specified Observable. All events will be passed
5707  * to the supplied function with the event name + standard signature of the event
5708  * <b>before</b> the event is fired. If the supplied function returns false,
5709  * the event will not fire.
5710  * @param {Observable} o The Observable to capture
5711  * @param {Function} fn The function to call
5712  * @param {Object} scope (optional) The scope (this object) for the fn
5713  * @static
5714  */
5715 Roo.util.Observable.capture = function(o, fn, scope){
5716     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5717 };
5718
5719 /**
5720  * Removes <b>all</b> added captures from the Observable.
5721  * @param {Observable} o The Observable to release
5722  * @static
5723  */
5724 Roo.util.Observable.releaseCapture = function(o){
5725     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5726 };
5727
5728 (function(){
5729
5730     var createBuffered = function(h, o, scope){
5731         var task = new Roo.util.DelayedTask();
5732         return function(){
5733             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5734         };
5735     };
5736
5737     var createSingle = function(h, e, fn, scope){
5738         return function(){
5739             e.removeListener(fn, scope);
5740             return h.apply(scope, arguments);
5741         };
5742     };
5743
5744     var createDelayed = function(h, o, scope){
5745         return function(){
5746             var args = Array.prototype.slice.call(arguments, 0);
5747             setTimeout(function(){
5748                 h.apply(scope, args);
5749             }, o.delay || 10);
5750         };
5751     };
5752
5753     Roo.util.Event = function(obj, name){
5754         this.name = name;
5755         this.obj = obj;
5756         this.listeners = [];
5757     };
5758
5759     Roo.util.Event.prototype = {
5760         addListener : function(fn, scope, options){
5761             var o = options || {};
5762             scope = scope || this.obj;
5763             if(!this.isListening(fn, scope)){
5764                 var l = {fn: fn, scope: scope, options: o};
5765                 var h = fn;
5766                 if(o.delay){
5767                     h = createDelayed(h, o, scope);
5768                 }
5769                 if(o.single){
5770                     h = createSingle(h, this, fn, scope);
5771                 }
5772                 if(o.buffer){
5773                     h = createBuffered(h, o, scope);
5774                 }
5775                 l.fireFn = h;
5776                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5777                     this.listeners.push(l);
5778                 }else{
5779                     this.listeners = this.listeners.slice(0);
5780                     this.listeners.push(l);
5781                 }
5782             }
5783         },
5784
5785         findListener : function(fn, scope){
5786             scope = scope || this.obj;
5787             var ls = this.listeners;
5788             for(var i = 0, len = ls.length; i < len; i++){
5789                 var l = ls[i];
5790                 if(l.fn == fn && l.scope == scope){
5791                     return i;
5792                 }
5793             }
5794             return -1;
5795         },
5796
5797         isListening : function(fn, scope){
5798             return this.findListener(fn, scope) != -1;
5799         },
5800
5801         removeListener : function(fn, scope){
5802             var index;
5803             if((index = this.findListener(fn, scope)) != -1){
5804                 if(!this.firing){
5805                     this.listeners.splice(index, 1);
5806                 }else{
5807                     this.listeners = this.listeners.slice(0);
5808                     this.listeners.splice(index, 1);
5809                 }
5810                 return true;
5811             }
5812             return false;
5813         },
5814
5815         clearListeners : function(){
5816             this.listeners = [];
5817         },
5818
5819         fire : function(){
5820             var ls = this.listeners, scope, len = ls.length;
5821             if(len > 0){
5822                 this.firing = true;
5823                 var args = Array.prototype.slice.call(arguments, 0);
5824                 for(var i = 0; i < len; i++){
5825                     var l = ls[i];
5826                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5827                         this.firing = false;
5828                         return false;
5829                     }
5830                 }
5831                 this.firing = false;
5832             }
5833             return true;
5834         }
5835     };
5836 })();/*
5837  * Based on:
5838  * Ext JS Library 1.1.1
5839  * Copyright(c) 2006-2007, Ext JS, LLC.
5840  *
5841  * Originally Released Under LGPL - original licence link has changed is not relivant.
5842  *
5843  * Fork - LGPL
5844  * <script type="text/javascript">
5845  */
5846
5847 /**
5848  * @class Roo.EventManager
5849  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5850  * several useful events directly.
5851  * See {@link Roo.EventObject} for more details on normalized event objects.
5852  * @singleton
5853  */
5854 Roo.EventManager = function(){
5855     var docReadyEvent, docReadyProcId, docReadyState = false;
5856     var resizeEvent, resizeTask, textEvent, textSize;
5857     var E = Roo.lib.Event;
5858     var D = Roo.lib.Dom;
5859
5860
5861     var fireDocReady = function(){
5862         if(!docReadyState){
5863             docReadyState = true;
5864             Roo.isReady = true;
5865             if(docReadyProcId){
5866                 clearInterval(docReadyProcId);
5867             }
5868             if(Roo.isGecko || Roo.isOpera) {
5869                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5870             }
5871             if(Roo.isIE){
5872                 var defer = document.getElementById("ie-deferred-loader");
5873                 if(defer){
5874                     defer.onreadystatechange = null;
5875                     defer.parentNode.removeChild(defer);
5876                 }
5877             }
5878             if(docReadyEvent){
5879                 docReadyEvent.fire();
5880                 docReadyEvent.clearListeners();
5881             }
5882         }
5883     };
5884     
5885     var initDocReady = function(){
5886         docReadyEvent = new Roo.util.Event();
5887         if(Roo.isGecko || Roo.isOpera) {
5888             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5889         }else if(Roo.isIE){
5890             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5891             var defer = document.getElementById("ie-deferred-loader");
5892             defer.onreadystatechange = function(){
5893                 if(this.readyState == "complete"){
5894                     fireDocReady();
5895                 }
5896             };
5897         }else if(Roo.isSafari){ 
5898             docReadyProcId = setInterval(function(){
5899                 var rs = document.readyState;
5900                 if(rs == "complete") {
5901                     fireDocReady();     
5902                  }
5903             }, 10);
5904         }
5905         // no matter what, make sure it fires on load
5906         E.on(window, "load", fireDocReady);
5907     };
5908
5909     var createBuffered = function(h, o){
5910         var task = new Roo.util.DelayedTask(h);
5911         return function(e){
5912             // create new event object impl so new events don't wipe out properties
5913             e = new Roo.EventObjectImpl(e);
5914             task.delay(o.buffer, h, null, [e]);
5915         };
5916     };
5917
5918     var createSingle = function(h, el, ename, fn){
5919         return function(e){
5920             Roo.EventManager.removeListener(el, ename, fn);
5921             h(e);
5922         };
5923     };
5924
5925     var createDelayed = function(h, o){
5926         return function(e){
5927             // create new event object impl so new events don't wipe out properties
5928             e = new Roo.EventObjectImpl(e);
5929             setTimeout(function(){
5930                 h(e);
5931             }, o.delay || 10);
5932         };
5933     };
5934
5935     var listen = function(element, ename, opt, fn, scope){
5936         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5937         fn = fn || o.fn; scope = scope || o.scope;
5938         var el = Roo.getDom(element);
5939         if(!el){
5940             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5941         }
5942         var h = function(e){
5943             e = Roo.EventObject.setEvent(e);
5944             var t;
5945             if(o.delegate){
5946                 t = e.getTarget(o.delegate, el);
5947                 if(!t){
5948                     return;
5949                 }
5950             }else{
5951                 t = e.target;
5952             }
5953             if(o.stopEvent === true){
5954                 e.stopEvent();
5955             }
5956             if(o.preventDefault === true){
5957                e.preventDefault();
5958             }
5959             if(o.stopPropagation === true){
5960                 e.stopPropagation();
5961             }
5962
5963             if(o.normalized === false){
5964                 e = e.browserEvent;
5965             }
5966
5967             fn.call(scope || el, e, t, o);
5968         };
5969         if(o.delay){
5970             h = createDelayed(h, o);
5971         }
5972         if(o.single){
5973             h = createSingle(h, el, ename, fn);
5974         }
5975         if(o.buffer){
5976             h = createBuffered(h, o);
5977         }
5978         fn._handlers = fn._handlers || [];
5979         fn._handlers.push([Roo.id(el), ename, h]);
5980
5981         E.on(el, ename, h);
5982         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5983             el.addEventListener("DOMMouseScroll", h, false);
5984             E.on(window, 'unload', function(){
5985                 el.removeEventListener("DOMMouseScroll", h, false);
5986             });
5987         }
5988         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5989             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5990         }
5991         return h;
5992     };
5993
5994     var stopListening = function(el, ename, fn){
5995         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5996         if(hds){
5997             for(var i = 0, len = hds.length; i < len; i++){
5998                 var h = hds[i];
5999                 if(h[0] == id && h[1] == ename){
6000                     hd = h[2];
6001                     hds.splice(i, 1);
6002                     break;
6003                 }
6004             }
6005         }
6006         E.un(el, ename, hd);
6007         el = Roo.getDom(el);
6008         if(ename == "mousewheel" && el.addEventListener){
6009             el.removeEventListener("DOMMouseScroll", hd, false);
6010         }
6011         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6012             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6013         }
6014     };
6015
6016     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6017     
6018     var pub = {
6019         
6020         
6021         /** 
6022          * Fix for doc tools
6023          * @scope Roo.EventManager
6024          */
6025         
6026         
6027         /** 
6028          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6029          * object with a Roo.EventObject
6030          * @param {Function} fn        The method the event invokes
6031          * @param {Object}   scope    An object that becomes the scope of the handler
6032          * @param {boolean}  override If true, the obj passed in becomes
6033          *                             the execution scope of the listener
6034          * @return {Function} The wrapped function
6035          * @deprecated
6036          */
6037         wrap : function(fn, scope, override){
6038             return function(e){
6039                 Roo.EventObject.setEvent(e);
6040                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6041             };
6042         },
6043         
6044         /**
6045      * Appends an event handler to an element (shorthand for addListener)
6046      * @param {String/HTMLElement}   element        The html element or id to assign the
6047      * @param {String}   eventName The type of event to listen for
6048      * @param {Function} handler The method the event invokes
6049      * @param {Object}   scope (optional) The scope in which to execute the handler
6050      * function. The handler function's "this" context.
6051      * @param {Object}   options (optional) An object containing handler configuration
6052      * properties. This may contain any of the following properties:<ul>
6053      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6054      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6055      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6056      * <li>preventDefault {Boolean} True to prevent the default action</li>
6057      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6058      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6059      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6060      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6061      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6062      * by the specified number of milliseconds. If the event fires again within that time, the original
6063      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6064      * </ul><br>
6065      * <p>
6066      * <b>Combining Options</b><br>
6067      * Using the options argument, it is possible to combine different types of listeners:<br>
6068      * <br>
6069      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6070      * Code:<pre><code>
6071 el.on('click', this.onClick, this, {
6072     single: true,
6073     delay: 100,
6074     stopEvent : true,
6075     forumId: 4
6076 });</code></pre>
6077      * <p>
6078      * <b>Attaching multiple handlers in 1 call</b><br>
6079       * The method also allows for a single argument to be passed which is a config object containing properties
6080      * which specify multiple handlers.
6081      * <p>
6082      * Code:<pre><code>
6083 el.on({
6084     'click' : {
6085         fn: this.onClick
6086         scope: this,
6087         delay: 100
6088     },
6089     'mouseover' : {
6090         fn: this.onMouseOver
6091         scope: this
6092     },
6093     'mouseout' : {
6094         fn: this.onMouseOut
6095         scope: this
6096     }
6097 });</code></pre>
6098      * <p>
6099      * Or a shorthand syntax:<br>
6100      * Code:<pre><code>
6101 el.on({
6102     'click' : this.onClick,
6103     'mouseover' : this.onMouseOver,
6104     'mouseout' : this.onMouseOut
6105     scope: this
6106 });</code></pre>
6107      */
6108         addListener : function(element, eventName, fn, scope, options){
6109             if(typeof eventName == "object"){
6110                 var o = eventName;
6111                 for(var e in o){
6112                     if(propRe.test(e)){
6113                         continue;
6114                     }
6115                     if(typeof o[e] == "function"){
6116                         // shared options
6117                         listen(element, e, o, o[e], o.scope);
6118                     }else{
6119                         // individual options
6120                         listen(element, e, o[e]);
6121                     }
6122                 }
6123                 return;
6124             }
6125             return listen(element, eventName, options, fn, scope);
6126         },
6127         
6128         /**
6129          * Removes an event handler
6130          *
6131          * @param {String/HTMLElement}   element        The id or html element to remove the 
6132          *                             event from
6133          * @param {String}   eventName     The type of event
6134          * @param {Function} fn
6135          * @return {Boolean} True if a listener was actually removed
6136          */
6137         removeListener : function(element, eventName, fn){
6138             return stopListening(element, eventName, fn);
6139         },
6140         
6141         /**
6142          * Fires when the document is ready (before onload and before images are loaded). Can be 
6143          * accessed shorthanded Roo.onReady().
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An  object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onDocumentReady : function(fn, scope, options){
6149             if(docReadyState){ // if it already fired
6150                 docReadyEvent.addListener(fn, scope, options);
6151                 docReadyEvent.fire();
6152                 docReadyEvent.clearListeners();
6153                 return;
6154             }
6155             if(!docReadyEvent){
6156                 initDocReady();
6157             }
6158             docReadyEvent.addListener(fn, scope, options);
6159         },
6160         
6161         /**
6162          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6163          * @param {Function} fn        The method the event invokes
6164          * @param {Object}   scope    An object that becomes the scope of the handler
6165          * @param {boolean}  options
6166          */
6167         onWindowResize : function(fn, scope, options){
6168             if(!resizeEvent){
6169                 resizeEvent = new Roo.util.Event();
6170                 resizeTask = new Roo.util.DelayedTask(function(){
6171                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6172                 });
6173                 E.on(window, "resize", function(){
6174                     if(Roo.isIE){
6175                         resizeTask.delay(50);
6176                     }else{
6177                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6178                     }
6179                 });
6180             }
6181             resizeEvent.addListener(fn, scope, options);
6182         },
6183
6184         /**
6185          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6186          * @param {Function} fn        The method the event invokes
6187          * @param {Object}   scope    An object that becomes the scope of the handler
6188          * @param {boolean}  options
6189          */
6190         onTextResize : function(fn, scope, options){
6191             if(!textEvent){
6192                 textEvent = new Roo.util.Event();
6193                 var textEl = new Roo.Element(document.createElement('div'));
6194                 textEl.dom.className = 'x-text-resize';
6195                 textEl.dom.innerHTML = 'X';
6196                 textEl.appendTo(document.body);
6197                 textSize = textEl.dom.offsetHeight;
6198                 setInterval(function(){
6199                     if(textEl.dom.offsetHeight != textSize){
6200                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6201                     }
6202                 }, this.textResizeInterval);
6203             }
6204             textEvent.addListener(fn, scope, options);
6205         },
6206
6207         /**
6208          * Removes the passed window resize listener.
6209          * @param {Function} fn        The method the event invokes
6210          * @param {Object}   scope    The scope of handler
6211          */
6212         removeResizeListener : function(fn, scope){
6213             if(resizeEvent){
6214                 resizeEvent.removeListener(fn, scope);
6215             }
6216         },
6217
6218         // private
6219         fireResize : function(){
6220             if(resizeEvent){
6221                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6222             }   
6223         },
6224         /**
6225          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6226          */
6227         ieDeferSrc : false,
6228         /**
6229          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6230          */
6231         textResizeInterval : 50
6232     };
6233     
6234     /**
6235      * Fix for doc tools
6236      * @scopeAlias pub=Roo.EventManager
6237      */
6238     
6239      /**
6240      * Appends an event handler to an element (shorthand for addListener)
6241      * @param {String/HTMLElement}   element        The html element or id to assign the
6242      * @param {String}   eventName The type of event to listen for
6243      * @param {Function} handler The method the event invokes
6244      * @param {Object}   scope (optional) The scope in which to execute the handler
6245      * function. The handler function's "this" context.
6246      * @param {Object}   options (optional) An object containing handler configuration
6247      * properties. This may contain any of the following properties:<ul>
6248      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6249      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6250      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6251      * <li>preventDefault {Boolean} True to prevent the default action</li>
6252      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6253      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6254      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6255      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6256      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6257      * by the specified number of milliseconds. If the event fires again within that time, the original
6258      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6259      * </ul><br>
6260      * <p>
6261      * <b>Combining Options</b><br>
6262      * Using the options argument, it is possible to combine different types of listeners:<br>
6263      * <br>
6264      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6265      * Code:<pre><code>
6266 el.on('click', this.onClick, this, {
6267     single: true,
6268     delay: 100,
6269     stopEvent : true,
6270     forumId: 4
6271 });</code></pre>
6272      * <p>
6273      * <b>Attaching multiple handlers in 1 call</b><br>
6274       * The method also allows for a single argument to be passed which is a config object containing properties
6275      * which specify multiple handlers.
6276      * <p>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : {
6280         fn: this.onClick
6281         scope: this,
6282         delay: 100
6283     },
6284     'mouseover' : {
6285         fn: this.onMouseOver
6286         scope: this
6287     },
6288     'mouseout' : {
6289         fn: this.onMouseOut
6290         scope: this
6291     }
6292 });</code></pre>
6293      * <p>
6294      * Or a shorthand syntax:<br>
6295      * Code:<pre><code>
6296 el.on({
6297     'click' : this.onClick,
6298     'mouseover' : this.onMouseOver,
6299     'mouseout' : this.onMouseOut
6300     scope: this
6301 });</code></pre>
6302      */
6303     pub.on = pub.addListener;
6304     pub.un = pub.removeListener;
6305
6306     pub.stoppedMouseDownEvent = new Roo.util.Event();
6307     return pub;
6308 }();
6309 /**
6310   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6311   * @param {Function} fn        The method the event invokes
6312   * @param {Object}   scope    An  object that becomes the scope of the handler
6313   * @param {boolean}  override If true, the obj passed in becomes
6314   *                             the execution scope of the listener
6315   * @member Roo
6316   * @method onReady
6317  */
6318 Roo.onReady = Roo.EventManager.onDocumentReady;
6319
6320 Roo.onReady(function(){
6321     var bd = Roo.get(document.body);
6322     if(!bd){ return; }
6323
6324     var cls = [
6325             Roo.isIE ? "roo-ie"
6326             : Roo.isGecko ? "roo-gecko"
6327             : Roo.isOpera ? "roo-opera"
6328             : Roo.isSafari ? "roo-safari" : ""];
6329
6330     if(Roo.isMac){
6331         cls.push("roo-mac");
6332     }
6333     if(Roo.isLinux){
6334         cls.push("roo-linux");
6335     }
6336     if(Roo.isBorderBox){
6337         cls.push('roo-border-box');
6338     }
6339     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6340         var p = bd.dom.parentNode;
6341         if(p){
6342             p.className += ' roo-strict';
6343         }
6344     }
6345     bd.addClass(cls.join(' '));
6346 });
6347
6348 /**
6349  * @class Roo.EventObject
6350  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6351  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6352  * Example:
6353  * <pre><code>
6354  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6355     e.preventDefault();
6356     var target = e.getTarget();
6357     ...
6358  }
6359  var myDiv = Roo.get("myDiv");
6360  myDiv.on("click", handleClick);
6361  //or
6362  Roo.EventManager.on("myDiv", 'click', handleClick);
6363  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6364  </code></pre>
6365  * @singleton
6366  */
6367 Roo.EventObject = function(){
6368     
6369     var E = Roo.lib.Event;
6370     
6371     // safari keypress events for special keys return bad keycodes
6372     var safariKeys = {
6373         63234 : 37, // left
6374         63235 : 39, // right
6375         63232 : 38, // up
6376         63233 : 40, // down
6377         63276 : 33, // page up
6378         63277 : 34, // page down
6379         63272 : 46, // delete
6380         63273 : 36, // home
6381         63275 : 35  // end
6382     };
6383
6384     // normalize button clicks
6385     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6386                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6387
6388     Roo.EventObjectImpl = function(e){
6389         if(e){
6390             this.setEvent(e.browserEvent || e);
6391         }
6392     };
6393     Roo.EventObjectImpl.prototype = {
6394         /**
6395          * Used to fix doc tools.
6396          * @scope Roo.EventObject.prototype
6397          */
6398             
6399
6400         
6401         
6402         /** The normal browser event */
6403         browserEvent : null,
6404         /** The button pressed in a mouse event */
6405         button : -1,
6406         /** True if the shift key was down during the event */
6407         shiftKey : false,
6408         /** True if the control key was down during the event */
6409         ctrlKey : false,
6410         /** True if the alt key was down during the event */
6411         altKey : false,
6412
6413         /** Key constant 
6414         * @type Number */
6415         BACKSPACE : 8,
6416         /** Key constant 
6417         * @type Number */
6418         TAB : 9,
6419         /** Key constant 
6420         * @type Number */
6421         RETURN : 13,
6422         /** Key constant 
6423         * @type Number */
6424         ENTER : 13,
6425         /** Key constant 
6426         * @type Number */
6427         SHIFT : 16,
6428         /** Key constant 
6429         * @type Number */
6430         CONTROL : 17,
6431         /** Key constant 
6432         * @type Number */
6433         ESC : 27,
6434         /** Key constant 
6435         * @type Number */
6436         SPACE : 32,
6437         /** Key constant 
6438         * @type Number */
6439         PAGEUP : 33,
6440         /** Key constant 
6441         * @type Number */
6442         PAGEDOWN : 34,
6443         /** Key constant 
6444         * @type Number */
6445         END : 35,
6446         /** Key constant 
6447         * @type Number */
6448         HOME : 36,
6449         /** Key constant 
6450         * @type Number */
6451         LEFT : 37,
6452         /** Key constant 
6453         * @type Number */
6454         UP : 38,
6455         /** Key constant 
6456         * @type Number */
6457         RIGHT : 39,
6458         /** Key constant 
6459         * @type Number */
6460         DOWN : 40,
6461         /** Key constant 
6462         * @type Number */
6463         DELETE : 46,
6464         /** Key constant 
6465         * @type Number */
6466         F5 : 116,
6467
6468            /** @private */
6469         setEvent : function(e){
6470             if(e == this || (e && e.browserEvent)){ // already wrapped
6471                 return e;
6472             }
6473             this.browserEvent = e;
6474             if(e){
6475                 // normalize buttons
6476                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6477                 if(e.type == 'click' && this.button == -1){
6478                     this.button = 0;
6479                 }
6480                 this.type = e.type;
6481                 this.shiftKey = e.shiftKey;
6482                 // mac metaKey behaves like ctrlKey
6483                 this.ctrlKey = e.ctrlKey || e.metaKey;
6484                 this.altKey = e.altKey;
6485                 // in getKey these will be normalized for the mac
6486                 this.keyCode = e.keyCode;
6487                 // keyup warnings on firefox.
6488                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6489                 // cache the target for the delayed and or buffered events
6490                 this.target = E.getTarget(e);
6491                 // same for XY
6492                 this.xy = E.getXY(e);
6493             }else{
6494                 this.button = -1;
6495                 this.shiftKey = false;
6496                 this.ctrlKey = false;
6497                 this.altKey = false;
6498                 this.keyCode = 0;
6499                 this.charCode =0;
6500                 this.target = null;
6501                 this.xy = [0, 0];
6502             }
6503             return this;
6504         },
6505
6506         /**
6507          * Stop the event (preventDefault and stopPropagation)
6508          */
6509         stopEvent : function(){
6510             if(this.browserEvent){
6511                 if(this.browserEvent.type == 'mousedown'){
6512                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6513                 }
6514                 E.stopEvent(this.browserEvent);
6515             }
6516         },
6517
6518         /**
6519          * Prevents the browsers default handling of the event.
6520          */
6521         preventDefault : function(){
6522             if(this.browserEvent){
6523                 E.preventDefault(this.browserEvent);
6524             }
6525         },
6526
6527         /** @private */
6528         isNavKeyPress : function(){
6529             var k = this.keyCode;
6530             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6531             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6532         },
6533
6534         isSpecialKey : function(){
6535             var k = this.keyCode;
6536             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6537             (k == 16) || (k == 17) ||
6538             (k >= 18 && k <= 20) ||
6539             (k >= 33 && k <= 35) ||
6540             (k >= 36 && k <= 39) ||
6541             (k >= 44 && k <= 45);
6542         },
6543         /**
6544          * Cancels bubbling of the event.
6545          */
6546         stopPropagation : function(){
6547             if(this.browserEvent){
6548                 if(this.type == 'mousedown'){
6549                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6550                 }
6551                 E.stopPropagation(this.browserEvent);
6552             }
6553         },
6554
6555         /**
6556          * Gets the key code for the event.
6557          * @return {Number}
6558          */
6559         getCharCode : function(){
6560             return this.charCode || this.keyCode;
6561         },
6562
6563         /**
6564          * Returns a normalized keyCode for the event.
6565          * @return {Number} The key code
6566          */
6567         getKey : function(){
6568             var k = this.keyCode || this.charCode;
6569             return Roo.isSafari ? (safariKeys[k] || k) : k;
6570         },
6571
6572         /**
6573          * Gets the x coordinate of the event.
6574          * @return {Number}
6575          */
6576         getPageX : function(){
6577             return this.xy[0];
6578         },
6579
6580         /**
6581          * Gets the y coordinate of the event.
6582          * @return {Number}
6583          */
6584         getPageY : function(){
6585             return this.xy[1];
6586         },
6587
6588         /**
6589          * Gets the time of the event.
6590          * @return {Number}
6591          */
6592         getTime : function(){
6593             if(this.browserEvent){
6594                 return E.getTime(this.browserEvent);
6595             }
6596             return null;
6597         },
6598
6599         /**
6600          * Gets the page coordinates of the event.
6601          * @return {Array} The xy values like [x, y]
6602          */
6603         getXY : function(){
6604             return this.xy;
6605         },
6606
6607         /**
6608          * Gets the target for the event.
6609          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6610          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6611                 search as a number or element (defaults to 10 || document.body)
6612          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6613          * @return {HTMLelement}
6614          */
6615         getTarget : function(selector, maxDepth, returnEl){
6616             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6617         },
6618         /**
6619          * Gets the related target.
6620          * @return {HTMLElement}
6621          */
6622         getRelatedTarget : function(){
6623             if(this.browserEvent){
6624                 return E.getRelatedTarget(this.browserEvent);
6625             }
6626             return null;
6627         },
6628
6629         /**
6630          * Normalizes mouse wheel delta across browsers
6631          * @return {Number} The delta
6632          */
6633         getWheelDelta : function(){
6634             var e = this.browserEvent;
6635             var delta = 0;
6636             if(e.wheelDelta){ /* IE/Opera. */
6637                 delta = e.wheelDelta/120;
6638             }else if(e.detail){ /* Mozilla case. */
6639                 delta = -e.detail/3;
6640             }
6641             return delta;
6642         },
6643
6644         /**
6645          * Returns true if the control, meta, shift or alt key was pressed during this event.
6646          * @return {Boolean}
6647          */
6648         hasModifier : function(){
6649             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6650         },
6651
6652         /**
6653          * Returns true if the target of this event equals el or is a child of el
6654          * @param {String/HTMLElement/Element} el
6655          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6656          * @return {Boolean}
6657          */
6658         within : function(el, related){
6659             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6660             return t && Roo.fly(el).contains(t);
6661         },
6662
6663         getPoint : function(){
6664             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6665         }
6666     };
6667
6668     return new Roo.EventObjectImpl();
6669 }();
6670             
6671     /*
6672  * Based on:
6673  * Ext JS Library 1.1.1
6674  * Copyright(c) 2006-2007, Ext JS, LLC.
6675  *
6676  * Originally Released Under LGPL - original licence link has changed is not relivant.
6677  *
6678  * Fork - LGPL
6679  * <script type="text/javascript">
6680  */
6681
6682  
6683 // was in Composite Element!??!?!
6684  
6685 (function(){
6686     var D = Roo.lib.Dom;
6687     var E = Roo.lib.Event;
6688     var A = Roo.lib.Anim;
6689
6690     // local style camelizing for speed
6691     var propCache = {};
6692     var camelRe = /(-[a-z])/gi;
6693     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6694     var view = document.defaultView;
6695
6696 /**
6697  * @class Roo.Element
6698  * Represents an Element in the DOM.<br><br>
6699  * Usage:<br>
6700 <pre><code>
6701 var el = Roo.get("my-div");
6702
6703 // or with getEl
6704 var el = getEl("my-div");
6705
6706 // or with a DOM element
6707 var el = Roo.get(myDivElement);
6708 </code></pre>
6709  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6710  * each call instead of constructing a new one.<br><br>
6711  * <b>Animations</b><br />
6712  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6713  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6714 <pre>
6715 Option    Default   Description
6716 --------- --------  ---------------------------------------------
6717 duration  .35       The duration of the animation in seconds
6718 easing    easeOut   The YUI easing method
6719 callback  none      A function to execute when the anim completes
6720 scope     this      The scope (this) of the callback function
6721 </pre>
6722 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6723 * manipulate the animation. Here's an example:
6724 <pre><code>
6725 var el = Roo.get("my-div");
6726
6727 // no animation
6728 el.setWidth(100);
6729
6730 // default animation
6731 el.setWidth(100, true);
6732
6733 // animation with some options set
6734 el.setWidth(100, {
6735     duration: 1,
6736     callback: this.foo,
6737     scope: this
6738 });
6739
6740 // using the "anim" property to get the Anim object
6741 var opt = {
6742     duration: 1,
6743     callback: this.foo,
6744     scope: this
6745 };
6746 el.setWidth(100, opt);
6747 ...
6748 if(opt.anim.isAnimated()){
6749     opt.anim.stop();
6750 }
6751 </code></pre>
6752 * <b> Composite (Collections of) Elements</b><br />
6753  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6754  * @constructor Create a new Element directly.
6755  * @param {String/HTMLElement} element
6756  * @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).
6757  */
6758     Roo.Element = function(element, forceNew){
6759         var dom = typeof element == "string" ?
6760                 document.getElementById(element) : element;
6761         if(!dom){ // invalid id/element
6762             return null;
6763         }
6764         var id = dom.id;
6765         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6766             return Roo.Element.cache[id];
6767         }
6768
6769         /**
6770          * The DOM element
6771          * @type HTMLElement
6772          */
6773         this.dom = dom;
6774
6775         /**
6776          * The DOM element ID
6777          * @type String
6778          */
6779         this.id = id || Roo.id(dom);
6780     };
6781
6782     var El = Roo.Element;
6783
6784     El.prototype = {
6785         /**
6786          * The element's default display mode  (defaults to "")
6787          * @type String
6788          */
6789         originalDisplay : "",
6790
6791         visibilityMode : 1,
6792         /**
6793          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6794          * @type String
6795          */
6796         defaultUnit : "px",
6797         /**
6798          * Sets the element's visibility mode. When setVisible() is called it
6799          * will use this to determine whether to set the visibility or the display property.
6800          * @param visMode Element.VISIBILITY or Element.DISPLAY
6801          * @return {Roo.Element} this
6802          */
6803         setVisibilityMode : function(visMode){
6804             this.visibilityMode = visMode;
6805             return this;
6806         },
6807         /**
6808          * Convenience method for setVisibilityMode(Element.DISPLAY)
6809          * @param {String} display (optional) What to set display to when visible
6810          * @return {Roo.Element} this
6811          */
6812         enableDisplayMode : function(display){
6813             this.setVisibilityMode(El.DISPLAY);
6814             if(typeof display != "undefined") this.originalDisplay = display;
6815             return this;
6816         },
6817
6818         /**
6819          * 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)
6820          * @param {String} selector The simple selector to test
6821          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6822                 search as a number or element (defaults to 10 || document.body)
6823          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6824          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6825          */
6826         findParent : function(simpleSelector, maxDepth, returnEl){
6827             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6828             maxDepth = maxDepth || 50;
6829             if(typeof maxDepth != "number"){
6830                 stopEl = Roo.getDom(maxDepth);
6831                 maxDepth = 10;
6832             }
6833             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6834                 if(dq.is(p, simpleSelector)){
6835                     return returnEl ? Roo.get(p) : p;
6836                 }
6837                 depth++;
6838                 p = p.parentNode;
6839             }
6840             return null;
6841         },
6842
6843
6844         /**
6845          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6846          * @param {String} selector The simple selector to test
6847          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6848                 search as a number or element (defaults to 10 || document.body)
6849          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6850          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6851          */
6852         findParentNode : function(simpleSelector, maxDepth, returnEl){
6853             var p = Roo.fly(this.dom.parentNode, '_internal');
6854             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6855         },
6856
6857         /**
6858          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6859          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6860          * @param {String} selector The simple selector to test
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6864          */
6865         up : function(simpleSelector, maxDepth){
6866             return this.findParentNode(simpleSelector, maxDepth, true);
6867         },
6868
6869
6870
6871         /**
6872          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6873          * @param {String} selector The simple selector to test
6874          * @return {Boolean} True if this element matches the selector, else false
6875          */
6876         is : function(simpleSelector){
6877             return Roo.DomQuery.is(this.dom, simpleSelector);
6878         },
6879
6880         /**
6881          * Perform animation on this element.
6882          * @param {Object} args The YUI animation control args
6883          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6884          * @param {Function} onComplete (optional) Function to call when animation completes
6885          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6886          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6887          * @return {Roo.Element} this
6888          */
6889         animate : function(args, duration, onComplete, easing, animType){
6890             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6891             return this;
6892         },
6893
6894         /*
6895          * @private Internal animation call
6896          */
6897         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6898             animType = animType || 'run';
6899             opt = opt || {};
6900             var anim = Roo.lib.Anim[animType](
6901                 this.dom, args,
6902                 (opt.duration || defaultDur) || .35,
6903                 (opt.easing || defaultEase) || 'easeOut',
6904                 function(){
6905                     Roo.callback(cb, this);
6906                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6907                 },
6908                 this
6909             );
6910             opt.anim = anim;
6911             return anim;
6912         },
6913
6914         // private legacy anim prep
6915         preanim : function(a, i){
6916             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6917         },
6918
6919         /**
6920          * Removes worthless text nodes
6921          * @param {Boolean} forceReclean (optional) By default the element
6922          * keeps track if it has been cleaned already so
6923          * you can call this over and over. However, if you update the element and
6924          * need to force a reclean, you can pass true.
6925          */
6926         clean : function(forceReclean){
6927             if(this.isCleaned && forceReclean !== true){
6928                 return this;
6929             }
6930             var ns = /\S/;
6931             var d = this.dom, n = d.firstChild, ni = -1;
6932             while(n){
6933                 var nx = n.nextSibling;
6934                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6935                     d.removeChild(n);
6936                 }else{
6937                     n.nodeIndex = ++ni;
6938                 }
6939                 n = nx;
6940             }
6941             this.isCleaned = true;
6942             return this;
6943         },
6944
6945         // private
6946         calcOffsetsTo : function(el){
6947             el = Roo.get(el);
6948             var d = el.dom;
6949             var restorePos = false;
6950             if(el.getStyle('position') == 'static'){
6951                 el.position('relative');
6952                 restorePos = true;
6953             }
6954             var x = 0, y =0;
6955             var op = this.dom;
6956             while(op && op != d && op.tagName != 'HTML'){
6957                 x+= op.offsetLeft;
6958                 y+= op.offsetTop;
6959                 op = op.offsetParent;
6960             }
6961             if(restorePos){
6962                 el.position('static');
6963             }
6964             return [x, y];
6965         },
6966
6967         /**
6968          * Scrolls this element into view within the passed container.
6969          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6970          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6971          * @return {Roo.Element} this
6972          */
6973         scrollIntoView : function(container, hscroll){
6974             var c = Roo.getDom(container) || document.body;
6975             var el = this.dom;
6976
6977             var o = this.calcOffsetsTo(c),
6978                 l = o[0],
6979                 t = o[1],
6980                 b = t+el.offsetHeight,
6981                 r = l+el.offsetWidth;
6982
6983             var ch = c.clientHeight;
6984             var ct = parseInt(c.scrollTop, 10);
6985             var cl = parseInt(c.scrollLeft, 10);
6986             var cb = ct + ch;
6987             var cr = cl + c.clientWidth;
6988
6989             if(t < ct){
6990                 c.scrollTop = t;
6991             }else if(b > cb){
6992                 c.scrollTop = b-ch;
6993             }
6994
6995             if(hscroll !== false){
6996                 if(l < cl){
6997                     c.scrollLeft = l;
6998                 }else if(r > cr){
6999                     c.scrollLeft = r-c.clientWidth;
7000                 }
7001             }
7002             return this;
7003         },
7004
7005         // private
7006         scrollChildIntoView : function(child, hscroll){
7007             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7008         },
7009
7010         /**
7011          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7012          * the new height may not be available immediately.
7013          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7014          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7015          * @param {Function} onComplete (optional) Function to call when animation completes
7016          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7017          * @return {Roo.Element} this
7018          */
7019         autoHeight : function(animate, duration, onComplete, easing){
7020             var oldHeight = this.getHeight();
7021             this.clip();
7022             this.setHeight(1); // force clipping
7023             setTimeout(function(){
7024                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7025                 if(!animate){
7026                     this.setHeight(height);
7027                     this.unclip();
7028                     if(typeof onComplete == "function"){
7029                         onComplete();
7030                     }
7031                 }else{
7032                     this.setHeight(oldHeight); // restore original height
7033                     this.setHeight(height, animate, duration, function(){
7034                         this.unclip();
7035                         if(typeof onComplete == "function") onComplete();
7036                     }.createDelegate(this), easing);
7037                 }
7038             }.createDelegate(this), 0);
7039             return this;
7040         },
7041
7042         /**
7043          * Returns true if this element is an ancestor of the passed element
7044          * @param {HTMLElement/String} el The element to check
7045          * @return {Boolean} True if this element is an ancestor of el, else false
7046          */
7047         contains : function(el){
7048             if(!el){return false;}
7049             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7050         },
7051
7052         /**
7053          * Checks whether the element is currently visible using both visibility and display properties.
7054          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7055          * @return {Boolean} True if the element is currently visible, else false
7056          */
7057         isVisible : function(deep) {
7058             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7059             if(deep !== true || !vis){
7060                 return vis;
7061             }
7062             var p = this.dom.parentNode;
7063             while(p && p.tagName.toLowerCase() != "body"){
7064                 if(!Roo.fly(p, '_isVisible').isVisible()){
7065                     return false;
7066                 }
7067                 p = p.parentNode;
7068             }
7069             return true;
7070         },
7071
7072         /**
7073          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7074          * @param {String} selector The CSS selector
7075          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7076          * @return {CompositeElement/CompositeElementLite} The composite element
7077          */
7078         select : function(selector, unique){
7079             return El.select(selector, unique, this.dom);
7080         },
7081
7082         /**
7083          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7084          * @param {String} selector The CSS selector
7085          * @return {Array} An array of the matched nodes
7086          */
7087         query : function(selector, unique){
7088             return Roo.DomQuery.select(selector, this.dom);
7089         },
7090
7091         /**
7092          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7093          * @param {String} selector The CSS selector
7094          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7095          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7096          */
7097         child : function(selector, returnDom){
7098             var n = Roo.DomQuery.selectNode(selector, this.dom);
7099             return returnDom ? n : Roo.get(n);
7100         },
7101
7102         /**
7103          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7104          * @param {String} selector The CSS selector
7105          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7106          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7107          */
7108         down : function(selector, returnDom){
7109             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7110             return returnDom ? n : Roo.get(n);
7111         },
7112
7113         /**
7114          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7115          * @param {String} group The group the DD object is member of
7116          * @param {Object} config The DD config object
7117          * @param {Object} overrides An object containing methods to override/implement on the DD object
7118          * @return {Roo.dd.DD} The DD object
7119          */
7120         initDD : function(group, config, overrides){
7121             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7122             return Roo.apply(dd, overrides);
7123         },
7124
7125         /**
7126          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7127          * @param {String} group The group the DDProxy object is member of
7128          * @param {Object} config The DDProxy config object
7129          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7130          * @return {Roo.dd.DDProxy} The DDProxy object
7131          */
7132         initDDProxy : function(group, config, overrides){
7133             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7134             return Roo.apply(dd, overrides);
7135         },
7136
7137         /**
7138          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7139          * @param {String} group The group the DDTarget object is member of
7140          * @param {Object} config The DDTarget config object
7141          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7142          * @return {Roo.dd.DDTarget} The DDTarget object
7143          */
7144         initDDTarget : function(group, config, overrides){
7145             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7146             return Roo.apply(dd, overrides);
7147         },
7148
7149         /**
7150          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7151          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7152          * @param {Boolean} visible Whether the element is visible
7153          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7154          * @return {Roo.Element} this
7155          */
7156          setVisible : function(visible, animate){
7157             if(!animate || !A){
7158                 if(this.visibilityMode == El.DISPLAY){
7159                     this.setDisplayed(visible);
7160                 }else{
7161                     this.fixDisplay();
7162                     this.dom.style.visibility = visible ? "visible" : "hidden";
7163                 }
7164             }else{
7165                 // closure for composites
7166                 var dom = this.dom;
7167                 var visMode = this.visibilityMode;
7168                 if(visible){
7169                     this.setOpacity(.01);
7170                     this.setVisible(true);
7171                 }
7172                 this.anim({opacity: { to: (visible?1:0) }},
7173                       this.preanim(arguments, 1),
7174                       null, .35, 'easeIn', function(){
7175                          if(!visible){
7176                              if(visMode == El.DISPLAY){
7177                                  dom.style.display = "none";
7178                              }else{
7179                                  dom.style.visibility = "hidden";
7180                              }
7181                              Roo.get(dom).setOpacity(1);
7182                          }
7183                      });
7184             }
7185             return this;
7186         },
7187
7188         /**
7189          * Returns true if display is not "none"
7190          * @return {Boolean}
7191          */
7192         isDisplayed : function() {
7193             return this.getStyle("display") != "none";
7194         },
7195
7196         /**
7197          * Toggles the element's visibility or display, depending on visibility mode.
7198          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7199          * @return {Roo.Element} this
7200          */
7201         toggle : function(animate){
7202             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7203             return this;
7204         },
7205
7206         /**
7207          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7208          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7209          * @return {Roo.Element} this
7210          */
7211         setDisplayed : function(value) {
7212             if(typeof value == "boolean"){
7213                value = value ? this.originalDisplay : "none";
7214             }
7215             this.setStyle("display", value);
7216             return this;
7217         },
7218
7219         /**
7220          * Tries to focus the element. Any exceptions are caught and ignored.
7221          * @return {Roo.Element} this
7222          */
7223         focus : function() {
7224             try{
7225                 this.dom.focus();
7226             }catch(e){}
7227             return this;
7228         },
7229
7230         /**
7231          * Tries to blur the element. Any exceptions are caught and ignored.
7232          * @return {Roo.Element} this
7233          */
7234         blur : function() {
7235             try{
7236                 this.dom.blur();
7237             }catch(e){}
7238             return this;
7239         },
7240
7241         /**
7242          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7243          * @param {String/Array} className The CSS class to add, or an array of classes
7244          * @return {Roo.Element} this
7245          */
7246         addClass : function(className){
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.addClass(className[i]);
7250                 }
7251             }else{
7252                 if(className && !this.hasClass(className)){
7253                     this.dom.className = this.dom.className + " " + className;
7254                 }
7255             }
7256             return this;
7257         },
7258
7259         /**
7260          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7261          * @param {String/Array} className The CSS class to add, or an array of classes
7262          * @return {Roo.Element} this
7263          */
7264         radioClass : function(className){
7265             var siblings = this.dom.parentNode.childNodes;
7266             for(var i = 0; i < siblings.length; i++) {
7267                 var s = siblings[i];
7268                 if(s.nodeType == 1){
7269                     Roo.get(s).removeClass(className);
7270                 }
7271             }
7272             this.addClass(className);
7273             return this;
7274         },
7275
7276         /**
7277          * Removes one or more CSS classes from the element.
7278          * @param {String/Array} className The CSS class to remove, or an array of classes
7279          * @return {Roo.Element} this
7280          */
7281         removeClass : function(className){
7282             if(!className || !this.dom.className){
7283                 return this;
7284             }
7285             if(className instanceof Array){
7286                 for(var i = 0, len = className.length; i < len; i++) {
7287                     this.removeClass(className[i]);
7288                 }
7289             }else{
7290                 if(this.hasClass(className)){
7291                     var re = this.classReCache[className];
7292                     if (!re) {
7293                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7294                        this.classReCache[className] = re;
7295                     }
7296                     this.dom.className =
7297                         this.dom.className.replace(re, " ");
7298                 }
7299             }
7300             return this;
7301         },
7302
7303         // private
7304         classReCache: {},
7305
7306         /**
7307          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7308          * @param {String} className The CSS class to toggle
7309          * @return {Roo.Element} this
7310          */
7311         toggleClass : function(className){
7312             if(this.hasClass(className)){
7313                 this.removeClass(className);
7314             }else{
7315                 this.addClass(className);
7316             }
7317             return this;
7318         },
7319
7320         /**
7321          * Checks if the specified CSS class exists on this element's DOM node.
7322          * @param {String} className The CSS class to check for
7323          * @return {Boolean} True if the class exists, else false
7324          */
7325         hasClass : function(className){
7326             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7327         },
7328
7329         /**
7330          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7331          * @param {String} oldClassName The CSS class to replace
7332          * @param {String} newClassName The replacement CSS class
7333          * @return {Roo.Element} this
7334          */
7335         replaceClass : function(oldClassName, newClassName){
7336             this.removeClass(oldClassName);
7337             this.addClass(newClassName);
7338             return this;
7339         },
7340
7341         /**
7342          * Returns an object with properties matching the styles requested.
7343          * For example, el.getStyles('color', 'font-size', 'width') might return
7344          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7345          * @param {String} style1 A style name
7346          * @param {String} style2 A style name
7347          * @param {String} etc.
7348          * @return {Object} The style object
7349          */
7350         getStyles : function(){
7351             var a = arguments, len = a.length, r = {};
7352             for(var i = 0; i < len; i++){
7353                 r[a[i]] = this.getStyle(a[i]);
7354             }
7355             return r;
7356         },
7357
7358         /**
7359          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7360          * @param {String} property The style property whose value is returned.
7361          * @return {String} The current value of the style property for this element.
7362          */
7363         getStyle : function(){
7364             return view && view.getComputedStyle ?
7365                 function(prop){
7366                     var el = this.dom, v, cs, camel;
7367                     if(prop == 'float'){
7368                         prop = "cssFloat";
7369                     }
7370                     if(el.style && (v = el.style[prop])){
7371                         return v;
7372                     }
7373                     if(cs = view.getComputedStyle(el, "")){
7374                         if(!(camel = propCache[prop])){
7375                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7376                         }
7377                         return cs[camel];
7378                     }
7379                     return null;
7380                 } :
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'opacity'){
7384                         if(typeof el.style.filter == 'string'){
7385                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7386                             if(m){
7387                                 var fv = parseFloat(m[1]);
7388                                 if(!isNaN(fv)){
7389                                     return fv ? fv / 100 : 0;
7390                                 }
7391                             }
7392                         }
7393                         return 1;
7394                     }else if(prop == 'float'){
7395                         prop = "styleFloat";
7396                     }
7397                     if(!(camel = propCache[prop])){
7398                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7399                     }
7400                     if(v = el.style[camel]){
7401                         return v;
7402                     }
7403                     if(cs = el.currentStyle){
7404                         return cs[camel];
7405                     }
7406                     return null;
7407                 };
7408         }(),
7409
7410         /**
7411          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7412          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7413          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7414          * @return {Roo.Element} this
7415          */
7416         setStyle : function(prop, value){
7417             if(typeof prop == "string"){
7418                 
7419                 if (prop == 'float') {
7420                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7421                     return this;
7422                 }
7423                 
7424                 var camel;
7425                 if(!(camel = propCache[prop])){
7426                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7427                 }
7428                 
7429                 if(camel == 'opacity') {
7430                     this.setOpacity(value);
7431                 }else{
7432                     this.dom.style[camel] = value;
7433                 }
7434             }else{
7435                 for(var style in prop){
7436                     if(typeof prop[style] != "function"){
7437                        this.setStyle(style, prop[style]);
7438                     }
7439                 }
7440             }
7441             return this;
7442         },
7443
7444         /**
7445          * More flexible version of {@link #setStyle} for setting style properties.
7446          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7447          * a function which returns such a specification.
7448          * @return {Roo.Element} this
7449          */
7450         applyStyles : function(style){
7451             Roo.DomHelper.applyStyles(this.dom, style);
7452             return this;
7453         },
7454
7455         /**
7456           * 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).
7457           * @return {Number} The X position of the element
7458           */
7459         getX : function(){
7460             return D.getX(this.dom);
7461         },
7462
7463         /**
7464           * 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).
7465           * @return {Number} The Y position of the element
7466           */
7467         getY : function(){
7468             return D.getY(this.dom);
7469         },
7470
7471         /**
7472           * 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).
7473           * @return {Array} The XY position of the element
7474           */
7475         getXY : function(){
7476             return D.getXY(this.dom);
7477         },
7478
7479         /**
7480          * 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).
7481          * @param {Number} The X position of the element
7482          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7483          * @return {Roo.Element} this
7484          */
7485         setX : function(x, animate){
7486             if(!animate || !A){
7487                 D.setX(this.dom, x);
7488             }else{
7489                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7490             }
7491             return this;
7492         },
7493
7494         /**
7495          * 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).
7496          * @param {Number} The Y position of the element
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setY : function(y, animate){
7501             if(!animate || !A){
7502                 D.setY(this.dom, y);
7503             }else{
7504                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7505             }
7506             return this;
7507         },
7508
7509         /**
7510          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7511          * @param {String} left The left CSS property value
7512          * @return {Roo.Element} this
7513          */
7514         setLeft : function(left){
7515             this.setStyle("left", this.addUnits(left));
7516             return this;
7517         },
7518
7519         /**
7520          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7521          * @param {String} top The top CSS property value
7522          * @return {Roo.Element} this
7523          */
7524         setTop : function(top){
7525             this.setStyle("top", this.addUnits(top));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the element's CSS right style.
7531          * @param {String} right The right CSS property value
7532          * @return {Roo.Element} this
7533          */
7534         setRight : function(right){
7535             this.setStyle("right", this.addUnits(right));
7536             return this;
7537         },
7538
7539         /**
7540          * Sets the element's CSS bottom style.
7541          * @param {String} bottom The bottom CSS property value
7542          * @return {Roo.Element} this
7543          */
7544         setBottom : function(bottom){
7545             this.setStyle("bottom", this.addUnits(bottom));
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 {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7553          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7554          * @return {Roo.Element} this
7555          */
7556         setXY : function(pos, animate){
7557             if(!animate || !A){
7558                 D.setXY(this.dom, pos);
7559             }else{
7560                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7561             }
7562             return this;
7563         },
7564
7565         /**
7566          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7567          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7568          * @param {Number} x X value for new position (coordinates are page-based)
7569          * @param {Number} y Y value for new position (coordinates are page-based)
7570          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7571          * @return {Roo.Element} this
7572          */
7573         setLocation : function(x, y, animate){
7574             this.setXY([x, y], this.preanim(arguments, 2));
7575             return this;
7576         },
7577
7578         /**
7579          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7580          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} x X value for new position (coordinates are page-based)
7582          * @param {Number} y Y value for new position (coordinates are page-based)
7583          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7584          * @return {Roo.Element} this
7585          */
7586         moveTo : function(x, y, animate){
7587             this.setXY([x, y], this.preanim(arguments, 2));
7588             return this;
7589         },
7590
7591         /**
7592          * Returns the region of the given element.
7593          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7594          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7595          */
7596         getRegion : function(){
7597             return D.getRegion(this.dom);
7598         },
7599
7600         /**
7601          * Returns the offset height of the element
7602          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7603          * @return {Number} The element's height
7604          */
7605         getHeight : function(contentHeight){
7606             var h = this.dom.offsetHeight || 0;
7607             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7608         },
7609
7610         /**
7611          * Returns the offset width of the element
7612          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7613          * @return {Number} The element's width
7614          */
7615         getWidth : function(contentWidth){
7616             var w = this.dom.offsetWidth || 0;
7617             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7618         },
7619
7620         /**
7621          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7622          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7623          * if a height has not been set using CSS.
7624          * @return {Number}
7625          */
7626         getComputedHeight : function(){
7627             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7628             if(!h){
7629                 h = parseInt(this.getStyle('height'), 10) || 0;
7630                 if(!this.isBorderBox()){
7631                     h += this.getFrameWidth('tb');
7632                 }
7633             }
7634             return h;
7635         },
7636
7637         /**
7638          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7639          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7640          * if a width has not been set using CSS.
7641          * @return {Number}
7642          */
7643         getComputedWidth : function(){
7644             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7645             if(!w){
7646                 w = parseInt(this.getStyle('width'), 10) || 0;
7647                 if(!this.isBorderBox()){
7648                     w += this.getFrameWidth('lr');
7649                 }
7650             }
7651             return w;
7652         },
7653
7654         /**
7655          * Returns the size of the element.
7656          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7657          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7658          */
7659         getSize : function(contentSize){
7660             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7661         },
7662
7663         /**
7664          * Returns the width and height of the viewport.
7665          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7666          */
7667         getViewSize : function(){
7668             var d = this.dom, doc = document, aw = 0, ah = 0;
7669             if(d == doc || d == doc.body){
7670                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7671             }else{
7672                 return {
7673                     width : d.clientWidth,
7674                     height: d.clientHeight
7675                 };
7676             }
7677         },
7678
7679         /**
7680          * Returns the value of the "value" attribute
7681          * @param {Boolean} asNumber true to parse the value as a number
7682          * @return {String/Number}
7683          */
7684         getValue : function(asNumber){
7685             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7686         },
7687
7688         // private
7689         adjustWidth : function(width){
7690             if(typeof width == "number"){
7691                 if(this.autoBoxAdjust && !this.isBorderBox()){
7692                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7693                 }
7694                 if(width < 0){
7695                     width = 0;
7696                 }
7697             }
7698             return width;
7699         },
7700
7701         // private
7702         adjustHeight : function(height){
7703             if(typeof height == "number"){
7704                if(this.autoBoxAdjust && !this.isBorderBox()){
7705                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7706                }
7707                if(height < 0){
7708                    height = 0;
7709                }
7710             }
7711             return height;
7712         },
7713
7714         /**
7715          * Set the width of the element
7716          * @param {Number} width The new width
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         setWidth : function(width, animate){
7721             width = this.adjustWidth(width);
7722             if(!animate || !A){
7723                 this.dom.style.width = this.addUnits(width);
7724             }else{
7725                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7726             }
7727             return this;
7728         },
7729
7730         /**
7731          * Set the height of the element
7732          * @param {Number} height The new height
7733          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736          setHeight : function(height, animate){
7737             height = this.adjustHeight(height);
7738             if(!animate || !A){
7739                 this.dom.style.height = this.addUnits(height);
7740             }else{
7741                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7748          * @param {Number} width The new width
7749          * @param {Number} height The new height
7750          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753          setSize : function(width, height, animate){
7754             if(typeof width == "object"){ // in case of object from getSize()
7755                 height = width.height; width = width.width;
7756             }
7757             width = this.adjustWidth(width); height = this.adjustHeight(height);
7758             if(!animate || !A){
7759                 this.dom.style.width = this.addUnits(width);
7760                 this.dom.style.height = this.addUnits(height);
7761             }else{
7762                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7763             }
7764             return this;
7765         },
7766
7767         /**
7768          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7769          * @param {Number} x X value for new position (coordinates are page-based)
7770          * @param {Number} y Y value for new position (coordinates are page-based)
7771          * @param {Number} width The new width
7772          * @param {Number} height The new height
7773          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7774          * @return {Roo.Element} this
7775          */
7776         setBounds : function(x, y, width, height, animate){
7777             if(!animate || !A){
7778                 this.setSize(width, height);
7779                 this.setLocation(x, y);
7780             }else{
7781                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7782                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7783                               this.preanim(arguments, 4), 'motion');
7784             }
7785             return this;
7786         },
7787
7788         /**
7789          * 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.
7790          * @param {Roo.lib.Region} region The region to fill
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setRegion : function(region, animate){
7795             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7796             return this;
7797         },
7798
7799         /**
7800          * Appends an event handler
7801          *
7802          * @param {String}   eventName     The type of event to append
7803          * @param {Function} fn        The method the event invokes
7804          * @param {Object} scope       (optional) The scope (this object) of the fn
7805          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7806          */
7807         addListener : function(eventName, fn, scope, options){
7808             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7809         },
7810
7811         /**
7812          * Removes an event handler from this element
7813          * @param {String} eventName the type of event to remove
7814          * @param {Function} fn the method the event invokes
7815          * @return {Roo.Element} this
7816          */
7817         removeListener : function(eventName, fn){
7818             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7819             return this;
7820         },
7821
7822         /**
7823          * Removes all previous added listeners from this element
7824          * @return {Roo.Element} this
7825          */
7826         removeAllListeners : function(){
7827             E.purgeElement(this.dom);
7828             return this;
7829         },
7830
7831         relayEvent : function(eventName, observable){
7832             this.on(eventName, function(e){
7833                 observable.fireEvent(eventName, e);
7834             });
7835         },
7836
7837         /**
7838          * Set the opacity of the element
7839          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7840          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843          setOpacity : function(opacity, animate){
7844             if(!animate || !A){
7845                 var s = this.dom.style;
7846                 if(Roo.isIE){
7847                     s.zoom = 1;
7848                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7849                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7850                 }else{
7851                     s.opacity = opacity;
7852                 }
7853             }else{
7854                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7855             }
7856             return this;
7857         },
7858
7859         /**
7860          * Gets the left X coordinate
7861          * @param {Boolean} local True to get the local css position instead of page coordinate
7862          * @return {Number}
7863          */
7864         getLeft : function(local){
7865             if(!local){
7866                 return this.getX();
7867             }else{
7868                 return parseInt(this.getStyle("left"), 10) || 0;
7869             }
7870         },
7871
7872         /**
7873          * Gets the right X coordinate of the element (element X position + element width)
7874          * @param {Boolean} local True to get the local css position instead of page coordinate
7875          * @return {Number}
7876          */
7877         getRight : function(local){
7878             if(!local){
7879                 return this.getX() + this.getWidth();
7880             }else{
7881                 return (this.getLeft(true) + this.getWidth()) || 0;
7882             }
7883         },
7884
7885         /**
7886          * Gets the top Y coordinate
7887          * @param {Boolean} local True to get the local css position instead of page coordinate
7888          * @return {Number}
7889          */
7890         getTop : function(local) {
7891             if(!local){
7892                 return this.getY();
7893             }else{
7894                 return parseInt(this.getStyle("top"), 10) || 0;
7895             }
7896         },
7897
7898         /**
7899          * Gets the bottom Y coordinate of the element (element Y position + element height)
7900          * @param {Boolean} local True to get the local css position instead of page coordinate
7901          * @return {Number}
7902          */
7903         getBottom : function(local){
7904             if(!local){
7905                 return this.getY() + this.getHeight();
7906             }else{
7907                 return (this.getTop(true) + this.getHeight()) || 0;
7908             }
7909         },
7910
7911         /**
7912         * Initializes positioning on this element. If a desired position is not passed, it will make the
7913         * the element positioned relative IF it is not already positioned.
7914         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7915         * @param {Number} zIndex (optional) The zIndex to apply
7916         * @param {Number} x (optional) Set the page X position
7917         * @param {Number} y (optional) Set the page Y position
7918         */
7919         position : function(pos, zIndex, x, y){
7920             if(!pos){
7921                if(this.getStyle('position') == 'static'){
7922                    this.setStyle('position', 'relative');
7923                }
7924             }else{
7925                 this.setStyle("position", pos);
7926             }
7927             if(zIndex){
7928                 this.setStyle("z-index", zIndex);
7929             }
7930             if(x !== undefined && y !== undefined){
7931                 this.setXY([x, y]);
7932             }else if(x !== undefined){
7933                 this.setX(x);
7934             }else if(y !== undefined){
7935                 this.setY(y);
7936             }
7937         },
7938
7939         /**
7940         * Clear positioning back to the default when the document was loaded
7941         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7942         * @return {Roo.Element} this
7943          */
7944         clearPositioning : function(value){
7945             value = value ||'';
7946             this.setStyle({
7947                 "left": value,
7948                 "right": value,
7949                 "top": value,
7950                 "bottom": value,
7951                 "z-index": "",
7952                 "position" : "static"
7953             });
7954             return this;
7955         },
7956
7957         /**
7958         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7959         * snapshot before performing an update and then restoring the element.
7960         * @return {Object}
7961         */
7962         getPositioning : function(){
7963             var l = this.getStyle("left");
7964             var t = this.getStyle("top");
7965             return {
7966                 "position" : this.getStyle("position"),
7967                 "left" : l,
7968                 "right" : l ? "" : this.getStyle("right"),
7969                 "top" : t,
7970                 "bottom" : t ? "" : this.getStyle("bottom"),
7971                 "z-index" : this.getStyle("z-index")
7972             };
7973         },
7974
7975         /**
7976          * Gets the width of the border(s) for the specified side(s)
7977          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7978          * passing lr would get the border (l)eft width + the border (r)ight width.
7979          * @return {Number} The width of the sides passed added together
7980          */
7981         getBorderWidth : function(side){
7982             return this.addStyles(side, El.borders);
7983         },
7984
7985         /**
7986          * Gets the width of the padding(s) for the specified side(s)
7987          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7988          * passing lr would get the padding (l)eft + the padding (r)ight.
7989          * @return {Number} The padding of the sides passed added together
7990          */
7991         getPadding : function(side){
7992             return this.addStyles(side, El.paddings);
7993         },
7994
7995         /**
7996         * Set positioning with an object returned by getPositioning().
7997         * @param {Object} posCfg
7998         * @return {Roo.Element} this
7999          */
8000         setPositioning : function(pc){
8001             this.applyStyles(pc);
8002             if(pc.right == "auto"){
8003                 this.dom.style.right = "";
8004             }
8005             if(pc.bottom == "auto"){
8006                 this.dom.style.bottom = "";
8007             }
8008             return this;
8009         },
8010
8011         // private
8012         fixDisplay : function(){
8013             if(this.getStyle("display") == "none"){
8014                 this.setStyle("visibility", "hidden");
8015                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8016                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8017                     this.setStyle("display", "block");
8018                 }
8019             }
8020         },
8021
8022         /**
8023          * Quick set left and top adding default units
8024          * @param {String} left The left CSS property value
8025          * @param {String} top The top CSS property value
8026          * @return {Roo.Element} this
8027          */
8028          setLeftTop : function(left, top){
8029             this.dom.style.left = this.addUnits(left);
8030             this.dom.style.top = this.addUnits(top);
8031             return this;
8032         },
8033
8034         /**
8035          * Move this element relative to its current position.
8036          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8037          * @param {Number} distance How far to move the element in pixels
8038          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8039          * @return {Roo.Element} this
8040          */
8041          move : function(direction, distance, animate){
8042             var xy = this.getXY();
8043             direction = direction.toLowerCase();
8044             switch(direction){
8045                 case "l":
8046                 case "left":
8047                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8048                     break;
8049                case "r":
8050                case "right":
8051                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8052                     break;
8053                case "t":
8054                case "top":
8055                case "up":
8056                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8057                     break;
8058                case "b":
8059                case "bottom":
8060                case "down":
8061                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8062                     break;
8063             }
8064             return this;
8065         },
8066
8067         /**
8068          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8069          * @return {Roo.Element} this
8070          */
8071         clip : function(){
8072             if(!this.isClipped){
8073                this.isClipped = true;
8074                this.originalClip = {
8075                    "o": this.getStyle("overflow"),
8076                    "x": this.getStyle("overflow-x"),
8077                    "y": this.getStyle("overflow-y")
8078                };
8079                this.setStyle("overflow", "hidden");
8080                this.setStyle("overflow-x", "hidden");
8081                this.setStyle("overflow-y", "hidden");
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          *  Return clipping (overflow) to original clipping before clip() was called
8088          * @return {Roo.Element} this
8089          */
8090         unclip : function(){
8091             if(this.isClipped){
8092                 this.isClipped = false;
8093                 var o = this.originalClip;
8094                 if(o.o){this.setStyle("overflow", o.o);}
8095                 if(o.x){this.setStyle("overflow-x", o.x);}
8096                 if(o.y){this.setStyle("overflow-y", o.y);}
8097             }
8098             return this;
8099         },
8100
8101
8102         /**
8103          * Gets the x,y coordinates specified by the anchor position on the element.
8104          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8105          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8106          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8107          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8108          * @return {Array} [x, y] An array containing the element's x and y coordinates
8109          */
8110         getAnchorXY : function(anchor, local, s){
8111             //Passing a different size is useful for pre-calculating anchors,
8112             //especially for anchored animations that change the el size.
8113
8114             var w, h, vp = false;
8115             if(!s){
8116                 var d = this.dom;
8117                 if(d == document.body || d == document){
8118                     vp = true;
8119                     w = D.getViewWidth(); h = D.getViewHeight();
8120                 }else{
8121                     w = this.getWidth(); h = this.getHeight();
8122                 }
8123             }else{
8124                 w = s.width;  h = s.height;
8125             }
8126             var x = 0, y = 0, r = Math.round;
8127             switch((anchor || "tl").toLowerCase()){
8128                 case "c":
8129                     x = r(w*.5);
8130                     y = r(h*.5);
8131                 break;
8132                 case "t":
8133                     x = r(w*.5);
8134                     y = 0;
8135                 break;
8136                 case "l":
8137                     x = 0;
8138                     y = r(h*.5);
8139                 break;
8140                 case "r":
8141                     x = w;
8142                     y = r(h*.5);
8143                 break;
8144                 case "b":
8145                     x = r(w*.5);
8146                     y = h;
8147                 break;
8148                 case "tl":
8149                     x = 0;
8150                     y = 0;
8151                 break;
8152                 case "bl":
8153                     x = 0;
8154                     y = h;
8155                 break;
8156                 case "br":
8157                     x = w;
8158                     y = h;
8159                 break;
8160                 case "tr":
8161                     x = w;
8162                     y = 0;
8163                 break;
8164             }
8165             if(local === true){
8166                 return [x, y];
8167             }
8168             if(vp){
8169                 var sc = this.getScroll();
8170                 return [x + sc.left, y + sc.top];
8171             }
8172             //Add the element's offset xy
8173             var o = this.getXY();
8174             return [x+o[0], y+o[1]];
8175         },
8176
8177         /**
8178          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8179          * supported position values.
8180          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8181          * @param {String} position The position to align to.
8182          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8183          * @return {Array} [x, y]
8184          */
8185         getAlignToXY : function(el, p, o){
8186             el = Roo.get(el);
8187             var d = this.dom;
8188             if(!el.dom){
8189                 throw "Element.alignTo with an element that doesn't exist";
8190             }
8191             var c = false; //constrain to viewport
8192             var p1 = "", p2 = "";
8193             o = o || [0,0];
8194
8195             if(!p){
8196                 p = "tl-bl";
8197             }else if(p == "?"){
8198                 p = "tl-bl?";
8199             }else if(p.indexOf("-") == -1){
8200                 p = "tl-" + p;
8201             }
8202             p = p.toLowerCase();
8203             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8204             if(!m){
8205                throw "Element.alignTo with an invalid alignment " + p;
8206             }
8207             p1 = m[1]; p2 = m[2]; c = !!m[3];
8208
8209             //Subtract the aligned el's internal xy from the target's offset xy
8210             //plus custom offset to get the aligned el's new offset xy
8211             var a1 = this.getAnchorXY(p1, true);
8212             var a2 = el.getAnchorXY(p2, false);
8213             var x = a2[0] - a1[0] + o[0];
8214             var y = a2[1] - a1[1] + o[1];
8215             if(c){
8216                 //constrain the aligned el to viewport if necessary
8217                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8218                 // 5px of margin for ie
8219                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8220
8221                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8222                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8223                 //otherwise swap the aligned el to the opposite border of the target.
8224                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8225                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8226                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8227                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8228
8229                var doc = document;
8230                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8231                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8232
8233                if((x+w) > dw + scrollX){
8234                     x = swapX ? r.left-w : dw+scrollX-w;
8235                 }
8236                if(x < scrollX){
8237                    x = swapX ? r.right : scrollX;
8238                }
8239                if((y+h) > dh + scrollY){
8240                     y = swapY ? r.top-h : dh+scrollY-h;
8241                 }
8242                if (y < scrollY){
8243                    y = swapY ? r.bottom : scrollY;
8244                }
8245             }
8246             return [x,y];
8247         },
8248
8249         // private
8250         getConstrainToXY : function(){
8251             var os = {top:0, left:0, bottom:0, right: 0};
8252
8253             return function(el, local, offsets, proposedXY){
8254                 el = Roo.get(el);
8255                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8256
8257                 var vw, vh, vx = 0, vy = 0;
8258                 if(el.dom == document.body || el.dom == document){
8259                     vw = Roo.lib.Dom.getViewWidth();
8260                     vh = Roo.lib.Dom.getViewHeight();
8261                 }else{
8262                     vw = el.dom.clientWidth;
8263                     vh = el.dom.clientHeight;
8264                     if(!local){
8265                         var vxy = el.getXY();
8266                         vx = vxy[0];
8267                         vy = vxy[1];
8268                     }
8269                 }
8270
8271                 var s = el.getScroll();
8272
8273                 vx += offsets.left + s.left;
8274                 vy += offsets.top + s.top;
8275
8276                 vw -= offsets.right;
8277                 vh -= offsets.bottom;
8278
8279                 var vr = vx+vw;
8280                 var vb = vy+vh;
8281
8282                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8283                 var x = xy[0], y = xy[1];
8284                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8285
8286                 // only move it if it needs it
8287                 var moved = false;
8288
8289                 // first validate right/bottom
8290                 if((x + w) > vr){
8291                     x = vr - w;
8292                     moved = true;
8293                 }
8294                 if((y + h) > vb){
8295                     y = vb - h;
8296                     moved = true;
8297                 }
8298                 // then make sure top/left isn't negative
8299                 if(x < vx){
8300                     x = vx;
8301                     moved = true;
8302                 }
8303                 if(y < vy){
8304                     y = vy;
8305                     moved = true;
8306                 }
8307                 return moved ? [x, y] : false;
8308             };
8309         }(),
8310
8311         // private
8312         adjustForConstraints : function(xy, parent, offsets){
8313             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8314         },
8315
8316         /**
8317          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8318          * document it aligns it to the viewport.
8319          * The position parameter is optional, and can be specified in any one of the following formats:
8320          * <ul>
8321          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8322          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8323          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8324          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8325          *   <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
8326          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8327          * </ul>
8328          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8329          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8330          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8331          * that specified in order to enforce the viewport constraints.
8332          * Following are all of the supported anchor positions:
8333     <pre>
8334     Value  Description
8335     -----  -----------------------------
8336     tl     The top left corner (default)
8337     t      The center of the top edge
8338     tr     The top right corner
8339     l      The center of the left edge
8340     c      In the center of the element
8341     r      The center of the right edge
8342     bl     The bottom left corner
8343     b      The center of the bottom edge
8344     br     The bottom right corner
8345     </pre>
8346     Example Usage:
8347     <pre><code>
8348     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8349     el.alignTo("other-el");
8350
8351     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8352     el.alignTo("other-el", "tr?");
8353
8354     // align the bottom right corner of el with the center left edge of other-el
8355     el.alignTo("other-el", "br-l?");
8356
8357     // align the center of el with the bottom left corner of other-el and
8358     // adjust the x position by -6 pixels (and the y position by 0)
8359     el.alignTo("other-el", "c-bl", [-6, 0]);
8360     </code></pre>
8361          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8362          * @param {String} position The position to align to.
8363          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367         alignTo : function(element, position, offsets, animate){
8368             var xy = this.getAlignToXY(element, position, offsets);
8369             this.setXY(xy, this.preanim(arguments, 3));
8370             return this;
8371         },
8372
8373         /**
8374          * Anchors an element to another element and realigns it when the window is resized.
8375          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8376          * @param {String} position The position to align to.
8377          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8378          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8379          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8380          * is a number, it is used as the buffer delay (defaults to 50ms).
8381          * @param {Function} callback The function to call after the animation finishes
8382          * @return {Roo.Element} this
8383          */
8384         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8385             var action = function(){
8386                 this.alignTo(el, alignment, offsets, animate);
8387                 Roo.callback(callback, this);
8388             };
8389             Roo.EventManager.onWindowResize(action, this);
8390             var tm = typeof monitorScroll;
8391             if(tm != 'undefined'){
8392                 Roo.EventManager.on(window, 'scroll', action, this,
8393                     {buffer: tm == 'number' ? monitorScroll : 50});
8394             }
8395             action.call(this); // align immediately
8396             return this;
8397         },
8398         /**
8399          * Clears any opacity settings from this element. Required in some cases for IE.
8400          * @return {Roo.Element} this
8401          */
8402         clearOpacity : function(){
8403             if (window.ActiveXObject) {
8404                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8405                     this.dom.style.filter = "";
8406                 }
8407             } else {
8408                 this.dom.style.opacity = "";
8409                 this.dom.style["-moz-opacity"] = "";
8410                 this.dom.style["-khtml-opacity"] = "";
8411             }
8412             return this;
8413         },
8414
8415         /**
8416          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8417          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8418          * @return {Roo.Element} this
8419          */
8420         hide : function(animate){
8421             this.setVisible(false, this.preanim(arguments, 0));
8422             return this;
8423         },
8424
8425         /**
8426         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8427         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8428          * @return {Roo.Element} this
8429          */
8430         show : function(animate){
8431             this.setVisible(true, this.preanim(arguments, 0));
8432             return this;
8433         },
8434
8435         /**
8436          * @private Test if size has a unit, otherwise appends the default
8437          */
8438         addUnits : function(size){
8439             return Roo.Element.addUnits(size, this.defaultUnit);
8440         },
8441
8442         /**
8443          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8444          * @return {Roo.Element} this
8445          */
8446         beginMeasure : function(){
8447             var el = this.dom;
8448             if(el.offsetWidth || el.offsetHeight){
8449                 return this; // offsets work already
8450             }
8451             var changed = [];
8452             var p = this.dom, b = document.body; // start with this element
8453             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8454                 var pe = Roo.get(p);
8455                 if(pe.getStyle('display') == 'none'){
8456                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8457                     p.style.visibility = "hidden";
8458                     p.style.display = "block";
8459                 }
8460                 p = p.parentNode;
8461             }
8462             this._measureChanged = changed;
8463             return this;
8464
8465         },
8466
8467         /**
8468          * Restores displays to before beginMeasure was called
8469          * @return {Roo.Element} this
8470          */
8471         endMeasure : function(){
8472             var changed = this._measureChanged;
8473             if(changed){
8474                 for(var i = 0, len = changed.length; i < len; i++) {
8475                     var r = changed[i];
8476                     r.el.style.visibility = r.visibility;
8477                     r.el.style.display = "none";
8478                 }
8479                 this._measureChanged = null;
8480             }
8481             return this;
8482         },
8483
8484         /**
8485         * Update the innerHTML of this element, optionally searching for and processing scripts
8486         * @param {String} html The new HTML
8487         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8488         * @param {Function} callback For async script loading you can be noticed when the update completes
8489         * @return {Roo.Element} this
8490          */
8491         update : function(html, loadScripts, callback){
8492             if(typeof html == "undefined"){
8493                 html = "";
8494             }
8495             if(loadScripts !== true){
8496                 this.dom.innerHTML = html;
8497                 if(typeof callback == "function"){
8498                     callback();
8499                 }
8500                 return this;
8501             }
8502             var id = Roo.id();
8503             var dom = this.dom;
8504
8505             html += '<span id="' + id + '"></span>';
8506
8507             E.onAvailable(id, function(){
8508                 var hd = document.getElementsByTagName("head")[0];
8509                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8510                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8511                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8512
8513                 var match;
8514                 while(match = re.exec(html)){
8515                     var attrs = match[1];
8516                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8517                     if(srcMatch && srcMatch[2]){
8518                        var s = document.createElement("script");
8519                        s.src = srcMatch[2];
8520                        var typeMatch = attrs.match(typeRe);
8521                        if(typeMatch && typeMatch[2]){
8522                            s.type = typeMatch[2];
8523                        }
8524                        hd.appendChild(s);
8525                     }else if(match[2] && match[2].length > 0){
8526                         if(window.execScript) {
8527                            window.execScript(match[2]);
8528                         } else {
8529                             /**
8530                              * eval:var:id
8531                              * eval:var:dom
8532                              * eval:var:html
8533                              * 
8534                              */
8535                            window.eval(match[2]);
8536                         }
8537                     }
8538                 }
8539                 var el = document.getElementById(id);
8540                 if(el){el.parentNode.removeChild(el);}
8541                 if(typeof callback == "function"){
8542                     callback();
8543                 }
8544             });
8545             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8546             return this;
8547         },
8548
8549         /**
8550          * Direct access to the UpdateManager update() method (takes the same parameters).
8551          * @param {String/Function} url The url for this request or a function to call to get the url
8552          * @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}
8553          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8554          * @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.
8555          * @return {Roo.Element} this
8556          */
8557         load : function(){
8558             var um = this.getUpdateManager();
8559             um.update.apply(um, arguments);
8560             return this;
8561         },
8562
8563         /**
8564         * Gets this element's UpdateManager
8565         * @return {Roo.UpdateManager} The UpdateManager
8566         */
8567         getUpdateManager : function(){
8568             if(!this.updateManager){
8569                 this.updateManager = new Roo.UpdateManager(this);
8570             }
8571             return this.updateManager;
8572         },
8573
8574         /**
8575          * Disables text selection for this element (normalized across browsers)
8576          * @return {Roo.Element} this
8577          */
8578         unselectable : function(){
8579             this.dom.unselectable = "on";
8580             this.swallowEvent("selectstart", true);
8581             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8582             this.addClass("x-unselectable");
8583             return this;
8584         },
8585
8586         /**
8587         * Calculates the x, y to center this element on the screen
8588         * @return {Array} The x, y values [x, y]
8589         */
8590         getCenterXY : function(){
8591             return this.getAlignToXY(document, 'c-c');
8592         },
8593
8594         /**
8595         * Centers the Element in either the viewport, or another Element.
8596         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8597         */
8598         center : function(centerIn){
8599             this.alignTo(centerIn || document, 'c-c');
8600             return this;
8601         },
8602
8603         /**
8604          * Tests various css rules/browsers to determine if this element uses a border box
8605          * @return {Boolean}
8606          */
8607         isBorderBox : function(){
8608             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8609         },
8610
8611         /**
8612          * Return a box {x, y, width, height} that can be used to set another elements
8613          * size/location to match this element.
8614          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8615          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8616          * @return {Object} box An object in the format {x, y, width, height}
8617          */
8618         getBox : function(contentBox, local){
8619             var xy;
8620             if(!local){
8621                 xy = this.getXY();
8622             }else{
8623                 var left = parseInt(this.getStyle("left"), 10) || 0;
8624                 var top = parseInt(this.getStyle("top"), 10) || 0;
8625                 xy = [left, top];
8626             }
8627             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8628             if(!contentBox){
8629                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8630             }else{
8631                 var l = this.getBorderWidth("l")+this.getPadding("l");
8632                 var r = this.getBorderWidth("r")+this.getPadding("r");
8633                 var t = this.getBorderWidth("t")+this.getPadding("t");
8634                 var b = this.getBorderWidth("b")+this.getPadding("b");
8635                 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)};
8636             }
8637             bx.right = bx.x + bx.width;
8638             bx.bottom = bx.y + bx.height;
8639             return bx;
8640         },
8641
8642         /**
8643          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8644          for more information about the sides.
8645          * @param {String} sides
8646          * @return {Number}
8647          */
8648         getFrameWidth : function(sides, onlyContentBox){
8649             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8650         },
8651
8652         /**
8653          * 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.
8654          * @param {Object} box The box to fill {x, y, width, height}
8655          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8656          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         setBox : function(box, adjust, animate){
8660             var w = box.width, h = box.height;
8661             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8662                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8663                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8664             }
8665             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8666             return this;
8667         },
8668
8669         /**
8670          * Forces the browser to repaint this element
8671          * @return {Roo.Element} this
8672          */
8673          repaint : function(){
8674             var dom = this.dom;
8675             this.addClass("x-repaint");
8676             setTimeout(function(){
8677                 Roo.get(dom).removeClass("x-repaint");
8678             }, 1);
8679             return this;
8680         },
8681
8682         /**
8683          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8684          * then it returns the calculated width of the sides (see getPadding)
8685          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8686          * @return {Object/Number}
8687          */
8688         getMargins : function(side){
8689             if(!side){
8690                 return {
8691                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8692                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8693                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8694                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8695                 };
8696             }else{
8697                 return this.addStyles(side, El.margins);
8698              }
8699         },
8700
8701         // private
8702         addStyles : function(sides, styles){
8703             var val = 0, v, w;
8704             for(var i = 0, len = sides.length; i < len; i++){
8705                 v = this.getStyle(styles[sides.charAt(i)]);
8706                 if(v){
8707                      w = parseInt(v, 10);
8708                      if(w){ val += w; }
8709                 }
8710             }
8711             return val;
8712         },
8713
8714         /**
8715          * Creates a proxy element of this element
8716          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8717          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8718          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8719          * @return {Roo.Element} The new proxy element
8720          */
8721         createProxy : function(config, renderTo, matchBox){
8722             if(renderTo){
8723                 renderTo = Roo.getDom(renderTo);
8724             }else{
8725                 renderTo = document.body;
8726             }
8727             config = typeof config == "object" ?
8728                 config : {tag : "div", cls: config};
8729             var proxy = Roo.DomHelper.append(renderTo, config, true);
8730             if(matchBox){
8731                proxy.setBox(this.getBox());
8732             }
8733             return proxy;
8734         },
8735
8736         /**
8737          * Puts a mask over this element to disable user interaction. Requires core.css.
8738          * This method can only be applied to elements which accept child nodes.
8739          * @param {String} msg (optional) A message to display in the mask
8740          * @param {String} msgCls (optional) A css class to apply to the msg element
8741          * @return {Element} The mask  element
8742          */
8743         mask : function(msg, msgCls){
8744             if(this.getStyle("position") == "static"){
8745                 this.setStyle("position", "relative");
8746             }
8747             if(!this._mask){
8748                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8749             }
8750             this.addClass("x-masked");
8751             this._mask.setDisplayed(true);
8752             if(typeof msg == 'string'){
8753                 if(!this._maskMsg){
8754                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8755                 }
8756                 var mm = this._maskMsg;
8757                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8758                 mm.dom.firstChild.innerHTML = msg;
8759                 mm.setDisplayed(true);
8760                 mm.center(this);
8761             }
8762             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8763                 this._mask.setHeight(this.getHeight());
8764             }
8765             return this._mask;
8766         },
8767
8768         /**
8769          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8770          * it is cached for reuse.
8771          */
8772         unmask : function(removeEl){
8773             if(this._mask){
8774                 if(removeEl === true){
8775                     this._mask.remove();
8776                     delete this._mask;
8777                     if(this._maskMsg){
8778                         this._maskMsg.remove();
8779                         delete this._maskMsg;
8780                     }
8781                 }else{
8782                     this._mask.setDisplayed(false);
8783                     if(this._maskMsg){
8784                         this._maskMsg.setDisplayed(false);
8785                     }
8786                 }
8787             }
8788             this.removeClass("x-masked");
8789         },
8790
8791         /**
8792          * Returns true if this element is masked
8793          * @return {Boolean}
8794          */
8795         isMasked : function(){
8796             return this._mask && this._mask.isVisible();
8797         },
8798
8799         /**
8800          * Creates an iframe shim for this element to keep selects and other windowed objects from
8801          * showing through.
8802          * @return {Roo.Element} The new shim element
8803          */
8804         createShim : function(){
8805             var el = document.createElement('iframe');
8806             el.frameBorder = 'no';
8807             el.className = 'roo-shim';
8808             if(Roo.isIE && Roo.isSecure){
8809                 el.src = Roo.SSL_SECURE_URL;
8810             }
8811             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8812             shim.autoBoxAdjust = false;
8813             return shim;
8814         },
8815
8816         /**
8817          * Removes this element from the DOM and deletes it from the cache
8818          */
8819         remove : function(){
8820             if(this.dom.parentNode){
8821                 this.dom.parentNode.removeChild(this.dom);
8822             }
8823             delete El.cache[this.dom.id];
8824         },
8825
8826         /**
8827          * Sets up event handlers to add and remove a css class when the mouse is over this element
8828          * @param {String} className
8829          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8830          * mouseout events for children elements
8831          * @return {Roo.Element} this
8832          */
8833         addClassOnOver : function(className, preventFlicker){
8834             this.on("mouseover", function(){
8835                 Roo.fly(this, '_internal').addClass(className);
8836             }, this.dom);
8837             var removeFn = function(e){
8838                 if(preventFlicker !== true || !e.within(this, true)){
8839                     Roo.fly(this, '_internal').removeClass(className);
8840                 }
8841             };
8842             this.on("mouseout", removeFn, this.dom);
8843             return this;
8844         },
8845
8846         /**
8847          * Sets up event handlers to add and remove a css class when this element has the focus
8848          * @param {String} className
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnFocus : function(className){
8852             this.on("focus", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             this.on("blur", function(){
8856                 Roo.fly(this, '_internal').removeClass(className);
8857             }, this.dom);
8858             return this;
8859         },
8860         /**
8861          * 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)
8862          * @param {String} className
8863          * @return {Roo.Element} this
8864          */
8865         addClassOnClick : function(className){
8866             var dom = this.dom;
8867             this.on("mousedown", function(){
8868                 Roo.fly(dom, '_internal').addClass(className);
8869                 var d = Roo.get(document);
8870                 var fn = function(){
8871                     Roo.fly(dom, '_internal').removeClass(className);
8872                     d.removeListener("mouseup", fn);
8873                 };
8874                 d.on("mouseup", fn);
8875             });
8876             return this;
8877         },
8878
8879         /**
8880          * Stops the specified event from bubbling and optionally prevents the default action
8881          * @param {String} eventName
8882          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8883          * @return {Roo.Element} this
8884          */
8885         swallowEvent : function(eventName, preventDefault){
8886             var fn = function(e){
8887                 e.stopPropagation();
8888                 if(preventDefault){
8889                     e.preventDefault();
8890                 }
8891             };
8892             if(eventName instanceof Array){
8893                 for(var i = 0, len = eventName.length; i < len; i++){
8894                      this.on(eventName[i], fn);
8895                 }
8896                 return this;
8897             }
8898             this.on(eventName, fn);
8899             return this;
8900         },
8901
8902         /**
8903          * @private
8904          */
8905       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8906
8907         /**
8908          * Sizes this element to its parent element's dimensions performing
8909          * neccessary box adjustments.
8910          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8911          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8912          * @return {Roo.Element} this
8913          */
8914         fitToParent : function(monitorResize, targetParent) {
8915           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8916           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8917           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8918             return;
8919           }
8920           var p = Roo.get(targetParent || this.dom.parentNode);
8921           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8922           if (monitorResize === true) {
8923             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8924             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8925           }
8926           return this;
8927         },
8928
8929         /**
8930          * Gets the next sibling, skipping text nodes
8931          * @return {HTMLElement} The next sibling or null
8932          */
8933         getNextSibling : function(){
8934             var n = this.dom.nextSibling;
8935             while(n && n.nodeType != 1){
8936                 n = n.nextSibling;
8937             }
8938             return n;
8939         },
8940
8941         /**
8942          * Gets the previous sibling, skipping text nodes
8943          * @return {HTMLElement} The previous sibling or null
8944          */
8945         getPrevSibling : function(){
8946             var n = this.dom.previousSibling;
8947             while(n && n.nodeType != 1){
8948                 n = n.previousSibling;
8949             }
8950             return n;
8951         },
8952
8953
8954         /**
8955          * Appends the passed element(s) to this element
8956          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8957          * @return {Roo.Element} this
8958          */
8959         appendChild: function(el){
8960             el = Roo.get(el);
8961             el.appendTo(this);
8962             return this;
8963         },
8964
8965         /**
8966          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8967          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8968          * automatically generated with the specified attributes.
8969          * @param {HTMLElement} insertBefore (optional) a child element of this element
8970          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8971          * @return {Roo.Element} The new child element
8972          */
8973         createChild: function(config, insertBefore, returnDom){
8974             config = config || {tag:'div'};
8975             if(insertBefore){
8976                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8977             }
8978             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8979         },
8980
8981         /**
8982          * Appends this element to the passed element
8983          * @param {String/HTMLElement/Element} el The new parent element
8984          * @return {Roo.Element} this
8985          */
8986         appendTo: function(el){
8987             el = Roo.getDom(el);
8988             el.appendChild(this.dom);
8989             return this;
8990         },
8991
8992         /**
8993          * Inserts this element before the passed element in the DOM
8994          * @param {String/HTMLElement/Element} el The element to insert before
8995          * @return {Roo.Element} this
8996          */
8997         insertBefore: function(el){
8998             el = Roo.getDom(el);
8999             el.parentNode.insertBefore(this.dom, el);
9000             return this;
9001         },
9002
9003         /**
9004          * Inserts this element after the passed element in the DOM
9005          * @param {String/HTMLElement/Element} el The element to insert after
9006          * @return {Roo.Element} this
9007          */
9008         insertAfter: function(el){
9009             el = Roo.getDom(el);
9010             el.parentNode.insertBefore(this.dom, el.nextSibling);
9011             return this;
9012         },
9013
9014         /**
9015          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9016          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9017          * @return {Roo.Element} The new child
9018          */
9019         insertFirst: function(el, returnDom){
9020             el = el || {};
9021             if(typeof el == 'object' && !el.nodeType){ // dh config
9022                 return this.createChild(el, this.dom.firstChild, returnDom);
9023             }else{
9024                 el = Roo.getDom(el);
9025                 this.dom.insertBefore(el, this.dom.firstChild);
9026                 return !returnDom ? Roo.get(el) : el;
9027             }
9028         },
9029
9030         /**
9031          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9032          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9033          * @param {String} where (optional) 'before' or 'after' defaults to before
9034          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9035          * @return {Roo.Element} the inserted Element
9036          */
9037         insertSibling: function(el, where, returnDom){
9038             where = where ? where.toLowerCase() : 'before';
9039             el = el || {};
9040             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9041
9042             if(typeof el == 'object' && !el.nodeType){ // dh config
9043                 if(where == 'after' && !this.dom.nextSibling){
9044                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9045                 }else{
9046                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9047                 }
9048
9049             }else{
9050                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9051                             where == 'before' ? this.dom : this.dom.nextSibling);
9052                 if(!returnDom){
9053                     rt = Roo.get(rt);
9054                 }
9055             }
9056             return rt;
9057         },
9058
9059         /**
9060          * Creates and wraps this element with another element
9061          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9062          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9063          * @return {HTMLElement/Element} The newly created wrapper element
9064          */
9065         wrap: function(config, returnDom){
9066             if(!config){
9067                 config = {tag: "div"};
9068             }
9069             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9070             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9071             return newEl;
9072         },
9073
9074         /**
9075          * Replaces the passed element with this element
9076          * @param {String/HTMLElement/Element} el The element to replace
9077          * @return {Roo.Element} this
9078          */
9079         replace: function(el){
9080             el = Roo.get(el);
9081             this.insertBefore(el);
9082             el.remove();
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts an html fragment into this element
9088          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9089          * @param {String} html The HTML fragment
9090          * @param {Boolean} returnEl True to return an Roo.Element
9091          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9092          */
9093         insertHtml : function(where, html, returnEl){
9094             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9095             return returnEl ? Roo.get(el) : el;
9096         },
9097
9098         /**
9099          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9100          * @param {Object} o The object with the attributes
9101          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9102          * @return {Roo.Element} this
9103          */
9104         set : function(o, useSet){
9105             var el = this.dom;
9106             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9107             for(var attr in o){
9108                 if(attr == "style" || typeof o[attr] == "function") continue;
9109                 if(attr=="cls"){
9110                     el.className = o["cls"];
9111                 }else{
9112                     if(useSet) el.setAttribute(attr, o[attr]);
9113                     else el[attr] = o[attr];
9114                 }
9115             }
9116             if(o.style){
9117                 Roo.DomHelper.applyStyles(el, o.style);
9118             }
9119             return this;
9120         },
9121
9122         /**
9123          * Convenience method for constructing a KeyMap
9124          * @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:
9125          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9126          * @param {Function} fn The function to call
9127          * @param {Object} scope (optional) The scope of the function
9128          * @return {Roo.KeyMap} The KeyMap created
9129          */
9130         addKeyListener : function(key, fn, scope){
9131             var config;
9132             if(typeof key != "object" || key instanceof Array){
9133                 config = {
9134                     key: key,
9135                     fn: fn,
9136                     scope: scope
9137                 };
9138             }else{
9139                 config = {
9140                     key : key.key,
9141                     shift : key.shift,
9142                     ctrl : key.ctrl,
9143                     alt : key.alt,
9144                     fn: fn,
9145                     scope: scope
9146                 };
9147             }
9148             return new Roo.KeyMap(this, config);
9149         },
9150
9151         /**
9152          * Creates a KeyMap for this element
9153          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9154          * @return {Roo.KeyMap} The KeyMap created
9155          */
9156         addKeyMap : function(config){
9157             return new Roo.KeyMap(this, config);
9158         },
9159
9160         /**
9161          * Returns true if this element is scrollable.
9162          * @return {Boolean}
9163          */
9164          isScrollable : function(){
9165             var dom = this.dom;
9166             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9167         },
9168
9169         /**
9170          * 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().
9171          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9172          * @param {Number} value The new scroll value
9173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9174          * @return {Element} this
9175          */
9176
9177         scrollTo : function(side, value, animate){
9178             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9179             if(!animate || !A){
9180                 this.dom[prop] = value;
9181             }else{
9182                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9183                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9190          * within this element's scrollable range.
9191          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9192          * @param {Number} distance How far to scroll the element in pixels
9193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9194          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9195          * was scrolled as far as it could go.
9196          */
9197          scroll : function(direction, distance, animate){
9198              if(!this.isScrollable()){
9199                  return;
9200              }
9201              var el = this.dom;
9202              var l = el.scrollLeft, t = el.scrollTop;
9203              var w = el.scrollWidth, h = el.scrollHeight;
9204              var cw = el.clientWidth, ch = el.clientHeight;
9205              direction = direction.toLowerCase();
9206              var scrolled = false;
9207              var a = this.preanim(arguments, 2);
9208              switch(direction){
9209                  case "l":
9210                  case "left":
9211                      if(w - l > cw){
9212                          var v = Math.min(l + distance, w-cw);
9213                          this.scrollTo("left", v, a);
9214                          scrolled = true;
9215                      }
9216                      break;
9217                 case "r":
9218                 case "right":
9219                      if(l > 0){
9220                          var v = Math.max(l - distance, 0);
9221                          this.scrollTo("left", v, a);
9222                          scrolled = true;
9223                      }
9224                      break;
9225                 case "t":
9226                 case "top":
9227                 case "up":
9228                      if(t > 0){
9229                          var v = Math.max(t - distance, 0);
9230                          this.scrollTo("top", v, a);
9231                          scrolled = true;
9232                      }
9233                      break;
9234                 case "b":
9235                 case "bottom":
9236                 case "down":
9237                      if(h - t > ch){
9238                          var v = Math.min(t + distance, h-ch);
9239                          this.scrollTo("top", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243              }
9244              return scrolled;
9245         },
9246
9247         /**
9248          * Translates the passed page coordinates into left/top css values for this element
9249          * @param {Number/Array} x The page x or an array containing [x, y]
9250          * @param {Number} y The page y
9251          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9252          */
9253         translatePoints : function(x, y){
9254             if(typeof x == 'object' || x instanceof Array){
9255                 y = x[1]; x = x[0];
9256             }
9257             var p = this.getStyle('position');
9258             var o = this.getXY();
9259
9260             var l = parseInt(this.getStyle('left'), 10);
9261             var t = parseInt(this.getStyle('top'), 10);
9262
9263             if(isNaN(l)){
9264                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9265             }
9266             if(isNaN(t)){
9267                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9268             }
9269
9270             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9271         },
9272
9273         /**
9274          * Returns the current scroll position of the element.
9275          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9276          */
9277         getScroll : function(){
9278             var d = this.dom, doc = document;
9279             if(d == doc || d == doc.body){
9280                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9281                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9282                 return {left: l, top: t};
9283             }else{
9284                 return {left: d.scrollLeft, top: d.scrollTop};
9285             }
9286         },
9287
9288         /**
9289          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9290          * are convert to standard 6 digit hex color.
9291          * @param {String} attr The css attribute
9292          * @param {String} defaultValue The default value to use when a valid color isn't found
9293          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9294          * YUI color anims.
9295          */
9296         getColor : function(attr, defaultValue, prefix){
9297             var v = this.getStyle(attr);
9298             if(!v || v == "transparent" || v == "inherit") {
9299                 return defaultValue;
9300             }
9301             var color = typeof prefix == "undefined" ? "#" : prefix;
9302             if(v.substr(0, 4) == "rgb("){
9303                 var rvs = v.slice(4, v.length -1).split(",");
9304                 for(var i = 0; i < 3; i++){
9305                     var h = parseInt(rvs[i]).toString(16);
9306                     if(h < 16){
9307                         h = "0" + h;
9308                     }
9309                     color += h;
9310                 }
9311             } else {
9312                 if(v.substr(0, 1) == "#"){
9313                     if(v.length == 4) {
9314                         for(var i = 1; i < 4; i++){
9315                             var c = v.charAt(i);
9316                             color +=  c + c;
9317                         }
9318                     }else if(v.length == 7){
9319                         color += v.substr(1);
9320                     }
9321                 }
9322             }
9323             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9324         },
9325
9326         /**
9327          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9328          * gradient background, rounded corners and a 4-way shadow.
9329          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9330          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9331          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9332          * @return {Roo.Element} this
9333          */
9334         boxWrap : function(cls){
9335             cls = cls || 'x-box';
9336             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9337             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9338             return el;
9339         },
9340
9341         /**
9342          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9343          * @param {String} namespace The namespace in which to look for the attribute
9344          * @param {String} name The attribute name
9345          * @return {String} The attribute value
9346          */
9347         getAttributeNS : Roo.isIE ? function(ns, name){
9348             var d = this.dom;
9349             var type = typeof d[ns+":"+name];
9350             if(type != 'undefined' && type != 'unknown'){
9351                 return d[ns+":"+name];
9352             }
9353             return d[name];
9354         } : function(ns, name){
9355             var d = this.dom;
9356             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9357         }
9358     };
9359
9360     var ep = El.prototype;
9361
9362     /**
9363      * Appends an event handler (Shorthand for addListener)
9364      * @param {String}   eventName     The type of event to append
9365      * @param {Function} fn        The method the event invokes
9366      * @param {Object} scope       (optional) The scope (this object) of the fn
9367      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368      * @method
9369      */
9370     ep.on = ep.addListener;
9371         // backwards compat
9372     ep.mon = ep.addListener;
9373
9374     /**
9375      * Removes an event handler from this element (shorthand for removeListener)
9376      * @param {String} eventName the type of event to remove
9377      * @param {Function} fn the method the event invokes
9378      * @return {Roo.Element} this
9379      * @method
9380      */
9381     ep.un = ep.removeListener;
9382
9383     /**
9384      * true to automatically adjust width and height settings for box-model issues (default to true)
9385      */
9386     ep.autoBoxAdjust = true;
9387
9388     // private
9389     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9390
9391     // private
9392     El.addUnits = function(v, defaultUnit){
9393         if(v === "" || v == "auto"){
9394             return v;
9395         }
9396         if(v === undefined){
9397             return '';
9398         }
9399         if(typeof v == "number" || !El.unitPattern.test(v)){
9400             return v + (defaultUnit || 'px');
9401         }
9402         return v;
9403     };
9404
9405     // special markup used throughout Roo when box wrapping elements
9406     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>';
9407     /**
9408      * Visibility mode constant - Use visibility to hide element
9409      * @static
9410      * @type Number
9411      */
9412     El.VISIBILITY = 1;
9413     /**
9414      * Visibility mode constant - Use display to hide element
9415      * @static
9416      * @type Number
9417      */
9418     El.DISPLAY = 2;
9419
9420     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9421     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9422     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9423
9424
9425
9426     /**
9427      * @private
9428      */
9429     El.cache = {};
9430
9431     var docEl;
9432
9433     /**
9434      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9435      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9436      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9437      * @return {Element} The Element object
9438      * @static
9439      */
9440     El.get = function(el){
9441         var ex, elm, id;
9442         if(!el){ return null; }
9443         if(typeof el == "string"){ // element id
9444             if(!(elm = document.getElementById(el))){
9445                 return null;
9446             }
9447             if(ex = El.cache[el]){
9448                 ex.dom = elm;
9449             }else{
9450                 ex = El.cache[el] = new El(elm);
9451             }
9452             return ex;
9453         }else if(el.tagName){ // dom element
9454             if(!(id = el.id)){
9455                 id = Roo.id(el);
9456             }
9457             if(ex = El.cache[id]){
9458                 ex.dom = el;
9459             }else{
9460                 ex = El.cache[id] = new El(el);
9461             }
9462             return ex;
9463         }else if(el instanceof El){
9464             if(el != docEl){
9465                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9466                                                               // catch case where it hasn't been appended
9467                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9468             }
9469             return el;
9470         }else if(el.isComposite){
9471             return el;
9472         }else if(el instanceof Array){
9473             return El.select(el);
9474         }else if(el == document){
9475             // create a bogus element object representing the document object
9476             if(!docEl){
9477                 var f = function(){};
9478                 f.prototype = El.prototype;
9479                 docEl = new f();
9480                 docEl.dom = document;
9481             }
9482             return docEl;
9483         }
9484         return null;
9485     };
9486
9487     // private
9488     El.uncache = function(el){
9489         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9490             if(a[i]){
9491                 delete El.cache[a[i].id || a[i]];
9492             }
9493         }
9494     };
9495
9496     // private
9497     // Garbage collection - uncache elements/purge listeners on orphaned elements
9498     // so we don't hold a reference and cause the browser to retain them
9499     El.garbageCollect = function(){
9500         if(!Roo.enableGarbageCollector){
9501             clearInterval(El.collectorThread);
9502             return;
9503         }
9504         for(var eid in El.cache){
9505             var el = El.cache[eid], d = el.dom;
9506             // -------------------------------------------------------
9507             // Determining what is garbage:
9508             // -------------------------------------------------------
9509             // !d
9510             // dom node is null, definitely garbage
9511             // -------------------------------------------------------
9512             // !d.parentNode
9513             // no parentNode == direct orphan, definitely garbage
9514             // -------------------------------------------------------
9515             // !d.offsetParent && !document.getElementById(eid)
9516             // display none elements have no offsetParent so we will
9517             // also try to look it up by it's id. However, check
9518             // offsetParent first so we don't do unneeded lookups.
9519             // This enables collection of elements that are not orphans
9520             // directly, but somewhere up the line they have an orphan
9521             // parent.
9522             // -------------------------------------------------------
9523             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9524                 delete El.cache[eid];
9525                 if(d && Roo.enableListenerCollection){
9526                     E.purgeElement(d);
9527                 }
9528             }
9529         }
9530     }
9531     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9532
9533
9534     // dom is optional
9535     El.Flyweight = function(dom){
9536         this.dom = dom;
9537     };
9538     El.Flyweight.prototype = El.prototype;
9539
9540     El._flyweights = {};
9541     /**
9542      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9543      * the dom node can be overwritten by other code.
9544      * @param {String/HTMLElement} el The dom node or id
9545      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9546      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9547      * @static
9548      * @return {Element} The shared Element object
9549      */
9550     El.fly = function(el, named){
9551         named = named || '_global';
9552         el = Roo.getDom(el);
9553         if(!el){
9554             return null;
9555         }
9556         if(!El._flyweights[named]){
9557             El._flyweights[named] = new El.Flyweight();
9558         }
9559         El._flyweights[named].dom = el;
9560         return El._flyweights[named];
9561     };
9562
9563     /**
9564      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9565      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9566      * Shorthand of {@link Roo.Element#get}
9567      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9568      * @return {Element} The Element object
9569      * @member Roo
9570      * @method get
9571      */
9572     Roo.get = El.get;
9573     /**
9574      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9575      * the dom node can be overwritten by other code.
9576      * Shorthand of {@link Roo.Element#fly}
9577      * @param {String/HTMLElement} el The dom node or id
9578      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9579      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9580      * @static
9581      * @return {Element} The shared Element object
9582      * @member Roo
9583      * @method fly
9584      */
9585     Roo.fly = El.fly;
9586
9587     // speedy lookup for elements never to box adjust
9588     var noBoxAdjust = Roo.isStrict ? {
9589         select:1
9590     } : {
9591         input:1, select:1, textarea:1
9592     };
9593     if(Roo.isIE || Roo.isGecko){
9594         noBoxAdjust['button'] = 1;
9595     }
9596
9597
9598     Roo.EventManager.on(window, 'unload', function(){
9599         delete El.cache;
9600         delete El._flyweights;
9601     });
9602 })();
9603
9604
9605
9606
9607 if(Roo.DomQuery){
9608     Roo.Element.selectorFunction = Roo.DomQuery.select;
9609 }
9610
9611 Roo.Element.select = function(selector, unique, root){
9612     var els;
9613     if(typeof selector == "string"){
9614         els = Roo.Element.selectorFunction(selector, root);
9615     }else if(selector.length !== undefined){
9616         els = selector;
9617     }else{
9618         throw "Invalid selector";
9619     }
9620     if(unique === true){
9621         return new Roo.CompositeElement(els);
9622     }else{
9623         return new Roo.CompositeElementLite(els);
9624     }
9625 };
9626 /**
9627  * Selects elements based on the passed CSS selector to enable working on them as 1.
9628  * @param {String/Array} selector The CSS selector or an array of elements
9629  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9630  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9631  * @return {CompositeElementLite/CompositeElement}
9632  * @member Roo
9633  * @method select
9634  */
9635 Roo.select = Roo.Element.select;
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650 /*
9651  * Based on:
9652  * Ext JS Library 1.1.1
9653  * Copyright(c) 2006-2007, Ext JS, LLC.
9654  *
9655  * Originally Released Under LGPL - original licence link has changed is not relivant.
9656  *
9657  * Fork - LGPL
9658  * <script type="text/javascript">
9659  */
9660
9661
9662
9663 //Notifies Element that fx methods are available
9664 Roo.enableFx = true;
9665
9666 /**
9667  * @class Roo.Fx
9668  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9669  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9670  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9671  * Element effects to work.</p><br/>
9672  *
9673  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9674  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9675  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9676  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9677  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9678  * expected results and should be done with care.</p><br/>
9679  *
9680  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9681  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9682 <pre>
9683 Value  Description
9684 -----  -----------------------------
9685 tl     The top left corner
9686 t      The center of the top edge
9687 tr     The top right corner
9688 l      The center of the left edge
9689 r      The center of the right edge
9690 bl     The bottom left corner
9691 b      The center of the bottom edge
9692 br     The bottom right corner
9693 </pre>
9694  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9695  * below are common options that can be passed to any Fx method.</b>
9696  * @cfg {Function} callback A function called when the effect is finished
9697  * @cfg {Object} scope The scope of the effect function
9698  * @cfg {String} easing A valid Easing value for the effect
9699  * @cfg {String} afterCls A css class to apply after the effect
9700  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9701  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9702  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9703  * effects that end with the element being visually hidden, ignored otherwise)
9704  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9705  * a function which returns such a specification that will be applied to the Element after the effect finishes
9706  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9707  * @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
9708  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9709  */
9710 Roo.Fx = {
9711         /**
9712          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9713          * origin for the slide effect.  This function automatically handles wrapping the element with
9714          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9715          * Usage:
9716          *<pre><code>
9717 // default: slide the element in from the top
9718 el.slideIn();
9719
9720 // custom: slide the element in from the right with a 2-second duration
9721 el.slideIn('r', { duration: 2 });
9722
9723 // common config options shown with default values
9724 el.slideIn('t', {
9725     easing: 'easeOut',
9726     duration: .5
9727 });
9728 </code></pre>
9729          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9730          * @param {Object} options (optional) Object literal with any of the Fx config options
9731          * @return {Roo.Element} The Element
9732          */
9733     slideIn : function(anchor, o){
9734         var el = this.getFxEl();
9735         o = o || {};
9736
9737         el.queueFx(o, function(){
9738
9739             anchor = anchor || "t";
9740
9741             // fix display to visibility
9742             this.fixDisplay();
9743
9744             // restore values after effect
9745             var r = this.getFxRestore();
9746             var b = this.getBox();
9747             // fixed size for slide
9748             this.setSize(b);
9749
9750             // wrap if needed
9751             var wrap = this.fxWrap(r.pos, o, "hidden");
9752
9753             var st = this.dom.style;
9754             st.visibility = "visible";
9755             st.position = "absolute";
9756
9757             // clear out temp styles after slide and unwrap
9758             var after = function(){
9759                 el.fxUnwrap(wrap, r.pos, o);
9760                 st.width = r.width;
9761                 st.height = r.height;
9762                 el.afterFx(o);
9763             };
9764             // time to calc the positions
9765             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9766
9767             switch(anchor.toLowerCase()){
9768                 case "t":
9769                     wrap.setSize(b.width, 0);
9770                     st.left = st.bottom = "0";
9771                     a = {height: bh};
9772                 break;
9773                 case "l":
9774                     wrap.setSize(0, b.height);
9775                     st.right = st.top = "0";
9776                     a = {width: bw};
9777                 break;
9778                 case "r":
9779                     wrap.setSize(0, b.height);
9780                     wrap.setX(b.right);
9781                     st.left = st.top = "0";
9782                     a = {width: bw, points: pt};
9783                 break;
9784                 case "b":
9785                     wrap.setSize(b.width, 0);
9786                     wrap.setY(b.bottom);
9787                     st.left = st.top = "0";
9788                     a = {height: bh, points: pt};
9789                 break;
9790                 case "tl":
9791                     wrap.setSize(0, 0);
9792                     st.right = st.bottom = "0";
9793                     a = {width: bw, height: bh};
9794                 break;
9795                 case "bl":
9796                     wrap.setSize(0, 0);
9797                     wrap.setY(b.y+b.height);
9798                     st.right = st.top = "0";
9799                     a = {width: bw, height: bh, points: pt};
9800                 break;
9801                 case "br":
9802                     wrap.setSize(0, 0);
9803                     wrap.setXY([b.right, b.bottom]);
9804                     st.left = st.top = "0";
9805                     a = {width: bw, height: bh, points: pt};
9806                 break;
9807                 case "tr":
9808                     wrap.setSize(0, 0);
9809                     wrap.setX(b.x+b.width);
9810                     st.left = st.bottom = "0";
9811                     a = {width: bw, height: bh, points: pt};
9812                 break;
9813             }
9814             this.dom.style.visibility = "visible";
9815             wrap.show();
9816
9817             arguments.callee.anim = wrap.fxanim(a,
9818                 o,
9819                 'motion',
9820                 .5,
9821                 'easeOut', after);
9822         });
9823         return this;
9824     },
9825     
9826         /**
9827          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9828          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9829          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9830          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9831          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9832          * Usage:
9833          *<pre><code>
9834 // default: slide the element out to the top
9835 el.slideOut();
9836
9837 // custom: slide the element out to the right with a 2-second duration
9838 el.slideOut('r', { duration: 2 });
9839
9840 // common config options shown with default values
9841 el.slideOut('t', {
9842     easing: 'easeOut',
9843     duration: .5,
9844     remove: false,
9845     useDisplay: false
9846 });
9847 </code></pre>
9848          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9849          * @param {Object} options (optional) Object literal with any of the Fx config options
9850          * @return {Roo.Element} The Element
9851          */
9852     slideOut : function(anchor, o){
9853         var el = this.getFxEl();
9854         o = o || {};
9855
9856         el.queueFx(o, function(){
9857
9858             anchor = anchor || "t";
9859
9860             // restore values after effect
9861             var r = this.getFxRestore();
9862             
9863             var b = this.getBox();
9864             // fixed size for slide
9865             this.setSize(b);
9866
9867             // wrap if needed
9868             var wrap = this.fxWrap(r.pos, o, "visible");
9869
9870             var st = this.dom.style;
9871             st.visibility = "visible";
9872             st.position = "absolute";
9873
9874             wrap.setSize(b);
9875
9876             var after = function(){
9877                 if(o.useDisplay){
9878                     el.setDisplayed(false);
9879                 }else{
9880                     el.hide();
9881                 }
9882
9883                 el.fxUnwrap(wrap, r.pos, o);
9884
9885                 st.width = r.width;
9886                 st.height = r.height;
9887
9888                 el.afterFx(o);
9889             };
9890
9891             var a, zero = {to: 0};
9892             switch(anchor.toLowerCase()){
9893                 case "t":
9894                     st.left = st.bottom = "0";
9895                     a = {height: zero};
9896                 break;
9897                 case "l":
9898                     st.right = st.top = "0";
9899                     a = {width: zero};
9900                 break;
9901                 case "r":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, points: {to:[b.right, b.y]}};
9904                 break;
9905                 case "b":
9906                     st.left = st.top = "0";
9907                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9908                 break;
9909                 case "tl":
9910                     st.right = st.bottom = "0";
9911                     a = {width: zero, height: zero};
9912                 break;
9913                 case "bl":
9914                     st.right = st.top = "0";
9915                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9916                 break;
9917                 case "br":
9918                     st.left = st.top = "0";
9919                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9920                 break;
9921                 case "tr":
9922                     st.left = st.bottom = "0";
9923                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9924                 break;
9925             }
9926
9927             arguments.callee.anim = wrap.fxanim(a,
9928                 o,
9929                 'motion',
9930                 .5,
9931                 "easeOut", after);
9932         });
9933         return this;
9934     },
9935
9936         /**
9937          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9938          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9939          * The element must be removed from the DOM using the 'remove' config option if desired.
9940          * Usage:
9941          *<pre><code>
9942 // default
9943 el.puff();
9944
9945 // common config options shown with default values
9946 el.puff({
9947     easing: 'easeOut',
9948     duration: .5,
9949     remove: false,
9950     useDisplay: false
9951 });
9952 </code></pre>
9953          * @param {Object} options (optional) Object literal with any of the Fx config options
9954          * @return {Roo.Element} The Element
9955          */
9956     puff : function(o){
9957         var el = this.getFxEl();
9958         o = o || {};
9959
9960         el.queueFx(o, function(){
9961             this.clearOpacity();
9962             this.show();
9963
9964             // restore values after effect
9965             var r = this.getFxRestore();
9966             var st = this.dom.style;
9967
9968             var after = function(){
9969                 if(o.useDisplay){
9970                     el.setDisplayed(false);
9971                 }else{
9972                     el.hide();
9973                 }
9974
9975                 el.clearOpacity();
9976
9977                 el.setPositioning(r.pos);
9978                 st.width = r.width;
9979                 st.height = r.height;
9980                 st.fontSize = '';
9981                 el.afterFx(o);
9982             };
9983
9984             var width = this.getWidth();
9985             var height = this.getHeight();
9986
9987             arguments.callee.anim = this.fxanim({
9988                     width : {to: this.adjustWidth(width * 2)},
9989                     height : {to: this.adjustHeight(height * 2)},
9990                     points : {by: [-(width * .5), -(height * .5)]},
9991                     opacity : {to: 0},
9992                     fontSize: {to:200, unit: "%"}
9993                 },
9994                 o,
9995                 'motion',
9996                 .5,
9997                 "easeOut", after);
9998         });
9999         return this;
10000     },
10001
10002         /**
10003          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10004          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10005          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10006          * Usage:
10007          *<pre><code>
10008 // default
10009 el.switchOff();
10010
10011 // all config options shown with default values
10012 el.switchOff({
10013     easing: 'easeIn',
10014     duration: .3,
10015     remove: false,
10016     useDisplay: false
10017 });
10018 </code></pre>
10019          * @param {Object} options (optional) Object literal with any of the Fx config options
10020          * @return {Roo.Element} The Element
10021          */
10022     switchOff : function(o){
10023         var el = this.getFxEl();
10024         o = o || {};
10025
10026         el.queueFx(o, function(){
10027             this.clearOpacity();
10028             this.clip();
10029
10030             // restore values after effect
10031             var r = this.getFxRestore();
10032             var st = this.dom.style;
10033
10034             var after = function(){
10035                 if(o.useDisplay){
10036                     el.setDisplayed(false);
10037                 }else{
10038                     el.hide();
10039                 }
10040
10041                 el.clearOpacity();
10042                 el.setPositioning(r.pos);
10043                 st.width = r.width;
10044                 st.height = r.height;
10045
10046                 el.afterFx(o);
10047             };
10048
10049             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10050                 this.clearOpacity();
10051                 (function(){
10052                     this.fxanim({
10053                         height:{to:1},
10054                         points:{by:[0, this.getHeight() * .5]}
10055                     }, o, 'motion', 0.3, 'easeIn', after);
10056                 }).defer(100, this);
10057             });
10058         });
10059         return this;
10060     },
10061
10062     /**
10063      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10064      * changed using the "attr" config option) and then fading back to the original color. If no original
10065      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10066      * Usage:
10067 <pre><code>
10068 // default: highlight background to yellow
10069 el.highlight();
10070
10071 // custom: highlight foreground text to blue for 2 seconds
10072 el.highlight("0000ff", { attr: 'color', duration: 2 });
10073
10074 // common config options shown with default values
10075 el.highlight("ffff9c", {
10076     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10077     endColor: (current color) or "ffffff",
10078     easing: 'easeIn',
10079     duration: 1
10080 });
10081 </code></pre>
10082      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10083      * @param {Object} options (optional) Object literal with any of the Fx config options
10084      * @return {Roo.Element} The Element
10085      */ 
10086     highlight : function(color, o){
10087         var el = this.getFxEl();
10088         o = o || {};
10089
10090         el.queueFx(o, function(){
10091             color = color || "ffff9c";
10092             attr = o.attr || "backgroundColor";
10093
10094             this.clearOpacity();
10095             this.show();
10096
10097             var origColor = this.getColor(attr);
10098             var restoreColor = this.dom.style[attr];
10099             endColor = (o.endColor || origColor) || "ffffff";
10100
10101             var after = function(){
10102                 el.dom.style[attr] = restoreColor;
10103                 el.afterFx(o);
10104             };
10105
10106             var a = {};
10107             a[attr] = {from: color, to: endColor};
10108             arguments.callee.anim = this.fxanim(a,
10109                 o,
10110                 'color',
10111                 1,
10112                 'easeIn', after);
10113         });
10114         return this;
10115     },
10116
10117    /**
10118     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10119     * Usage:
10120 <pre><code>
10121 // default: a single light blue ripple
10122 el.frame();
10123
10124 // custom: 3 red ripples lasting 3 seconds total
10125 el.frame("ff0000", 3, { duration: 3 });
10126
10127 // common config options shown with default values
10128 el.frame("C3DAF9", 1, {
10129     duration: 1 //duration of entire animation (not each individual ripple)
10130     // Note: Easing is not configurable and will be ignored if included
10131 });
10132 </code></pre>
10133     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10134     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10135     * @param {Object} options (optional) Object literal with any of the Fx config options
10136     * @return {Roo.Element} The Element
10137     */
10138     frame : function(color, count, o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             color = color || "#C3DAF9";
10144             if(color.length == 6){
10145                 color = "#" + color;
10146             }
10147             count = count || 1;
10148             duration = o.duration || 1;
10149             this.show();
10150
10151             var b = this.getBox();
10152             var animFn = function(){
10153                 var proxy = this.createProxy({
10154
10155                      style:{
10156                         visbility:"hidden",
10157                         position:"absolute",
10158                         "z-index":"35000", // yee haw
10159                         border:"0px solid " + color
10160                      }
10161                   });
10162                 var scale = Roo.isBorderBox ? 2 : 1;
10163                 proxy.animate({
10164                     top:{from:b.y, to:b.y - 20},
10165                     left:{from:b.x, to:b.x - 20},
10166                     borderWidth:{from:0, to:10},
10167                     opacity:{from:1, to:0},
10168                     height:{from:b.height, to:(b.height + (20*scale))},
10169                     width:{from:b.width, to:(b.width + (20*scale))}
10170                 }, duration, function(){
10171                     proxy.remove();
10172                 });
10173                 if(--count > 0){
10174                      animFn.defer((duration/2)*1000, this);
10175                 }else{
10176                     el.afterFx(o);
10177                 }
10178             };
10179             animFn.call(this);
10180         });
10181         return this;
10182     },
10183
10184    /**
10185     * Creates a pause before any subsequent queued effects begin.  If there are
10186     * no effects queued after the pause it will have no effect.
10187     * Usage:
10188 <pre><code>
10189 el.pause(1);
10190 </code></pre>
10191     * @param {Number} seconds The length of time to pause (in seconds)
10192     * @return {Roo.Element} The Element
10193     */
10194     pause : function(seconds){
10195         var el = this.getFxEl();
10196         var o = {};
10197
10198         el.queueFx(o, function(){
10199             setTimeout(function(){
10200                 el.afterFx(o);
10201             }, seconds * 1000);
10202         });
10203         return this;
10204     },
10205
10206    /**
10207     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10208     * using the "endOpacity" config option.
10209     * Usage:
10210 <pre><code>
10211 // default: fade in from opacity 0 to 100%
10212 el.fadeIn();
10213
10214 // custom: fade in from opacity 0 to 75% over 2 seconds
10215 el.fadeIn({ endOpacity: .75, duration: 2});
10216
10217 // common config options shown with default values
10218 el.fadeIn({
10219     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10220     easing: 'easeOut',
10221     duration: .5
10222 });
10223 </code></pre>
10224     * @param {Object} options (optional) Object literal with any of the Fx config options
10225     * @return {Roo.Element} The Element
10226     */
10227     fadeIn : function(o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230         el.queueFx(o, function(){
10231             this.setOpacity(0);
10232             this.fixDisplay();
10233             this.dom.style.visibility = 'visible';
10234             var to = o.endOpacity || 1;
10235             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10236                 o, null, .5, "easeOut", function(){
10237                 if(to == 1){
10238                     this.clearOpacity();
10239                 }
10240                 el.afterFx(o);
10241             });
10242         });
10243         return this;
10244     },
10245
10246    /**
10247     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10248     * using the "endOpacity" config option.
10249     * Usage:
10250 <pre><code>
10251 // default: fade out from the element's current opacity to 0
10252 el.fadeOut();
10253
10254 // custom: fade out from the element's current opacity to 25% over 2 seconds
10255 el.fadeOut({ endOpacity: .25, duration: 2});
10256
10257 // common config options shown with default values
10258 el.fadeOut({
10259     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10260     easing: 'easeOut',
10261     duration: .5
10262     remove: false,
10263     useDisplay: false
10264 });
10265 </code></pre>
10266     * @param {Object} options (optional) Object literal with any of the Fx config options
10267     * @return {Roo.Element} The Element
10268     */
10269     fadeOut : function(o){
10270         var el = this.getFxEl();
10271         o = o || {};
10272         el.queueFx(o, function(){
10273             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10274                 o, null, .5, "easeOut", function(){
10275                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10276                      this.dom.style.display = "none";
10277                 }else{
10278                      this.dom.style.visibility = "hidden";
10279                 }
10280                 this.clearOpacity();
10281                 el.afterFx(o);
10282             });
10283         });
10284         return this;
10285     },
10286
10287    /**
10288     * Animates the transition of an element's dimensions from a starting height/width
10289     * to an ending height/width.
10290     * Usage:
10291 <pre><code>
10292 // change height and width to 100x100 pixels
10293 el.scale(100, 100);
10294
10295 // common config options shown with default values.  The height and width will default to
10296 // the element's existing values if passed as null.
10297 el.scale(
10298     [element's width],
10299     [element's height], {
10300     easing: 'easeOut',
10301     duration: .35
10302 });
10303 </code></pre>
10304     * @param {Number} width  The new width (pass undefined to keep the original width)
10305     * @param {Number} height  The new height (pass undefined to keep the original height)
10306     * @param {Object} options (optional) Object literal with any of the Fx config options
10307     * @return {Roo.Element} The Element
10308     */
10309     scale : function(w, h, o){
10310         this.shift(Roo.apply({}, o, {
10311             width: w,
10312             height: h
10313         }));
10314         return this;
10315     },
10316
10317    /**
10318     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10319     * Any of these properties not specified in the config object will not be changed.  This effect 
10320     * requires that at least one new dimension, position or opacity setting must be passed in on
10321     * the config object in order for the function to have any effect.
10322     * Usage:
10323 <pre><code>
10324 // slide the element horizontally to x position 200 while changing the height and opacity
10325 el.shift({ x: 200, height: 50, opacity: .8 });
10326
10327 // common config options shown with default values.
10328 el.shift({
10329     width: [element's width],
10330     height: [element's height],
10331     x: [element's x position],
10332     y: [element's y position],
10333     opacity: [element's opacity],
10334     easing: 'easeOut',
10335     duration: .35
10336 });
10337 </code></pre>
10338     * @param {Object} options  Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     shift : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10346             if(w !== undefined){
10347                 a.width = {to: this.adjustWidth(w)};
10348             }
10349             if(h !== undefined){
10350                 a.height = {to: this.adjustHeight(h)};
10351             }
10352             if(x !== undefined || y !== undefined){
10353                 a.points = {to: [
10354                     x !== undefined ? x : this.getX(),
10355                     y !== undefined ? y : this.getY()
10356                 ]};
10357             }
10358             if(op !== undefined){
10359                 a.opacity = {to: op};
10360             }
10361             if(o.xy !== undefined){
10362                 a.points = {to: o.xy};
10363             }
10364             arguments.callee.anim = this.fxanim(a,
10365                 o, 'motion', .35, "easeOut", function(){
10366                 el.afterFx(o);
10367             });
10368         });
10369         return this;
10370     },
10371
10372         /**
10373          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10374          * ending point of the effect.
10375          * Usage:
10376          *<pre><code>
10377 // default: slide the element downward while fading out
10378 el.ghost();
10379
10380 // custom: slide the element out to the right with a 2-second duration
10381 el.ghost('r', { duration: 2 });
10382
10383 // common config options shown with default values
10384 el.ghost('b', {
10385     easing: 'easeOut',
10386     duration: .5
10387     remove: false,
10388     useDisplay: false
10389 });
10390 </code></pre>
10391          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10392          * @param {Object} options (optional) Object literal with any of the Fx config options
10393          * @return {Roo.Element} The Element
10394          */
10395     ghost : function(anchor, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             anchor = anchor || "b";
10401
10402             // restore values after effect
10403             var r = this.getFxRestore();
10404             var w = this.getWidth(),
10405                 h = this.getHeight();
10406
10407             var st = this.dom.style;
10408
10409             var after = function(){
10410                 if(o.useDisplay){
10411                     el.setDisplayed(false);
10412                 }else{
10413                     el.hide();
10414                 }
10415
10416                 el.clearOpacity();
10417                 el.setPositioning(r.pos);
10418                 st.width = r.width;
10419                 st.height = r.height;
10420
10421                 el.afterFx(o);
10422             };
10423
10424             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10425             switch(anchor.toLowerCase()){
10426                 case "t":
10427                     pt.by = [0, -h];
10428                 break;
10429                 case "l":
10430                     pt.by = [-w, 0];
10431                 break;
10432                 case "r":
10433                     pt.by = [w, 0];
10434                 break;
10435                 case "b":
10436                     pt.by = [0, h];
10437                 break;
10438                 case "tl":
10439                     pt.by = [-w, -h];
10440                 break;
10441                 case "bl":
10442                     pt.by = [-w, h];
10443                 break;
10444                 case "br":
10445                     pt.by = [w, h];
10446                 break;
10447                 case "tr":
10448                     pt.by = [w, -h];
10449                 break;
10450             }
10451
10452             arguments.callee.anim = this.fxanim(a,
10453                 o,
10454                 'motion',
10455                 .5,
10456                 "easeOut", after);
10457         });
10458         return this;
10459     },
10460
10461         /**
10462          * Ensures that all effects queued after syncFx is called on the element are
10463          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10464          * @return {Roo.Element} The Element
10465          */
10466     syncFx : function(){
10467         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10468             block : false,
10469             concurrent : true,
10470             stopFx : false
10471         });
10472         return this;
10473     },
10474
10475         /**
10476          * Ensures that all effects queued after sequenceFx is called on the element are
10477          * run in sequence.  This is the opposite of {@link #syncFx}.
10478          * @return {Roo.Element} The Element
10479          */
10480     sequenceFx : function(){
10481         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10482             block : false,
10483             concurrent : false,
10484             stopFx : false
10485         });
10486         return this;
10487     },
10488
10489         /* @private */
10490     nextFx : function(){
10491         var ef = this.fxQueue[0];
10492         if(ef){
10493             ef.call(this);
10494         }
10495     },
10496
10497         /**
10498          * Returns true if the element has any effects actively running or queued, else returns false.
10499          * @return {Boolean} True if element has active effects, else false
10500          */
10501     hasActiveFx : function(){
10502         return this.fxQueue && this.fxQueue[0];
10503     },
10504
10505         /**
10506          * Stops any running effects and clears the element's internal effects queue if it contains
10507          * any additional effects that haven't started yet.
10508          * @return {Roo.Element} The Element
10509          */
10510     stopFx : function(){
10511         if(this.hasActiveFx()){
10512             var cur = this.fxQueue[0];
10513             if(cur && cur.anim && cur.anim.isAnimated()){
10514                 this.fxQueue = [cur]; // clear out others
10515                 cur.anim.stop(true);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     beforeFx : function(o){
10523         if(this.hasActiveFx() && !o.concurrent){
10524            if(o.stopFx){
10525                this.stopFx();
10526                return true;
10527            }
10528            return false;
10529         }
10530         return true;
10531     },
10532
10533         /**
10534          * Returns true if the element is currently blocking so that no other effect can be queued
10535          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10536          * used to ensure that an effect initiated by a user action runs to completion prior to the
10537          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10538          * @return {Boolean} True if blocking, else false
10539          */
10540     hasFxBlock : function(){
10541         var q = this.fxQueue;
10542         return q && q[0] && q[0].block;
10543     },
10544
10545         /* @private */
10546     queueFx : function(o, fn){
10547         if(!this.fxQueue){
10548             this.fxQueue = [];
10549         }
10550         if(!this.hasFxBlock()){
10551             Roo.applyIf(o, this.fxDefaults);
10552             if(!o.concurrent){
10553                 var run = this.beforeFx(o);
10554                 fn.block = o.block;
10555                 this.fxQueue.push(fn);
10556                 if(run){
10557                     this.nextFx();
10558                 }
10559             }else{
10560                 fn.call(this);
10561             }
10562         }
10563         return this;
10564     },
10565
10566         /* @private */
10567     fxWrap : function(pos, o, vis){
10568         var wrap;
10569         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10570             var wrapXY;
10571             if(o.fixPosition){
10572                 wrapXY = this.getXY();
10573             }
10574             var div = document.createElement("div");
10575             div.style.visibility = vis;
10576             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10577             wrap.setPositioning(pos);
10578             if(wrap.getStyle("position") == "static"){
10579                 wrap.position("relative");
10580             }
10581             this.clearPositioning('auto');
10582             wrap.clip();
10583             wrap.dom.appendChild(this.dom);
10584             if(wrapXY){
10585                 wrap.setXY(wrapXY);
10586             }
10587         }
10588         return wrap;
10589     },
10590
10591         /* @private */
10592     fxUnwrap : function(wrap, pos, o){
10593         this.clearPositioning();
10594         this.setPositioning(pos);
10595         if(!o.wrap){
10596             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10597             wrap.remove();
10598         }
10599     },
10600
10601         /* @private */
10602     getFxRestore : function(){
10603         var st = this.dom.style;
10604         return {pos: this.getPositioning(), width: st.width, height : st.height};
10605     },
10606
10607         /* @private */
10608     afterFx : function(o){
10609         if(o.afterStyle){
10610             this.applyStyles(o.afterStyle);
10611         }
10612         if(o.afterCls){
10613             this.addClass(o.afterCls);
10614         }
10615         if(o.remove === true){
10616             this.remove();
10617         }
10618         Roo.callback(o.callback, o.scope, [this]);
10619         if(!o.concurrent){
10620             this.fxQueue.shift();
10621             this.nextFx();
10622         }
10623     },
10624
10625         /* @private */
10626     getFxEl : function(){ // support for composite element fx
10627         return Roo.get(this.dom);
10628     },
10629
10630         /* @private */
10631     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10632         animType = animType || 'run';
10633         opt = opt || {};
10634         var anim = Roo.lib.Anim[animType](
10635             this.dom, args,
10636             (opt.duration || defaultDur) || .35,
10637             (opt.easing || defaultEase) || 'easeOut',
10638             function(){
10639                 Roo.callback(cb, this);
10640             },
10641             this
10642         );
10643         opt.anim = anim;
10644         return anim;
10645     }
10646 };
10647
10648 // backwords compat
10649 Roo.Fx.resize = Roo.Fx.scale;
10650
10651 //When included, Roo.Fx is automatically applied to Element so that all basic
10652 //effects are available directly via the Element API
10653 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10654  * Based on:
10655  * Ext JS Library 1.1.1
10656  * Copyright(c) 2006-2007, Ext JS, LLC.
10657  *
10658  * Originally Released Under LGPL - original licence link has changed is not relivant.
10659  *
10660  * Fork - LGPL
10661  * <script type="text/javascript">
10662  */
10663
10664
10665 /**
10666  * @class Roo.CompositeElement
10667  * Standard composite class. Creates a Roo.Element for every element in the collection.
10668  * <br><br>
10669  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10670  * actions will be performed on all the elements in this collection.</b>
10671  * <br><br>
10672  * All methods return <i>this</i> and can be chained.
10673  <pre><code>
10674  var els = Roo.select("#some-el div.some-class", true);
10675  // or select directly from an existing element
10676  var el = Roo.get('some-el');
10677  el.select('div.some-class', true);
10678
10679  els.setWidth(100); // all elements become 100 width
10680  els.hide(true); // all elements fade out and hide
10681  // or
10682  els.setWidth(100).hide(true);
10683  </code></pre>
10684  */
10685 Roo.CompositeElement = function(els){
10686     this.elements = [];
10687     this.addElements(els);
10688 };
10689 Roo.CompositeElement.prototype = {
10690     isComposite: true,
10691     addElements : function(els){
10692         if(!els) return this;
10693         if(typeof els == "string"){
10694             els = Roo.Element.selectorFunction(els);
10695         }
10696         var yels = this.elements;
10697         var index = yels.length-1;
10698         for(var i = 0, len = els.length; i < len; i++) {
10699                 yels[++index] = Roo.get(els[i]);
10700         }
10701         return this;
10702     },
10703
10704     /**
10705     * Clears this composite and adds the elements returned by the passed selector.
10706     * @param {String/Array} els A string CSS selector, an array of elements or an element
10707     * @return {CompositeElement} this
10708     */
10709     fill : function(els){
10710         this.elements = [];
10711         this.add(els);
10712         return this;
10713     },
10714
10715     /**
10716     * Filters this composite to only elements that match the passed selector.
10717     * @param {String} selector A string CSS selector
10718     * @return {CompositeElement} this
10719     */
10720     filter : function(selector){
10721         var els = [];
10722         this.each(function(el){
10723             if(el.is(selector)){
10724                 els[els.length] = el.dom;
10725             }
10726         });
10727         this.fill(els);
10728         return this;
10729     },
10730
10731     invoke : function(fn, args){
10732         var els = this.elements;
10733         for(var i = 0, len = els.length; i < len; i++) {
10734                 Roo.Element.prototype[fn].apply(els[i], args);
10735         }
10736         return this;
10737     },
10738     /**
10739     * Adds elements to this composite.
10740     * @param {String/Array} els A string CSS selector, an array of elements or an element
10741     * @return {CompositeElement} this
10742     */
10743     add : function(els){
10744         if(typeof els == "string"){
10745             this.addElements(Roo.Element.selectorFunction(els));
10746         }else if(els.length !== undefined){
10747             this.addElements(els);
10748         }else{
10749             this.addElements([els]);
10750         }
10751         return this;
10752     },
10753     /**
10754     * Calls the passed function passing (el, this, index) for each element in this composite.
10755     * @param {Function} fn The function to call
10756     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10757     * @return {CompositeElement} this
10758     */
10759     each : function(fn, scope){
10760         var els = this.elements;
10761         for(var i = 0, len = els.length; i < len; i++){
10762             if(fn.call(scope || els[i], els[i], this, i) === false) {
10763                 break;
10764             }
10765         }
10766         return this;
10767     },
10768
10769     /**
10770      * Returns the Element object at the specified index
10771      * @param {Number} index
10772      * @return {Roo.Element}
10773      */
10774     item : function(index){
10775         return this.elements[index] || null;
10776     },
10777
10778     /**
10779      * Returns the first Element
10780      * @return {Roo.Element}
10781      */
10782     first : function(){
10783         return this.item(0);
10784     },
10785
10786     /**
10787      * Returns the last Element
10788      * @return {Roo.Element}
10789      */
10790     last : function(){
10791         return this.item(this.elements.length-1);
10792     },
10793
10794     /**
10795      * Returns the number of elements in this composite
10796      * @return Number
10797      */
10798     getCount : function(){
10799         return this.elements.length;
10800     },
10801
10802     /**
10803      * Returns true if this composite contains the passed element
10804      * @return Boolean
10805      */
10806     contains : function(el){
10807         return this.indexOf(el) !== -1;
10808     },
10809
10810     /**
10811      * Returns true if this composite contains the passed element
10812      * @return Boolean
10813      */
10814     indexOf : function(el){
10815         return this.elements.indexOf(Roo.get(el));
10816     },
10817
10818
10819     /**
10820     * Removes the specified element(s).
10821     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10822     * or an array of any of those.
10823     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10824     * @return {CompositeElement} this
10825     */
10826     removeElement : function(el, removeDom){
10827         if(el instanceof Array){
10828             for(var i = 0, len = el.length; i < len; i++){
10829                 this.removeElement(el[i]);
10830             }
10831             return this;
10832         }
10833         var index = typeof el == 'number' ? el : this.indexOf(el);
10834         if(index !== -1){
10835             if(removeDom){
10836                 var d = this.elements[index];
10837                 if(d.dom){
10838                     d.remove();
10839                 }else{
10840                     d.parentNode.removeChild(d);
10841                 }
10842             }
10843             this.elements.splice(index, 1);
10844         }
10845         return this;
10846     },
10847
10848     /**
10849     * Replaces the specified element with the passed element.
10850     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10851     * to replace.
10852     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10853     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10854     * @return {CompositeElement} this
10855     */
10856     replaceElement : function(el, replacement, domReplace){
10857         var index = typeof el == 'number' ? el : this.indexOf(el);
10858         if(index !== -1){
10859             if(domReplace){
10860                 this.elements[index].replaceWith(replacement);
10861             }else{
10862                 this.elements.splice(index, 1, Roo.get(replacement))
10863             }
10864         }
10865         return this;
10866     },
10867
10868     /**
10869      * Removes all elements.
10870      */
10871     clear : function(){
10872         this.elements = [];
10873     }
10874 };
10875 (function(){
10876     Roo.CompositeElement.createCall = function(proto, fnName){
10877         if(!proto[fnName]){
10878             proto[fnName] = function(){
10879                 return this.invoke(fnName, arguments);
10880             };
10881         }
10882     };
10883     for(var fnName in Roo.Element.prototype){
10884         if(typeof Roo.Element.prototype[fnName] == "function"){
10885             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10886         }
10887     };
10888 })();
10889 /*
10890  * Based on:
10891  * Ext JS Library 1.1.1
10892  * Copyright(c) 2006-2007, Ext JS, LLC.
10893  *
10894  * Originally Released Under LGPL - original licence link has changed is not relivant.
10895  *
10896  * Fork - LGPL
10897  * <script type="text/javascript">
10898  */
10899
10900 /**
10901  * @class Roo.CompositeElementLite
10902  * @extends Roo.CompositeElement
10903  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class");
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class');
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre><br><br>
10915  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10916  * actions will be performed on all the elements in this collection.</b>
10917  */
10918 Roo.CompositeElementLite = function(els){
10919     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10920     this.el = new Roo.Element.Flyweight();
10921 };
10922 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10923     addElements : function(els){
10924         if(els){
10925             if(els instanceof Array){
10926                 this.elements = this.elements.concat(els);
10927             }else{
10928                 var yels = this.elements;
10929                 var index = yels.length-1;
10930                 for(var i = 0, len = els.length; i < len; i++) {
10931                     yels[++index] = els[i];
10932                 }
10933             }
10934         }
10935         return this;
10936     },
10937     invoke : function(fn, args){
10938         var els = this.elements;
10939         var el = this.el;
10940         for(var i = 0, len = els.length; i < len; i++) {
10941             el.dom = els[i];
10942                 Roo.Element.prototype[fn].apply(el, args);
10943         }
10944         return this;
10945     },
10946     /**
10947      * Returns a flyweight Element of the dom element object at the specified index
10948      * @param {Number} index
10949      * @return {Roo.Element}
10950      */
10951     item : function(index){
10952         if(!this.elements[index]){
10953             return null;
10954         }
10955         this.el.dom = this.elements[index];
10956         return this.el;
10957     },
10958
10959     // fixes scope with flyweight
10960     addListener : function(eventName, handler, scope, opt){
10961         var els = this.elements;
10962         for(var i = 0, len = els.length; i < len; i++) {
10963             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10970     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10971     * a reference to the dom node, use el.dom.</b>
10972     * @param {Function} fn The function to call
10973     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10974     * @return {CompositeElement} this
10975     */
10976     each : function(fn, scope){
10977         var els = this.elements;
10978         var el = this.el;
10979         for(var i = 0, len = els.length; i < len; i++){
10980             el.dom = els[i];
10981                 if(fn.call(scope || el, el, this, i) === false){
10982                 break;
10983             }
10984         }
10985         return this;
10986     },
10987
10988     indexOf : function(el){
10989         return this.elements.indexOf(Roo.getDom(el));
10990     },
10991
10992     replaceElement : function(el, replacement, domReplace){
10993         var index = typeof el == 'number' ? el : this.indexOf(el);
10994         if(index !== -1){
10995             replacement = Roo.getDom(replacement);
10996             if(domReplace){
10997                 var d = this.elements[index];
10998                 d.parentNode.insertBefore(replacement, d);
10999                 d.parentNode.removeChild(d);
11000             }
11001             this.elements.splice(index, 1, replacement);
11002         }
11003         return this;
11004     }
11005 });
11006 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11007
11008 /*
11009  * Based on:
11010  * Ext JS Library 1.1.1
11011  * Copyright(c) 2006-2007, Ext JS, LLC.
11012  *
11013  * Originally Released Under LGPL - original licence link has changed is not relivant.
11014  *
11015  * Fork - LGPL
11016  * <script type="text/javascript">
11017  */
11018
11019  
11020
11021 /**
11022  * @class Roo.data.Connection
11023  * @extends Roo.util.Observable
11024  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11025  * either to a configured URL, or to a URL specified at request time.<br><br>
11026  * <p>
11027  * Requests made by this class are asynchronous, and will return immediately. No data from
11028  * the server will be available to the statement immediately following the {@link #request} call.
11029  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11030  * <p>
11031  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11032  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11033  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11034  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11035  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11036  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11037  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11038  * standard DOM methods.
11039  * @constructor
11040  * @param {Object} config a configuration object.
11041  */
11042 Roo.data.Connection = function(config){
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046          * @event beforerequest
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Connection} conn This Connection object.
11049          * @param {Object} options The options config object passed to the {@link #request} method.
11050          */
11051         "beforerequest" : true,
11052         /**
11053          * @event requestcomplete
11054          * Fires if the request was successfully completed.
11055          * @param {Connection} conn This Connection object.
11056          * @param {Object} response The XHR object containing the response data.
11057          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11058          * @param {Object} options The options config object passed to the {@link #request} method.
11059          */
11060         "requestcomplete" : true,
11061         /**
11062          * @event requestexception
11063          * Fires if an error HTTP status was returned from the server.
11064          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11065          * @param {Connection} conn This Connection object.
11066          * @param {Object} response The XHR object containing the response data.
11067          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11068          * @param {Object} options The options config object passed to the {@link #request} method.
11069          */
11070         "requestexception" : true
11071     });
11072     Roo.data.Connection.superclass.constructor.call(this);
11073 };
11074
11075 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11076     /**
11077      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11078      */
11079     /**
11080      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11081      * extra parameters to each request made by this object. (defaults to undefined)
11082      */
11083     /**
11084      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11085      *  to each request made by this object. (defaults to undefined)
11086      */
11087     /**
11088      * @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)
11089      */
11090     /**
11091      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11092      */
11093     timeout : 30000,
11094     /**
11095      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11096      * @type Boolean
11097      */
11098     autoAbort:false,
11099
11100     /**
11101      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11102      * @type Boolean
11103      */
11104     disableCaching: true,
11105
11106     /**
11107      * Sends an HTTP request to a remote server.
11108      * @param {Object} options An object which may contain the following properties:<ul>
11109      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11110      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11111      * request, a url encoded string or a function to call to get either.</li>
11112      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11113      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11114      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11115      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11116      * <li>options {Object} The parameter to the request call.</li>
11117      * <li>success {Boolean} True if the request succeeded.</li>
11118      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11119      * </ul></li>
11120      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11121      * The callback is passed the following parameters:<ul>
11122      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11123      * <li>options {Object} The parameter to the request call.</li>
11124      * </ul></li>
11125      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11126      * The callback is passed the following parameters:<ul>
11127      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11128      * <li>options {Object} The parameter to the request call.</li>
11129      * </ul></li>
11130      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11131      * for the callback function. Defaults to the browser window.</li>
11132      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11133      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11134      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11135      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11136      * params for the post data. Any params will be appended to the URL.</li>
11137      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11138      * </ul>
11139      * @return {Number} transactionId
11140      */
11141     request : function(o){
11142         if(this.fireEvent("beforerequest", this, o) !== false){
11143             var p = o.params;
11144
11145             if(typeof p == "function"){
11146                 p = p.call(o.scope||window, o);
11147             }
11148             if(typeof p == "object"){
11149                 p = Roo.urlEncode(o.params);
11150             }
11151             if(this.extraParams){
11152                 var extras = Roo.urlEncode(this.extraParams);
11153                 p = p ? (p + '&' + extras) : extras;
11154             }
11155
11156             var url = o.url || this.url;
11157             if(typeof url == 'function'){
11158                 url = url.call(o.scope||window, o);
11159             }
11160
11161             if(o.form){
11162                 var form = Roo.getDom(o.form);
11163                 url = url || form.action;
11164
11165                 var enctype = form.getAttribute("enctype");
11166                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11167                     return this.doFormUpload(o, p, url);
11168                 }
11169                 var f = Roo.lib.Ajax.serializeForm(form);
11170                 p = p ? (p + '&' + f) : f;
11171             }
11172
11173             var hs = o.headers;
11174             if(this.defaultHeaders){
11175                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11176                 if(!o.headers){
11177                     o.headers = hs;
11178                 }
11179             }
11180
11181             var cb = {
11182                 success: this.handleResponse,
11183                 failure: this.handleFailure,
11184                 scope: this,
11185                 argument: {options: o},
11186                 timeout : this.timeout
11187             };
11188
11189             var method = o.method||this.method||(p ? "POST" : "GET");
11190
11191             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11192                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11193             }
11194
11195             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11196                 if(o.autoAbort){
11197                     this.abort();
11198                 }
11199             }else if(this.autoAbort !== false){
11200                 this.abort();
11201             }
11202
11203             if((method == 'GET' && p) || o.xmlData){
11204                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11205                 p = '';
11206             }
11207             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11208             return this.transId;
11209         }else{
11210             Roo.callback(o.callback, o.scope, [o, null, null]);
11211             return null;
11212         }
11213     },
11214
11215     /**
11216      * Determine whether this object has a request outstanding.
11217      * @param {Number} transactionId (Optional) defaults to the last transaction
11218      * @return {Boolean} True if there is an outstanding request.
11219      */
11220     isLoading : function(transId){
11221         if(transId){
11222             return Roo.lib.Ajax.isCallInProgress(transId);
11223         }else{
11224             return this.transId ? true : false;
11225         }
11226     },
11227
11228     /**
11229      * Aborts any outstanding request.
11230      * @param {Number} transactionId (Optional) defaults to the last transaction
11231      */
11232     abort : function(transId){
11233         if(transId || this.isLoading()){
11234             Roo.lib.Ajax.abort(transId || this.transId);
11235         }
11236     },
11237
11238     // private
11239     handleResponse : function(response){
11240         this.transId = false;
11241         var options = response.argument.options;
11242         response.argument = options ? options.argument : null;
11243         this.fireEvent("requestcomplete", this, response, options);
11244         Roo.callback(options.success, options.scope, [response, options]);
11245         Roo.callback(options.callback, options.scope, [options, true, response]);
11246     },
11247
11248     // private
11249     handleFailure : function(response, e){
11250         this.transId = false;
11251         var options = response.argument.options;
11252         response.argument = options ? options.argument : null;
11253         this.fireEvent("requestexception", this, response, options, e);
11254         Roo.callback(options.failure, options.scope, [response, options]);
11255         Roo.callback(options.callback, options.scope, [options, false, response]);
11256     },
11257
11258     // private
11259     doFormUpload : function(o, ps, url){
11260         var id = Roo.id();
11261         var frame = document.createElement('iframe');
11262         frame.id = id;
11263         frame.name = id;
11264         frame.className = 'x-hidden';
11265         if(Roo.isIE){
11266             frame.src = Roo.SSL_SECURE_URL;
11267         }
11268         document.body.appendChild(frame);
11269
11270         if(Roo.isIE){
11271            document.frames[id].name = id;
11272         }
11273
11274         var form = Roo.getDom(o.form);
11275         form.target = id;
11276         form.method = 'POST';
11277         form.enctype = form.encoding = 'multipart/form-data';
11278         if(url){
11279             form.action = url;
11280         }
11281
11282         var hiddens, hd;
11283         if(ps){ // add dynamic params
11284             hiddens = [];
11285             ps = Roo.urlDecode(ps, false);
11286             for(var k in ps){
11287                 if(ps.hasOwnProperty(k)){
11288                     hd = document.createElement('input');
11289                     hd.type = 'hidden';
11290                     hd.name = k;
11291                     hd.value = ps[k];
11292                     form.appendChild(hd);
11293                     hiddens.push(hd);
11294                 }
11295             }
11296         }
11297
11298         function cb(){
11299             var r = {  // bogus response object
11300                 responseText : '',
11301                 responseXML : null
11302             };
11303
11304             r.argument = o ? o.argument : null;
11305
11306             try { //
11307                 var doc;
11308                 if(Roo.isIE){
11309                     doc = frame.contentWindow.document;
11310                 }else {
11311                     doc = (frame.contentDocument || window.frames[id].document);
11312                 }
11313                 if(doc && doc.body){
11314                     r.responseText = doc.body.innerHTML;
11315                 }
11316                 if(doc && doc.XMLDocument){
11317                     r.responseXML = doc.XMLDocument;
11318                 }else {
11319                     r.responseXML = doc;
11320                 }
11321             }
11322             catch(e) {
11323                 // ignore
11324             }
11325
11326             Roo.EventManager.removeListener(frame, 'load', cb, this);
11327
11328             this.fireEvent("requestcomplete", this, r, o);
11329             Roo.callback(o.success, o.scope, [r, o]);
11330             Roo.callback(o.callback, o.scope, [o, true, r]);
11331
11332             setTimeout(function(){document.body.removeChild(frame);}, 100);
11333         }
11334
11335         Roo.EventManager.on(frame, 'load', cb, this);
11336         form.submit();
11337
11338         if(hiddens){ // remove dynamic params
11339             for(var i = 0, len = hiddens.length; i < len; i++){
11340                 form.removeChild(hiddens[i]);
11341             }
11342         }
11343     }
11344 });
11345
11346 /**
11347  * @class Roo.Ajax
11348  * @extends Roo.data.Connection
11349  * Global Ajax request class.
11350  *
11351  * @singleton
11352  */
11353 Roo.Ajax = new Roo.data.Connection({
11354     // fix up the docs
11355    /**
11356      * @cfg {String} url @hide
11357      */
11358     /**
11359      * @cfg {Object} extraParams @hide
11360      */
11361     /**
11362      * @cfg {Object} defaultHeaders @hide
11363      */
11364     /**
11365      * @cfg {String} method (Optional) @hide
11366      */
11367     /**
11368      * @cfg {Number} timeout (Optional) @hide
11369      */
11370     /**
11371      * @cfg {Boolean} autoAbort (Optional) @hide
11372      */
11373
11374     /**
11375      * @cfg {Boolean} disableCaching (Optional) @hide
11376      */
11377
11378     /**
11379      * @property  disableCaching
11380      * True to add a unique cache-buster param to GET requests. (defaults to true)
11381      * @type Boolean
11382      */
11383     /**
11384      * @property  url
11385      * The default URL to be used for requests to the server. (defaults to undefined)
11386      * @type String
11387      */
11388     /**
11389      * @property  extraParams
11390      * An object containing properties which are used as
11391      * extra parameters to each request made by this object. (defaults to undefined)
11392      * @type Object
11393      */
11394     /**
11395      * @property  defaultHeaders
11396      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11397      * @type Object
11398      */
11399     /**
11400      * @property  method
11401      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11402      * @type String
11403      */
11404     /**
11405      * @property  timeout
11406      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11407      * @type Number
11408      */
11409
11410     /**
11411      * @property  autoAbort
11412      * Whether a new request should abort any pending requests. (defaults to false)
11413      * @type Boolean
11414      */
11415     autoAbort : false,
11416
11417     /**
11418      * Serialize the passed form into a url encoded string
11419      * @param {String/HTMLElement} form
11420      * @return {String}
11421      */
11422     serializeForm : function(form){
11423         return Roo.lib.Ajax.serializeForm(form);
11424     }
11425 });/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435  
11436 /**
11437  * @class Roo.Ajax
11438  * @extends Roo.data.Connection
11439  * Global Ajax request class.
11440  *
11441  * @instanceOf  Roo.data.Connection
11442  */
11443 Roo.Ajax = new Roo.data.Connection({
11444     // fix up the docs
11445     
11446     /**
11447      * fix up scoping
11448      * @scope Roo.Ajax
11449      */
11450     
11451    /**
11452      * @cfg {String} url @hide
11453      */
11454     /**
11455      * @cfg {Object} extraParams @hide
11456      */
11457     /**
11458      * @cfg {Object} defaultHeaders @hide
11459      */
11460     /**
11461      * @cfg {String} method (Optional) @hide
11462      */
11463     /**
11464      * @cfg {Number} timeout (Optional) @hide
11465      */
11466     /**
11467      * @cfg {Boolean} autoAbort (Optional) @hide
11468      */
11469
11470     /**
11471      * @cfg {Boolean} disableCaching (Optional) @hide
11472      */
11473
11474     /**
11475      * @property  disableCaching
11476      * True to add a unique cache-buster param to GET requests. (defaults to true)
11477      * @type Boolean
11478      */
11479     /**
11480      * @property  url
11481      * The default URL to be used for requests to the server. (defaults to undefined)
11482      * @type String
11483      */
11484     /**
11485      * @property  extraParams
11486      * An object containing properties which are used as
11487      * extra parameters to each request made by this object. (defaults to undefined)
11488      * @type Object
11489      */
11490     /**
11491      * @property  defaultHeaders
11492      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11493      * @type Object
11494      */
11495     /**
11496      * @property  method
11497      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11498      * @type String
11499      */
11500     /**
11501      * @property  timeout
11502      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11503      * @type Number
11504      */
11505
11506     /**
11507      * @property  autoAbort
11508      * Whether a new request should abort any pending requests. (defaults to false)
11509      * @type Boolean
11510      */
11511     autoAbort : false,
11512
11513     /**
11514      * Serialize the passed form into a url encoded string
11515      * @param {String/HTMLElement} form
11516      * @return {String}
11517      */
11518     serializeForm : function(form){
11519         return Roo.lib.Ajax.serializeForm(form);
11520     }
11521 });/*
11522  * Based on:
11523  * Ext JS Library 1.1.1
11524  * Copyright(c) 2006-2007, Ext JS, LLC.
11525  *
11526  * Originally Released Under LGPL - original licence link has changed is not relivant.
11527  *
11528  * Fork - LGPL
11529  * <script type="text/javascript">
11530  */
11531
11532  
11533 /**
11534  * @class Roo.UpdateManager
11535  * @extends Roo.util.Observable
11536  * Provides AJAX-style update for Element object.<br><br>
11537  * Usage:<br>
11538  * <pre><code>
11539  * // Get it from a Roo.Element object
11540  * var el = Roo.get("foo");
11541  * var mgr = el.getUpdateManager();
11542  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11543  * ...
11544  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11545  * <br>
11546  * // or directly (returns the same UpdateManager instance)
11547  * var mgr = new Roo.UpdateManager("myElementId");
11548  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11549  * mgr.on("update", myFcnNeedsToKnow);
11550  * <br>
11551    // short handed call directly from the element object
11552    Roo.get("foo").load({
11553         url: "bar.php",
11554         scripts:true,
11555         params: "for=bar",
11556         text: "Loading Foo..."
11557    });
11558  * </code></pre>
11559  * @constructor
11560  * Create new UpdateManager directly.
11561  * @param {String/HTMLElement/Roo.Element} el The element to update
11562  * @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).
11563  */
11564 Roo.UpdateManager = function(el, forceNew){
11565     el = Roo.get(el);
11566     if(!forceNew && el.updateManager){
11567         return el.updateManager;
11568     }
11569     /**
11570      * The Element object
11571      * @type Roo.Element
11572      */
11573     this.el = el;
11574     /**
11575      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11576      * @type String
11577      */
11578     this.defaultUrl = null;
11579
11580     this.addEvents({
11581         /**
11582          * @event beforeupdate
11583          * Fired before an update is made, return false from your handler and the update is cancelled.
11584          * @param {Roo.Element} el
11585          * @param {String/Object/Function} url
11586          * @param {String/Object} params
11587          */
11588         "beforeupdate": true,
11589         /**
11590          * @event update
11591          * Fired after successful update is made.
11592          * @param {Roo.Element} el
11593          * @param {Object} oResponseObject The response Object
11594          */
11595         "update": true,
11596         /**
11597          * @event failure
11598          * Fired on update failure.
11599          * @param {Roo.Element} el
11600          * @param {Object} oResponseObject The response Object
11601          */
11602         "failure": true
11603     });
11604     var d = Roo.UpdateManager.defaults;
11605     /**
11606      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11607      * @type String
11608      */
11609     this.sslBlankUrl = d.sslBlankUrl;
11610     /**
11611      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11612      * @type Boolean
11613      */
11614     this.disableCaching = d.disableCaching;
11615     /**
11616      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11617      * @type String
11618      */
11619     this.indicatorText = d.indicatorText;
11620     /**
11621      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11622      * @type String
11623      */
11624     this.showLoadIndicator = d.showLoadIndicator;
11625     /**
11626      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11627      * @type Number
11628      */
11629     this.timeout = d.timeout;
11630
11631     /**
11632      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11633      * @type Boolean
11634      */
11635     this.loadScripts = d.loadScripts;
11636
11637     /**
11638      * Transaction object of current executing transaction
11639      */
11640     this.transaction = null;
11641
11642     /**
11643      * @private
11644      */
11645     this.autoRefreshProcId = null;
11646     /**
11647      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11648      * @type Function
11649      */
11650     this.refreshDelegate = this.refresh.createDelegate(this);
11651     /**
11652      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11653      * @type Function
11654      */
11655     this.updateDelegate = this.update.createDelegate(this);
11656     /**
11657      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11658      * @type Function
11659      */
11660     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11661     /**
11662      * @private
11663      */
11664     this.successDelegate = this.processSuccess.createDelegate(this);
11665     /**
11666      * @private
11667      */
11668     this.failureDelegate = this.processFailure.createDelegate(this);
11669
11670     if(!this.renderer){
11671      /**
11672       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11673       */
11674     this.renderer = new Roo.UpdateManager.BasicRenderer();
11675     }
11676     
11677     Roo.UpdateManager.superclass.constructor.call(this);
11678 };
11679
11680 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11681     /**
11682      * Get the Element this UpdateManager is bound to
11683      * @return {Roo.Element} The element
11684      */
11685     getEl : function(){
11686         return this.el;
11687     },
11688     /**
11689      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11690      * @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:
11691 <pre><code>
11692 um.update({<br/>
11693     url: "your-url.php",<br/>
11694     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11695     callback: yourFunction,<br/>
11696     scope: yourObject, //(optional scope)  <br/>
11697     discardUrl: false, <br/>
11698     nocache: false,<br/>
11699     text: "Loading...",<br/>
11700     timeout: 30,<br/>
11701     scripts: false<br/>
11702 });
11703 </code></pre>
11704      * The only required property is url. The optional properties nocache, text and scripts
11705      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11706      * @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}
11707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11708      * @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.
11709      */
11710     update : function(url, params, callback, discardUrl){
11711         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11712             var method = this.method, cfg;
11713             if(typeof url == "object"){ // must be config object
11714                 cfg = url;
11715                 url = cfg.url;
11716                 params = params || cfg.params;
11717                 callback = callback || cfg.callback;
11718                 discardUrl = discardUrl || cfg.discardUrl;
11719                 if(callback && cfg.scope){
11720                     callback = callback.createDelegate(cfg.scope);
11721                 }
11722                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11723                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11724                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11725                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11726                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11727             }
11728             this.showLoading();
11729             if(!discardUrl){
11730                 this.defaultUrl = url;
11731             }
11732             if(typeof url == "function"){
11733                 url = url.call(this);
11734             }
11735
11736             method = method || (params ? "POST" : "GET");
11737             if(method == "GET"){
11738                 url = this.prepareUrl(url);
11739             }
11740
11741             var o = Roo.apply(cfg ||{}, {
11742                 url : url,
11743                 params: params,
11744                 success: this.successDelegate,
11745                 failure: this.failureDelegate,
11746                 callback: undefined,
11747                 timeout: (this.timeout*1000),
11748                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11749             });
11750
11751             this.transaction = Roo.Ajax.request(o);
11752         }
11753     },
11754
11755     /**
11756      * 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.
11757      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11758      * @param {String/HTMLElement} form The form Id or form element
11759      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11760      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11761      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11762      */
11763     formUpdate : function(form, url, reset, callback){
11764         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11765             if(typeof url == "function"){
11766                 url = url.call(this);
11767             }
11768             form = Roo.getDom(form);
11769             this.transaction = Roo.Ajax.request({
11770                 form: form,
11771                 url:url,
11772                 success: this.successDelegate,
11773                 failure: this.failureDelegate,
11774                 timeout: (this.timeout*1000),
11775                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11776             });
11777             this.showLoading.defer(1, this);
11778         }
11779     },
11780
11781     /**
11782      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11784      */
11785     refresh : function(callback){
11786         if(this.defaultUrl == null){
11787             return;
11788         }
11789         this.update(this.defaultUrl, null, callback, true);
11790     },
11791
11792     /**
11793      * Set this element to auto refresh.
11794      * @param {Number} interval How often to update (in seconds).
11795      * @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)
11796      * @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}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11798      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11799      */
11800     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11801         if(refreshNow){
11802             this.update(url || this.defaultUrl, params, callback, true);
11803         }
11804         if(this.autoRefreshProcId){
11805             clearInterval(this.autoRefreshProcId);
11806         }
11807         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11808     },
11809
11810     /**
11811      * Stop auto refresh on this element.
11812      */
11813      stopAutoRefresh : function(){
11814         if(this.autoRefreshProcId){
11815             clearInterval(this.autoRefreshProcId);
11816             delete this.autoRefreshProcId;
11817         }
11818     },
11819
11820     isAutoRefreshing : function(){
11821        return this.autoRefreshProcId ? true : false;
11822     },
11823     /**
11824      * Called to update the element to "Loading" state. Override to perform custom action.
11825      */
11826     showLoading : function(){
11827         if(this.showLoadIndicator){
11828             this.el.update(this.indicatorText);
11829         }
11830     },
11831
11832     /**
11833      * Adds unique parameter to query string if disableCaching = true
11834      * @private
11835      */
11836     prepareUrl : function(url){
11837         if(this.disableCaching){
11838             var append = "_dc=" + (new Date().getTime());
11839             if(url.indexOf("?") !== -1){
11840                 url += "&" + append;
11841             }else{
11842                 url += "?" + append;
11843             }
11844         }
11845         return url;
11846     },
11847
11848     /**
11849      * @private
11850      */
11851     processSuccess : function(response){
11852         this.transaction = null;
11853         if(response.argument.form && response.argument.reset){
11854             try{ // put in try/catch since some older FF releases had problems with this
11855                 response.argument.form.reset();
11856             }catch(e){}
11857         }
11858         if(this.loadScripts){
11859             this.renderer.render(this.el, response, this,
11860                 this.updateComplete.createDelegate(this, [response]));
11861         }else{
11862             this.renderer.render(this.el, response, this);
11863             this.updateComplete(response);
11864         }
11865     },
11866
11867     updateComplete : function(response){
11868         this.fireEvent("update", this.el, response);
11869         if(typeof response.argument.callback == "function"){
11870             response.argument.callback(this.el, true, response);
11871         }
11872     },
11873
11874     /**
11875      * @private
11876      */
11877     processFailure : function(response){
11878         this.transaction = null;
11879         this.fireEvent("failure", this.el, response);
11880         if(typeof response.argument.callback == "function"){
11881             response.argument.callback(this.el, false, response);
11882         }
11883     },
11884
11885     /**
11886      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11887      * @param {Object} renderer The object implementing the render() method
11888      */
11889     setRenderer : function(renderer){
11890         this.renderer = renderer;
11891     },
11892
11893     getRenderer : function(){
11894        return this.renderer;
11895     },
11896
11897     /**
11898      * Set the defaultUrl used for updates
11899      * @param {String/Function} defaultUrl The url or a function to call to get the url
11900      */
11901     setDefaultUrl : function(defaultUrl){
11902         this.defaultUrl = defaultUrl;
11903     },
11904
11905     /**
11906      * Aborts the executing transaction
11907      */
11908     abort : function(){
11909         if(this.transaction){
11910             Roo.Ajax.abort(this.transaction);
11911         }
11912     },
11913
11914     /**
11915      * Returns true if an update is in progress
11916      * @return {Boolean}
11917      */
11918     isUpdating : function(){
11919         if(this.transaction){
11920             return Roo.Ajax.isLoading(this.transaction);
11921         }
11922         return false;
11923     }
11924 });
11925
11926 /**
11927  * @class Roo.UpdateManager.defaults
11928  * @static (not really - but it helps the doc tool)
11929  * The defaults collection enables customizing the default properties of UpdateManager
11930  */
11931    Roo.UpdateManager.defaults = {
11932        /**
11933          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11934          * @type Number
11935          */
11936          timeout : 30,
11937
11938          /**
11939          * True to process scripts by default (Defaults to false).
11940          * @type Boolean
11941          */
11942         loadScripts : false,
11943
11944         /**
11945         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11946         * @type String
11947         */
11948         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11949         /**
11950          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11951          * @type Boolean
11952          */
11953         disableCaching : false,
11954         /**
11955          * Whether to show indicatorText when loading (Defaults to true).
11956          * @type Boolean
11957          */
11958         showLoadIndicator : true,
11959         /**
11960          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11961          * @type String
11962          */
11963         indicatorText : '<div class="loading-indicator">Loading...</div>'
11964    };
11965
11966 /**
11967  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11968  *Usage:
11969  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11970  * @param {String/HTMLElement/Roo.Element} el The element to update
11971  * @param {String} url The url
11972  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11973  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11974  * @static
11975  * @deprecated
11976  * @member Roo.UpdateManager
11977  */
11978 Roo.UpdateManager.updateElement = function(el, url, params, options){
11979     var um = Roo.get(el, true).getUpdateManager();
11980     Roo.apply(um, options);
11981     um.update(url, params, options ? options.callback : null);
11982 };
11983 // alias for backwards compat
11984 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11985 /**
11986  * @class Roo.UpdateManager.BasicRenderer
11987  * Default Content renderer. Updates the elements innerHTML with the responseText.
11988  */
11989 Roo.UpdateManager.BasicRenderer = function(){};
11990
11991 Roo.UpdateManager.BasicRenderer.prototype = {
11992     /**
11993      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11994      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11995      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11996      * @param {Roo.Element} el The element being rendered
11997      * @param {Object} response The YUI Connect response object
11998      * @param {UpdateManager} updateManager The calling update manager
11999      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12000      */
12001      render : function(el, response, updateManager, callback){
12002         el.update(response.responseText, updateManager.loadScripts, callback);
12003     }
12004 };
12005 /*
12006  * Based on:
12007  * Ext JS Library 1.1.1
12008  * Copyright(c) 2006-2007, Ext JS, LLC.
12009  *
12010  * Originally Released Under LGPL - original licence link has changed is not relivant.
12011  *
12012  * Fork - LGPL
12013  * <script type="text/javascript">
12014  */
12015
12016 /**
12017  * @class Roo.util.DelayedTask
12018  * Provides a convenient method of performing setTimeout where a new
12019  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12020  * You can use this class to buffer
12021  * the keypress events for a certain number of milliseconds, and perform only if they stop
12022  * for that amount of time.
12023  * @constructor The parameters to this constructor serve as defaults and are not required.
12024  * @param {Function} fn (optional) The default function to timeout
12025  * @param {Object} scope (optional) The default scope of that timeout
12026  * @param {Array} args (optional) The default Array of arguments
12027  */
12028 Roo.util.DelayedTask = function(fn, scope, args){
12029     var id = null, d, t;
12030
12031     var call = function(){
12032         var now = new Date().getTime();
12033         if(now - t >= d){
12034             clearInterval(id);
12035             id = null;
12036             fn.apply(scope, args || []);
12037         }
12038     };
12039     /**
12040      * Cancels any pending timeout and queues a new one
12041      * @param {Number} delay The milliseconds to delay
12042      * @param {Function} newFn (optional) Overrides function passed to constructor
12043      * @param {Object} newScope (optional) Overrides scope passed to constructor
12044      * @param {Array} newArgs (optional) Overrides args passed to constructor
12045      */
12046     this.delay = function(delay, newFn, newScope, newArgs){
12047         if(id && delay != d){
12048             this.cancel();
12049         }
12050         d = delay;
12051         t = new Date().getTime();
12052         fn = newFn || fn;
12053         scope = newScope || scope;
12054         args = newArgs || args;
12055         if(!id){
12056             id = setInterval(call, d);
12057         }
12058     };
12059
12060     /**
12061      * Cancel the last queued timeout
12062      */
12063     this.cancel = function(){
12064         if(id){
12065             clearInterval(id);
12066             id = null;
12067         }
12068     };
12069 };/*
12070  * Based on:
12071  * Ext JS Library 1.1.1
12072  * Copyright(c) 2006-2007, Ext JS, LLC.
12073  *
12074  * Originally Released Under LGPL - original licence link has changed is not relivant.
12075  *
12076  * Fork - LGPL
12077  * <script type="text/javascript">
12078  */
12079  
12080  
12081 Roo.util.TaskRunner = function(interval){
12082     interval = interval || 10;
12083     var tasks = [], removeQueue = [];
12084     var id = 0;
12085     var running = false;
12086
12087     var stopThread = function(){
12088         running = false;
12089         clearInterval(id);
12090         id = 0;
12091     };
12092
12093     var startThread = function(){
12094         if(!running){
12095             running = true;
12096             id = setInterval(runTasks, interval);
12097         }
12098     };
12099
12100     var removeTask = function(task){
12101         removeQueue.push(task);
12102         if(task.onStop){
12103             task.onStop();
12104         }
12105     };
12106
12107     var runTasks = function(){
12108         if(removeQueue.length > 0){
12109             for(var i = 0, len = removeQueue.length; i < len; i++){
12110                 tasks.remove(removeQueue[i]);
12111             }
12112             removeQueue = [];
12113             if(tasks.length < 1){
12114                 stopThread();
12115                 return;
12116             }
12117         }
12118         var now = new Date().getTime();
12119         for(var i = 0, len = tasks.length; i < len; ++i){
12120             var t = tasks[i];
12121             var itime = now - t.taskRunTime;
12122             if(t.interval <= itime){
12123                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12124                 t.taskRunTime = now;
12125                 if(rt === false || t.taskRunCount === t.repeat){
12126                     removeTask(t);
12127                     return;
12128                 }
12129             }
12130             if(t.duration && t.duration <= (now - t.taskStartTime)){
12131                 removeTask(t);
12132             }
12133         }
12134     };
12135
12136     /**
12137      * Queues a new task.
12138      * @param {Object} task
12139      */
12140     this.start = function(task){
12141         tasks.push(task);
12142         task.taskStartTime = new Date().getTime();
12143         task.taskRunTime = 0;
12144         task.taskRunCount = 0;
12145         startThread();
12146         return task;
12147     };
12148
12149     this.stop = function(task){
12150         removeTask(task);
12151         return task;
12152     };
12153
12154     this.stopAll = function(){
12155         stopThread();
12156         for(var i = 0, len = tasks.length; i < len; i++){
12157             if(tasks[i].onStop){
12158                 tasks[i].onStop();
12159             }
12160         }
12161         tasks = [];
12162         removeQueue = [];
12163     };
12164 };
12165
12166 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12167  * Based on:
12168  * Ext JS Library 1.1.1
12169  * Copyright(c) 2006-2007, Ext JS, LLC.
12170  *
12171  * Originally Released Under LGPL - original licence link has changed is not relivant.
12172  *
12173  * Fork - LGPL
12174  * <script type="text/javascript">
12175  */
12176
12177  
12178 /**
12179  * @class Roo.util.MixedCollection
12180  * @extends Roo.util.Observable
12181  * A Collection class that maintains both numeric indexes and keys and exposes events.
12182  * @constructor
12183  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12184  * collection (defaults to false)
12185  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12186  * and return the key value for that item.  This is used when available to look up the key on items that
12187  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12188  * equivalent to providing an implementation for the {@link #getKey} method.
12189  */
12190 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12191     this.items = [];
12192     this.map = {};
12193     this.keys = [];
12194     this.length = 0;
12195     this.addEvents({
12196         /**
12197          * @event clear
12198          * Fires when the collection is cleared.
12199          */
12200         "clear" : true,
12201         /**
12202          * @event add
12203          * Fires when an item is added to the collection.
12204          * @param {Number} index The index at which the item was added.
12205          * @param {Object} o The item added.
12206          * @param {String} key The key associated with the added item.
12207          */
12208         "add" : true,
12209         /**
12210          * @event replace
12211          * Fires when an item is replaced in the collection.
12212          * @param {String} key he key associated with the new added.
12213          * @param {Object} old The item being replaced.
12214          * @param {Object} new The new item.
12215          */
12216         "replace" : true,
12217         /**
12218          * @event remove
12219          * Fires when an item is removed from the collection.
12220          * @param {Object} o The item being removed.
12221          * @param {String} key (optional) The key associated with the removed item.
12222          */
12223         "remove" : true,
12224         "sort" : true
12225     });
12226     this.allowFunctions = allowFunctions === true;
12227     if(keyFn){
12228         this.getKey = keyFn;
12229     }
12230     Roo.util.MixedCollection.superclass.constructor.call(this);
12231 };
12232
12233 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12234     allowFunctions : false,
12235     
12236 /**
12237  * Adds an item to the collection.
12238  * @param {String} key The key to associate with the item
12239  * @param {Object} o The item to add.
12240  * @return {Object} The item added.
12241  */
12242     add : function(key, o){
12243         if(arguments.length == 1){
12244             o = arguments[0];
12245             key = this.getKey(o);
12246         }
12247         if(typeof key == "undefined" || key === null){
12248             this.length++;
12249             this.items.push(o);
12250             this.keys.push(null);
12251         }else{
12252             var old = this.map[key];
12253             if(old){
12254                 return this.replace(key, o);
12255             }
12256             this.length++;
12257             this.items.push(o);
12258             this.map[key] = o;
12259             this.keys.push(key);
12260         }
12261         this.fireEvent("add", this.length-1, o, key);
12262         return o;
12263     },
12264        
12265 /**
12266   * MixedCollection has a generic way to fetch keys if you implement getKey.
12267 <pre><code>
12268 // normal way
12269 var mc = new Roo.util.MixedCollection();
12270 mc.add(someEl.dom.id, someEl);
12271 mc.add(otherEl.dom.id, otherEl);
12272 //and so on
12273
12274 // using getKey
12275 var mc = new Roo.util.MixedCollection();
12276 mc.getKey = function(el){
12277    return el.dom.id;
12278 };
12279 mc.add(someEl);
12280 mc.add(otherEl);
12281
12282 // or via the constructor
12283 var mc = new Roo.util.MixedCollection(false, function(el){
12284    return el.dom.id;
12285 });
12286 mc.add(someEl);
12287 mc.add(otherEl);
12288 </code></pre>
12289  * @param o {Object} The item for which to find the key.
12290  * @return {Object} The key for the passed item.
12291  */
12292     getKey : function(o){
12293          return o.id; 
12294     },
12295    
12296 /**
12297  * Replaces an item in the collection.
12298  * @param {String} key The key associated with the item to replace, or the item to replace.
12299  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12300  * @return {Object}  The new item.
12301  */
12302     replace : function(key, o){
12303         if(arguments.length == 1){
12304             o = arguments[0];
12305             key = this.getKey(o);
12306         }
12307         var old = this.item(key);
12308         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12309              return this.add(key, o);
12310         }
12311         var index = this.indexOfKey(key);
12312         this.items[index] = o;
12313         this.map[key] = o;
12314         this.fireEvent("replace", key, old, o);
12315         return o;
12316     },
12317    
12318 /**
12319  * Adds all elements of an Array or an Object to the collection.
12320  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12321  * an Array of values, each of which are added to the collection.
12322  */
12323     addAll : function(objs){
12324         if(arguments.length > 1 || objs instanceof Array){
12325             var args = arguments.length > 1 ? arguments : objs;
12326             for(var i = 0, len = args.length; i < len; i++){
12327                 this.add(args[i]);
12328             }
12329         }else{
12330             for(var key in objs){
12331                 if(this.allowFunctions || typeof objs[key] != "function"){
12332                     this.add(key, objs[key]);
12333                 }
12334             }
12335         }
12336     },
12337    
12338 /**
12339  * Executes the specified function once for every item in the collection, passing each
12340  * item as the first and only parameter. returning false from the function will stop the iteration.
12341  * @param {Function} fn The function to execute for each item.
12342  * @param {Object} scope (optional) The scope in which to execute the function.
12343  */
12344     each : function(fn, scope){
12345         var items = [].concat(this.items); // each safe for removal
12346         for(var i = 0, len = items.length; i < len; i++){
12347             if(fn.call(scope || items[i], items[i], i, len) === false){
12348                 break;
12349             }
12350         }
12351     },
12352    
12353 /**
12354  * Executes the specified function once for every key in the collection, passing each
12355  * key, and its associated item as the first two parameters.
12356  * @param {Function} fn The function to execute for each item.
12357  * @param {Object} scope (optional) The scope in which to execute the function.
12358  */
12359     eachKey : function(fn, scope){
12360         for(var i = 0, len = this.keys.length; i < len; i++){
12361             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12362         }
12363     },
12364    
12365 /**
12366  * Returns the first item in the collection which elicits a true return value from the
12367  * passed selection function.
12368  * @param {Function} fn The selection function to execute for each item.
12369  * @param {Object} scope (optional) The scope in which to execute the function.
12370  * @return {Object} The first item in the collection which returned true from the selection function.
12371  */
12372     find : function(fn, scope){
12373         for(var i = 0, len = this.items.length; i < len; i++){
12374             if(fn.call(scope || window, this.items[i], this.keys[i])){
12375                 return this.items[i];
12376             }
12377         }
12378         return null;
12379     },
12380    
12381 /**
12382  * Inserts an item at the specified index in the collection.
12383  * @param {Number} index The index to insert the item at.
12384  * @param {String} key The key to associate with the new item, or the item itself.
12385  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12386  * @return {Object} The item inserted.
12387  */
12388     insert : function(index, key, o){
12389         if(arguments.length == 2){
12390             o = arguments[1];
12391             key = this.getKey(o);
12392         }
12393         if(index >= this.length){
12394             return this.add(key, o);
12395         }
12396         this.length++;
12397         this.items.splice(index, 0, o);
12398         if(typeof key != "undefined" && key != null){
12399             this.map[key] = o;
12400         }
12401         this.keys.splice(index, 0, key);
12402         this.fireEvent("add", index, o, key);
12403         return o;
12404     },
12405    
12406 /**
12407  * Removed an item from the collection.
12408  * @param {Object} o The item to remove.
12409  * @return {Object} The item removed.
12410  */
12411     remove : function(o){
12412         return this.removeAt(this.indexOf(o));
12413     },
12414    
12415 /**
12416  * Remove an item from a specified index in the collection.
12417  * @param {Number} index The index within the collection of the item to remove.
12418  */
12419     removeAt : function(index){
12420         if(index < this.length && index >= 0){
12421             this.length--;
12422             var o = this.items[index];
12423             this.items.splice(index, 1);
12424             var key = this.keys[index];
12425             if(typeof key != "undefined"){
12426                 delete this.map[key];
12427             }
12428             this.keys.splice(index, 1);
12429             this.fireEvent("remove", o, key);
12430         }
12431     },
12432    
12433 /**
12434  * Removed an item associated with the passed key fom the collection.
12435  * @param {String} key The key of the item to remove.
12436  */
12437     removeKey : function(key){
12438         return this.removeAt(this.indexOfKey(key));
12439     },
12440    
12441 /**
12442  * Returns the number of items in the collection.
12443  * @return {Number} the number of items in the collection.
12444  */
12445     getCount : function(){
12446         return this.length; 
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed Object.
12451  * @param {Object} o The item to find the index of.
12452  * @return {Number} index of the item.
12453  */
12454     indexOf : function(o){
12455         if(!this.items.indexOf){
12456             for(var i = 0, len = this.items.length; i < len; i++){
12457                 if(this.items[i] == o) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.items.indexOf(o);
12462         }
12463     },
12464    
12465 /**
12466  * Returns index within the collection of the passed key.
12467  * @param {String} key The key to find the index of.
12468  * @return {Number} index of the key.
12469  */
12470     indexOfKey : function(key){
12471         if(!this.keys.indexOf){
12472             for(var i = 0, len = this.keys.length; i < len; i++){
12473                 if(this.keys[i] == key) return i;
12474             }
12475             return -1;
12476         }else{
12477             return this.keys.indexOf(key);
12478         }
12479     },
12480    
12481 /**
12482  * Returns the item associated with the passed key OR index. Key has priority over index.
12483  * @param {String/Number} key The key or index of the item.
12484  * @return {Object} The item associated with the passed key.
12485  */
12486     item : function(key){
12487         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12488         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12489     },
12490     
12491 /**
12492  * Returns the item at the specified index.
12493  * @param {Number} index The index of the item.
12494  * @return {Object}
12495  */
12496     itemAt : function(index){
12497         return this.items[index];
12498     },
12499     
12500 /**
12501  * Returns the item associated with the passed key.
12502  * @param {String/Number} key The key of the item.
12503  * @return {Object} The item associated with the passed key.
12504  */
12505     key : function(key){
12506         return this.map[key];
12507     },
12508    
12509 /**
12510  * Returns true if the collection contains the passed Object as an item.
12511  * @param {Object} o  The Object to look for in the collection.
12512  * @return {Boolean} True if the collection contains the Object as an item.
12513  */
12514     contains : function(o){
12515         return this.indexOf(o) != -1;
12516     },
12517    
12518 /**
12519  * Returns true if the collection contains the passed Object as a key.
12520  * @param {String} key The key to look for in the collection.
12521  * @return {Boolean} True if the collection contains the Object as a key.
12522  */
12523     containsKey : function(key){
12524         return typeof this.map[key] != "undefined";
12525     },
12526    
12527 /**
12528  * Removes all items from the collection.
12529  */
12530     clear : function(){
12531         this.length = 0;
12532         this.items = [];
12533         this.keys = [];
12534         this.map = {};
12535         this.fireEvent("clear");
12536     },
12537    
12538 /**
12539  * Returns the first item in the collection.
12540  * @return {Object} the first item in the collection..
12541  */
12542     first : function(){
12543         return this.items[0]; 
12544     },
12545    
12546 /**
12547  * Returns the last item in the collection.
12548  * @return {Object} the last item in the collection..
12549  */
12550     last : function(){
12551         return this.items[this.length-1];   
12552     },
12553     
12554     _sort : function(property, dir, fn){
12555         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12556         fn = fn || function(a, b){
12557             return a-b;
12558         };
12559         var c = [], k = this.keys, items = this.items;
12560         for(var i = 0, len = items.length; i < len; i++){
12561             c[c.length] = {key: k[i], value: items[i], index: i};
12562         }
12563         c.sort(function(a, b){
12564             var v = fn(a[property], b[property]) * dsc;
12565             if(v == 0){
12566                 v = (a.index < b.index ? -1 : 1);
12567             }
12568             return v;
12569         });
12570         for(var i = 0, len = c.length; i < len; i++){
12571             items[i] = c[i].value;
12572             k[i] = c[i].key;
12573         }
12574         this.fireEvent("sort", this);
12575     },
12576     
12577     /**
12578      * Sorts this collection with the passed comparison function
12579      * @param {String} direction (optional) "ASC" or "DESC"
12580      * @param {Function} fn (optional) comparison function
12581      */
12582     sort : function(dir, fn){
12583         this._sort("value", dir, fn);
12584     },
12585     
12586     /**
12587      * Sorts this collection by keys
12588      * @param {String} direction (optional) "ASC" or "DESC"
12589      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12590      */
12591     keySort : function(dir, fn){
12592         this._sort("key", dir, fn || function(a, b){
12593             return String(a).toUpperCase()-String(b).toUpperCase();
12594         });
12595     },
12596     
12597     /**
12598      * Returns a range of items in this collection
12599      * @param {Number} startIndex (optional) defaults to 0
12600      * @param {Number} endIndex (optional) default to the last item
12601      * @return {Array} An array of items
12602      */
12603     getRange : function(start, end){
12604         var items = this.items;
12605         if(items.length < 1){
12606             return [];
12607         }
12608         start = start || 0;
12609         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12610         var r = [];
12611         if(start <= end){
12612             for(var i = start; i <= end; i++) {
12613                     r[r.length] = items[i];
12614             }
12615         }else{
12616             for(var i = start; i >= end; i--) {
12617                     r[r.length] = items[i];
12618             }
12619         }
12620         return r;
12621     },
12622         
12623     /**
12624      * Filter the <i>objects</i> in this collection by a specific property. 
12625      * Returns a new collection that has been filtered.
12626      * @param {String} property A property on your objects
12627      * @param {String/RegExp} value Either string that the property values 
12628      * should start with or a RegExp to test against the property
12629      * @return {MixedCollection} The new filtered collection
12630      */
12631     filter : function(property, value){
12632         if(!value.exec){ // not a regex
12633             value = String(value);
12634             if(value.length == 0){
12635                 return this.clone();
12636             }
12637             value = new RegExp("^" + Roo.escapeRe(value), "i");
12638         }
12639         return this.filterBy(function(o){
12640             return o && value.test(o[property]);
12641         });
12642         },
12643     
12644     /**
12645      * Filter by a function. * Returns a new collection that has been filtered.
12646      * The passed function will be called with each 
12647      * object in the collection. If the function returns true, the value is included 
12648      * otherwise it is filtered.
12649      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12650      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12651      * @return {MixedCollection} The new filtered collection
12652      */
12653     filterBy : function(fn, scope){
12654         var r = new Roo.util.MixedCollection();
12655         r.getKey = this.getKey;
12656         var k = this.keys, it = this.items;
12657         for(var i = 0, len = it.length; i < len; i++){
12658             if(fn.call(scope||this, it[i], k[i])){
12659                                 r.add(k[i], it[i]);
12660                         }
12661         }
12662         return r;
12663     },
12664     
12665     /**
12666      * Creates a duplicate of this collection
12667      * @return {MixedCollection}
12668      */
12669     clone : function(){
12670         var r = new Roo.util.MixedCollection();
12671         var k = this.keys, it = this.items;
12672         for(var i = 0, len = it.length; i < len; i++){
12673             r.add(k[i], it[i]);
12674         }
12675         r.getKey = this.getKey;
12676         return r;
12677     }
12678 });
12679 /**
12680  * Returns the item associated with the passed key or index.
12681  * @method
12682  * @param {String/Number} key The key or index of the item.
12683  * @return {Object} The item associated with the passed key.
12684  */
12685 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12686  * Based on:
12687  * Ext JS Library 1.1.1
12688  * Copyright(c) 2006-2007, Ext JS, LLC.
12689  *
12690  * Originally Released Under LGPL - original licence link has changed is not relivant.
12691  *
12692  * Fork - LGPL
12693  * <script type="text/javascript">
12694  */
12695 /**
12696  * @class Roo.util.JSON
12697  * Modified version of Douglas Crockford"s json.js that doesn"t
12698  * mess with the Object prototype 
12699  * http://www.json.org/js.html
12700  * @singleton
12701  */
12702 Roo.util.JSON = new (function(){
12703     var useHasOwn = {}.hasOwnProperty ? true : false;
12704     
12705     // crashes Safari in some instances
12706     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12707     
12708     var pad = function(n) {
12709         return n < 10 ? "0" + n : n;
12710     };
12711     
12712     var m = {
12713         "\b": '\\b',
12714         "\t": '\\t',
12715         "\n": '\\n',
12716         "\f": '\\f',
12717         "\r": '\\r',
12718         '"' : '\\"',
12719         "\\": '\\\\'
12720     };
12721
12722     var encodeString = function(s){
12723         if (/["\\\x00-\x1f]/.test(s)) {
12724             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12725                 var c = m[b];
12726                 if(c){
12727                     return c;
12728                 }
12729                 c = b.charCodeAt();
12730                 return "\\u00" +
12731                     Math.floor(c / 16).toString(16) +
12732                     (c % 16).toString(16);
12733             }) + '"';
12734         }
12735         return '"' + s + '"';
12736     };
12737     
12738     var encodeArray = function(o){
12739         var a = ["["], b, i, l = o.length, v;
12740             for (i = 0; i < l; i += 1) {
12741                 v = o[i];
12742                 switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if (b) {
12749                             a.push(',');
12750                         }
12751                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12752                         b = true;
12753                 }
12754             }
12755             a.push("]");
12756             return a.join("");
12757     };
12758     
12759     var encodeDate = function(o){
12760         return '"' + o.getFullYear() + "-" +
12761                 pad(o.getMonth() + 1) + "-" +
12762                 pad(o.getDate()) + "T" +
12763                 pad(o.getHours()) + ":" +
12764                 pad(o.getMinutes()) + ":" +
12765                 pad(o.getSeconds()) + '"';
12766     };
12767     
12768     /**
12769      * Encodes an Object, Array or other value
12770      * @param {Mixed} o The variable to encode
12771      * @return {String} The JSON string
12772      */
12773     this.encode = function(o)
12774     {
12775         // should this be extended to fully wrap stringify..
12776         
12777         if(typeof o == "undefined" || o === null){
12778             return "null";
12779         }else if(o instanceof Array){
12780             return encodeArray(o);
12781         }else if(o instanceof Date){
12782             return encodeDate(o);
12783         }else if(typeof o == "string"){
12784             return encodeString(o);
12785         }else if(typeof o == "number"){
12786             return isFinite(o) ? String(o) : "null";
12787         }else if(typeof o == "boolean"){
12788             return String(o);
12789         }else {
12790             var a = ["{"], b, i, v;
12791             for (i in o) {
12792                 if(!useHasOwn || o.hasOwnProperty(i)) {
12793                     v = o[i];
12794                     switch (typeof v) {
12795                     case "undefined":
12796                     case "function":
12797                     case "unknown":
12798                         break;
12799                     default:
12800                         if(b){
12801                             a.push(',');
12802                         }
12803                         a.push(this.encode(i), ":",
12804                                 v === null ? "null" : this.encode(v));
12805                         b = true;
12806                     }
12807                 }
12808             }
12809             a.push("}");
12810             return a.join("");
12811         }
12812     };
12813     
12814     /**
12815      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12816      * @param {String} json The JSON string
12817      * @return {Object} The resulting object
12818      */
12819     this.decode = function(json){
12820         
12821         return  /** eval:var:json */ eval("(" + json + ')');
12822     };
12823 })();
12824 /** 
12825  * Shorthand for {@link Roo.util.JSON#encode}
12826  * @member Roo encode 
12827  * @method */
12828 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12829 /** 
12830  * Shorthand for {@link Roo.util.JSON#decode}
12831  * @member Roo decode 
12832  * @method */
12833 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12834 /*
12835  * Based on:
12836  * Ext JS Library 1.1.1
12837  * Copyright(c) 2006-2007, Ext JS, LLC.
12838  *
12839  * Originally Released Under LGPL - original licence link has changed is not relivant.
12840  *
12841  * Fork - LGPL
12842  * <script type="text/javascript">
12843  */
12844  
12845 /**
12846  * @class Roo.util.Format
12847  * Reusable data formatting functions
12848  * @singleton
12849  */
12850 Roo.util.Format = function(){
12851     var trimRe = /^\s+|\s+$/g;
12852     return {
12853         /**
12854          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12855          * @param {String} value The string to truncate
12856          * @param {Number} length The maximum length to allow before truncating
12857          * @return {String} The converted text
12858          */
12859         ellipsis : function(value, len){
12860             if(value && value.length > len){
12861                 return value.substr(0, len-3)+"...";
12862             }
12863             return value;
12864         },
12865
12866         /**
12867          * Checks a reference and converts it to empty string if it is undefined
12868          * @param {Mixed} value Reference to check
12869          * @return {Mixed} Empty string if converted, otherwise the original value
12870          */
12871         undef : function(value){
12872             return typeof value != "undefined" ? value : "";
12873         },
12874
12875         /**
12876          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12877          * @param {String} value The string to encode
12878          * @return {String} The encoded text
12879          */
12880         htmlEncode : function(value){
12881             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12882         },
12883
12884         /**
12885          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12886          * @param {String} value The string to decode
12887          * @return {String} The decoded text
12888          */
12889         htmlDecode : function(value){
12890             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12891         },
12892
12893         /**
12894          * Trims any whitespace from either side of a string
12895          * @param {String} value The text to trim
12896          * @return {String} The trimmed text
12897          */
12898         trim : function(value){
12899             return String(value).replace(trimRe, "");
12900         },
12901
12902         /**
12903          * Returns a substring from within an original string
12904          * @param {String} value The original text
12905          * @param {Number} start The start index of the substring
12906          * @param {Number} length The length of the substring
12907          * @return {String} The substring
12908          */
12909         substr : function(value, start, length){
12910             return String(value).substr(start, length);
12911         },
12912
12913         /**
12914          * Converts a string to all lower case letters
12915          * @param {String} value The text to convert
12916          * @return {String} The converted text
12917          */
12918         lowercase : function(value){
12919             return String(value).toLowerCase();
12920         },
12921
12922         /**
12923          * Converts a string to all upper case letters
12924          * @param {String} value The text to convert
12925          * @return {String} The converted text
12926          */
12927         uppercase : function(value){
12928             return String(value).toUpperCase();
12929         },
12930
12931         /**
12932          * Converts the first character only of a string to upper case
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         capitalize : function(value){
12937             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12938         },
12939
12940         // private
12941         call : function(value, fn){
12942             if(arguments.length > 2){
12943                 var args = Array.prototype.slice.call(arguments, 2);
12944                 args.unshift(value);
12945                  
12946                 return /** eval:var:value */  eval(fn).apply(window, args);
12947             }else{
12948                 /** eval:var:value */
12949                 return /** eval:var:value */ eval(fn).call(window, value);
12950             }
12951         },
12952
12953        
12954         /**
12955          * safer version of Math.toFixed..??/
12956          * @param {Number/String} value The numeric value to format
12957          * @param {Number/String} value Decimal places 
12958          * @return {String} The formatted currency string
12959          */
12960         toFixed : function(v, n)
12961         {
12962             // why not use to fixed - precision is buggered???
12963             if (!n) {
12964                 return Math.round(v-0);
12965             }
12966             var fact = Math.pow(10,n+1);
12967             v = (Math.round((v-0)*fact))/fact;
12968             var z = (''+fact).substring(2);
12969             if (v == Math.floor(v)) {
12970                 return Math.floor(v) + '.' + z;
12971             }
12972             
12973             // now just padd decimals..
12974             var ps = String(v).split('.');
12975             var fd = (ps[1] + z);
12976             var r = fd.substring(0,n); 
12977             var rm = fd.substring(n); 
12978             if (rm < 5) {
12979                 return ps[0] + '.' + r;
12980             }
12981             r*=1; // turn it into a number;
12982             r++;
12983             if (String(r).length != n) {
12984                 ps[0]*=1;
12985                 ps[0]++;
12986                 r = String(r).substring(1); // chop the end off.
12987             }
12988             
12989             return ps[0] + '.' + r;
12990              
12991         },
12992         
12993         /**
12994          * Format a number as US currency
12995          * @param {Number/String} value The numeric value to format
12996          * @return {String} The formatted currency string
12997          */
12998         usMoney : function(v){
12999             v = (Math.round((v-0)*100))/100;
13000             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13001             v = String(v);
13002             var ps = v.split('.');
13003             var whole = ps[0];
13004             var sub = ps[1] ? '.'+ ps[1] : '.00';
13005             var r = /(\d+)(\d{3})/;
13006             while (r.test(whole)) {
13007                 whole = whole.replace(r, '$1' + ',' + '$2');
13008             }
13009             return "$" + whole + sub ;
13010         },
13011         
13012         /**
13013          * Parse a value into a formatted date using the specified format pattern.
13014          * @param {Mixed} value The value to format
13015          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13016          * @return {String} The formatted date string
13017          */
13018         date : function(v, format){
13019             if(!v){
13020                 return "";
13021             }
13022             if(!(v instanceof Date)){
13023                 v = new Date(Date.parse(v));
13024             }
13025             return v.dateFormat(format || "m/d/Y");
13026         },
13027
13028         /**
13029          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13030          * @param {String} format Any valid date format string
13031          * @return {Function} The date formatting function
13032          */
13033         dateRenderer : function(format){
13034             return function(v){
13035                 return Roo.util.Format.date(v, format);  
13036             };
13037         },
13038
13039         // private
13040         stripTagsRE : /<\/?[^>]+>/gi,
13041         
13042         /**
13043          * Strips all HTML tags
13044          * @param {Mixed} value The text from which to strip tags
13045          * @return {String} The stripped text
13046          */
13047         stripTags : function(v){
13048             return !v ? v : String(v).replace(this.stripTagsRE, "");
13049         }
13050     };
13051 }();/*
13052  * Based on:
13053  * Ext JS Library 1.1.1
13054  * Copyright(c) 2006-2007, Ext JS, LLC.
13055  *
13056  * Originally Released Under LGPL - original licence link has changed is not relivant.
13057  *
13058  * Fork - LGPL
13059  * <script type="text/javascript">
13060  */
13061
13062
13063  
13064
13065 /**
13066  * @class Roo.MasterTemplate
13067  * @extends Roo.Template
13068  * Provides a template that can have child templates. The syntax is:
13069 <pre><code>
13070 var t = new Roo.MasterTemplate(
13071         '&lt;select name="{name}"&gt;',
13072                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13073         '&lt;/select&gt;'
13074 );
13075 t.add('options', {value: 'foo', text: 'bar'});
13076 // or you can add multiple child elements in one shot
13077 t.addAll('options', [
13078     {value: 'foo', text: 'bar'},
13079     {value: 'foo2', text: 'bar2'},
13080     {value: 'foo3', text: 'bar3'}
13081 ]);
13082 // then append, applying the master template values
13083 t.append('my-form', {name: 'my-select'});
13084 </code></pre>
13085 * A name attribute for the child template is not required if you have only one child
13086 * template or you want to refer to them by index.
13087  */
13088 Roo.MasterTemplate = function(){
13089     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13090     this.originalHtml = this.html;
13091     var st = {};
13092     var m, re = this.subTemplateRe;
13093     re.lastIndex = 0;
13094     var subIndex = 0;
13095     while(m = re.exec(this.html)){
13096         var name = m[1], content = m[2];
13097         st[subIndex] = {
13098             name: name,
13099             index: subIndex,
13100             buffer: [],
13101             tpl : new Roo.Template(content)
13102         };
13103         if(name){
13104             st[name] = st[subIndex];
13105         }
13106         st[subIndex].tpl.compile();
13107         st[subIndex].tpl.call = this.call.createDelegate(this);
13108         subIndex++;
13109     }
13110     this.subCount = subIndex;
13111     this.subs = st;
13112 };
13113 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13114     /**
13115     * The regular expression used to match sub templates
13116     * @type RegExp
13117     * @property
13118     */
13119     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13120
13121     /**
13122      * Applies the passed values to a child template.
13123      * @param {String/Number} name (optional) The name or index of the child template
13124      * @param {Array/Object} values The values to be applied to the template
13125      * @return {MasterTemplate} this
13126      */
13127      add : function(name, values){
13128         if(arguments.length == 1){
13129             values = arguments[0];
13130             name = 0;
13131         }
13132         var s = this.subs[name];
13133         s.buffer[s.buffer.length] = s.tpl.apply(values);
13134         return this;
13135     },
13136
13137     /**
13138      * Applies all the passed values to a child template.
13139      * @param {String/Number} name (optional) The name or index of the child template
13140      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13141      * @param {Boolean} reset (optional) True to reset the template first
13142      * @return {MasterTemplate} this
13143      */
13144     fill : function(name, values, reset){
13145         var a = arguments;
13146         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13147             values = a[0];
13148             name = 0;
13149             reset = a[1];
13150         }
13151         if(reset){
13152             this.reset();
13153         }
13154         for(var i = 0, len = values.length; i < len; i++){
13155             this.add(name, values[i]);
13156         }
13157         return this;
13158     },
13159
13160     /**
13161      * Resets the template for reuse
13162      * @return {MasterTemplate} this
13163      */
13164      reset : function(){
13165         var s = this.subs;
13166         for(var i = 0; i < this.subCount; i++){
13167             s[i].buffer = [];
13168         }
13169         return this;
13170     },
13171
13172     applyTemplate : function(values){
13173         var s = this.subs;
13174         var replaceIndex = -1;
13175         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13176             return s[++replaceIndex].buffer.join("");
13177         });
13178         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13179     },
13180
13181     apply : function(){
13182         return this.applyTemplate.apply(this, arguments);
13183     },
13184
13185     compile : function(){return this;}
13186 });
13187
13188 /**
13189  * Alias for fill().
13190  * @method
13191  */
13192 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13193  /**
13194  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13195  * var tpl = Roo.MasterTemplate.from('element-id');
13196  * @param {String/HTMLElement} el
13197  * @param {Object} config
13198  * @static
13199  */
13200 Roo.MasterTemplate.from = function(el, config){
13201     el = Roo.getDom(el);
13202     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13203 };/*
13204  * Based on:
13205  * Ext JS Library 1.1.1
13206  * Copyright(c) 2006-2007, Ext JS, LLC.
13207  *
13208  * Originally Released Under LGPL - original licence link has changed is not relivant.
13209  *
13210  * Fork - LGPL
13211  * <script type="text/javascript">
13212  */
13213
13214  
13215 /**
13216  * @class Roo.util.CSS
13217  * Utility class for manipulating CSS rules
13218  * @singleton
13219  */
13220 Roo.util.CSS = function(){
13221         var rules = null;
13222         var doc = document;
13223
13224     var camelRe = /(-[a-z])/gi;
13225     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13226
13227    return {
13228    /**
13229     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13230     * tag and appended to the HEAD of the document.
13231     * @param {String|Object} cssText The text containing the css rules
13232     * @param {String} id An id to add to the stylesheet for later removal
13233     * @return {StyleSheet}
13234     */
13235     createStyleSheet : function(cssText, id){
13236         var ss;
13237         var head = doc.getElementsByTagName("head")[0];
13238         var nrules = doc.createElement("style");
13239         nrules.setAttribute("type", "text/css");
13240         if(id){
13241             nrules.setAttribute("id", id);
13242         }
13243         if (typeof(cssText) != 'string') {
13244             // support object maps..
13245             // not sure if this a good idea.. 
13246             // perhaps it should be merged with the general css handling
13247             // and handle js style props.
13248             var cssTextNew = [];
13249             for(var n in cssText) {
13250                 var citems = [];
13251                 for(var k in cssText[n]) {
13252                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13253                 }
13254                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13255                 
13256             }
13257             cssText = cssTextNew.join("\n");
13258             
13259         }
13260        
13261        
13262        if(Roo.isIE){
13263            head.appendChild(nrules);
13264            ss = nrules.styleSheet;
13265            ss.cssText = cssText;
13266        }else{
13267            try{
13268                 nrules.appendChild(doc.createTextNode(cssText));
13269            }catch(e){
13270                nrules.cssText = cssText; 
13271            }
13272            head.appendChild(nrules);
13273            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13274        }
13275        this.cacheStyleSheet(ss);
13276        return ss;
13277    },
13278
13279    /**
13280     * Removes a style or link tag by id
13281     * @param {String} id The id of the tag
13282     */
13283    removeStyleSheet : function(id){
13284        var existing = doc.getElementById(id);
13285        if(existing){
13286            existing.parentNode.removeChild(existing);
13287        }
13288    },
13289
13290    /**
13291     * Dynamically swaps an existing stylesheet reference for a new one
13292     * @param {String} id The id of an existing link tag to remove
13293     * @param {String} url The href of the new stylesheet to include
13294     */
13295    swapStyleSheet : function(id, url){
13296        this.removeStyleSheet(id);
13297        var ss = doc.createElement("link");
13298        ss.setAttribute("rel", "stylesheet");
13299        ss.setAttribute("type", "text/css");
13300        ss.setAttribute("id", id);
13301        ss.setAttribute("href", url);
13302        doc.getElementsByTagName("head")[0].appendChild(ss);
13303    },
13304    
13305    /**
13306     * Refresh the rule cache if you have dynamically added stylesheets
13307     * @return {Object} An object (hash) of rules indexed by selector
13308     */
13309    refreshCache : function(){
13310        return this.getRules(true);
13311    },
13312
13313    // private
13314    cacheStyleSheet : function(stylesheet){
13315        if(!rules){
13316            rules = {};
13317        }
13318        try{// try catch for cross domain access issue
13319            var ssRules = stylesheet.cssRules || stylesheet.rules;
13320            for(var j = ssRules.length-1; j >= 0; --j){
13321                rules[ssRules[j].selectorText] = ssRules[j];
13322            }
13323        }catch(e){}
13324    },
13325    
13326    /**
13327     * Gets all css rules for the document
13328     * @param {Boolean} refreshCache true to refresh the internal cache
13329     * @return {Object} An object (hash) of rules indexed by selector
13330     */
13331    getRules : function(refreshCache){
13332                 if(rules == null || refreshCache){
13333                         rules = {};
13334                         var ds = doc.styleSheets;
13335                         for(var i =0, len = ds.length; i < len; i++){
13336                             try{
13337                         this.cacheStyleSheet(ds[i]);
13338                     }catch(e){} 
13339                 }
13340                 }
13341                 return rules;
13342         },
13343         
13344         /**
13345     * Gets an an individual CSS rule by selector(s)
13346     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13347     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13348     * @return {CSSRule} The CSS rule or null if one is not found
13349     */
13350    getRule : function(selector, refreshCache){
13351                 var rs = this.getRules(refreshCache);
13352                 if(!(selector instanceof Array)){
13353                     return rs[selector];
13354                 }
13355                 for(var i = 0; i < selector.length; i++){
13356                         if(rs[selector[i]]){
13357                                 return rs[selector[i]];
13358                         }
13359                 }
13360                 return null;
13361         },
13362         
13363         
13364         /**
13365     * Updates a rule property
13366     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13367     * @param {String} property The css property
13368     * @param {String} value The new value for the property
13369     * @return {Boolean} true If a rule was found and updated
13370     */
13371    updateRule : function(selector, property, value){
13372                 if(!(selector instanceof Array)){
13373                         var rule = this.getRule(selector);
13374                         if(rule){
13375                                 rule.style[property.replace(camelRe, camelFn)] = value;
13376                                 return true;
13377                         }
13378                 }else{
13379                         for(var i = 0; i < selector.length; i++){
13380                                 if(this.updateRule(selector[i], property, value)){
13381                                         return true;
13382                                 }
13383                         }
13384                 }
13385                 return false;
13386         }
13387    };   
13388 }();/*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398
13399  
13400
13401 /**
13402  * @class Roo.util.ClickRepeater
13403  * @extends Roo.util.Observable
13404  * 
13405  * A wrapper class which can be applied to any element. Fires a "click" event while the
13406  * mouse is pressed. The interval between firings may be specified in the config but
13407  * defaults to 10 milliseconds.
13408  * 
13409  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13410  * 
13411  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13412  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13413  * Similar to an autorepeat key delay.
13414  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13415  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13416  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13417  *           "interval" and "delay" are ignored. "immediate" is honored.
13418  * @cfg {Boolean} preventDefault True to prevent the default click event
13419  * @cfg {Boolean} stopDefault True to stop the default click event
13420  * 
13421  * @history
13422  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13423  *     2007-02-02 jvs Renamed to ClickRepeater
13424  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13425  *
13426  *  @constructor
13427  * @param {String/HTMLElement/Element} el The element to listen on
13428  * @param {Object} config
13429  **/
13430 Roo.util.ClickRepeater = function(el, config)
13431 {
13432     this.el = Roo.get(el);
13433     this.el.unselectable();
13434
13435     Roo.apply(this, config);
13436
13437     this.addEvents({
13438     /**
13439      * @event mousedown
13440      * Fires when the mouse button is depressed.
13441      * @param {Roo.util.ClickRepeater} this
13442      */
13443         "mousedown" : true,
13444     /**
13445      * @event click
13446      * Fires on a specified interval during the time the element is pressed.
13447      * @param {Roo.util.ClickRepeater} this
13448      */
13449         "click" : true,
13450     /**
13451      * @event mouseup
13452      * Fires when the mouse key is released.
13453      * @param {Roo.util.ClickRepeater} this
13454      */
13455         "mouseup" : true
13456     });
13457
13458     this.el.on("mousedown", this.handleMouseDown, this);
13459     if(this.preventDefault || this.stopDefault){
13460         this.el.on("click", function(e){
13461             if(this.preventDefault){
13462                 e.preventDefault();
13463             }
13464             if(this.stopDefault){
13465                 e.stopEvent();
13466             }
13467         }, this);
13468     }
13469
13470     // allow inline handler
13471     if(this.handler){
13472         this.on("click", this.handler,  this.scope || this);
13473     }
13474
13475     Roo.util.ClickRepeater.superclass.constructor.call(this);
13476 };
13477
13478 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13479     interval : 20,
13480     delay: 250,
13481     preventDefault : true,
13482     stopDefault : false,
13483     timer : 0,
13484
13485     // private
13486     handleMouseDown : function(){
13487         clearTimeout(this.timer);
13488         this.el.blur();
13489         if(this.pressClass){
13490             this.el.addClass(this.pressClass);
13491         }
13492         this.mousedownTime = new Date();
13493
13494         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13495         this.el.on("mouseout", this.handleMouseOut, this);
13496
13497         this.fireEvent("mousedown", this);
13498         this.fireEvent("click", this);
13499         
13500         this.timer = this.click.defer(this.delay || this.interval, this);
13501     },
13502
13503     // private
13504     click : function(){
13505         this.fireEvent("click", this);
13506         this.timer = this.click.defer(this.getInterval(), this);
13507     },
13508
13509     // private
13510     getInterval: function(){
13511         if(!this.accelerate){
13512             return this.interval;
13513         }
13514         var pressTime = this.mousedownTime.getElapsed();
13515         if(pressTime < 500){
13516             return 400;
13517         }else if(pressTime < 1700){
13518             return 320;
13519         }else if(pressTime < 2600){
13520             return 250;
13521         }else if(pressTime < 3500){
13522             return 180;
13523         }else if(pressTime < 4400){
13524             return 140;
13525         }else if(pressTime < 5300){
13526             return 80;
13527         }else if(pressTime < 6200){
13528             return 50;
13529         }else{
13530             return 10;
13531         }
13532     },
13533
13534     // private
13535     handleMouseOut : function(){
13536         clearTimeout(this.timer);
13537         if(this.pressClass){
13538             this.el.removeClass(this.pressClass);
13539         }
13540         this.el.on("mouseover", this.handleMouseReturn, this);
13541     },
13542
13543     // private
13544     handleMouseReturn : function(){
13545         this.el.un("mouseover", this.handleMouseReturn);
13546         if(this.pressClass){
13547             this.el.addClass(this.pressClass);
13548         }
13549         this.click();
13550     },
13551
13552     // private
13553     handleMouseUp : function(){
13554         clearTimeout(this.timer);
13555         this.el.un("mouseover", this.handleMouseReturn);
13556         this.el.un("mouseout", this.handleMouseOut);
13557         Roo.get(document).un("mouseup", this.handleMouseUp);
13558         this.el.removeClass(this.pressClass);
13559         this.fireEvent("mouseup", this);
13560     }
13561 });/*
13562  * Based on:
13563  * Ext JS Library 1.1.1
13564  * Copyright(c) 2006-2007, Ext JS, LLC.
13565  *
13566  * Originally Released Under LGPL - original licence link has changed is not relivant.
13567  *
13568  * Fork - LGPL
13569  * <script type="text/javascript">
13570  */
13571
13572  
13573 /**
13574  * @class Roo.KeyNav
13575  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13576  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13577  * way to implement custom navigation schemes for any UI component.</p>
13578  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13579  * pageUp, pageDown, del, home, end.  Usage:</p>
13580  <pre><code>
13581 var nav = new Roo.KeyNav("my-element", {
13582     "left" : function(e){
13583         this.moveLeft(e.ctrlKey);
13584     },
13585     "right" : function(e){
13586         this.moveRight(e.ctrlKey);
13587     },
13588     "enter" : function(e){
13589         this.save();
13590     },
13591     scope : this
13592 });
13593 </code></pre>
13594  * @constructor
13595  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13596  * @param {Object} config The config
13597  */
13598 Roo.KeyNav = function(el, config){
13599     this.el = Roo.get(el);
13600     Roo.apply(this, config);
13601     if(!this.disabled){
13602         this.disabled = true;
13603         this.enable();
13604     }
13605 };
13606
13607 Roo.KeyNav.prototype = {
13608     /**
13609      * @cfg {Boolean} disabled
13610      * True to disable this KeyNav instance (defaults to false)
13611      */
13612     disabled : false,
13613     /**
13614      * @cfg {String} defaultEventAction
13615      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13616      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13617      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13618      */
13619     defaultEventAction: "stopEvent",
13620     /**
13621      * @cfg {Boolean} forceKeyDown
13622      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13623      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13624      * handle keydown instead of keypress.
13625      */
13626     forceKeyDown : false,
13627
13628     // private
13629     prepareEvent : function(e){
13630         var k = e.getKey();
13631         var h = this.keyToHandler[k];
13632         //if(h && this[h]){
13633         //    e.stopPropagation();
13634         //}
13635         if(Roo.isSafari && h && k >= 37 && k <= 40){
13636             e.stopEvent();
13637         }
13638     },
13639
13640     // private
13641     relay : function(e){
13642         var k = e.getKey();
13643         var h = this.keyToHandler[k];
13644         if(h && this[h]){
13645             if(this.doRelay(e, this[h], h) !== true){
13646                 e[this.defaultEventAction]();
13647             }
13648         }
13649     },
13650
13651     // private
13652     doRelay : function(e, h, hname){
13653         return h.call(this.scope || this, e);
13654     },
13655
13656     // possible handlers
13657     enter : false,
13658     left : false,
13659     right : false,
13660     up : false,
13661     down : false,
13662     tab : false,
13663     esc : false,
13664     pageUp : false,
13665     pageDown : false,
13666     del : false,
13667     home : false,
13668     end : false,
13669
13670     // quick lookup hash
13671     keyToHandler : {
13672         37 : "left",
13673         39 : "right",
13674         38 : "up",
13675         40 : "down",
13676         33 : "pageUp",
13677         34 : "pageDown",
13678         46 : "del",
13679         36 : "home",
13680         35 : "end",
13681         13 : "enter",
13682         27 : "esc",
13683         9  : "tab"
13684     },
13685
13686         /**
13687          * Enable this KeyNav
13688          */
13689         enable: function(){
13690                 if(this.disabled){
13691             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13692             // the EventObject will normalize Safari automatically
13693             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13694                 this.el.on("keydown", this.relay,  this);
13695             }else{
13696                 this.el.on("keydown", this.prepareEvent,  this);
13697                 this.el.on("keypress", this.relay,  this);
13698             }
13699                     this.disabled = false;
13700                 }
13701         },
13702
13703         /**
13704          * Disable this KeyNav
13705          */
13706         disable: function(){
13707                 if(!this.disabled){
13708                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13709                 this.el.un("keydown", this.relay);
13710             }else{
13711                 this.el.un("keydown", this.prepareEvent);
13712                 this.el.un("keypress", this.relay);
13713             }
13714                     this.disabled = true;
13715                 }
13716         }
13717 };/*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727
13728  
13729 /**
13730  * @class Roo.KeyMap
13731  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13732  * The constructor accepts the same config object as defined by {@link #addBinding}.
13733  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13734  * combination it will call the function with this signature (if the match is a multi-key
13735  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13736  * A KeyMap can also handle a string representation of keys.<br />
13737  * Usage:
13738  <pre><code>
13739 // map one key by key code
13740 var map = new Roo.KeyMap("my-element", {
13741     key: 13, // or Roo.EventObject.ENTER
13742     fn: myHandler,
13743     scope: myObject
13744 });
13745
13746 // map multiple keys to one action by string
13747 var map = new Roo.KeyMap("my-element", {
13748     key: "a\r\n\t",
13749     fn: myHandler,
13750     scope: myObject
13751 });
13752
13753 // map multiple keys to multiple actions by strings and array of codes
13754 var map = new Roo.KeyMap("my-element", [
13755     {
13756         key: [10,13],
13757         fn: function(){ alert("Return was pressed"); }
13758     }, {
13759         key: "abc",
13760         fn: function(){ alert('a, b or c was pressed'); }
13761     }, {
13762         key: "\t",
13763         ctrl:true,
13764         shift:true,
13765         fn: function(){ alert('Control + shift + tab was pressed.'); }
13766     }
13767 ]);
13768 </code></pre>
13769  * <b>Note: A KeyMap starts enabled</b>
13770  * @constructor
13771  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13772  * @param {Object} config The config (see {@link #addBinding})
13773  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13774  */
13775 Roo.KeyMap = function(el, config, eventName){
13776     this.el  = Roo.get(el);
13777     this.eventName = eventName || "keydown";
13778     this.bindings = [];
13779     if(config){
13780         this.addBinding(config);
13781     }
13782     this.enable();
13783 };
13784
13785 Roo.KeyMap.prototype = {
13786     /**
13787      * True to stop the event from bubbling and prevent the default browser action if the
13788      * key was handled by the KeyMap (defaults to false)
13789      * @type Boolean
13790      */
13791     stopEvent : false,
13792
13793     /**
13794      * Add a new binding to this KeyMap. The following config object properties are supported:
13795      * <pre>
13796 Property    Type             Description
13797 ----------  ---------------  ----------------------------------------------------------------------
13798 key         String/Array     A single keycode or an array of keycodes to handle
13799 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13800 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13801 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13802 fn          Function         The function to call when KeyMap finds the expected key combination
13803 scope       Object           The scope of the callback function
13804 </pre>
13805      *
13806      * Usage:
13807      * <pre><code>
13808 // Create a KeyMap
13809 var map = new Roo.KeyMap(document, {
13810     key: Roo.EventObject.ENTER,
13811     fn: handleKey,
13812     scope: this
13813 });
13814
13815 //Add a new binding to the existing KeyMap later
13816 map.addBinding({
13817     key: 'abc',
13818     shift: true,
13819     fn: handleKey,
13820     scope: this
13821 });
13822 </code></pre>
13823      * @param {Object/Array} config A single KeyMap config or an array of configs
13824      */
13825         addBinding : function(config){
13826         if(config instanceof Array){
13827             for(var i = 0, len = config.length; i < len; i++){
13828                 this.addBinding(config[i]);
13829             }
13830             return;
13831         }
13832         var keyCode = config.key,
13833             shift = config.shift, 
13834             ctrl = config.ctrl, 
13835             alt = config.alt,
13836             fn = config.fn,
13837             scope = config.scope;
13838         if(typeof keyCode == "string"){
13839             var ks = [];
13840             var keyString = keyCode.toUpperCase();
13841             for(var j = 0, len = keyString.length; j < len; j++){
13842                 ks.push(keyString.charCodeAt(j));
13843             }
13844             keyCode = ks;
13845         }
13846         var keyArray = keyCode instanceof Array;
13847         var handler = function(e){
13848             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13849                 var k = e.getKey();
13850                 if(keyArray){
13851                     for(var i = 0, len = keyCode.length; i < len; i++){
13852                         if(keyCode[i] == k){
13853                           if(this.stopEvent){
13854                               e.stopEvent();
13855                           }
13856                           fn.call(scope || window, k, e);
13857                           return;
13858                         }
13859                     }
13860                 }else{
13861                     if(k == keyCode){
13862                         if(this.stopEvent){
13863                            e.stopEvent();
13864                         }
13865                         fn.call(scope || window, k, e);
13866                     }
13867                 }
13868             }
13869         };
13870         this.bindings.push(handler);  
13871         },
13872
13873     /**
13874      * Shorthand for adding a single key listener
13875      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13876      * following options:
13877      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13878      * @param {Function} fn The function to call
13879      * @param {Object} scope (optional) The scope of the function
13880      */
13881     on : function(key, fn, scope){
13882         var keyCode, shift, ctrl, alt;
13883         if(typeof key == "object" && !(key instanceof Array)){
13884             keyCode = key.key;
13885             shift = key.shift;
13886             ctrl = key.ctrl;
13887             alt = key.alt;
13888         }else{
13889             keyCode = key;
13890         }
13891         this.addBinding({
13892             key: keyCode,
13893             shift: shift,
13894             ctrl: ctrl,
13895             alt: alt,
13896             fn: fn,
13897             scope: scope
13898         })
13899     },
13900
13901     // private
13902     handleKeyDown : function(e){
13903             if(this.enabled){ //just in case
13904             var b = this.bindings;
13905             for(var i = 0, len = b.length; i < len; i++){
13906                 b[i].call(this, e);
13907             }
13908             }
13909         },
13910         
13911         /**
13912          * Returns true if this KeyMap is enabled
13913          * @return {Boolean} 
13914          */
13915         isEnabled : function(){
13916             return this.enabled;  
13917         },
13918         
13919         /**
13920          * Enables this KeyMap
13921          */
13922         enable: function(){
13923                 if(!this.enabled){
13924                     this.el.on(this.eventName, this.handleKeyDown, this);
13925                     this.enabled = true;
13926                 }
13927         },
13928
13929         /**
13930          * Disable this KeyMap
13931          */
13932         disable: function(){
13933                 if(this.enabled){
13934                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13935                     this.enabled = false;
13936                 }
13937         }
13938 };/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949  
13950 /**
13951  * @class Roo.util.TextMetrics
13952  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13953  * wide, in pixels, a given block of text will be.
13954  * @singleton
13955  */
13956 Roo.util.TextMetrics = function(){
13957     var shared;
13958     return {
13959         /**
13960          * Measures the size of the specified text
13961          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13962          * that can affect the size of the rendered text
13963          * @param {String} text The text to measure
13964          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13965          * in order to accurately measure the text height
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         measure : function(el, text, fixedWidth){
13969             if(!shared){
13970                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13971             }
13972             shared.bind(el);
13973             shared.setFixedWidth(fixedWidth || 'auto');
13974             return shared.getSize(text);
13975         },
13976
13977         /**
13978          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13979          * the overhead of multiple calls to initialize the style properties on each measurement.
13980          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13981          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13982          * in order to accurately measure the text height
13983          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13984          */
13985         createInstance : function(el, fixedWidth){
13986             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13987         }
13988     };
13989 }();
13990
13991  
13992
13993 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13994     var ml = new Roo.Element(document.createElement('div'));
13995     document.body.appendChild(ml.dom);
13996     ml.position('absolute');
13997     ml.setLeftTop(-1000, -1000);
13998     ml.hide();
13999
14000     if(fixedWidth){
14001         ml.setWidth(fixedWidth);
14002     }
14003      
14004     var instance = {
14005         /**
14006          * Returns the size of the specified text based on the internal element's style and width properties
14007          * @memberOf Roo.util.TextMetrics.Instance#
14008          * @param {String} text The text to measure
14009          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14010          */
14011         getSize : function(text){
14012             ml.update(text);
14013             var s = ml.getSize();
14014             ml.update('');
14015             return s;
14016         },
14017
14018         /**
14019          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14020          * that can affect the size of the rendered text
14021          * @memberOf Roo.util.TextMetrics.Instance#
14022          * @param {String/HTMLElement} el The element, dom node or id
14023          */
14024         bind : function(el){
14025             ml.setStyle(
14026                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14027             );
14028         },
14029
14030         /**
14031          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14032          * to set a fixed width in order to accurately measure the text height.
14033          * @memberOf Roo.util.TextMetrics.Instance#
14034          * @param {Number} width The width to set on the element
14035          */
14036         setFixedWidth : function(width){
14037             ml.setWidth(width);
14038         },
14039
14040         /**
14041          * Returns the measured width of the specified text
14042          * @memberOf Roo.util.TextMetrics.Instance#
14043          * @param {String} text The text to measure
14044          * @return {Number} width The width in pixels
14045          */
14046         getWidth : function(text){
14047             ml.dom.style.width = 'auto';
14048             return this.getSize(text).width;
14049         },
14050
14051         /**
14052          * Returns the measured height of the specified text.  For multiline text, be sure to call
14053          * {@link #setFixedWidth} if necessary.
14054          * @memberOf Roo.util.TextMetrics.Instance#
14055          * @param {String} text The text to measure
14056          * @return {Number} height The height in pixels
14057          */
14058         getHeight : function(text){
14059             return this.getSize(text).height;
14060         }
14061     };
14062
14063     instance.bind(bindTo);
14064
14065     return instance;
14066 };
14067
14068 // backwards compat
14069 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14070  * Based on:
14071  * Ext JS Library 1.1.1
14072  * Copyright(c) 2006-2007, Ext JS, LLC.
14073  *
14074  * Originally Released Under LGPL - original licence link has changed is not relivant.
14075  *
14076  * Fork - LGPL
14077  * <script type="text/javascript">
14078  */
14079
14080 /**
14081  * @class Roo.state.Provider
14082  * Abstract base class for state provider implementations. This class provides methods
14083  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14084  * Provider interface.
14085  */
14086 Roo.state.Provider = function(){
14087     /**
14088      * @event statechange
14089      * Fires when a state change occurs.
14090      * @param {Provider} this This state provider
14091      * @param {String} key The state key which was changed
14092      * @param {String} value The encoded value for the state
14093      */
14094     this.addEvents({
14095         "statechange": true
14096     });
14097     this.state = {};
14098     Roo.state.Provider.superclass.constructor.call(this);
14099 };
14100 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14101     /**
14102      * Returns the current value for a key
14103      * @param {String} name The key name
14104      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14105      * @return {Mixed} The state data
14106      */
14107     get : function(name, defaultValue){
14108         return typeof this.state[name] == "undefined" ?
14109             defaultValue : this.state[name];
14110     },
14111     
14112     /**
14113      * Clears a value from the state
14114      * @param {String} name The key name
14115      */
14116     clear : function(name){
14117         delete this.state[name];
14118         this.fireEvent("statechange", this, name, null);
14119     },
14120     
14121     /**
14122      * Sets the value for a key
14123      * @param {String} name The key name
14124      * @param {Mixed} value The value to set
14125      */
14126     set : function(name, value){
14127         this.state[name] = value;
14128         this.fireEvent("statechange", this, name, value);
14129     },
14130     
14131     /**
14132      * Decodes a string previously encoded with {@link #encodeValue}.
14133      * @param {String} value The value to decode
14134      * @return {Mixed} The decoded value
14135      */
14136     decodeValue : function(cookie){
14137         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14138         var matches = re.exec(unescape(cookie));
14139         if(!matches || !matches[1]) return; // non state cookie
14140         var type = matches[1];
14141         var v = matches[2];
14142         switch(type){
14143             case "n":
14144                 return parseFloat(v);
14145             case "d":
14146                 return new Date(Date.parse(v));
14147             case "b":
14148                 return (v == "1");
14149             case "a":
14150                 var all = [];
14151                 var values = v.split("^");
14152                 for(var i = 0, len = values.length; i < len; i++){
14153                     all.push(this.decodeValue(values[i]));
14154                 }
14155                 return all;
14156            case "o":
14157                 var all = {};
14158                 var values = v.split("^");
14159                 for(var i = 0, len = values.length; i < len; i++){
14160                     var kv = values[i].split("=");
14161                     all[kv[0]] = this.decodeValue(kv[1]);
14162                 }
14163                 return all;
14164            default:
14165                 return v;
14166         }
14167     },
14168     
14169     /**
14170      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14171      * @param {Mixed} value The value to encode
14172      * @return {String} The encoded value
14173      */
14174     encodeValue : function(v){
14175         var enc;
14176         if(typeof v == "number"){
14177             enc = "n:" + v;
14178         }else if(typeof v == "boolean"){
14179             enc = "b:" + (v ? "1" : "0");
14180         }else if(v instanceof Date){
14181             enc = "d:" + v.toGMTString();
14182         }else if(v instanceof Array){
14183             var flat = "";
14184             for(var i = 0, len = v.length; i < len; i++){
14185                 flat += this.encodeValue(v[i]);
14186                 if(i != len-1) flat += "^";
14187             }
14188             enc = "a:" + flat;
14189         }else if(typeof v == "object"){
14190             var flat = "";
14191             for(var key in v){
14192                 if(typeof v[key] != "function"){
14193                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14194                 }
14195             }
14196             enc = "o:" + flat.substring(0, flat.length-1);
14197         }else{
14198             enc = "s:" + v;
14199         }
14200         return escape(enc);        
14201     }
14202 });
14203
14204 /*
14205  * Based on:
14206  * Ext JS Library 1.1.1
14207  * Copyright(c) 2006-2007, Ext JS, LLC.
14208  *
14209  * Originally Released Under LGPL - original licence link has changed is not relivant.
14210  *
14211  * Fork - LGPL
14212  * <script type="text/javascript">
14213  */
14214 /**
14215  * @class Roo.state.Manager
14216  * This is the global state manager. By default all components that are "state aware" check this class
14217  * for state information if you don't pass them a custom state provider. In order for this class
14218  * to be useful, it must be initialized with a provider when your application initializes.
14219  <pre><code>
14220 // in your initialization function
14221 init : function(){
14222    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14223    ...
14224    // supposed you have a {@link Roo.BorderLayout}
14225    var layout = new Roo.BorderLayout(...);
14226    layout.restoreState();
14227    // or a {Roo.BasicDialog}
14228    var dialog = new Roo.BasicDialog(...);
14229    dialog.restoreState();
14230  </code></pre>
14231  * @singleton
14232  */
14233 Roo.state.Manager = function(){
14234     var provider = new Roo.state.Provider();
14235     
14236     return {
14237         /**
14238          * Configures the default state provider for your application
14239          * @param {Provider} stateProvider The state provider to set
14240          */
14241         setProvider : function(stateProvider){
14242             provider = stateProvider;
14243         },
14244         
14245         /**
14246          * Returns the current value for a key
14247          * @param {String} name The key name
14248          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14249          * @return {Mixed} The state data
14250          */
14251         get : function(key, defaultValue){
14252             return provider.get(key, defaultValue);
14253         },
14254         
14255         /**
14256          * Sets the value for a key
14257          * @param {String} name The key name
14258          * @param {Mixed} value The state data
14259          */
14260          set : function(key, value){
14261             provider.set(key, value);
14262         },
14263         
14264         /**
14265          * Clears a value from the state
14266          * @param {String} name The key name
14267          */
14268         clear : function(key){
14269             provider.clear(key);
14270         },
14271         
14272         /**
14273          * Gets the currently configured state provider
14274          * @return {Provider} The state provider
14275          */
14276         getProvider : function(){
14277             return provider;
14278         }
14279     };
14280 }();
14281 /*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291 /**
14292  * @class Roo.state.CookieProvider
14293  * @extends Roo.state.Provider
14294  * The default Provider implementation which saves state via cookies.
14295  * <br />Usage:
14296  <pre><code>
14297    var cp = new Roo.state.CookieProvider({
14298        path: "/cgi-bin/",
14299        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14300        domain: "roojs.com"
14301    })
14302    Roo.state.Manager.setProvider(cp);
14303  </code></pre>
14304  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14305  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14306  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14307  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14308  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14309  * domain the page is running on including the 'www' like 'www.roojs.com')
14310  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14311  * @constructor
14312  * Create a new CookieProvider
14313  * @param {Object} config The configuration object
14314  */
14315 Roo.state.CookieProvider = function(config){
14316     Roo.state.CookieProvider.superclass.constructor.call(this);
14317     this.path = "/";
14318     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14319     this.domain = null;
14320     this.secure = false;
14321     Roo.apply(this, config);
14322     this.state = this.readCookies();
14323 };
14324
14325 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14326     // private
14327     set : function(name, value){
14328         if(typeof value == "undefined" || value === null){
14329             this.clear(name);
14330             return;
14331         }
14332         this.setCookie(name, value);
14333         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14334     },
14335
14336     // private
14337     clear : function(name){
14338         this.clearCookie(name);
14339         Roo.state.CookieProvider.superclass.clear.call(this, name);
14340     },
14341
14342     // private
14343     readCookies : function(){
14344         var cookies = {};
14345         var c = document.cookie + ";";
14346         var re = /\s?(.*?)=(.*?);/g;
14347         var matches;
14348         while((matches = re.exec(c)) != null){
14349             var name = matches[1];
14350             var value = matches[2];
14351             if(name && name.substring(0,3) == "ys-"){
14352                 cookies[name.substr(3)] = this.decodeValue(value);
14353             }
14354         }
14355         return cookies;
14356     },
14357
14358     // private
14359     setCookie : function(name, value){
14360         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14361            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14362            ((this.path == null) ? "" : ("; path=" + this.path)) +
14363            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14364            ((this.secure == true) ? "; secure" : "");
14365     },
14366
14367     // private
14368     clearCookie : function(name){
14369         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14370            ((this.path == null) ? "" : ("; path=" + this.path)) +
14371            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14372            ((this.secure == true) ? "; secure" : "");
14373     }
14374 });/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384
14385
14386
14387 /*
14388  * These classes are derivatives of the similarly named classes in the YUI Library.
14389  * The original license:
14390  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14391  * Code licensed under the BSD License:
14392  * http://developer.yahoo.net/yui/license.txt
14393  */
14394
14395 (function() {
14396
14397 var Event=Roo.EventManager;
14398 var Dom=Roo.lib.Dom;
14399
14400 /**
14401  * @class Roo.dd.DragDrop
14402  * Defines the interface and base operation of items that that can be
14403  * dragged or can be drop targets.  It was designed to be extended, overriding
14404  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14405  * Up to three html elements can be associated with a DragDrop instance:
14406  * <ul>
14407  * <li>linked element: the element that is passed into the constructor.
14408  * This is the element which defines the boundaries for interaction with
14409  * other DragDrop objects.</li>
14410  * <li>handle element(s): The drag operation only occurs if the element that
14411  * was clicked matches a handle element.  By default this is the linked
14412  * element, but there are times that you will want only a portion of the
14413  * linked element to initiate the drag operation, and the setHandleElId()
14414  * method provides a way to define this.</li>
14415  * <li>drag element: this represents the element that would be moved along
14416  * with the cursor during a drag operation.  By default, this is the linked
14417  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14418  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14419  * </li>
14420  * </ul>
14421  * This class should not be instantiated until the onload event to ensure that
14422  * the associated elements are available.
14423  * The following would define a DragDrop obj that would interact with any
14424  * other DragDrop obj in the "group1" group:
14425  * <pre>
14426  *  dd = new Roo.dd.DragDrop("div1", "group1");
14427  * </pre>
14428  * Since none of the event handlers have been implemented, nothing would
14429  * actually happen if you were to run the code above.  Normally you would
14430  * override this class or one of the default implementations, but you can
14431  * also override the methods you want on an instance of the class...
14432  * <pre>
14433  *  dd.onDragDrop = function(e, id) {
14434  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14435  *  }
14436  * </pre>
14437  * @constructor
14438  * @param {String} id of the element that is linked to this instance
14439  * @param {String} sGroup the group of related DragDrop objects
14440  * @param {object} config an object containing configurable attributes
14441  *                Valid properties for DragDrop:
14442  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14443  */
14444 Roo.dd.DragDrop = function(id, sGroup, config) {
14445     if (id) {
14446         this.init(id, sGroup, config);
14447     }
14448 };
14449
14450 Roo.dd.DragDrop.prototype = {
14451
14452     /**
14453      * The id of the element associated with this object.  This is what we
14454      * refer to as the "linked element" because the size and position of
14455      * this element is used to determine when the drag and drop objects have
14456      * interacted.
14457      * @property id
14458      * @type String
14459      */
14460     id: null,
14461
14462     /**
14463      * Configuration attributes passed into the constructor
14464      * @property config
14465      * @type object
14466      */
14467     config: null,
14468
14469     /**
14470      * The id of the element that will be dragged.  By default this is same
14471      * as the linked element , but could be changed to another element. Ex:
14472      * Roo.dd.DDProxy
14473      * @property dragElId
14474      * @type String
14475      * @private
14476      */
14477     dragElId: null,
14478
14479     /**
14480      * the id of the element that initiates the drag operation.  By default
14481      * this is the linked element, but could be changed to be a child of this
14482      * element.  This lets us do things like only starting the drag when the
14483      * header element within the linked html element is clicked.
14484      * @property handleElId
14485      * @type String
14486      * @private
14487      */
14488     handleElId: null,
14489
14490     /**
14491      * An associative array of HTML tags that will be ignored if clicked.
14492      * @property invalidHandleTypes
14493      * @type {string: string}
14494      */
14495     invalidHandleTypes: null,
14496
14497     /**
14498      * An associative array of ids for elements that will be ignored if clicked
14499      * @property invalidHandleIds
14500      * @type {string: string}
14501      */
14502     invalidHandleIds: null,
14503
14504     /**
14505      * An indexted array of css class names for elements that will be ignored
14506      * if clicked.
14507      * @property invalidHandleClasses
14508      * @type string[]
14509      */
14510     invalidHandleClasses: null,
14511
14512     /**
14513      * The linked element's absolute X position at the time the drag was
14514      * started
14515      * @property startPageX
14516      * @type int
14517      * @private
14518      */
14519     startPageX: 0,
14520
14521     /**
14522      * The linked element's absolute X position at the time the drag was
14523      * started
14524      * @property startPageY
14525      * @type int
14526      * @private
14527      */
14528     startPageY: 0,
14529
14530     /**
14531      * The group defines a logical collection of DragDrop objects that are
14532      * related.  Instances only get events when interacting with other
14533      * DragDrop object in the same group.  This lets us define multiple
14534      * groups using a single DragDrop subclass if we want.
14535      * @property groups
14536      * @type {string: string}
14537      */
14538     groups: null,
14539
14540     /**
14541      * Individual drag/drop instances can be locked.  This will prevent
14542      * onmousedown start drag.
14543      * @property locked
14544      * @type boolean
14545      * @private
14546      */
14547     locked: false,
14548
14549     /**
14550      * Lock this instance
14551      * @method lock
14552      */
14553     lock: function() { this.locked = true; },
14554
14555     /**
14556      * Unlock this instace
14557      * @method unlock
14558      */
14559     unlock: function() { this.locked = false; },
14560
14561     /**
14562      * By default, all insances can be a drop target.  This can be disabled by
14563      * setting isTarget to false.
14564      * @method isTarget
14565      * @type boolean
14566      */
14567     isTarget: true,
14568
14569     /**
14570      * The padding configured for this drag and drop object for calculating
14571      * the drop zone intersection with this object.
14572      * @method padding
14573      * @type int[]
14574      */
14575     padding: null,
14576
14577     /**
14578      * Cached reference to the linked element
14579      * @property _domRef
14580      * @private
14581      */
14582     _domRef: null,
14583
14584     /**
14585      * Internal typeof flag
14586      * @property __ygDragDrop
14587      * @private
14588      */
14589     __ygDragDrop: true,
14590
14591     /**
14592      * Set to true when horizontal contraints are applied
14593      * @property constrainX
14594      * @type boolean
14595      * @private
14596      */
14597     constrainX: false,
14598
14599     /**
14600      * Set to true when vertical contraints are applied
14601      * @property constrainY
14602      * @type boolean
14603      * @private
14604      */
14605     constrainY: false,
14606
14607     /**
14608      * The left constraint
14609      * @property minX
14610      * @type int
14611      * @private
14612      */
14613     minX: 0,
14614
14615     /**
14616      * The right constraint
14617      * @property maxX
14618      * @type int
14619      * @private
14620      */
14621     maxX: 0,
14622
14623     /**
14624      * The up constraint
14625      * @property minY
14626      * @type int
14627      * @type int
14628      * @private
14629      */
14630     minY: 0,
14631
14632     /**
14633      * The down constraint
14634      * @property maxY
14635      * @type int
14636      * @private
14637      */
14638     maxY: 0,
14639
14640     /**
14641      * Maintain offsets when we resetconstraints.  Set to true when you want
14642      * the position of the element relative to its parent to stay the same
14643      * when the page changes
14644      *
14645      * @property maintainOffset
14646      * @type boolean
14647      */
14648     maintainOffset: false,
14649
14650     /**
14651      * Array of pixel locations the element will snap to if we specified a
14652      * horizontal graduation/interval.  This array is generated automatically
14653      * when you define a tick interval.
14654      * @property xTicks
14655      * @type int[]
14656      */
14657     xTicks: null,
14658
14659     /**
14660      * Array of pixel locations the element will snap to if we specified a
14661      * vertical graduation/interval.  This array is generated automatically
14662      * when you define a tick interval.
14663      * @property yTicks
14664      * @type int[]
14665      */
14666     yTicks: null,
14667
14668     /**
14669      * By default the drag and drop instance will only respond to the primary
14670      * button click (left button for a right-handed mouse).  Set to true to
14671      * allow drag and drop to start with any mouse click that is propogated
14672      * by the browser
14673      * @property primaryButtonOnly
14674      * @type boolean
14675      */
14676     primaryButtonOnly: true,
14677
14678     /**
14679      * The availabe property is false until the linked dom element is accessible.
14680      * @property available
14681      * @type boolean
14682      */
14683     available: false,
14684
14685     /**
14686      * By default, drags can only be initiated if the mousedown occurs in the
14687      * region the linked element is.  This is done in part to work around a
14688      * bug in some browsers that mis-report the mousedown if the previous
14689      * mouseup happened outside of the window.  This property is set to true
14690      * if outer handles are defined.
14691      *
14692      * @property hasOuterHandles
14693      * @type boolean
14694      * @default false
14695      */
14696     hasOuterHandles: false,
14697
14698     /**
14699      * Code that executes immediately before the startDrag event
14700      * @method b4StartDrag
14701      * @private
14702      */
14703     b4StartDrag: function(x, y) { },
14704
14705     /**
14706      * Abstract method called after a drag/drop object is clicked
14707      * and the drag or mousedown time thresholds have beeen met.
14708      * @method startDrag
14709      * @param {int} X click location
14710      * @param {int} Y click location
14711      */
14712     startDrag: function(x, y) { /* override this */ },
14713
14714     /**
14715      * Code that executes immediately before the onDrag event
14716      * @method b4Drag
14717      * @private
14718      */
14719     b4Drag: function(e) { },
14720
14721     /**
14722      * Abstract method called during the onMouseMove event while dragging an
14723      * object.
14724      * @method onDrag
14725      * @param {Event} e the mousemove event
14726      */
14727     onDrag: function(e) { /* override this */ },
14728
14729     /**
14730      * Abstract method called when this element fist begins hovering over
14731      * another DragDrop obj
14732      * @method onDragEnter
14733      * @param {Event} e the mousemove event
14734      * @param {String|DragDrop[]} id In POINT mode, the element
14735      * id this is hovering over.  In INTERSECT mode, an array of one or more
14736      * dragdrop items being hovered over.
14737      */
14738     onDragEnter: function(e, id) { /* override this */ },
14739
14740     /**
14741      * Code that executes immediately before the onDragOver event
14742      * @method b4DragOver
14743      * @private
14744      */
14745     b4DragOver: function(e) { },
14746
14747     /**
14748      * Abstract method called when this element is hovering over another
14749      * DragDrop obj
14750      * @method onDragOver
14751      * @param {Event} e the mousemove event
14752      * @param {String|DragDrop[]} id In POINT mode, the element
14753      * id this is hovering over.  In INTERSECT mode, an array of dd items
14754      * being hovered over.
14755      */
14756     onDragOver: function(e, id) { /* override this */ },
14757
14758     /**
14759      * Code that executes immediately before the onDragOut event
14760      * @method b4DragOut
14761      * @private
14762      */
14763     b4DragOut: function(e) { },
14764
14765     /**
14766      * Abstract method called when we are no longer hovering over an element
14767      * @method onDragOut
14768      * @param {Event} e the mousemove event
14769      * @param {String|DragDrop[]} id In POINT mode, the element
14770      * id this was hovering over.  In INTERSECT mode, an array of dd items
14771      * that the mouse is no longer over.
14772      */
14773     onDragOut: function(e, id) { /* override this */ },
14774
14775     /**
14776      * Code that executes immediately before the onDragDrop event
14777      * @method b4DragDrop
14778      * @private
14779      */
14780     b4DragDrop: function(e) { },
14781
14782     /**
14783      * Abstract method called when this item is dropped on another DragDrop
14784      * obj
14785      * @method onDragDrop
14786      * @param {Event} e the mouseup event
14787      * @param {String|DragDrop[]} id In POINT mode, the element
14788      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14789      * was dropped on.
14790      */
14791     onDragDrop: function(e, id) { /* override this */ },
14792
14793     /**
14794      * Abstract method called when this item is dropped on an area with no
14795      * drop target
14796      * @method onInvalidDrop
14797      * @param {Event} e the mouseup event
14798      */
14799     onInvalidDrop: function(e) { /* override this */ },
14800
14801     /**
14802      * Code that executes immediately before the endDrag event
14803      * @method b4EndDrag
14804      * @private
14805      */
14806     b4EndDrag: function(e) { },
14807
14808     /**
14809      * Fired when we are done dragging the object
14810      * @method endDrag
14811      * @param {Event} e the mouseup event
14812      */
14813     endDrag: function(e) { /* override this */ },
14814
14815     /**
14816      * Code executed immediately before the onMouseDown event
14817      * @method b4MouseDown
14818      * @param {Event} e the mousedown event
14819      * @private
14820      */
14821     b4MouseDown: function(e) {  },
14822
14823     /**
14824      * Event handler that fires when a drag/drop obj gets a mousedown
14825      * @method onMouseDown
14826      * @param {Event} e the mousedown event
14827      */
14828     onMouseDown: function(e) { /* override this */ },
14829
14830     /**
14831      * Event handler that fires when a drag/drop obj gets a mouseup
14832      * @method onMouseUp
14833      * @param {Event} e the mouseup event
14834      */
14835     onMouseUp: function(e) { /* override this */ },
14836
14837     /**
14838      * Override the onAvailable method to do what is needed after the initial
14839      * position was determined.
14840      * @method onAvailable
14841      */
14842     onAvailable: function () {
14843     },
14844
14845     /*
14846      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14847      * @type Object
14848      */
14849     defaultPadding : {left:0, right:0, top:0, bottom:0},
14850
14851     /*
14852      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14853  *
14854  * Usage:
14855  <pre><code>
14856  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14857                 { dragElId: "existingProxyDiv" });
14858  dd.startDrag = function(){
14859      this.constrainTo("parent-id");
14860  };
14861  </code></pre>
14862  * Or you can initalize it using the {@link Roo.Element} object:
14863  <pre><code>
14864  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14865      startDrag : function(){
14866          this.constrainTo("parent-id");
14867      }
14868  });
14869  </code></pre>
14870      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14871      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14872      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14873      * an object containing the sides to pad. For example: {right:10, bottom:10}
14874      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14875      */
14876     constrainTo : function(constrainTo, pad, inContent){
14877         if(typeof pad == "number"){
14878             pad = {left: pad, right:pad, top:pad, bottom:pad};
14879         }
14880         pad = pad || this.defaultPadding;
14881         var b = Roo.get(this.getEl()).getBox();
14882         var ce = Roo.get(constrainTo);
14883         var s = ce.getScroll();
14884         var c, cd = ce.dom;
14885         if(cd == document.body){
14886             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14887         }else{
14888             xy = ce.getXY();
14889             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14890         }
14891
14892
14893         var topSpace = b.y - c.y;
14894         var leftSpace = b.x - c.x;
14895
14896         this.resetConstraints();
14897         this.setXConstraint(leftSpace - (pad.left||0), // left
14898                 c.width - leftSpace - b.width - (pad.right||0) //right
14899         );
14900         this.setYConstraint(topSpace - (pad.top||0), //top
14901                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14902         );
14903     },
14904
14905     /**
14906      * Returns a reference to the linked element
14907      * @method getEl
14908      * @return {HTMLElement} the html element
14909      */
14910     getEl: function() {
14911         if (!this._domRef) {
14912             this._domRef = Roo.getDom(this.id);
14913         }
14914
14915         return this._domRef;
14916     },
14917
14918     /**
14919      * Returns a reference to the actual element to drag.  By default this is
14920      * the same as the html element, but it can be assigned to another
14921      * element. An example of this can be found in Roo.dd.DDProxy
14922      * @method getDragEl
14923      * @return {HTMLElement} the html element
14924      */
14925     getDragEl: function() {
14926         return Roo.getDom(this.dragElId);
14927     },
14928
14929     /**
14930      * Sets up the DragDrop object.  Must be called in the constructor of any
14931      * Roo.dd.DragDrop subclass
14932      * @method init
14933      * @param id the id of the linked element
14934      * @param {String} sGroup the group of related items
14935      * @param {object} config configuration attributes
14936      */
14937     init: function(id, sGroup, config) {
14938         this.initTarget(id, sGroup, config);
14939         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14940         // Event.on(this.id, "selectstart", Event.preventDefault);
14941     },
14942
14943     /**
14944      * Initializes Targeting functionality only... the object does not
14945      * get a mousedown handler.
14946      * @method initTarget
14947      * @param id the id of the linked element
14948      * @param {String} sGroup the group of related items
14949      * @param {object} config configuration attributes
14950      */
14951     initTarget: function(id, sGroup, config) {
14952
14953         // configuration attributes
14954         this.config = config || {};
14955
14956         // create a local reference to the drag and drop manager
14957         this.DDM = Roo.dd.DDM;
14958         // initialize the groups array
14959         this.groups = {};
14960
14961         // assume that we have an element reference instead of an id if the
14962         // parameter is not a string
14963         if (typeof id !== "string") {
14964             id = Roo.id(id);
14965         }
14966
14967         // set the id
14968         this.id = id;
14969
14970         // add to an interaction group
14971         this.addToGroup((sGroup) ? sGroup : "default");
14972
14973         // We don't want to register this as the handle with the manager
14974         // so we just set the id rather than calling the setter.
14975         this.handleElId = id;
14976
14977         // the linked element is the element that gets dragged by default
14978         this.setDragElId(id);
14979
14980         // by default, clicked anchors will not start drag operations.
14981         this.invalidHandleTypes = { A: "A" };
14982         this.invalidHandleIds = {};
14983         this.invalidHandleClasses = [];
14984
14985         this.applyConfig();
14986
14987         this.handleOnAvailable();
14988     },
14989
14990     /**
14991      * Applies the configuration parameters that were passed into the constructor.
14992      * This is supposed to happen at each level through the inheritance chain.  So
14993      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14994      * DragDrop in order to get all of the parameters that are available in
14995      * each object.
14996      * @method applyConfig
14997      */
14998     applyConfig: function() {
14999
15000         // configurable properties:
15001         //    padding, isTarget, maintainOffset, primaryButtonOnly
15002         this.padding           = this.config.padding || [0, 0, 0, 0];
15003         this.isTarget          = (this.config.isTarget !== false);
15004         this.maintainOffset    = (this.config.maintainOffset);
15005         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15006
15007     },
15008
15009     /**
15010      * Executed when the linked element is available
15011      * @method handleOnAvailable
15012      * @private
15013      */
15014     handleOnAvailable: function() {
15015         this.available = true;
15016         this.resetConstraints();
15017         this.onAvailable();
15018     },
15019
15020      /**
15021      * Configures the padding for the target zone in px.  Effectively expands
15022      * (or reduces) the virtual object size for targeting calculations.
15023      * Supports css-style shorthand; if only one parameter is passed, all sides
15024      * will have that padding, and if only two are passed, the top and bottom
15025      * will have the first param, the left and right the second.
15026      * @method setPadding
15027      * @param {int} iTop    Top pad
15028      * @param {int} iRight  Right pad
15029      * @param {int} iBot    Bot pad
15030      * @param {int} iLeft   Left pad
15031      */
15032     setPadding: function(iTop, iRight, iBot, iLeft) {
15033         // this.padding = [iLeft, iRight, iTop, iBot];
15034         if (!iRight && 0 !== iRight) {
15035             this.padding = [iTop, iTop, iTop, iTop];
15036         } else if (!iBot && 0 !== iBot) {
15037             this.padding = [iTop, iRight, iTop, iRight];
15038         } else {
15039             this.padding = [iTop, iRight, iBot, iLeft];
15040         }
15041     },
15042
15043     /**
15044      * Stores the initial placement of the linked element.
15045      * @method setInitialPosition
15046      * @param {int} diffX   the X offset, default 0
15047      * @param {int} diffY   the Y offset, default 0
15048      */
15049     setInitPosition: function(diffX, diffY) {
15050         var el = this.getEl();
15051
15052         if (!this.DDM.verifyEl(el)) {
15053             return;
15054         }
15055
15056         var dx = diffX || 0;
15057         var dy = diffY || 0;
15058
15059         var p = Dom.getXY( el );
15060
15061         this.initPageX = p[0] - dx;
15062         this.initPageY = p[1] - dy;
15063
15064         this.lastPageX = p[0];
15065         this.lastPageY = p[1];
15066
15067
15068         this.setStartPosition(p);
15069     },
15070
15071     /**
15072      * Sets the start position of the element.  This is set when the obj
15073      * is initialized, the reset when a drag is started.
15074      * @method setStartPosition
15075      * @param pos current position (from previous lookup)
15076      * @private
15077      */
15078     setStartPosition: function(pos) {
15079         var p = pos || Dom.getXY( this.getEl() );
15080         this.deltaSetXY = null;
15081
15082         this.startPageX = p[0];
15083         this.startPageY = p[1];
15084     },
15085
15086     /**
15087      * Add this instance to a group of related drag/drop objects.  All
15088      * instances belong to at least one group, and can belong to as many
15089      * groups as needed.
15090      * @method addToGroup
15091      * @param sGroup {string} the name of the group
15092      */
15093     addToGroup: function(sGroup) {
15094         this.groups[sGroup] = true;
15095         this.DDM.regDragDrop(this, sGroup);
15096     },
15097
15098     /**
15099      * Remove's this instance from the supplied interaction group
15100      * @method removeFromGroup
15101      * @param {string}  sGroup  The group to drop
15102      */
15103     removeFromGroup: function(sGroup) {
15104         if (this.groups[sGroup]) {
15105             delete this.groups[sGroup];
15106         }
15107
15108         this.DDM.removeDDFromGroup(this, sGroup);
15109     },
15110
15111     /**
15112      * Allows you to specify that an element other than the linked element
15113      * will be moved with the cursor during a drag
15114      * @method setDragElId
15115      * @param id {string} the id of the element that will be used to initiate the drag
15116      */
15117     setDragElId: function(id) {
15118         this.dragElId = id;
15119     },
15120
15121     /**
15122      * Allows you to specify a child of the linked element that should be
15123      * used to initiate the drag operation.  An example of this would be if
15124      * you have a content div with text and links.  Clicking anywhere in the
15125      * content area would normally start the drag operation.  Use this method
15126      * to specify that an element inside of the content div is the element
15127      * that starts the drag operation.
15128      * @method setHandleElId
15129      * @param id {string} the id of the element that will be used to
15130      * initiate the drag.
15131      */
15132     setHandleElId: function(id) {
15133         if (typeof id !== "string") {
15134             id = Roo.id(id);
15135         }
15136         this.handleElId = id;
15137         this.DDM.regHandle(this.id, id);
15138     },
15139
15140     /**
15141      * Allows you to set an element outside of the linked element as a drag
15142      * handle
15143      * @method setOuterHandleElId
15144      * @param id the id of the element that will be used to initiate the drag
15145      */
15146     setOuterHandleElId: function(id) {
15147         if (typeof id !== "string") {
15148             id = Roo.id(id);
15149         }
15150         Event.on(id, "mousedown",
15151                 this.handleMouseDown, this);
15152         this.setHandleElId(id);
15153
15154         this.hasOuterHandles = true;
15155     },
15156
15157     /**
15158      * Remove all drag and drop hooks for this element
15159      * @method unreg
15160      */
15161     unreg: function() {
15162         Event.un(this.id, "mousedown",
15163                 this.handleMouseDown);
15164         this._domRef = null;
15165         this.DDM._remove(this);
15166     },
15167
15168     destroy : function(){
15169         this.unreg();
15170     },
15171
15172     /**
15173      * Returns true if this instance is locked, or the drag drop mgr is locked
15174      * (meaning that all drag/drop is disabled on the page.)
15175      * @method isLocked
15176      * @return {boolean} true if this obj or all drag/drop is locked, else
15177      * false
15178      */
15179     isLocked: function() {
15180         return (this.DDM.isLocked() || this.locked);
15181     },
15182
15183     /**
15184      * Fired when this object is clicked
15185      * @method handleMouseDown
15186      * @param {Event} e
15187      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15188      * @private
15189      */
15190     handleMouseDown: function(e, oDD){
15191         if (this.primaryButtonOnly && e.button != 0) {
15192             return;
15193         }
15194
15195         if (this.isLocked()) {
15196             return;
15197         }
15198
15199         this.DDM.refreshCache(this.groups);
15200
15201         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15202         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15203         } else {
15204             if (this.clickValidator(e)) {
15205
15206                 // set the initial element position
15207                 this.setStartPosition();
15208
15209
15210                 this.b4MouseDown(e);
15211                 this.onMouseDown(e);
15212
15213                 this.DDM.handleMouseDown(e, this);
15214
15215                 this.DDM.stopEvent(e);
15216             } else {
15217
15218
15219             }
15220         }
15221     },
15222
15223     clickValidator: function(e) {
15224         var target = e.getTarget();
15225         return ( this.isValidHandleChild(target) &&
15226                     (this.id == this.handleElId ||
15227                         this.DDM.handleWasClicked(target, this.id)) );
15228     },
15229
15230     /**
15231      * Allows you to specify a tag name that should not start a drag operation
15232      * when clicked.  This is designed to facilitate embedding links within a
15233      * drag handle that do something other than start the drag.
15234      * @method addInvalidHandleType
15235      * @param {string} tagName the type of element to exclude
15236      */
15237     addInvalidHandleType: function(tagName) {
15238         var type = tagName.toUpperCase();
15239         this.invalidHandleTypes[type] = type;
15240     },
15241
15242     /**
15243      * Lets you to specify an element id for a child of a drag handle
15244      * that should not initiate a drag
15245      * @method addInvalidHandleId
15246      * @param {string} id the element id of the element you wish to ignore
15247      */
15248     addInvalidHandleId: function(id) {
15249         if (typeof id !== "string") {
15250             id = Roo.id(id);
15251         }
15252         this.invalidHandleIds[id] = id;
15253     },
15254
15255     /**
15256      * Lets you specify a css class of elements that will not initiate a drag
15257      * @method addInvalidHandleClass
15258      * @param {string} cssClass the class of the elements you wish to ignore
15259      */
15260     addInvalidHandleClass: function(cssClass) {
15261         this.invalidHandleClasses.push(cssClass);
15262     },
15263
15264     /**
15265      * Unsets an excluded tag name set by addInvalidHandleType
15266      * @method removeInvalidHandleType
15267      * @param {string} tagName the type of element to unexclude
15268      */
15269     removeInvalidHandleType: function(tagName) {
15270         var type = tagName.toUpperCase();
15271         // this.invalidHandleTypes[type] = null;
15272         delete this.invalidHandleTypes[type];
15273     },
15274
15275     /**
15276      * Unsets an invalid handle id
15277      * @method removeInvalidHandleId
15278      * @param {string} id the id of the element to re-enable
15279      */
15280     removeInvalidHandleId: function(id) {
15281         if (typeof id !== "string") {
15282             id = Roo.id(id);
15283         }
15284         delete this.invalidHandleIds[id];
15285     },
15286
15287     /**
15288      * Unsets an invalid css class
15289      * @method removeInvalidHandleClass
15290      * @param {string} cssClass the class of the element(s) you wish to
15291      * re-enable
15292      */
15293     removeInvalidHandleClass: function(cssClass) {
15294         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15295             if (this.invalidHandleClasses[i] == cssClass) {
15296                 delete this.invalidHandleClasses[i];
15297             }
15298         }
15299     },
15300
15301     /**
15302      * Checks the tag exclusion list to see if this click should be ignored
15303      * @method isValidHandleChild
15304      * @param {HTMLElement} node the HTMLElement to evaluate
15305      * @return {boolean} true if this is a valid tag type, false if not
15306      */
15307     isValidHandleChild: function(node) {
15308
15309         var valid = true;
15310         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15311         var nodeName;
15312         try {
15313             nodeName = node.nodeName.toUpperCase();
15314         } catch(e) {
15315             nodeName = node.nodeName;
15316         }
15317         valid = valid && !this.invalidHandleTypes[nodeName];
15318         valid = valid && !this.invalidHandleIds[node.id];
15319
15320         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15321             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15322         }
15323
15324
15325         return valid;
15326
15327     },
15328
15329     /**
15330      * Create the array of horizontal tick marks if an interval was specified
15331      * in setXConstraint().
15332      * @method setXTicks
15333      * @private
15334      */
15335     setXTicks: function(iStartX, iTickSize) {
15336         this.xTicks = [];
15337         this.xTickSize = iTickSize;
15338
15339         var tickMap = {};
15340
15341         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15342             if (!tickMap[i]) {
15343                 this.xTicks[this.xTicks.length] = i;
15344                 tickMap[i] = true;
15345             }
15346         }
15347
15348         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15349             if (!tickMap[i]) {
15350                 this.xTicks[this.xTicks.length] = i;
15351                 tickMap[i] = true;
15352             }
15353         }
15354
15355         this.xTicks.sort(this.DDM.numericSort) ;
15356     },
15357
15358     /**
15359      * Create the array of vertical tick marks if an interval was specified in
15360      * setYConstraint().
15361      * @method setYTicks
15362      * @private
15363      */
15364     setYTicks: function(iStartY, iTickSize) {
15365         this.yTicks = [];
15366         this.yTickSize = iTickSize;
15367
15368         var tickMap = {};
15369
15370         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15371             if (!tickMap[i]) {
15372                 this.yTicks[this.yTicks.length] = i;
15373                 tickMap[i] = true;
15374             }
15375         }
15376
15377         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15378             if (!tickMap[i]) {
15379                 this.yTicks[this.yTicks.length] = i;
15380                 tickMap[i] = true;
15381             }
15382         }
15383
15384         this.yTicks.sort(this.DDM.numericSort) ;
15385     },
15386
15387     /**
15388      * By default, the element can be dragged any place on the screen.  Use
15389      * this method to limit the horizontal travel of the element.  Pass in
15390      * 0,0 for the parameters if you want to lock the drag to the y axis.
15391      * @method setXConstraint
15392      * @param {int} iLeft the number of pixels the element can move to the left
15393      * @param {int} iRight the number of pixels the element can move to the
15394      * right
15395      * @param {int} iTickSize optional parameter for specifying that the
15396      * element
15397      * should move iTickSize pixels at a time.
15398      */
15399     setXConstraint: function(iLeft, iRight, iTickSize) {
15400         this.leftConstraint = iLeft;
15401         this.rightConstraint = iRight;
15402
15403         this.minX = this.initPageX - iLeft;
15404         this.maxX = this.initPageX + iRight;
15405         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15406
15407         this.constrainX = true;
15408     },
15409
15410     /**
15411      * Clears any constraints applied to this instance.  Also clears ticks
15412      * since they can't exist independent of a constraint at this time.
15413      * @method clearConstraints
15414      */
15415     clearConstraints: function() {
15416         this.constrainX = false;
15417         this.constrainY = false;
15418         this.clearTicks();
15419     },
15420
15421     /**
15422      * Clears any tick interval defined for this instance
15423      * @method clearTicks
15424      */
15425     clearTicks: function() {
15426         this.xTicks = null;
15427         this.yTicks = null;
15428         this.xTickSize = 0;
15429         this.yTickSize = 0;
15430     },
15431
15432     /**
15433      * By default, the element can be dragged any place on the screen.  Set
15434      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15435      * parameters if you want to lock the drag to the x axis.
15436      * @method setYConstraint
15437      * @param {int} iUp the number of pixels the element can move up
15438      * @param {int} iDown the number of pixels the element can move down
15439      * @param {int} iTickSize optional parameter for specifying that the
15440      * element should move iTickSize pixels at a time.
15441      */
15442     setYConstraint: function(iUp, iDown, iTickSize) {
15443         this.topConstraint = iUp;
15444         this.bottomConstraint = iDown;
15445
15446         this.minY = this.initPageY - iUp;
15447         this.maxY = this.initPageY + iDown;
15448         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15449
15450         this.constrainY = true;
15451
15452     },
15453
15454     /**
15455      * resetConstraints must be called if you manually reposition a dd element.
15456      * @method resetConstraints
15457      * @param {boolean} maintainOffset
15458      */
15459     resetConstraints: function() {
15460
15461
15462         // Maintain offsets if necessary
15463         if (this.initPageX || this.initPageX === 0) {
15464             // figure out how much this thing has moved
15465             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15466             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15467
15468             this.setInitPosition(dx, dy);
15469
15470         // This is the first time we have detected the element's position
15471         } else {
15472             this.setInitPosition();
15473         }
15474
15475         if (this.constrainX) {
15476             this.setXConstraint( this.leftConstraint,
15477                                  this.rightConstraint,
15478                                  this.xTickSize        );
15479         }
15480
15481         if (this.constrainY) {
15482             this.setYConstraint( this.topConstraint,
15483                                  this.bottomConstraint,
15484                                  this.yTickSize         );
15485         }
15486     },
15487
15488     /**
15489      * Normally the drag element is moved pixel by pixel, but we can specify
15490      * that it move a number of pixels at a time.  This method resolves the
15491      * location when we have it set up like this.
15492      * @method getTick
15493      * @param {int} val where we want to place the object
15494      * @param {int[]} tickArray sorted array of valid points
15495      * @return {int} the closest tick
15496      * @private
15497      */
15498     getTick: function(val, tickArray) {
15499
15500         if (!tickArray) {
15501             // If tick interval is not defined, it is effectively 1 pixel,
15502             // so we return the value passed to us.
15503             return val;
15504         } else if (tickArray[0] >= val) {
15505             // The value is lower than the first tick, so we return the first
15506             // tick.
15507             return tickArray[0];
15508         } else {
15509             for (var i=0, len=tickArray.length; i<len; ++i) {
15510                 var next = i + 1;
15511                 if (tickArray[next] && tickArray[next] >= val) {
15512                     var diff1 = val - tickArray[i];
15513                     var diff2 = tickArray[next] - val;
15514                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15515                 }
15516             }
15517
15518             // The value is larger than the last tick, so we return the last
15519             // tick.
15520             return tickArray[tickArray.length - 1];
15521         }
15522     },
15523
15524     /**
15525      * toString method
15526      * @method toString
15527      * @return {string} string representation of the dd obj
15528      */
15529     toString: function() {
15530         return ("DragDrop " + this.id);
15531     }
15532
15533 };
15534
15535 })();
15536 /*
15537  * Based on:
15538  * Ext JS Library 1.1.1
15539  * Copyright(c) 2006-2007, Ext JS, LLC.
15540  *
15541  * Originally Released Under LGPL - original licence link has changed is not relivant.
15542  *
15543  * Fork - LGPL
15544  * <script type="text/javascript">
15545  */
15546
15547
15548 /**
15549  * The drag and drop utility provides a framework for building drag and drop
15550  * applications.  In addition to enabling drag and drop for specific elements,
15551  * the drag and drop elements are tracked by the manager class, and the
15552  * interactions between the various elements are tracked during the drag and
15553  * the implementing code is notified about these important moments.
15554  */
15555
15556 // Only load the library once.  Rewriting the manager class would orphan
15557 // existing drag and drop instances.
15558 if (!Roo.dd.DragDropMgr) {
15559
15560 /**
15561  * @class Roo.dd.DragDropMgr
15562  * DragDropMgr is a singleton that tracks the element interaction for
15563  * all DragDrop items in the window.  Generally, you will not call
15564  * this class directly, but it does have helper methods that could
15565  * be useful in your DragDrop implementations.
15566  * @singleton
15567  */
15568 Roo.dd.DragDropMgr = function() {
15569
15570     var Event = Roo.EventManager;
15571
15572     return {
15573
15574         /**
15575          * Two dimensional Array of registered DragDrop objects.  The first
15576          * dimension is the DragDrop item group, the second the DragDrop
15577          * object.
15578          * @property ids
15579          * @type {string: string}
15580          * @private
15581          * @static
15582          */
15583         ids: {},
15584
15585         /**
15586          * Array of element ids defined as drag handles.  Used to determine
15587          * if the element that generated the mousedown event is actually the
15588          * handle and not the html element itself.
15589          * @property handleIds
15590          * @type {string: string}
15591          * @private
15592          * @static
15593          */
15594         handleIds: {},
15595
15596         /**
15597          * the DragDrop object that is currently being dragged
15598          * @property dragCurrent
15599          * @type DragDrop
15600          * @private
15601          * @static
15602          **/
15603         dragCurrent: null,
15604
15605         /**
15606          * the DragDrop object(s) that are being hovered over
15607          * @property dragOvers
15608          * @type Array
15609          * @private
15610          * @static
15611          */
15612         dragOvers: {},
15613
15614         /**
15615          * the X distance between the cursor and the object being dragged
15616          * @property deltaX
15617          * @type int
15618          * @private
15619          * @static
15620          */
15621         deltaX: 0,
15622
15623         /**
15624          * the Y distance between the cursor and the object being dragged
15625          * @property deltaY
15626          * @type int
15627          * @private
15628          * @static
15629          */
15630         deltaY: 0,
15631
15632         /**
15633          * Flag to determine if we should prevent the default behavior of the
15634          * events we define. By default this is true, but this can be set to
15635          * false if you need the default behavior (not recommended)
15636          * @property preventDefault
15637          * @type boolean
15638          * @static
15639          */
15640         preventDefault: true,
15641
15642         /**
15643          * Flag to determine if we should stop the propagation of the events
15644          * we generate. This is true by default but you may want to set it to
15645          * false if the html element contains other features that require the
15646          * mouse click.
15647          * @property stopPropagation
15648          * @type boolean
15649          * @static
15650          */
15651         stopPropagation: true,
15652
15653         /**
15654          * Internal flag that is set to true when drag and drop has been
15655          * intialized
15656          * @property initialized
15657          * @private
15658          * @static
15659          */
15660         initalized: false,
15661
15662         /**
15663          * All drag and drop can be disabled.
15664          * @property locked
15665          * @private
15666          * @static
15667          */
15668         locked: false,
15669
15670         /**
15671          * Called the first time an element is registered.
15672          * @method init
15673          * @private
15674          * @static
15675          */
15676         init: function() {
15677             this.initialized = true;
15678         },
15679
15680         /**
15681          * In point mode, drag and drop interaction is defined by the
15682          * location of the cursor during the drag/drop
15683          * @property POINT
15684          * @type int
15685          * @static
15686          */
15687         POINT: 0,
15688
15689         /**
15690          * In intersect mode, drag and drop interactio nis defined by the
15691          * overlap of two or more drag and drop objects.
15692          * @property INTERSECT
15693          * @type int
15694          * @static
15695          */
15696         INTERSECT: 1,
15697
15698         /**
15699          * The current drag and drop mode.  Default: POINT
15700          * @property mode
15701          * @type int
15702          * @static
15703          */
15704         mode: 0,
15705
15706         /**
15707          * Runs method on all drag and drop objects
15708          * @method _execOnAll
15709          * @private
15710          * @static
15711          */
15712         _execOnAll: function(sMethod, args) {
15713             for (var i in this.ids) {
15714                 for (var j in this.ids[i]) {
15715                     var oDD = this.ids[i][j];
15716                     if (! this.isTypeOfDD(oDD)) {
15717                         continue;
15718                     }
15719                     oDD[sMethod].apply(oDD, args);
15720                 }
15721             }
15722         },
15723
15724         /**
15725          * Drag and drop initialization.  Sets up the global event handlers
15726          * @method _onLoad
15727          * @private
15728          * @static
15729          */
15730         _onLoad: function() {
15731
15732             this.init();
15733
15734
15735             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15736             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15737             Event.on(window,   "unload",    this._onUnload, this, true);
15738             Event.on(window,   "resize",    this._onResize, this, true);
15739             // Event.on(window,   "mouseout",    this._test);
15740
15741         },
15742
15743         /**
15744          * Reset constraints on all drag and drop objs
15745          * @method _onResize
15746          * @private
15747          * @static
15748          */
15749         _onResize: function(e) {
15750             this._execOnAll("resetConstraints", []);
15751         },
15752
15753         /**
15754          * Lock all drag and drop functionality
15755          * @method lock
15756          * @static
15757          */
15758         lock: function() { this.locked = true; },
15759
15760         /**
15761          * Unlock all drag and drop functionality
15762          * @method unlock
15763          * @static
15764          */
15765         unlock: function() { this.locked = false; },
15766
15767         /**
15768          * Is drag and drop locked?
15769          * @method isLocked
15770          * @return {boolean} True if drag and drop is locked, false otherwise.
15771          * @static
15772          */
15773         isLocked: function() { return this.locked; },
15774
15775         /**
15776          * Location cache that is set for all drag drop objects when a drag is
15777          * initiated, cleared when the drag is finished.
15778          * @property locationCache
15779          * @private
15780          * @static
15781          */
15782         locationCache: {},
15783
15784         /**
15785          * Set useCache to false if you want to force object the lookup of each
15786          * drag and drop linked element constantly during a drag.
15787          * @property useCache
15788          * @type boolean
15789          * @static
15790          */
15791         useCache: true,
15792
15793         /**
15794          * The number of pixels that the mouse needs to move after the
15795          * mousedown before the drag is initiated.  Default=3;
15796          * @property clickPixelThresh
15797          * @type int
15798          * @static
15799          */
15800         clickPixelThresh: 3,
15801
15802         /**
15803          * The number of milliseconds after the mousedown event to initiate the
15804          * drag if we don't get a mouseup event. Default=1000
15805          * @property clickTimeThresh
15806          * @type int
15807          * @static
15808          */
15809         clickTimeThresh: 350,
15810
15811         /**
15812          * Flag that indicates that either the drag pixel threshold or the
15813          * mousdown time threshold has been met
15814          * @property dragThreshMet
15815          * @type boolean
15816          * @private
15817          * @static
15818          */
15819         dragThreshMet: false,
15820
15821         /**
15822          * Timeout used for the click time threshold
15823          * @property clickTimeout
15824          * @type Object
15825          * @private
15826          * @static
15827          */
15828         clickTimeout: null,
15829
15830         /**
15831          * The X position of the mousedown event stored for later use when a
15832          * drag threshold is met.
15833          * @property startX
15834          * @type int
15835          * @private
15836          * @static
15837          */
15838         startX: 0,
15839
15840         /**
15841          * The Y position of the mousedown event stored for later use when a
15842          * drag threshold is met.
15843          * @property startY
15844          * @type int
15845          * @private
15846          * @static
15847          */
15848         startY: 0,
15849
15850         /**
15851          * Each DragDrop instance must be registered with the DragDropMgr.
15852          * This is executed in DragDrop.init()
15853          * @method regDragDrop
15854          * @param {DragDrop} oDD the DragDrop object to register
15855          * @param {String} sGroup the name of the group this element belongs to
15856          * @static
15857          */
15858         regDragDrop: function(oDD, sGroup) {
15859             if (!this.initialized) { this.init(); }
15860
15861             if (!this.ids[sGroup]) {
15862                 this.ids[sGroup] = {};
15863             }
15864             this.ids[sGroup][oDD.id] = oDD;
15865         },
15866
15867         /**
15868          * Removes the supplied dd instance from the supplied group. Executed
15869          * by DragDrop.removeFromGroup, so don't call this function directly.
15870          * @method removeDDFromGroup
15871          * @private
15872          * @static
15873          */
15874         removeDDFromGroup: function(oDD, sGroup) {
15875             if (!this.ids[sGroup]) {
15876                 this.ids[sGroup] = {};
15877             }
15878
15879             var obj = this.ids[sGroup];
15880             if (obj && obj[oDD.id]) {
15881                 delete obj[oDD.id];
15882             }
15883         },
15884
15885         /**
15886          * Unregisters a drag and drop item.  This is executed in
15887          * DragDrop.unreg, use that method instead of calling this directly.
15888          * @method _remove
15889          * @private
15890          * @static
15891          */
15892         _remove: function(oDD) {
15893             for (var g in oDD.groups) {
15894                 if (g && this.ids[g][oDD.id]) {
15895                     delete this.ids[g][oDD.id];
15896                 }
15897             }
15898             delete this.handleIds[oDD.id];
15899         },
15900
15901         /**
15902          * Each DragDrop handle element must be registered.  This is done
15903          * automatically when executing DragDrop.setHandleElId()
15904          * @method regHandle
15905          * @param {String} sDDId the DragDrop id this element is a handle for
15906          * @param {String} sHandleId the id of the element that is the drag
15907          * handle
15908          * @static
15909          */
15910         regHandle: function(sDDId, sHandleId) {
15911             if (!this.handleIds[sDDId]) {
15912                 this.handleIds[sDDId] = {};
15913             }
15914             this.handleIds[sDDId][sHandleId] = sHandleId;
15915         },
15916
15917         /**
15918          * Utility function to determine if a given element has been
15919          * registered as a drag drop item.
15920          * @method isDragDrop
15921          * @param {String} id the element id to check
15922          * @return {boolean} true if this element is a DragDrop item,
15923          * false otherwise
15924          * @static
15925          */
15926         isDragDrop: function(id) {
15927             return ( this.getDDById(id) ) ? true : false;
15928         },
15929
15930         /**
15931          * Returns the drag and drop instances that are in all groups the
15932          * passed in instance belongs to.
15933          * @method getRelated
15934          * @param {DragDrop} p_oDD the obj to get related data for
15935          * @param {boolean} bTargetsOnly if true, only return targetable objs
15936          * @return {DragDrop[]} the related instances
15937          * @static
15938          */
15939         getRelated: function(p_oDD, bTargetsOnly) {
15940             var oDDs = [];
15941             for (var i in p_oDD.groups) {
15942                 for (j in this.ids[i]) {
15943                     var dd = this.ids[i][j];
15944                     if (! this.isTypeOfDD(dd)) {
15945                         continue;
15946                     }
15947                     if (!bTargetsOnly || dd.isTarget) {
15948                         oDDs[oDDs.length] = dd;
15949                     }
15950                 }
15951             }
15952
15953             return oDDs;
15954         },
15955
15956         /**
15957          * Returns true if the specified dd target is a legal target for
15958          * the specifice drag obj
15959          * @method isLegalTarget
15960          * @param {DragDrop} the drag obj
15961          * @param {DragDrop} the target
15962          * @return {boolean} true if the target is a legal target for the
15963          * dd obj
15964          * @static
15965          */
15966         isLegalTarget: function (oDD, oTargetDD) {
15967             var targets = this.getRelated(oDD, true);
15968             for (var i=0, len=targets.length;i<len;++i) {
15969                 if (targets[i].id == oTargetDD.id) {
15970                     return true;
15971                 }
15972             }
15973
15974             return false;
15975         },
15976
15977         /**
15978          * My goal is to be able to transparently determine if an object is
15979          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15980          * returns "object", oDD.constructor.toString() always returns
15981          * "DragDrop" and not the name of the subclass.  So for now it just
15982          * evaluates a well-known variable in DragDrop.
15983          * @method isTypeOfDD
15984          * @param {Object} the object to evaluate
15985          * @return {boolean} true if typeof oDD = DragDrop
15986          * @static
15987          */
15988         isTypeOfDD: function (oDD) {
15989             return (oDD && oDD.__ygDragDrop);
15990         },
15991
15992         /**
15993          * Utility function to determine if a given element has been
15994          * registered as a drag drop handle for the given Drag Drop object.
15995          * @method isHandle
15996          * @param {String} id the element id to check
15997          * @return {boolean} true if this element is a DragDrop handle, false
15998          * otherwise
15999          * @static
16000          */
16001         isHandle: function(sDDId, sHandleId) {
16002             return ( this.handleIds[sDDId] &&
16003                             this.handleIds[sDDId][sHandleId] );
16004         },
16005
16006         /**
16007          * Returns the DragDrop instance for a given id
16008          * @method getDDById
16009          * @param {String} id the id of the DragDrop object
16010          * @return {DragDrop} the drag drop object, null if it is not found
16011          * @static
16012          */
16013         getDDById: function(id) {
16014             for (var i in this.ids) {
16015                 if (this.ids[i][id]) {
16016                     return this.ids[i][id];
16017                 }
16018             }
16019             return null;
16020         },
16021
16022         /**
16023          * Fired after a registered DragDrop object gets the mousedown event.
16024          * Sets up the events required to track the object being dragged
16025          * @method handleMouseDown
16026          * @param {Event} e the event
16027          * @param oDD the DragDrop object being dragged
16028          * @private
16029          * @static
16030          */
16031         handleMouseDown: function(e, oDD) {
16032             if(Roo.QuickTips){
16033                 Roo.QuickTips.disable();
16034             }
16035             this.currentTarget = e.getTarget();
16036
16037             this.dragCurrent = oDD;
16038
16039             var el = oDD.getEl();
16040
16041             // track start position
16042             this.startX = e.getPageX();
16043             this.startY = e.getPageY();
16044
16045             this.deltaX = this.startX - el.offsetLeft;
16046             this.deltaY = this.startY - el.offsetTop;
16047
16048             this.dragThreshMet = false;
16049
16050             this.clickTimeout = setTimeout(
16051                     function() {
16052                         var DDM = Roo.dd.DDM;
16053                         DDM.startDrag(DDM.startX, DDM.startY);
16054                     },
16055                     this.clickTimeThresh );
16056         },
16057
16058         /**
16059          * Fired when either the drag pixel threshol or the mousedown hold
16060          * time threshold has been met.
16061          * @method startDrag
16062          * @param x {int} the X position of the original mousedown
16063          * @param y {int} the Y position of the original mousedown
16064          * @static
16065          */
16066         startDrag: function(x, y) {
16067             clearTimeout(this.clickTimeout);
16068             if (this.dragCurrent) {
16069                 this.dragCurrent.b4StartDrag(x, y);
16070                 this.dragCurrent.startDrag(x, y);
16071             }
16072             this.dragThreshMet = true;
16073         },
16074
16075         /**
16076          * Internal function to handle the mouseup event.  Will be invoked
16077          * from the context of the document.
16078          * @method handleMouseUp
16079          * @param {Event} e the event
16080          * @private
16081          * @static
16082          */
16083         handleMouseUp: function(e) {
16084
16085             if(Roo.QuickTips){
16086                 Roo.QuickTips.enable();
16087             }
16088             if (! this.dragCurrent) {
16089                 return;
16090             }
16091
16092             clearTimeout(this.clickTimeout);
16093
16094             if (this.dragThreshMet) {
16095                 this.fireEvents(e, true);
16096             } else {
16097             }
16098
16099             this.stopDrag(e);
16100
16101             this.stopEvent(e);
16102         },
16103
16104         /**
16105          * Utility to stop event propagation and event default, if these
16106          * features are turned on.
16107          * @method stopEvent
16108          * @param {Event} e the event as returned by this.getEvent()
16109          * @static
16110          */
16111         stopEvent: function(e){
16112             if(this.stopPropagation) {
16113                 e.stopPropagation();
16114             }
16115
16116             if (this.preventDefault) {
16117                 e.preventDefault();
16118             }
16119         },
16120
16121         /**
16122          * Internal function to clean up event handlers after the drag
16123          * operation is complete
16124          * @method stopDrag
16125          * @param {Event} e the event
16126          * @private
16127          * @static
16128          */
16129         stopDrag: function(e) {
16130             // Fire the drag end event for the item that was dragged
16131             if (this.dragCurrent) {
16132                 if (this.dragThreshMet) {
16133                     this.dragCurrent.b4EndDrag(e);
16134                     this.dragCurrent.endDrag(e);
16135                 }
16136
16137                 this.dragCurrent.onMouseUp(e);
16138             }
16139
16140             this.dragCurrent = null;
16141             this.dragOvers = {};
16142         },
16143
16144         /**
16145          * Internal function to handle the mousemove event.  Will be invoked
16146          * from the context of the html element.
16147          *
16148          * @TODO figure out what we can do about mouse events lost when the
16149          * user drags objects beyond the window boundary.  Currently we can
16150          * detect this in internet explorer by verifying that the mouse is
16151          * down during the mousemove event.  Firefox doesn't give us the
16152          * button state on the mousemove event.
16153          * @method handleMouseMove
16154          * @param {Event} e the event
16155          * @private
16156          * @static
16157          */
16158         handleMouseMove: function(e) {
16159             if (! this.dragCurrent) {
16160                 return true;
16161             }
16162
16163             // var button = e.which || e.button;
16164
16165             // check for IE mouseup outside of page boundary
16166             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16167                 this.stopEvent(e);
16168                 return this.handleMouseUp(e);
16169             }
16170
16171             if (!this.dragThreshMet) {
16172                 var diffX = Math.abs(this.startX - e.getPageX());
16173                 var diffY = Math.abs(this.startY - e.getPageY());
16174                 if (diffX > this.clickPixelThresh ||
16175                             diffY > this.clickPixelThresh) {
16176                     this.startDrag(this.startX, this.startY);
16177                 }
16178             }
16179
16180             if (this.dragThreshMet) {
16181                 this.dragCurrent.b4Drag(e);
16182                 this.dragCurrent.onDrag(e);
16183                 if(!this.dragCurrent.moveOnly){
16184                     this.fireEvents(e, false);
16185                 }
16186             }
16187
16188             this.stopEvent(e);
16189
16190             return true;
16191         },
16192
16193         /**
16194          * Iterates over all of the DragDrop elements to find ones we are
16195          * hovering over or dropping on
16196          * @method fireEvents
16197          * @param {Event} e the event
16198          * @param {boolean} isDrop is this a drop op or a mouseover op?
16199          * @private
16200          * @static
16201          */
16202         fireEvents: function(e, isDrop) {
16203             var dc = this.dragCurrent;
16204
16205             // If the user did the mouse up outside of the window, we could
16206             // get here even though we have ended the drag.
16207             if (!dc || dc.isLocked()) {
16208                 return;
16209             }
16210
16211             var pt = e.getPoint();
16212
16213             // cache the previous dragOver array
16214             var oldOvers = [];
16215
16216             var outEvts   = [];
16217             var overEvts  = [];
16218             var dropEvts  = [];
16219             var enterEvts = [];
16220
16221             // Check to see if the object(s) we were hovering over is no longer
16222             // being hovered over so we can fire the onDragOut event
16223             for (var i in this.dragOvers) {
16224
16225                 var ddo = this.dragOvers[i];
16226
16227                 if (! this.isTypeOfDD(ddo)) {
16228                     continue;
16229                 }
16230
16231                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16232                     outEvts.push( ddo );
16233                 }
16234
16235                 oldOvers[i] = true;
16236                 delete this.dragOvers[i];
16237             }
16238
16239             for (var sGroup in dc.groups) {
16240
16241                 if ("string" != typeof sGroup) {
16242                     continue;
16243                 }
16244
16245                 for (i in this.ids[sGroup]) {
16246                     var oDD = this.ids[sGroup][i];
16247                     if (! this.isTypeOfDD(oDD)) {
16248                         continue;
16249                     }
16250
16251                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16252                         if (this.isOverTarget(pt, oDD, this.mode)) {
16253                             // look for drop interactions
16254                             if (isDrop) {
16255                                 dropEvts.push( oDD );
16256                             // look for drag enter and drag over interactions
16257                             } else {
16258
16259                                 // initial drag over: dragEnter fires
16260                                 if (!oldOvers[oDD.id]) {
16261                                     enterEvts.push( oDD );
16262                                 // subsequent drag overs: dragOver fires
16263                                 } else {
16264                                     overEvts.push( oDD );
16265                                 }
16266
16267                                 this.dragOvers[oDD.id] = oDD;
16268                             }
16269                         }
16270                     }
16271                 }
16272             }
16273
16274             if (this.mode) {
16275                 if (outEvts.length) {
16276                     dc.b4DragOut(e, outEvts);
16277                     dc.onDragOut(e, outEvts);
16278                 }
16279
16280                 if (enterEvts.length) {
16281                     dc.onDragEnter(e, enterEvts);
16282                 }
16283
16284                 if (overEvts.length) {
16285                     dc.b4DragOver(e, overEvts);
16286                     dc.onDragOver(e, overEvts);
16287                 }
16288
16289                 if (dropEvts.length) {
16290                     dc.b4DragDrop(e, dropEvts);
16291                     dc.onDragDrop(e, dropEvts);
16292                 }
16293
16294             } else {
16295                 // fire dragout events
16296                 var len = 0;
16297                 for (i=0, len=outEvts.length; i<len; ++i) {
16298                     dc.b4DragOut(e, outEvts[i].id);
16299                     dc.onDragOut(e, outEvts[i].id);
16300                 }
16301
16302                 // fire enter events
16303                 for (i=0,len=enterEvts.length; i<len; ++i) {
16304                     // dc.b4DragEnter(e, oDD.id);
16305                     dc.onDragEnter(e, enterEvts[i].id);
16306                 }
16307
16308                 // fire over events
16309                 for (i=0,len=overEvts.length; i<len; ++i) {
16310                     dc.b4DragOver(e, overEvts[i].id);
16311                     dc.onDragOver(e, overEvts[i].id);
16312                 }
16313
16314                 // fire drop events
16315                 for (i=0, len=dropEvts.length; i<len; ++i) {
16316                     dc.b4DragDrop(e, dropEvts[i].id);
16317                     dc.onDragDrop(e, dropEvts[i].id);
16318                 }
16319
16320             }
16321
16322             // notify about a drop that did not find a target
16323             if (isDrop && !dropEvts.length) {
16324                 dc.onInvalidDrop(e);
16325             }
16326
16327         },
16328
16329         /**
16330          * Helper function for getting the best match from the list of drag
16331          * and drop objects returned by the drag and drop events when we are
16332          * in INTERSECT mode.  It returns either the first object that the
16333          * cursor is over, or the object that has the greatest overlap with
16334          * the dragged element.
16335          * @method getBestMatch
16336          * @param  {DragDrop[]} dds The array of drag and drop objects
16337          * targeted
16338          * @return {DragDrop}       The best single match
16339          * @static
16340          */
16341         getBestMatch: function(dds) {
16342             var winner = null;
16343             // Return null if the input is not what we expect
16344             //if (!dds || !dds.length || dds.length == 0) {
16345                // winner = null;
16346             // If there is only one item, it wins
16347             //} else if (dds.length == 1) {
16348
16349             var len = dds.length;
16350
16351             if (len == 1) {
16352                 winner = dds[0];
16353             } else {
16354                 // Loop through the targeted items
16355                 for (var i=0; i<len; ++i) {
16356                     var dd = dds[i];
16357                     // If the cursor is over the object, it wins.  If the
16358                     // cursor is over multiple matches, the first one we come
16359                     // to wins.
16360                     if (dd.cursorIsOver) {
16361                         winner = dd;
16362                         break;
16363                     // Otherwise the object with the most overlap wins
16364                     } else {
16365                         if (!winner ||
16366                             winner.overlap.getArea() < dd.overlap.getArea()) {
16367                             winner = dd;
16368                         }
16369                     }
16370                 }
16371             }
16372
16373             return winner;
16374         },
16375
16376         /**
16377          * Refreshes the cache of the top-left and bottom-right points of the
16378          * drag and drop objects in the specified group(s).  This is in the
16379          * format that is stored in the drag and drop instance, so typical
16380          * usage is:
16381          * <code>
16382          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16383          * </code>
16384          * Alternatively:
16385          * <code>
16386          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16387          * </code>
16388          * @TODO this really should be an indexed array.  Alternatively this
16389          * method could accept both.
16390          * @method refreshCache
16391          * @param {Object} groups an associative array of groups to refresh
16392          * @static
16393          */
16394         refreshCache: function(groups) {
16395             for (var sGroup in groups) {
16396                 if ("string" != typeof sGroup) {
16397                     continue;
16398                 }
16399                 for (var i in this.ids[sGroup]) {
16400                     var oDD = this.ids[sGroup][i];
16401
16402                     if (this.isTypeOfDD(oDD)) {
16403                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16404                         var loc = this.getLocation(oDD);
16405                         if (loc) {
16406                             this.locationCache[oDD.id] = loc;
16407                         } else {
16408                             delete this.locationCache[oDD.id];
16409                             // this will unregister the drag and drop object if
16410                             // the element is not in a usable state
16411                             // oDD.unreg();
16412                         }
16413                     }
16414                 }
16415             }
16416         },
16417
16418         /**
16419          * This checks to make sure an element exists and is in the DOM.  The
16420          * main purpose is to handle cases where innerHTML is used to remove
16421          * drag and drop objects from the DOM.  IE provides an 'unspecified
16422          * error' when trying to access the offsetParent of such an element
16423          * @method verifyEl
16424          * @param {HTMLElement} el the element to check
16425          * @return {boolean} true if the element looks usable
16426          * @static
16427          */
16428         verifyEl: function(el) {
16429             if (el) {
16430                 var parent;
16431                 if(Roo.isIE){
16432                     try{
16433                         parent = el.offsetParent;
16434                     }catch(e){}
16435                 }else{
16436                     parent = el.offsetParent;
16437                 }
16438                 if (parent) {
16439                     return true;
16440                 }
16441             }
16442
16443             return false;
16444         },
16445
16446         /**
16447          * Returns a Region object containing the drag and drop element's position
16448          * and size, including the padding configured for it
16449          * @method getLocation
16450          * @param {DragDrop} oDD the drag and drop object to get the
16451          *                       location for
16452          * @return {Roo.lib.Region} a Region object representing the total area
16453          *                             the element occupies, including any padding
16454          *                             the instance is configured for.
16455          * @static
16456          */
16457         getLocation: function(oDD) {
16458             if (! this.isTypeOfDD(oDD)) {
16459                 return null;
16460             }
16461
16462             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16463
16464             try {
16465                 pos= Roo.lib.Dom.getXY(el);
16466             } catch (e) { }
16467
16468             if (!pos) {
16469                 return null;
16470             }
16471
16472             x1 = pos[0];
16473             x2 = x1 + el.offsetWidth;
16474             y1 = pos[1];
16475             y2 = y1 + el.offsetHeight;
16476
16477             t = y1 - oDD.padding[0];
16478             r = x2 + oDD.padding[1];
16479             b = y2 + oDD.padding[2];
16480             l = x1 - oDD.padding[3];
16481
16482             return new Roo.lib.Region( t, r, b, l );
16483         },
16484
16485         /**
16486          * Checks the cursor location to see if it over the target
16487          * @method isOverTarget
16488          * @param {Roo.lib.Point} pt The point to evaluate
16489          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16490          * @return {boolean} true if the mouse is over the target
16491          * @private
16492          * @static
16493          */
16494         isOverTarget: function(pt, oTarget, intersect) {
16495             // use cache if available
16496             var loc = this.locationCache[oTarget.id];
16497             if (!loc || !this.useCache) {
16498                 loc = this.getLocation(oTarget);
16499                 this.locationCache[oTarget.id] = loc;
16500
16501             }
16502
16503             if (!loc) {
16504                 return false;
16505             }
16506
16507             oTarget.cursorIsOver = loc.contains( pt );
16508
16509             // DragDrop is using this as a sanity check for the initial mousedown
16510             // in this case we are done.  In POINT mode, if the drag obj has no
16511             // contraints, we are also done. Otherwise we need to evaluate the
16512             // location of the target as related to the actual location of the
16513             // dragged element.
16514             var dc = this.dragCurrent;
16515             if (!dc || !dc.getTargetCoord ||
16516                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16517                 return oTarget.cursorIsOver;
16518             }
16519
16520             oTarget.overlap = null;
16521
16522             // Get the current location of the drag element, this is the
16523             // location of the mouse event less the delta that represents
16524             // where the original mousedown happened on the element.  We
16525             // need to consider constraints and ticks as well.
16526             var pos = dc.getTargetCoord(pt.x, pt.y);
16527
16528             var el = dc.getDragEl();
16529             var curRegion = new Roo.lib.Region( pos.y,
16530                                                    pos.x + el.offsetWidth,
16531                                                    pos.y + el.offsetHeight,
16532                                                    pos.x );
16533
16534             var overlap = curRegion.intersect(loc);
16535
16536             if (overlap) {
16537                 oTarget.overlap = overlap;
16538                 return (intersect) ? true : oTarget.cursorIsOver;
16539             } else {
16540                 return false;
16541             }
16542         },
16543
16544         /**
16545          * unload event handler
16546          * @method _onUnload
16547          * @private
16548          * @static
16549          */
16550         _onUnload: function(e, me) {
16551             Roo.dd.DragDropMgr.unregAll();
16552         },
16553
16554         /**
16555          * Cleans up the drag and drop events and objects.
16556          * @method unregAll
16557          * @private
16558          * @static
16559          */
16560         unregAll: function() {
16561
16562             if (this.dragCurrent) {
16563                 this.stopDrag();
16564                 this.dragCurrent = null;
16565             }
16566
16567             this._execOnAll("unreg", []);
16568
16569             for (i in this.elementCache) {
16570                 delete this.elementCache[i];
16571             }
16572
16573             this.elementCache = {};
16574             this.ids = {};
16575         },
16576
16577         /**
16578          * A cache of DOM elements
16579          * @property elementCache
16580          * @private
16581          * @static
16582          */
16583         elementCache: {},
16584
16585         /**
16586          * Get the wrapper for the DOM element specified
16587          * @method getElWrapper
16588          * @param {String} id the id of the element to get
16589          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16590          * @private
16591          * @deprecated This wrapper isn't that useful
16592          * @static
16593          */
16594         getElWrapper: function(id) {
16595             var oWrapper = this.elementCache[id];
16596             if (!oWrapper || !oWrapper.el) {
16597                 oWrapper = this.elementCache[id] =
16598                     new this.ElementWrapper(Roo.getDom(id));
16599             }
16600             return oWrapper;
16601         },
16602
16603         /**
16604          * Returns the actual DOM element
16605          * @method getElement
16606          * @param {String} id the id of the elment to get
16607          * @return {Object} The element
16608          * @deprecated use Roo.getDom instead
16609          * @static
16610          */
16611         getElement: function(id) {
16612             return Roo.getDom(id);
16613         },
16614
16615         /**
16616          * Returns the style property for the DOM element (i.e.,
16617          * document.getElById(id).style)
16618          * @method getCss
16619          * @param {String} id the id of the elment to get
16620          * @return {Object} The style property of the element
16621          * @deprecated use Roo.getDom instead
16622          * @static
16623          */
16624         getCss: function(id) {
16625             var el = Roo.getDom(id);
16626             return (el) ? el.style : null;
16627         },
16628
16629         /**
16630          * Inner class for cached elements
16631          * @class DragDropMgr.ElementWrapper
16632          * @for DragDropMgr
16633          * @private
16634          * @deprecated
16635          */
16636         ElementWrapper: function(el) {
16637                 /**
16638                  * The element
16639                  * @property el
16640                  */
16641                 this.el = el || null;
16642                 /**
16643                  * The element id
16644                  * @property id
16645                  */
16646                 this.id = this.el && el.id;
16647                 /**
16648                  * A reference to the style property
16649                  * @property css
16650                  */
16651                 this.css = this.el && el.style;
16652             },
16653
16654         /**
16655          * Returns the X position of an html element
16656          * @method getPosX
16657          * @param el the element for which to get the position
16658          * @return {int} the X coordinate
16659          * @for DragDropMgr
16660          * @deprecated use Roo.lib.Dom.getX instead
16661          * @static
16662          */
16663         getPosX: function(el) {
16664             return Roo.lib.Dom.getX(el);
16665         },
16666
16667         /**
16668          * Returns the Y position of an html element
16669          * @method getPosY
16670          * @param el the element for which to get the position
16671          * @return {int} the Y coordinate
16672          * @deprecated use Roo.lib.Dom.getY instead
16673          * @static
16674          */
16675         getPosY: function(el) {
16676             return Roo.lib.Dom.getY(el);
16677         },
16678
16679         /**
16680          * Swap two nodes.  In IE, we use the native method, for others we
16681          * emulate the IE behavior
16682          * @method swapNode
16683          * @param n1 the first node to swap
16684          * @param n2 the other node to swap
16685          * @static
16686          */
16687         swapNode: function(n1, n2) {
16688             if (n1.swapNode) {
16689                 n1.swapNode(n2);
16690             } else {
16691                 var p = n2.parentNode;
16692                 var s = n2.nextSibling;
16693
16694                 if (s == n1) {
16695                     p.insertBefore(n1, n2);
16696                 } else if (n2 == n1.nextSibling) {
16697                     p.insertBefore(n2, n1);
16698                 } else {
16699                     n1.parentNode.replaceChild(n2, n1);
16700                     p.insertBefore(n1, s);
16701                 }
16702             }
16703         },
16704
16705         /**
16706          * Returns the current scroll position
16707          * @method getScroll
16708          * @private
16709          * @static
16710          */
16711         getScroll: function () {
16712             var t, l, dde=document.documentElement, db=document.body;
16713             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16714                 t = dde.scrollTop;
16715                 l = dde.scrollLeft;
16716             } else if (db) {
16717                 t = db.scrollTop;
16718                 l = db.scrollLeft;
16719             } else {
16720
16721             }
16722             return { top: t, left: l };
16723         },
16724
16725         /**
16726          * Returns the specified element style property
16727          * @method getStyle
16728          * @param {HTMLElement} el          the element
16729          * @param {string}      styleProp   the style property
16730          * @return {string} The value of the style property
16731          * @deprecated use Roo.lib.Dom.getStyle
16732          * @static
16733          */
16734         getStyle: function(el, styleProp) {
16735             return Roo.fly(el).getStyle(styleProp);
16736         },
16737
16738         /**
16739          * Gets the scrollTop
16740          * @method getScrollTop
16741          * @return {int} the document's scrollTop
16742          * @static
16743          */
16744         getScrollTop: function () { return this.getScroll().top; },
16745
16746         /**
16747          * Gets the scrollLeft
16748          * @method getScrollLeft
16749          * @return {int} the document's scrollTop
16750          * @static
16751          */
16752         getScrollLeft: function () { return this.getScroll().left; },
16753
16754         /**
16755          * Sets the x/y position of an element to the location of the
16756          * target element.
16757          * @method moveToEl
16758          * @param {HTMLElement} moveEl      The element to move
16759          * @param {HTMLElement} targetEl    The position reference element
16760          * @static
16761          */
16762         moveToEl: function (moveEl, targetEl) {
16763             var aCoord = Roo.lib.Dom.getXY(targetEl);
16764             Roo.lib.Dom.setXY(moveEl, aCoord);
16765         },
16766
16767         /**
16768          * Numeric array sort function
16769          * @method numericSort
16770          * @static
16771          */
16772         numericSort: function(a, b) { return (a - b); },
16773
16774         /**
16775          * Internal counter
16776          * @property _timeoutCount
16777          * @private
16778          * @static
16779          */
16780         _timeoutCount: 0,
16781
16782         /**
16783          * Trying to make the load order less important.  Without this we get
16784          * an error if this file is loaded before the Event Utility.
16785          * @method _addListeners
16786          * @private
16787          * @static
16788          */
16789         _addListeners: function() {
16790             var DDM = Roo.dd.DDM;
16791             if ( Roo.lib.Event && document ) {
16792                 DDM._onLoad();
16793             } else {
16794                 if (DDM._timeoutCount > 2000) {
16795                 } else {
16796                     setTimeout(DDM._addListeners, 10);
16797                     if (document && document.body) {
16798                         DDM._timeoutCount += 1;
16799                     }
16800                 }
16801             }
16802         },
16803
16804         /**
16805          * Recursively searches the immediate parent and all child nodes for
16806          * the handle element in order to determine wheter or not it was
16807          * clicked.
16808          * @method handleWasClicked
16809          * @param node the html element to inspect
16810          * @static
16811          */
16812         handleWasClicked: function(node, id) {
16813             if (this.isHandle(id, node.id)) {
16814                 return true;
16815             } else {
16816                 // check to see if this is a text node child of the one we want
16817                 var p = node.parentNode;
16818
16819                 while (p) {
16820                     if (this.isHandle(id, p.id)) {
16821                         return true;
16822                     } else {
16823                         p = p.parentNode;
16824                     }
16825                 }
16826             }
16827
16828             return false;
16829         }
16830
16831     };
16832
16833 }();
16834
16835 // shorter alias, save a few bytes
16836 Roo.dd.DDM = Roo.dd.DragDropMgr;
16837 Roo.dd.DDM._addListeners();
16838
16839 }/*
16840  * Based on:
16841  * Ext JS Library 1.1.1
16842  * Copyright(c) 2006-2007, Ext JS, LLC.
16843  *
16844  * Originally Released Under LGPL - original licence link has changed is not relivant.
16845  *
16846  * Fork - LGPL
16847  * <script type="text/javascript">
16848  */
16849
16850 /**
16851  * @class Roo.dd.DD
16852  * A DragDrop implementation where the linked element follows the
16853  * mouse cursor during a drag.
16854  * @extends Roo.dd.DragDrop
16855  * @constructor
16856  * @param {String} id the id of the linked element
16857  * @param {String} sGroup the group of related DragDrop items
16858  * @param {object} config an object containing configurable attributes
16859  *                Valid properties for DD:
16860  *                    scroll
16861  */
16862 Roo.dd.DD = function(id, sGroup, config) {
16863     if (id) {
16864         this.init(id, sGroup, config);
16865     }
16866 };
16867
16868 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16869
16870     /**
16871      * When set to true, the utility automatically tries to scroll the browser
16872      * window wehn a drag and drop element is dragged near the viewport boundary.
16873      * Defaults to true.
16874      * @property scroll
16875      * @type boolean
16876      */
16877     scroll: true,
16878
16879     /**
16880      * Sets the pointer offset to the distance between the linked element's top
16881      * left corner and the location the element was clicked
16882      * @method autoOffset
16883      * @param {int} iPageX the X coordinate of the click
16884      * @param {int} iPageY the Y coordinate of the click
16885      */
16886     autoOffset: function(iPageX, iPageY) {
16887         var x = iPageX - this.startPageX;
16888         var y = iPageY - this.startPageY;
16889         this.setDelta(x, y);
16890     },
16891
16892     /**
16893      * Sets the pointer offset.  You can call this directly to force the
16894      * offset to be in a particular location (e.g., pass in 0,0 to set it
16895      * to the center of the object)
16896      * @method setDelta
16897      * @param {int} iDeltaX the distance from the left
16898      * @param {int} iDeltaY the distance from the top
16899      */
16900     setDelta: function(iDeltaX, iDeltaY) {
16901         this.deltaX = iDeltaX;
16902         this.deltaY = iDeltaY;
16903     },
16904
16905     /**
16906      * Sets the drag element to the location of the mousedown or click event,
16907      * maintaining the cursor location relative to the location on the element
16908      * that was clicked.  Override this if you want to place the element in a
16909      * location other than where the cursor is.
16910      * @method setDragElPos
16911      * @param {int} iPageX the X coordinate of the mousedown or drag event
16912      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16913      */
16914     setDragElPos: function(iPageX, iPageY) {
16915         // the first time we do this, we are going to check to make sure
16916         // the element has css positioning
16917
16918         var el = this.getDragEl();
16919         this.alignElWithMouse(el, iPageX, iPageY);
16920     },
16921
16922     /**
16923      * Sets the element to the location of the mousedown or click event,
16924      * maintaining the cursor location relative to the location on the element
16925      * that was clicked.  Override this if you want to place the element in a
16926      * location other than where the cursor is.
16927      * @method alignElWithMouse
16928      * @param {HTMLElement} el the element to move
16929      * @param {int} iPageX the X coordinate of the mousedown or drag event
16930      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16931      */
16932     alignElWithMouse: function(el, iPageX, iPageY) {
16933         var oCoord = this.getTargetCoord(iPageX, iPageY);
16934         var fly = el.dom ? el : Roo.fly(el);
16935         if (!this.deltaSetXY) {
16936             var aCoord = [oCoord.x, oCoord.y];
16937             fly.setXY(aCoord);
16938             var newLeft = fly.getLeft(true);
16939             var newTop  = fly.getTop(true);
16940             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16941         } else {
16942             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16943         }
16944
16945         this.cachePosition(oCoord.x, oCoord.y);
16946         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16947         return oCoord;
16948     },
16949
16950     /**
16951      * Saves the most recent position so that we can reset the constraints and
16952      * tick marks on-demand.  We need to know this so that we can calculate the
16953      * number of pixels the element is offset from its original position.
16954      * @method cachePosition
16955      * @param iPageX the current x position (optional, this just makes it so we
16956      * don't have to look it up again)
16957      * @param iPageY the current y position (optional, this just makes it so we
16958      * don't have to look it up again)
16959      */
16960     cachePosition: function(iPageX, iPageY) {
16961         if (iPageX) {
16962             this.lastPageX = iPageX;
16963             this.lastPageY = iPageY;
16964         } else {
16965             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16966             this.lastPageX = aCoord[0];
16967             this.lastPageY = aCoord[1];
16968         }
16969     },
16970
16971     /**
16972      * Auto-scroll the window if the dragged object has been moved beyond the
16973      * visible window boundary.
16974      * @method autoScroll
16975      * @param {int} x the drag element's x position
16976      * @param {int} y the drag element's y position
16977      * @param {int} h the height of the drag element
16978      * @param {int} w the width of the drag element
16979      * @private
16980      */
16981     autoScroll: function(x, y, h, w) {
16982
16983         if (this.scroll) {
16984             // The client height
16985             var clientH = Roo.lib.Dom.getViewWidth();
16986
16987             // The client width
16988             var clientW = Roo.lib.Dom.getViewHeight();
16989
16990             // The amt scrolled down
16991             var st = this.DDM.getScrollTop();
16992
16993             // The amt scrolled right
16994             var sl = this.DDM.getScrollLeft();
16995
16996             // Location of the bottom of the element
16997             var bot = h + y;
16998
16999             // Location of the right of the element
17000             var right = w + x;
17001
17002             // The distance from the cursor to the bottom of the visible area,
17003             // adjusted so that we don't scroll if the cursor is beyond the
17004             // element drag constraints
17005             var toBot = (clientH + st - y - this.deltaY);
17006
17007             // The distance from the cursor to the right of the visible area
17008             var toRight = (clientW + sl - x - this.deltaX);
17009
17010
17011             // How close to the edge the cursor must be before we scroll
17012             // var thresh = (document.all) ? 100 : 40;
17013             var thresh = 40;
17014
17015             // How many pixels to scroll per autoscroll op.  This helps to reduce
17016             // clunky scrolling. IE is more sensitive about this ... it needs this
17017             // value to be higher.
17018             var scrAmt = (document.all) ? 80 : 30;
17019
17020             // Scroll down if we are near the bottom of the visible page and the
17021             // obj extends below the crease
17022             if ( bot > clientH && toBot < thresh ) {
17023                 window.scrollTo(sl, st + scrAmt);
17024             }
17025
17026             // Scroll up if the window is scrolled down and the top of the object
17027             // goes above the top border
17028             if ( y < st && st > 0 && y - st < thresh ) {
17029                 window.scrollTo(sl, st - scrAmt);
17030             }
17031
17032             // Scroll right if the obj is beyond the right border and the cursor is
17033             // near the border.
17034             if ( right > clientW && toRight < thresh ) {
17035                 window.scrollTo(sl + scrAmt, st);
17036             }
17037
17038             // Scroll left if the window has been scrolled to the right and the obj
17039             // extends past the left border
17040             if ( x < sl && sl > 0 && x - sl < thresh ) {
17041                 window.scrollTo(sl - scrAmt, st);
17042             }
17043         }
17044     },
17045
17046     /**
17047      * Finds the location the element should be placed if we want to move
17048      * it to where the mouse location less the click offset would place us.
17049      * @method getTargetCoord
17050      * @param {int} iPageX the X coordinate of the click
17051      * @param {int} iPageY the Y coordinate of the click
17052      * @return an object that contains the coordinates (Object.x and Object.y)
17053      * @private
17054      */
17055     getTargetCoord: function(iPageX, iPageY) {
17056
17057
17058         var x = iPageX - this.deltaX;
17059         var y = iPageY - this.deltaY;
17060
17061         if (this.constrainX) {
17062             if (x < this.minX) { x = this.minX; }
17063             if (x > this.maxX) { x = this.maxX; }
17064         }
17065
17066         if (this.constrainY) {
17067             if (y < this.minY) { y = this.minY; }
17068             if (y > this.maxY) { y = this.maxY; }
17069         }
17070
17071         x = this.getTick(x, this.xTicks);
17072         y = this.getTick(y, this.yTicks);
17073
17074
17075         return {x:x, y:y};
17076     },
17077
17078     /*
17079      * Sets up config options specific to this class. Overrides
17080      * Roo.dd.DragDrop, but all versions of this method through the
17081      * inheritance chain are called
17082      */
17083     applyConfig: function() {
17084         Roo.dd.DD.superclass.applyConfig.call(this);
17085         this.scroll = (this.config.scroll !== false);
17086     },
17087
17088     /*
17089      * Event that fires prior to the onMouseDown event.  Overrides
17090      * Roo.dd.DragDrop.
17091      */
17092     b4MouseDown: function(e) {
17093         // this.resetConstraints();
17094         this.autoOffset(e.getPageX(),
17095                             e.getPageY());
17096     },
17097
17098     /*
17099      * Event that fires prior to the onDrag event.  Overrides
17100      * Roo.dd.DragDrop.
17101      */
17102     b4Drag: function(e) {
17103         this.setDragElPos(e.getPageX(),
17104                             e.getPageY());
17105     },
17106
17107     toString: function() {
17108         return ("DD " + this.id);
17109     }
17110
17111     //////////////////////////////////////////////////////////////////////////
17112     // Debugging ygDragDrop events that can be overridden
17113     //////////////////////////////////////////////////////////////////////////
17114     /*
17115     startDrag: function(x, y) {
17116     },
17117
17118     onDrag: function(e) {
17119     },
17120
17121     onDragEnter: function(e, id) {
17122     },
17123
17124     onDragOver: function(e, id) {
17125     },
17126
17127     onDragOut: function(e, id) {
17128     },
17129
17130     onDragDrop: function(e, id) {
17131     },
17132
17133     endDrag: function(e) {
17134     }
17135
17136     */
17137
17138 });/*
17139  * Based on:
17140  * Ext JS Library 1.1.1
17141  * Copyright(c) 2006-2007, Ext JS, LLC.
17142  *
17143  * Originally Released Under LGPL - original licence link has changed is not relivant.
17144  *
17145  * Fork - LGPL
17146  * <script type="text/javascript">
17147  */
17148
17149 /**
17150  * @class Roo.dd.DDProxy
17151  * A DragDrop implementation that inserts an empty, bordered div into
17152  * the document that follows the cursor during drag operations.  At the time of
17153  * the click, the frame div is resized to the dimensions of the linked html
17154  * element, and moved to the exact location of the linked element.
17155  *
17156  * References to the "frame" element refer to the single proxy element that
17157  * was created to be dragged in place of all DDProxy elements on the
17158  * page.
17159  *
17160  * @extends Roo.dd.DD
17161  * @constructor
17162  * @param {String} id the id of the linked html element
17163  * @param {String} sGroup the group of related DragDrop objects
17164  * @param {object} config an object containing configurable attributes
17165  *                Valid properties for DDProxy in addition to those in DragDrop:
17166  *                   resizeFrame, centerFrame, dragElId
17167  */
17168 Roo.dd.DDProxy = function(id, sGroup, config) {
17169     if (id) {
17170         this.init(id, sGroup, config);
17171         this.initFrame();
17172     }
17173 };
17174
17175 /**
17176  * The default drag frame div id
17177  * @property Roo.dd.DDProxy.dragElId
17178  * @type String
17179  * @static
17180  */
17181 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17182
17183 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17184
17185     /**
17186      * By default we resize the drag frame to be the same size as the element
17187      * we want to drag (this is to get the frame effect).  We can turn it off
17188      * if we want a different behavior.
17189      * @property resizeFrame
17190      * @type boolean
17191      */
17192     resizeFrame: true,
17193
17194     /**
17195      * By default the frame is positioned exactly where the drag element is, so
17196      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17197      * you do not have constraints on the obj is to have the drag frame centered
17198      * around the cursor.  Set centerFrame to true for this effect.
17199      * @property centerFrame
17200      * @type boolean
17201      */
17202     centerFrame: false,
17203
17204     /**
17205      * Creates the proxy element if it does not yet exist
17206      * @method createFrame
17207      */
17208     createFrame: function() {
17209         var self = this;
17210         var body = document.body;
17211
17212         if (!body || !body.firstChild) {
17213             setTimeout( function() { self.createFrame(); }, 50 );
17214             return;
17215         }
17216
17217         var div = this.getDragEl();
17218
17219         if (!div) {
17220             div    = document.createElement("div");
17221             div.id = this.dragElId;
17222             var s  = div.style;
17223
17224             s.position   = "absolute";
17225             s.visibility = "hidden";
17226             s.cursor     = "move";
17227             s.border     = "2px solid #aaa";
17228             s.zIndex     = 999;
17229
17230             // appendChild can blow up IE if invoked prior to the window load event
17231             // while rendering a table.  It is possible there are other scenarios
17232             // that would cause this to happen as well.
17233             body.insertBefore(div, body.firstChild);
17234         }
17235     },
17236
17237     /**
17238      * Initialization for the drag frame element.  Must be called in the
17239      * constructor of all subclasses
17240      * @method initFrame
17241      */
17242     initFrame: function() {
17243         this.createFrame();
17244     },
17245
17246     applyConfig: function() {
17247         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17248
17249         this.resizeFrame = (this.config.resizeFrame !== false);
17250         this.centerFrame = (this.config.centerFrame);
17251         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17252     },
17253
17254     /**
17255      * Resizes the drag frame to the dimensions of the clicked object, positions
17256      * it over the object, and finally displays it
17257      * @method showFrame
17258      * @param {int} iPageX X click position
17259      * @param {int} iPageY Y click position
17260      * @private
17261      */
17262     showFrame: function(iPageX, iPageY) {
17263         var el = this.getEl();
17264         var dragEl = this.getDragEl();
17265         var s = dragEl.style;
17266
17267         this._resizeProxy();
17268
17269         if (this.centerFrame) {
17270             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17271                            Math.round(parseInt(s.height, 10)/2) );
17272         }
17273
17274         this.setDragElPos(iPageX, iPageY);
17275
17276         Roo.fly(dragEl).show();
17277     },
17278
17279     /**
17280      * The proxy is automatically resized to the dimensions of the linked
17281      * element when a drag is initiated, unless resizeFrame is set to false
17282      * @method _resizeProxy
17283      * @private
17284      */
17285     _resizeProxy: function() {
17286         if (this.resizeFrame) {
17287             var el = this.getEl();
17288             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17289         }
17290     },
17291
17292     // overrides Roo.dd.DragDrop
17293     b4MouseDown: function(e) {
17294         var x = e.getPageX();
17295         var y = e.getPageY();
17296         this.autoOffset(x, y);
17297         this.setDragElPos(x, y);
17298     },
17299
17300     // overrides Roo.dd.DragDrop
17301     b4StartDrag: function(x, y) {
17302         // show the drag frame
17303         this.showFrame(x, y);
17304     },
17305
17306     // overrides Roo.dd.DragDrop
17307     b4EndDrag: function(e) {
17308         Roo.fly(this.getDragEl()).hide();
17309     },
17310
17311     // overrides Roo.dd.DragDrop
17312     // By default we try to move the element to the last location of the frame.
17313     // This is so that the default behavior mirrors that of Roo.dd.DD.
17314     endDrag: function(e) {
17315
17316         var lel = this.getEl();
17317         var del = this.getDragEl();
17318
17319         // Show the drag frame briefly so we can get its position
17320         del.style.visibility = "";
17321
17322         this.beforeMove();
17323         // Hide the linked element before the move to get around a Safari
17324         // rendering bug.
17325         lel.style.visibility = "hidden";
17326         Roo.dd.DDM.moveToEl(lel, del);
17327         del.style.visibility = "hidden";
17328         lel.style.visibility = "";
17329
17330         this.afterDrag();
17331     },
17332
17333     beforeMove : function(){
17334
17335     },
17336
17337     afterDrag : function(){
17338
17339     },
17340
17341     toString: function() {
17342         return ("DDProxy " + this.id);
17343     }
17344
17345 });
17346 /*
17347  * Based on:
17348  * Ext JS Library 1.1.1
17349  * Copyright(c) 2006-2007, Ext JS, LLC.
17350  *
17351  * Originally Released Under LGPL - original licence link has changed is not relivant.
17352  *
17353  * Fork - LGPL
17354  * <script type="text/javascript">
17355  */
17356
17357  /**
17358  * @class Roo.dd.DDTarget
17359  * A DragDrop implementation that does not move, but can be a drop
17360  * target.  You would get the same result by simply omitting implementation
17361  * for the event callbacks, but this way we reduce the processing cost of the
17362  * event listener and the callbacks.
17363  * @extends Roo.dd.DragDrop
17364  * @constructor
17365  * @param {String} id the id of the element that is a drop target
17366  * @param {String} sGroup the group of related DragDrop objects
17367  * @param {object} config an object containing configurable attributes
17368  *                 Valid properties for DDTarget in addition to those in
17369  *                 DragDrop:
17370  *                    none
17371  */
17372 Roo.dd.DDTarget = function(id, sGroup, config) {
17373     if (id) {
17374         this.initTarget(id, sGroup, config);
17375     }
17376 };
17377
17378 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17379 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17380     toString: function() {
17381         return ("DDTarget " + this.id);
17382     }
17383 });
17384 /*
17385  * Based on:
17386  * Ext JS Library 1.1.1
17387  * Copyright(c) 2006-2007, Ext JS, LLC.
17388  *
17389  * Originally Released Under LGPL - original licence link has changed is not relivant.
17390  *
17391  * Fork - LGPL
17392  * <script type="text/javascript">
17393  */
17394  
17395
17396 /**
17397  * @class Roo.dd.ScrollManager
17398  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17399  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17400  * @singleton
17401  */
17402 Roo.dd.ScrollManager = function(){
17403     var ddm = Roo.dd.DragDropMgr;
17404     var els = {};
17405     var dragEl = null;
17406     var proc = {};
17407     
17408     var onStop = function(e){
17409         dragEl = null;
17410         clearProc();
17411     };
17412     
17413     var triggerRefresh = function(){
17414         if(ddm.dragCurrent){
17415              ddm.refreshCache(ddm.dragCurrent.groups);
17416         }
17417     };
17418     
17419     var doScroll = function(){
17420         if(ddm.dragCurrent){
17421             var dds = Roo.dd.ScrollManager;
17422             if(!dds.animate){
17423                 if(proc.el.scroll(proc.dir, dds.increment)){
17424                     triggerRefresh();
17425                 }
17426             }else{
17427                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17428             }
17429         }
17430     };
17431     
17432     var clearProc = function(){
17433         if(proc.id){
17434             clearInterval(proc.id);
17435         }
17436         proc.id = 0;
17437         proc.el = null;
17438         proc.dir = "";
17439     };
17440     
17441     var startProc = function(el, dir){
17442         clearProc();
17443         proc.el = el;
17444         proc.dir = dir;
17445         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17446     };
17447     
17448     var onFire = function(e, isDrop){
17449         if(isDrop || !ddm.dragCurrent){ return; }
17450         var dds = Roo.dd.ScrollManager;
17451         if(!dragEl || dragEl != ddm.dragCurrent){
17452             dragEl = ddm.dragCurrent;
17453             // refresh regions on drag start
17454             dds.refreshCache();
17455         }
17456         
17457         var xy = Roo.lib.Event.getXY(e);
17458         var pt = new Roo.lib.Point(xy[0], xy[1]);
17459         for(var id in els){
17460             var el = els[id], r = el._region;
17461             if(r && r.contains(pt) && el.isScrollable()){
17462                 if(r.bottom - pt.y <= dds.thresh){
17463                     if(proc.el != el){
17464                         startProc(el, "down");
17465                     }
17466                     return;
17467                 }else if(r.right - pt.x <= dds.thresh){
17468                     if(proc.el != el){
17469                         startProc(el, "left");
17470                     }
17471                     return;
17472                 }else if(pt.y - r.top <= dds.thresh){
17473                     if(proc.el != el){
17474                         startProc(el, "up");
17475                     }
17476                     return;
17477                 }else if(pt.x - r.left <= dds.thresh){
17478                     if(proc.el != el){
17479                         startProc(el, "right");
17480                     }
17481                     return;
17482                 }
17483             }
17484         }
17485         clearProc();
17486     };
17487     
17488     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17489     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17490     
17491     return {
17492         /**
17493          * Registers new overflow element(s) to auto scroll
17494          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17495          */
17496         register : function(el){
17497             if(el instanceof Array){
17498                 for(var i = 0, len = el.length; i < len; i++) {
17499                         this.register(el[i]);
17500                 }
17501             }else{
17502                 el = Roo.get(el);
17503                 els[el.id] = el;
17504             }
17505         },
17506         
17507         /**
17508          * Unregisters overflow element(s) so they are no longer scrolled
17509          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17510          */
17511         unregister : function(el){
17512             if(el instanceof Array){
17513                 for(var i = 0, len = el.length; i < len; i++) {
17514                         this.unregister(el[i]);
17515                 }
17516             }else{
17517                 el = Roo.get(el);
17518                 delete els[el.id];
17519             }
17520         },
17521         
17522         /**
17523          * The number of pixels from the edge of a container the pointer needs to be to 
17524          * trigger scrolling (defaults to 25)
17525          * @type Number
17526          */
17527         thresh : 25,
17528         
17529         /**
17530          * The number of pixels to scroll in each scroll increment (defaults to 50)
17531          * @type Number
17532          */
17533         increment : 100,
17534         
17535         /**
17536          * The frequency of scrolls in milliseconds (defaults to 500)
17537          * @type Number
17538          */
17539         frequency : 500,
17540         
17541         /**
17542          * True to animate the scroll (defaults to true)
17543          * @type Boolean
17544          */
17545         animate: true,
17546         
17547         /**
17548          * The animation duration in seconds - 
17549          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17550          * @type Number
17551          */
17552         animDuration: .4,
17553         
17554         /**
17555          * Manually trigger a cache refresh.
17556          */
17557         refreshCache : function(){
17558             for(var id in els){
17559                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17560                     els[id]._region = els[id].getRegion();
17561                 }
17562             }
17563         }
17564     };
17565 }();/*
17566  * Based on:
17567  * Ext JS Library 1.1.1
17568  * Copyright(c) 2006-2007, Ext JS, LLC.
17569  *
17570  * Originally Released Under LGPL - original licence link has changed is not relivant.
17571  *
17572  * Fork - LGPL
17573  * <script type="text/javascript">
17574  */
17575  
17576
17577 /**
17578  * @class Roo.dd.Registry
17579  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17580  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17581  * @singleton
17582  */
17583 Roo.dd.Registry = function(){
17584     var elements = {}; 
17585     var handles = {}; 
17586     var autoIdSeed = 0;
17587
17588     var getId = function(el, autogen){
17589         if(typeof el == "string"){
17590             return el;
17591         }
17592         var id = el.id;
17593         if(!id && autogen !== false){
17594             id = "roodd-" + (++autoIdSeed);
17595             el.id = id;
17596         }
17597         return id;
17598     };
17599     
17600     return {
17601     /**
17602      * Register a drag drop element
17603      * @param {String|HTMLElement} element The id or DOM node to register
17604      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17605      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17606      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17607      * populated in the data object (if applicable):
17608      * <pre>
17609 Value      Description<br />
17610 ---------  ------------------------------------------<br />
17611 handles    Array of DOM nodes that trigger dragging<br />
17612            for the element being registered<br />
17613 isHandle   True if the element passed in triggers<br />
17614            dragging itself, else false
17615 </pre>
17616      */
17617         register : function(el, data){
17618             data = data || {};
17619             if(typeof el == "string"){
17620                 el = document.getElementById(el);
17621             }
17622             data.ddel = el;
17623             elements[getId(el)] = data;
17624             if(data.isHandle !== false){
17625                 handles[data.ddel.id] = data;
17626             }
17627             if(data.handles){
17628                 var hs = data.handles;
17629                 for(var i = 0, len = hs.length; i < len; i++){
17630                         handles[getId(hs[i])] = data;
17631                 }
17632             }
17633         },
17634
17635     /**
17636      * Unregister a drag drop element
17637      * @param {String|HTMLElement}  element The id or DOM node to unregister
17638      */
17639         unregister : function(el){
17640             var id = getId(el, false);
17641             var data = elements[id];
17642             if(data){
17643                 delete elements[id];
17644                 if(data.handles){
17645                     var hs = data.handles;
17646                     for(var i = 0, len = hs.length; i < len; i++){
17647                         delete handles[getId(hs[i], false)];
17648                     }
17649                 }
17650             }
17651         },
17652
17653     /**
17654      * Returns the handle registered for a DOM Node by id
17655      * @param {String|HTMLElement} id The DOM node or id to look up
17656      * @return {Object} handle The custom handle data
17657      */
17658         getHandle : function(id){
17659             if(typeof id != "string"){ // must be element?
17660                 id = id.id;
17661             }
17662             return handles[id];
17663         },
17664
17665     /**
17666      * Returns the handle that is registered for the DOM node that is the target of the event
17667      * @param {Event} e The event
17668      * @return {Object} handle The custom handle data
17669      */
17670         getHandleFromEvent : function(e){
17671             var t = Roo.lib.Event.getTarget(e);
17672             return t ? handles[t.id] : null;
17673         },
17674
17675     /**
17676      * Returns a custom data object that is registered for a DOM node by id
17677      * @param {String|HTMLElement} id The DOM node or id to look up
17678      * @return {Object} data The custom data
17679      */
17680         getTarget : function(id){
17681             if(typeof id != "string"){ // must be element?
17682                 id = id.id;
17683             }
17684             return elements[id];
17685         },
17686
17687     /**
17688      * Returns a custom data object that is registered for the DOM node that is the target of the event
17689      * @param {Event} e The event
17690      * @return {Object} data The custom data
17691      */
17692         getTargetFromEvent : function(e){
17693             var t = Roo.lib.Event.getTarget(e);
17694             return t ? elements[t.id] || handles[t.id] : null;
17695         }
17696     };
17697 }();/*
17698  * Based on:
17699  * Ext JS Library 1.1.1
17700  * Copyright(c) 2006-2007, Ext JS, LLC.
17701  *
17702  * Originally Released Under LGPL - original licence link has changed is not relivant.
17703  *
17704  * Fork - LGPL
17705  * <script type="text/javascript">
17706  */
17707  
17708
17709 /**
17710  * @class Roo.dd.StatusProxy
17711  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17712  * default drag proxy used by all Roo.dd components.
17713  * @constructor
17714  * @param {Object} config
17715  */
17716 Roo.dd.StatusProxy = function(config){
17717     Roo.apply(this, config);
17718     this.id = this.id || Roo.id();
17719     this.el = new Roo.Layer({
17720         dh: {
17721             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17722                 {tag: "div", cls: "x-dd-drop-icon"},
17723                 {tag: "div", cls: "x-dd-drag-ghost"}
17724             ]
17725         }, 
17726         shadow: !config || config.shadow !== false
17727     });
17728     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17729     this.dropStatus = this.dropNotAllowed;
17730 };
17731
17732 Roo.dd.StatusProxy.prototype = {
17733     /**
17734      * @cfg {String} dropAllowed
17735      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17736      */
17737     dropAllowed : "x-dd-drop-ok",
17738     /**
17739      * @cfg {String} dropNotAllowed
17740      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17741      */
17742     dropNotAllowed : "x-dd-drop-nodrop",
17743
17744     /**
17745      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17746      * over the current target element.
17747      * @param {String} cssClass The css class for the new drop status indicator image
17748      */
17749     setStatus : function(cssClass){
17750         cssClass = cssClass || this.dropNotAllowed;
17751         if(this.dropStatus != cssClass){
17752             this.el.replaceClass(this.dropStatus, cssClass);
17753             this.dropStatus = cssClass;
17754         }
17755     },
17756
17757     /**
17758      * Resets the status indicator to the default dropNotAllowed value
17759      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17760      */
17761     reset : function(clearGhost){
17762         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17763         this.dropStatus = this.dropNotAllowed;
17764         if(clearGhost){
17765             this.ghost.update("");
17766         }
17767     },
17768
17769     /**
17770      * Updates the contents of the ghost element
17771      * @param {String} html The html that will replace the current innerHTML of the ghost element
17772      */
17773     update : function(html){
17774         if(typeof html == "string"){
17775             this.ghost.update(html);
17776         }else{
17777             this.ghost.update("");
17778             html.style.margin = "0";
17779             this.ghost.dom.appendChild(html);
17780         }
17781         // ensure float = none set?? cant remember why though.
17782         var el = this.ghost.dom.firstChild;
17783                 if(el){
17784                         Roo.fly(el).setStyle('float', 'none');
17785                 }
17786     },
17787     
17788     /**
17789      * Returns the underlying proxy {@link Roo.Layer}
17790      * @return {Roo.Layer} el
17791     */
17792     getEl : function(){
17793         return this.el;
17794     },
17795
17796     /**
17797      * Returns the ghost element
17798      * @return {Roo.Element} el
17799      */
17800     getGhost : function(){
17801         return this.ghost;
17802     },
17803
17804     /**
17805      * Hides the proxy
17806      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17807      */
17808     hide : function(clear){
17809         this.el.hide();
17810         if(clear){
17811             this.reset(true);
17812         }
17813     },
17814
17815     /**
17816      * Stops the repair animation if it's currently running
17817      */
17818     stop : function(){
17819         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17820             this.anim.stop();
17821         }
17822     },
17823
17824     /**
17825      * Displays this proxy
17826      */
17827     show : function(){
17828         this.el.show();
17829     },
17830
17831     /**
17832      * Force the Layer to sync its shadow and shim positions to the element
17833      */
17834     sync : function(){
17835         this.el.sync();
17836     },
17837
17838     /**
17839      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17840      * invalid drop operation by the item being dragged.
17841      * @param {Array} xy The XY position of the element ([x, y])
17842      * @param {Function} callback The function to call after the repair is complete
17843      * @param {Object} scope The scope in which to execute the callback
17844      */
17845     repair : function(xy, callback, scope){
17846         this.callback = callback;
17847         this.scope = scope;
17848         if(xy && this.animRepair !== false){
17849             this.el.addClass("x-dd-drag-repair");
17850             this.el.hideUnders(true);
17851             this.anim = this.el.shift({
17852                 duration: this.repairDuration || .5,
17853                 easing: 'easeOut',
17854                 xy: xy,
17855                 stopFx: true,
17856                 callback: this.afterRepair,
17857                 scope: this
17858             });
17859         }else{
17860             this.afterRepair();
17861         }
17862     },
17863
17864     // private
17865     afterRepair : function(){
17866         this.hide(true);
17867         if(typeof this.callback == "function"){
17868             this.callback.call(this.scope || this);
17869         }
17870         this.callback = null;
17871         this.scope = null;
17872     }
17873 };/*
17874  * Based on:
17875  * Ext JS Library 1.1.1
17876  * Copyright(c) 2006-2007, Ext JS, LLC.
17877  *
17878  * Originally Released Under LGPL - original licence link has changed is not relivant.
17879  *
17880  * Fork - LGPL
17881  * <script type="text/javascript">
17882  */
17883
17884 /**
17885  * @class Roo.dd.DragSource
17886  * @extends Roo.dd.DDProxy
17887  * A simple class that provides the basic implementation needed to make any element draggable.
17888  * @constructor
17889  * @param {String/HTMLElement/Element} el The container element
17890  * @param {Object} config
17891  */
17892 Roo.dd.DragSource = function(el, config){
17893     this.el = Roo.get(el);
17894     this.dragData = {};
17895     
17896     Roo.apply(this, config);
17897     
17898     if(!this.proxy){
17899         this.proxy = new Roo.dd.StatusProxy();
17900     }
17901
17902     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17903           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17904     
17905     this.dragging = false;
17906 };
17907
17908 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17909     /**
17910      * @cfg {String} dropAllowed
17911      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17912      */
17913     dropAllowed : "x-dd-drop-ok",
17914     /**
17915      * @cfg {String} dropNotAllowed
17916      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17917      */
17918     dropNotAllowed : "x-dd-drop-nodrop",
17919
17920     /**
17921      * Returns the data object associated with this drag source
17922      * @return {Object} data An object containing arbitrary data
17923      */
17924     getDragData : function(e){
17925         return this.dragData;
17926     },
17927
17928     // private
17929     onDragEnter : function(e, id){
17930         var target = Roo.dd.DragDropMgr.getDDById(id);
17931         this.cachedTarget = target;
17932         if(this.beforeDragEnter(target, e, id) !== false){
17933             if(target.isNotifyTarget){
17934                 var status = target.notifyEnter(this, e, this.dragData);
17935                 this.proxy.setStatus(status);
17936             }else{
17937                 this.proxy.setStatus(this.dropAllowed);
17938             }
17939             
17940             if(this.afterDragEnter){
17941                 /**
17942                  * An empty function by default, but provided so that you can perform a custom action
17943                  * when the dragged item enters the drop target by providing an implementation.
17944                  * @param {Roo.dd.DragDrop} target The drop target
17945                  * @param {Event} e The event object
17946                  * @param {String} id The id of the dragged element
17947                  * @method afterDragEnter
17948                  */
17949                 this.afterDragEnter(target, e, id);
17950             }
17951         }
17952     },
17953
17954     /**
17955      * An empty function by default, but provided so that you can perform a custom action
17956      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
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     beforeDragEnter : function(target, e, id){
17963         return true;
17964     },
17965
17966     // private
17967     alignElWithMouse: function() {
17968         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17969         this.proxy.sync();
17970     },
17971
17972     // private
17973     onDragOver : function(e, id){
17974         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17975         if(this.beforeDragOver(target, e, id) !== false){
17976             if(target.isNotifyTarget){
17977                 var status = target.notifyOver(this, e, this.dragData);
17978                 this.proxy.setStatus(status);
17979             }
17980
17981             if(this.afterDragOver){
17982                 /**
17983                  * An empty function by default, but provided so that you can perform a custom action
17984                  * while the dragged item is over the drop target by providing an implementation.
17985                  * @param {Roo.dd.DragDrop} target The drop target
17986                  * @param {Event} e The event object
17987                  * @param {String} id The id of the dragged element
17988                  * @method afterDragOver
17989                  */
17990                 this.afterDragOver(target, e, id);
17991             }
17992         }
17993     },
17994
17995     /**
17996      * An empty function by default, but provided so that you can perform a custom action
17997      * while the dragged item is over the drop target and optionally cancel the onDragOver.
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 event is valid, else false to cancel
18002      */
18003     beforeDragOver : function(target, e, id){
18004         return true;
18005     },
18006
18007     // private
18008     onDragOut : function(e, id){
18009         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18010         if(this.beforeDragOut(target, e, id) !== false){
18011             if(target.isNotifyTarget){
18012                 target.notifyOut(this, e, this.dragData);
18013             }
18014             this.proxy.reset();
18015             if(this.afterDragOut){
18016                 /**
18017                  * An empty function by default, but provided so that you can perform a custom action
18018                  * after the dragged item is dragged out of the target without dropping.
18019                  * @param {Roo.dd.DragDrop} target The drop target
18020                  * @param {Event} e The event object
18021                  * @param {String} id The id of the dragged element
18022                  * @method afterDragOut
18023                  */
18024                 this.afterDragOut(target, e, id);
18025             }
18026         }
18027         this.cachedTarget = null;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action before the dragged
18032      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18033      * @param {Roo.dd.DragDrop} target The drop target
18034      * @param {Event} e The event object
18035      * @param {String} id The id of the dragged element
18036      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18037      */
18038     beforeDragOut : function(target, e, id){
18039         return true;
18040     },
18041     
18042     // private
18043     onDragDrop : function(e, id){
18044         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18045         if(this.beforeDragDrop(target, e, id) !== false){
18046             if(target.isNotifyTarget){
18047                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18048                     this.onValidDrop(target, e, id);
18049                 }else{
18050                     this.onInvalidDrop(target, e, id);
18051                 }
18052             }else{
18053                 this.onValidDrop(target, e, id);
18054             }
18055             
18056             if(this.afterDragDrop){
18057                 /**
18058                  * An empty function by default, but provided so that you can perform a custom action
18059                  * after a valid drag drop has occurred by providing an implementation.
18060                  * @param {Roo.dd.DragDrop} target The drop target
18061                  * @param {Event} e The event object
18062                  * @param {String} id The id of the dropped element
18063                  * @method afterDragDrop
18064                  */
18065                 this.afterDragDrop(target, e, id);
18066             }
18067         }
18068         delete this.cachedTarget;
18069     },
18070
18071     /**
18072      * An empty function by default, but provided so that you can perform a custom action before the dragged
18073      * item is dropped onto the target and optionally cancel the onDragDrop.
18074      * @param {Roo.dd.DragDrop} target The drop target
18075      * @param {Event} e The event object
18076      * @param {String} id The id of the dragged element
18077      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18078      */
18079     beforeDragDrop : function(target, e, id){
18080         return true;
18081     },
18082
18083     // private
18084     onValidDrop : function(target, e, id){
18085         this.hideProxy();
18086         if(this.afterValidDrop){
18087             /**
18088              * An empty function by default, but provided so that you can perform a custom action
18089              * after a valid drop has occurred by providing an implementation.
18090              * @param {Object} target The target DD 
18091              * @param {Event} e The event object
18092              * @param {String} id The id of the dropped element
18093              * @method afterInvalidDrop
18094              */
18095             this.afterValidDrop(target, e, id);
18096         }
18097     },
18098
18099     // private
18100     getRepairXY : function(e, data){
18101         return this.el.getXY();  
18102     },
18103
18104     // private
18105     onInvalidDrop : function(target, e, id){
18106         this.beforeInvalidDrop(target, e, id);
18107         if(this.cachedTarget){
18108             if(this.cachedTarget.isNotifyTarget){
18109                 this.cachedTarget.notifyOut(this, e, this.dragData);
18110             }
18111             this.cacheTarget = null;
18112         }
18113         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18114
18115         if(this.afterInvalidDrop){
18116             /**
18117              * An empty function by default, but provided so that you can perform a custom action
18118              * after an invalid drop has occurred by providing an implementation.
18119              * @param {Event} e The event object
18120              * @param {String} id The id of the dropped element
18121              * @method afterInvalidDrop
18122              */
18123             this.afterInvalidDrop(e, id);
18124         }
18125     },
18126
18127     // private
18128     afterRepair : function(){
18129         if(Roo.enableFx){
18130             this.el.highlight(this.hlColor || "c3daf9");
18131         }
18132         this.dragging = false;
18133     },
18134
18135     /**
18136      * An empty function by default, but provided so that you can perform a custom action after an invalid
18137      * drop has occurred.
18138      * @param {Roo.dd.DragDrop} target The drop target
18139      * @param {Event} e The event object
18140      * @param {String} id The id of the dragged element
18141      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18142      */
18143     beforeInvalidDrop : function(target, e, id){
18144         return true;
18145     },
18146
18147     // private
18148     handleMouseDown : function(e){
18149         if(this.dragging) {
18150             return;
18151         }
18152         var data = this.getDragData(e);
18153         if(data && this.onBeforeDrag(data, e) !== false){
18154             this.dragData = data;
18155             this.proxy.stop();
18156             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18157         } 
18158     },
18159
18160     /**
18161      * An empty function by default, but provided so that you can perform a custom action before the initial
18162      * drag event begins and optionally cancel it.
18163      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18164      * @param {Event} e The event object
18165      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18166      */
18167     onBeforeDrag : function(data, e){
18168         return true;
18169     },
18170
18171     /**
18172      * An empty function by default, but provided so that you can perform a custom action once the initial
18173      * drag event has begun.  The drag cannot be canceled from this function.
18174      * @param {Number} x The x position of the click on the dragged object
18175      * @param {Number} y The y position of the click on the dragged object
18176      */
18177     onStartDrag : Roo.emptyFn,
18178
18179     // private - YUI override
18180     startDrag : function(x, y){
18181         this.proxy.reset();
18182         this.dragging = true;
18183         this.proxy.update("");
18184         this.onInitDrag(x, y);
18185         this.proxy.show();
18186     },
18187
18188     // private
18189     onInitDrag : function(x, y){
18190         var clone = this.el.dom.cloneNode(true);
18191         clone.id = Roo.id(); // prevent duplicate ids
18192         this.proxy.update(clone);
18193         this.onStartDrag(x, y);
18194         return true;
18195     },
18196
18197     /**
18198      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18199      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18200      */
18201     getProxy : function(){
18202         return this.proxy;  
18203     },
18204
18205     /**
18206      * Hides the drag source's {@link Roo.dd.StatusProxy}
18207      */
18208     hideProxy : function(){
18209         this.proxy.hide();  
18210         this.proxy.reset(true);
18211         this.dragging = false;
18212     },
18213
18214     // private
18215     triggerCacheRefresh : function(){
18216         Roo.dd.DDM.refreshCache(this.groups);
18217     },
18218
18219     // private - override to prevent hiding
18220     b4EndDrag: function(e) {
18221     },
18222
18223     // private - override to prevent moving
18224     endDrag : function(e){
18225         this.onEndDrag(this.dragData, e);
18226     },
18227
18228     // private
18229     onEndDrag : function(data, e){
18230     },
18231     
18232     // private - pin to cursor
18233     autoOffset : function(x, y) {
18234         this.setDelta(-12, -20);
18235     }    
18236 });/*
18237  * Based on:
18238  * Ext JS Library 1.1.1
18239  * Copyright(c) 2006-2007, Ext JS, LLC.
18240  *
18241  * Originally Released Under LGPL - original licence link has changed is not relivant.
18242  *
18243  * Fork - LGPL
18244  * <script type="text/javascript">
18245  */
18246
18247
18248 /**
18249  * @class Roo.dd.DropTarget
18250  * @extends Roo.dd.DDTarget
18251  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18252  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18253  * @constructor
18254  * @param {String/HTMLElement/Element} el The container element
18255  * @param {Object} config
18256  */
18257 Roo.dd.DropTarget = function(el, config){
18258     this.el = Roo.get(el);
18259     
18260     Roo.apply(this, config);
18261     
18262     if(this.containerScroll){
18263         Roo.dd.ScrollManager.register(this.el);
18264     }
18265     
18266     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18267           {isTarget: true});
18268
18269 };
18270
18271 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18272     /**
18273      * @cfg {String} overClass
18274      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18275      */
18276     /**
18277      * @cfg {String} dropAllowed
18278      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18279      */
18280     dropAllowed : "x-dd-drop-ok",
18281     /**
18282      * @cfg {String} dropNotAllowed
18283      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18284      */
18285     dropNotAllowed : "x-dd-drop-nodrop",
18286
18287     // private
18288     isTarget : true,
18289
18290     // private
18291     isNotifyTarget : true,
18292
18293     /**
18294      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18295      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18296      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18297      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18298      * @param {Event} e The event
18299      * @param {Object} data An object containing arbitrary data supplied by the drag source
18300      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18301      * underlying {@link Roo.dd.StatusProxy} can be updated
18302      */
18303     notifyEnter : function(dd, e, data){
18304         if(this.overClass){
18305             this.el.addClass(this.overClass);
18306         }
18307         return this.dropAllowed;
18308     },
18309
18310     /**
18311      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18312      * This method will be called on every mouse movement while the drag source is over the drop target.
18313      * This default implementation simply returns the dropAllowed config value.
18314      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18315      * @param {Event} e The event
18316      * @param {Object} data An object containing arbitrary data supplied by the drag source
18317      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18318      * underlying {@link Roo.dd.StatusProxy} can be updated
18319      */
18320     notifyOver : function(dd, e, data){
18321         return this.dropAllowed;
18322     },
18323
18324     /**
18325      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18326      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18327      * overClass (if any) from the drop element.
18328      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18329      * @param {Event} e The event
18330      * @param {Object} data An object containing arbitrary data supplied by the drag source
18331      */
18332     notifyOut : function(dd, e, data){
18333         if(this.overClass){
18334             this.el.removeClass(this.overClass);
18335         }
18336     },
18337
18338     /**
18339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18340      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18341      * implementation that does something to process the drop event and returns true so that the drag source's
18342      * repair action does not run.
18343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18344      * @param {Event} e The event
18345      * @param {Object} data An object containing arbitrary data supplied by the drag source
18346      * @return {Boolean} True if the drop was valid, else false
18347      */
18348     notifyDrop : function(dd, e, data){
18349         return false;
18350     }
18351 });/*
18352  * Based on:
18353  * Ext JS Library 1.1.1
18354  * Copyright(c) 2006-2007, Ext JS, LLC.
18355  *
18356  * Originally Released Under LGPL - original licence link has changed is not relivant.
18357  *
18358  * Fork - LGPL
18359  * <script type="text/javascript">
18360  */
18361
18362
18363 /**
18364  * @class Roo.dd.DragZone
18365  * @extends Roo.dd.DragSource
18366  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18367  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18368  * @constructor
18369  * @param {String/HTMLElement/Element} el The container element
18370  * @param {Object} config
18371  */
18372 Roo.dd.DragZone = function(el, config){
18373     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18374     if(this.containerScroll){
18375         Roo.dd.ScrollManager.register(this.el);
18376     }
18377 };
18378
18379 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18380     /**
18381      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18382      * for auto scrolling during drag operations.
18383      */
18384     /**
18385      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18386      * method after a failed drop (defaults to "c3daf9" - light blue)
18387      */
18388
18389     /**
18390      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18391      * for a valid target to drag based on the mouse down. Override this method
18392      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18393      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18394      * @param {EventObject} e The mouse down event
18395      * @return {Object} The dragData
18396      */
18397     getDragData : function(e){
18398         return Roo.dd.Registry.getHandleFromEvent(e);
18399     },
18400     
18401     /**
18402      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18403      * this.dragData.ddel
18404      * @param {Number} x The x position of the click on the dragged object
18405      * @param {Number} y The y position of the click on the dragged object
18406      * @return {Boolean} true to continue the drag, false to cancel
18407      */
18408     onInitDrag : function(x, y){
18409         this.proxy.update(this.dragData.ddel.cloneNode(true));
18410         this.onStartDrag(x, y);
18411         return true;
18412     },
18413     
18414     /**
18415      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18416      */
18417     afterRepair : function(){
18418         if(Roo.enableFx){
18419             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18420         }
18421         this.dragging = false;
18422     },
18423
18424     /**
18425      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18426      * the XY of this.dragData.ddel
18427      * @param {EventObject} e The mouse up event
18428      * @return {Array} The xy location (e.g. [100, 200])
18429      */
18430     getRepairXY : function(e){
18431         return Roo.Element.fly(this.dragData.ddel).getXY();  
18432     }
18433 });/*
18434  * Based on:
18435  * Ext JS Library 1.1.1
18436  * Copyright(c) 2006-2007, Ext JS, LLC.
18437  *
18438  * Originally Released Under LGPL - original licence link has changed is not relivant.
18439  *
18440  * Fork - LGPL
18441  * <script type="text/javascript">
18442  */
18443 /**
18444  * @class Roo.dd.DropZone
18445  * @extends Roo.dd.DropTarget
18446  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18447  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18448  * @constructor
18449  * @param {String/HTMLElement/Element} el The container element
18450  * @param {Object} config
18451  */
18452 Roo.dd.DropZone = function(el, config){
18453     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18454 };
18455
18456 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18457     /**
18458      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18459      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18460      * provide your own custom lookup.
18461      * @param {Event} e The event
18462      * @return {Object} data The custom data
18463      */
18464     getTargetFromEvent : function(e){
18465         return Roo.dd.Registry.getTargetFromEvent(e);
18466     },
18467
18468     /**
18469      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18470      * that it has registered.  This method has no default implementation and should be overridden to provide
18471      * node-specific processing if necessary.
18472      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18473      * {@link #getTargetFromEvent} for this node)
18474      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18475      * @param {Event} e The event
18476      * @param {Object} data An object containing arbitrary data supplied by the drag source
18477      */
18478     onNodeEnter : function(n, dd, e, data){
18479         
18480     },
18481
18482     /**
18483      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18484      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18485      * overridden to provide the proper feedback.
18486      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18487      * {@link #getTargetFromEvent} for this node)
18488      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18489      * @param {Event} e The event
18490      * @param {Object} data An object containing arbitrary data supplied by the drag source
18491      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18492      * underlying {@link Roo.dd.StatusProxy} can be updated
18493      */
18494     onNodeOver : function(n, dd, e, data){
18495         return this.dropAllowed;
18496     },
18497
18498     /**
18499      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18500      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18501      * node-specific processing if necessary.
18502      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18503      * {@link #getTargetFromEvent} for this node)
18504      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18505      * @param {Event} e The event
18506      * @param {Object} data An object containing arbitrary data supplied by the drag source
18507      */
18508     onNodeOut : function(n, dd, e, data){
18509         
18510     },
18511
18512     /**
18513      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18514      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18515      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18516      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18517      * {@link #getTargetFromEvent} for this node)
18518      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18519      * @param {Event} e The event
18520      * @param {Object} data An object containing arbitrary data supplied by the drag source
18521      * @return {Boolean} True if the drop was valid, else false
18522      */
18523     onNodeDrop : function(n, dd, e, data){
18524         return false;
18525     },
18526
18527     /**
18528      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18529      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18530      * it should be overridden to provide the proper feedback if necessary.
18531      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18532      * @param {Event} e The event
18533      * @param {Object} data An object containing arbitrary data supplied by the drag source
18534      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18535      * underlying {@link Roo.dd.StatusProxy} can be updated
18536      */
18537     onContainerOver : function(dd, e, data){
18538         return this.dropNotAllowed;
18539     },
18540
18541     /**
18542      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18543      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18544      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18545      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
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     onContainerDrop : function(dd, e, data){
18552         return false;
18553     },
18554
18555     /**
18556      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18557      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18558      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18559      * you should override this method and provide a custom implementation.
18560      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18561      * @param {Event} e The event
18562      * @param {Object} data An object containing arbitrary data supplied by the drag source
18563      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18564      * underlying {@link Roo.dd.StatusProxy} can be updated
18565      */
18566     notifyEnter : function(dd, e, data){
18567         return this.dropNotAllowed;
18568     },
18569
18570     /**
18571      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18572      * This method will be called on every mouse movement while the drag source is over the drop zone.
18573      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18574      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18575      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18576      * registered node, it will call {@link #onContainerOver}.
18577      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18578      * @param {Event} e The event
18579      * @param {Object} data An object containing arbitrary data supplied by the drag source
18580      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18581      * underlying {@link Roo.dd.StatusProxy} can be updated
18582      */
18583     notifyOver : function(dd, e, data){
18584         var n = this.getTargetFromEvent(e);
18585         if(!n){ // not over valid drop target
18586             if(this.lastOverNode){
18587                 this.onNodeOut(this.lastOverNode, dd, e, data);
18588                 this.lastOverNode = null;
18589             }
18590             return this.onContainerOver(dd, e, data);
18591         }
18592         if(this.lastOverNode != n){
18593             if(this.lastOverNode){
18594                 this.onNodeOut(this.lastOverNode, dd, e, data);
18595             }
18596             this.onNodeEnter(n, dd, e, data);
18597             this.lastOverNode = n;
18598         }
18599         return this.onNodeOver(n, dd, e, data);
18600     },
18601
18602     /**
18603      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18604      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18605      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18606      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18607      * @param {Event} e The event
18608      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18609      */
18610     notifyOut : function(dd, e, data){
18611         if(this.lastOverNode){
18612             this.onNodeOut(this.lastOverNode, dd, e, data);
18613             this.lastOverNode = null;
18614         }
18615     },
18616
18617     /**
18618      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18619      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18620      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18621      * otherwise it will call {@link #onContainerDrop}.
18622      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18623      * @param {Event} e The event
18624      * @param {Object} data An object containing arbitrary data supplied by the drag source
18625      * @return {Boolean} True if the drop was valid, else false
18626      */
18627     notifyDrop : function(dd, e, data){
18628         if(this.lastOverNode){
18629             this.onNodeOut(this.lastOverNode, dd, e, data);
18630             this.lastOverNode = null;
18631         }
18632         var n = this.getTargetFromEvent(e);
18633         return n ?
18634             this.onNodeDrop(n, dd, e, data) :
18635             this.onContainerDrop(dd, e, data);
18636     },
18637
18638     // private
18639     triggerCacheRefresh : function(){
18640         Roo.dd.DDM.refreshCache(this.groups);
18641     }  
18642 });/*
18643  * Based on:
18644  * Ext JS Library 1.1.1
18645  * Copyright(c) 2006-2007, Ext JS, LLC.
18646  *
18647  * Originally Released Under LGPL - original licence link has changed is not relivant.
18648  *
18649  * Fork - LGPL
18650  * <script type="text/javascript">
18651  */
18652
18653
18654 /**
18655  * @class Roo.data.SortTypes
18656  * @singleton
18657  * Defines the default sorting (casting?) comparison functions used when sorting data.
18658  */
18659 Roo.data.SortTypes = {
18660     /**
18661      * Default sort that does nothing
18662      * @param {Mixed} s The value being converted
18663      * @return {Mixed} The comparison value
18664      */
18665     none : function(s){
18666         return s;
18667     },
18668     
18669     /**
18670      * The regular expression used to strip tags
18671      * @type {RegExp}
18672      * @property
18673      */
18674     stripTagsRE : /<\/?[^>]+>/gi,
18675     
18676     /**
18677      * Strips all HTML tags to sort on text only
18678      * @param {Mixed} s The value being converted
18679      * @return {String} The comparison value
18680      */
18681     asText : function(s){
18682         return String(s).replace(this.stripTagsRE, "");
18683     },
18684     
18685     /**
18686      * Strips all HTML tags to sort on text only - Case insensitive
18687      * @param {Mixed} s The value being converted
18688      * @return {String} The comparison value
18689      */
18690     asUCText : function(s){
18691         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18692     },
18693     
18694     /**
18695      * Case insensitive string
18696      * @param {Mixed} s The value being converted
18697      * @return {String} The comparison value
18698      */
18699     asUCString : function(s) {
18700         return String(s).toUpperCase();
18701     },
18702     
18703     /**
18704      * Date sorting
18705      * @param {Mixed} s The value being converted
18706      * @return {Number} The comparison value
18707      */
18708     asDate : function(s) {
18709         if(!s){
18710             return 0;
18711         }
18712         if(s instanceof Date){
18713             return s.getTime();
18714         }
18715         return Date.parse(String(s));
18716     },
18717     
18718     /**
18719      * Float sorting
18720      * @param {Mixed} s The value being converted
18721      * @return {Float} The comparison value
18722      */
18723     asFloat : function(s) {
18724         var val = parseFloat(String(s).replace(/,/g, ""));
18725         if(isNaN(val)) val = 0;
18726         return val;
18727     },
18728     
18729     /**
18730      * Integer sorting
18731      * @param {Mixed} s The value being converted
18732      * @return {Number} The comparison value
18733      */
18734     asInt : function(s) {
18735         var val = parseInt(String(s).replace(/,/g, ""));
18736         if(isNaN(val)) val = 0;
18737         return val;
18738     }
18739 };/*
18740  * Based on:
18741  * Ext JS Library 1.1.1
18742  * Copyright(c) 2006-2007, Ext JS, LLC.
18743  *
18744  * Originally Released Under LGPL - original licence link has changed is not relivant.
18745  *
18746  * Fork - LGPL
18747  * <script type="text/javascript">
18748  */
18749
18750 /**
18751 * @class Roo.data.Record
18752  * Instances of this class encapsulate both record <em>definition</em> information, and record
18753  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18754  * to access Records cached in an {@link Roo.data.Store} object.<br>
18755  * <p>
18756  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18757  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18758  * objects.<br>
18759  * <p>
18760  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18761  * @constructor
18762  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18763  * {@link #create}. The parameters are the same.
18764  * @param {Array} data An associative Array of data values keyed by the field name.
18765  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18766  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18767  * not specified an integer id is generated.
18768  */
18769 Roo.data.Record = function(data, id){
18770     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18771     this.data = data;
18772 };
18773
18774 /**
18775  * Generate a constructor for a specific record layout.
18776  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18777  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18778  * Each field definition object may contain the following properties: <ul>
18779  * <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,
18780  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18781  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18782  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18783  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18784  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18785  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18786  * this may be omitted.</p></li>
18787  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18788  * <ul><li>auto (Default, implies no conversion)</li>
18789  * <li>string</li>
18790  * <li>int</li>
18791  * <li>float</li>
18792  * <li>boolean</li>
18793  * <li>date</li></ul></p></li>
18794  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18795  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18796  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18797  * by the Reader into an object that will be stored in the Record. It is passed the
18798  * following parameters:<ul>
18799  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18800  * </ul></p></li>
18801  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18802  * </ul>
18803  * <br>usage:<br><pre><code>
18804 var TopicRecord = Roo.data.Record.create(
18805     {name: 'title', mapping: 'topic_title'},
18806     {name: 'author', mapping: 'username'},
18807     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18808     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18809     {name: 'lastPoster', mapping: 'user2'},
18810     {name: 'excerpt', mapping: 'post_text'}
18811 );
18812
18813 var myNewRecord = new TopicRecord({
18814     title: 'Do my job please',
18815     author: 'noobie',
18816     totalPosts: 1,
18817     lastPost: new Date(),
18818     lastPoster: 'Animal',
18819     excerpt: 'No way dude!'
18820 });
18821 myStore.add(myNewRecord);
18822 </code></pre>
18823  * @method create
18824  * @static
18825  */
18826 Roo.data.Record.create = function(o){
18827     var f = function(){
18828         f.superclass.constructor.apply(this, arguments);
18829     };
18830     Roo.extend(f, Roo.data.Record);
18831     var p = f.prototype;
18832     p.fields = new Roo.util.MixedCollection(false, function(field){
18833         return field.name;
18834     });
18835     for(var i = 0, len = o.length; i < len; i++){
18836         p.fields.add(new Roo.data.Field(o[i]));
18837     }
18838     f.getField = function(name){
18839         return p.fields.get(name);  
18840     };
18841     return f;
18842 };
18843
18844 Roo.data.Record.AUTO_ID = 1000;
18845 Roo.data.Record.EDIT = 'edit';
18846 Roo.data.Record.REJECT = 'reject';
18847 Roo.data.Record.COMMIT = 'commit';
18848
18849 Roo.data.Record.prototype = {
18850     /**
18851      * Readonly flag - true if this record has been modified.
18852      * @type Boolean
18853      */
18854     dirty : false,
18855     editing : false,
18856     error: null,
18857     modified: null,
18858
18859     // private
18860     join : function(store){
18861         this.store = store;
18862     },
18863
18864     /**
18865      * Set the named field to the specified value.
18866      * @param {String} name The name of the field to set.
18867      * @param {Object} value The value to set the field to.
18868      */
18869     set : function(name, value){
18870         if(this.data[name] == value){
18871             return;
18872         }
18873         this.dirty = true;
18874         if(!this.modified){
18875             this.modified = {};
18876         }
18877         if(typeof this.modified[name] == 'undefined'){
18878             this.modified[name] = this.data[name];
18879         }
18880         this.data[name] = value;
18881         if(!this.editing){
18882             this.store.afterEdit(this);
18883         }       
18884     },
18885
18886     /**
18887      * Get the value of the named field.
18888      * @param {String} name The name of the field to get the value of.
18889      * @return {Object} The value of the field.
18890      */
18891     get : function(name){
18892         return this.data[name]; 
18893     },
18894
18895     // private
18896     beginEdit : function(){
18897         this.editing = true;
18898         this.modified = {}; 
18899     },
18900
18901     // private
18902     cancelEdit : function(){
18903         this.editing = false;
18904         delete this.modified;
18905     },
18906
18907     // private
18908     endEdit : function(){
18909         this.editing = false;
18910         if(this.dirty && this.store){
18911             this.store.afterEdit(this);
18912         }
18913     },
18914
18915     /**
18916      * Usually called by the {@link Roo.data.Store} which owns the Record.
18917      * Rejects all changes made to the Record since either creation, or the last commit operation.
18918      * Modified fields are reverted to their original values.
18919      * <p>
18920      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18921      * of reject operations.
18922      */
18923     reject : function(){
18924         var m = this.modified;
18925         for(var n in m){
18926             if(typeof m[n] != "function"){
18927                 this.data[n] = m[n];
18928             }
18929         }
18930         this.dirty = false;
18931         delete this.modified;
18932         this.editing = false;
18933         if(this.store){
18934             this.store.afterReject(this);
18935         }
18936     },
18937
18938     /**
18939      * Usually called by the {@link Roo.data.Store} which owns the Record.
18940      * Commits all changes made to the Record since either creation, or the last commit operation.
18941      * <p>
18942      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18943      * of commit operations.
18944      */
18945     commit : function(){
18946         this.dirty = false;
18947         delete this.modified;
18948         this.editing = false;
18949         if(this.store){
18950             this.store.afterCommit(this);
18951         }
18952     },
18953
18954     // private
18955     hasError : function(){
18956         return this.error != null;
18957     },
18958
18959     // private
18960     clearError : function(){
18961         this.error = null;
18962     },
18963
18964     /**
18965      * Creates a copy of this record.
18966      * @param {String} id (optional) A new record id if you don't want to use this record's id
18967      * @return {Record}
18968      */
18969     copy : function(newId) {
18970         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18971     }
18972 };/*
18973  * Based on:
18974  * Ext JS Library 1.1.1
18975  * Copyright(c) 2006-2007, Ext JS, LLC.
18976  *
18977  * Originally Released Under LGPL - original licence link has changed is not relivant.
18978  *
18979  * Fork - LGPL
18980  * <script type="text/javascript">
18981  */
18982
18983
18984
18985 /**
18986  * @class Roo.data.Store
18987  * @extends Roo.util.Observable
18988  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18989  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18990  * <p>
18991  * 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
18992  * has no knowledge of the format of the data returned by the Proxy.<br>
18993  * <p>
18994  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18995  * instances from the data object. These records are cached and made available through accessor functions.
18996  * @constructor
18997  * Creates a new Store.
18998  * @param {Object} config A config object containing the objects needed for the Store to access data,
18999  * and read the data into Records.
19000  */
19001 Roo.data.Store = function(config){
19002     this.data = new Roo.util.MixedCollection(false);
19003     this.data.getKey = function(o){
19004         return o.id;
19005     };
19006     this.baseParams = {};
19007     // private
19008     this.paramNames = {
19009         "start" : "start",
19010         "limit" : "limit",
19011         "sort" : "sort",
19012         "dir" : "dir"
19013     };
19014
19015     if(config && config.data){
19016         this.inlineData = config.data;
19017         delete config.data;
19018     }
19019
19020     Roo.apply(this, config);
19021     
19022     if(this.reader){ // reader passed
19023         this.reader = Roo.factory(this.reader, Roo.data);
19024         this.reader.xmodule = this.xmodule || false;
19025         if(!this.recordType){
19026             this.recordType = this.reader.recordType;
19027         }
19028         if(this.reader.onMetaChange){
19029             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19030         }
19031     }
19032
19033     if(this.recordType){
19034         this.fields = this.recordType.prototype.fields;
19035     }
19036     this.modified = [];
19037
19038     this.addEvents({
19039         /**
19040          * @event datachanged
19041          * Fires when the data cache has changed, and a widget which is using this Store
19042          * as a Record cache should refresh its view.
19043          * @param {Store} this
19044          */
19045         datachanged : true,
19046         /**
19047          * @event metachange
19048          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19049          * @param {Store} this
19050          * @param {Object} meta The JSON metadata
19051          */
19052         metachange : true,
19053         /**
19054          * @event add
19055          * Fires when Records have been added to the Store
19056          * @param {Store} this
19057          * @param {Roo.data.Record[]} records The array of Records added
19058          * @param {Number} index The index at which the record(s) were added
19059          */
19060         add : true,
19061         /**
19062          * @event remove
19063          * Fires when a Record has been removed from the Store
19064          * @param {Store} this
19065          * @param {Roo.data.Record} record The Record that was removed
19066          * @param {Number} index The index at which the record was removed
19067          */
19068         remove : true,
19069         /**
19070          * @event update
19071          * Fires when a Record has been updated
19072          * @param {Store} this
19073          * @param {Roo.data.Record} record The Record that was updated
19074          * @param {String} operation The update operation being performed.  Value may be one of:
19075          * <pre><code>
19076  Roo.data.Record.EDIT
19077  Roo.data.Record.REJECT
19078  Roo.data.Record.COMMIT
19079          * </code></pre>
19080          */
19081         update : true,
19082         /**
19083          * @event clear
19084          * Fires when the data cache has been cleared.
19085          * @param {Store} this
19086          */
19087         clear : true,
19088         /**
19089          * @event beforeload
19090          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19091          * the load action will be canceled.
19092          * @param {Store} this
19093          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19094          */
19095         beforeload : true,
19096         /**
19097          * @event load
19098          * Fires after a new set of Records has been loaded.
19099          * @param {Store} this
19100          * @param {Roo.data.Record[]} records The Records that were loaded
19101          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19102          */
19103         load : true,
19104         /**
19105          * @event loadexception
19106          * Fires if an exception occurs in the Proxy during loading.
19107          * Called with the signature of the Proxy's "loadexception" event.
19108          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19109          * 
19110          * @param {Proxy} 
19111          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19112          * @param {Object} load options 
19113          * @param {Object} jsonData from your request (normally this contains the Exception)
19114          */
19115         loadexception : true
19116     });
19117     
19118     if(this.proxy){
19119         this.proxy = Roo.factory(this.proxy, Roo.data);
19120         this.proxy.xmodule = this.xmodule || false;
19121         this.relayEvents(this.proxy,  ["loadexception"]);
19122     }
19123     this.sortToggle = {};
19124
19125     Roo.data.Store.superclass.constructor.call(this);
19126
19127     if(this.inlineData){
19128         this.loadData(this.inlineData);
19129         delete this.inlineData;
19130     }
19131 };
19132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19133      /**
19134     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19135     * without a remote query - used by combo/forms at present.
19136     */
19137     
19138     /**
19139     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19140     */
19141     /**
19142     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19143     */
19144     /**
19145     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19146     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19147     */
19148     /**
19149     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19150     * on any HTTP request
19151     */
19152     /**
19153     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19154     */
19155     /**
19156     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19157     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19158     */
19159     remoteSort : false,
19160
19161     /**
19162     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19163      * loaded or when a record is removed. (defaults to false).
19164     */
19165     pruneModifiedRecords : false,
19166
19167     // private
19168     lastOptions : null,
19169
19170     /**
19171      * Add Records to the Store and fires the add event.
19172      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19173      */
19174     add : function(records){
19175         records = [].concat(records);
19176         for(var i = 0, len = records.length; i < len; i++){
19177             records[i].join(this);
19178         }
19179         var index = this.data.length;
19180         this.data.addAll(records);
19181         this.fireEvent("add", this, records, index);
19182     },
19183
19184     /**
19185      * Remove a Record from the Store and fires the remove event.
19186      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19187      */
19188     remove : function(record){
19189         var index = this.data.indexOf(record);
19190         this.data.removeAt(index);
19191         if(this.pruneModifiedRecords){
19192             this.modified.remove(record);
19193         }
19194         this.fireEvent("remove", this, record, index);
19195     },
19196
19197     /**
19198      * Remove all Records from the Store and fires the clear event.
19199      */
19200     removeAll : function(){
19201         this.data.clear();
19202         if(this.pruneModifiedRecords){
19203             this.modified = [];
19204         }
19205         this.fireEvent("clear", this);
19206     },
19207
19208     /**
19209      * Inserts Records to the Store at the given index and fires the add event.
19210      * @param {Number} index The start index at which to insert the passed Records.
19211      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19212      */
19213     insert : function(index, records){
19214         records = [].concat(records);
19215         for(var i = 0, len = records.length; i < len; i++){
19216             this.data.insert(index, records[i]);
19217             records[i].join(this);
19218         }
19219         this.fireEvent("add", this, records, index);
19220     },
19221
19222     /**
19223      * Get the index within the cache of the passed Record.
19224      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19225      * @return {Number} The index of the passed Record. Returns -1 if not found.
19226      */
19227     indexOf : function(record){
19228         return this.data.indexOf(record);
19229     },
19230
19231     /**
19232      * Get the index within the cache of the Record with the passed id.
19233      * @param {String} id The id of the Record to find.
19234      * @return {Number} The index of the Record. Returns -1 if not found.
19235      */
19236     indexOfId : function(id){
19237         return this.data.indexOfKey(id);
19238     },
19239
19240     /**
19241      * Get the Record with the specified id.
19242      * @param {String} id The id of the Record to find.
19243      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19244      */
19245     getById : function(id){
19246         return this.data.key(id);
19247     },
19248
19249     /**
19250      * Get the Record at the specified index.
19251      * @param {Number} index The index of the Record to find.
19252      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19253      */
19254     getAt : function(index){
19255         return this.data.itemAt(index);
19256     },
19257
19258     /**
19259      * Returns a range of Records between specified indices.
19260      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19261      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19262      * @return {Roo.data.Record[]} An array of Records
19263      */
19264     getRange : function(start, end){
19265         return this.data.getRange(start, end);
19266     },
19267
19268     // private
19269     storeOptions : function(o){
19270         o = Roo.apply({}, o);
19271         delete o.callback;
19272         delete o.scope;
19273         this.lastOptions = o;
19274     },
19275
19276     /**
19277      * Loads the Record cache from the configured Proxy using the configured Reader.
19278      * <p>
19279      * If using remote paging, then the first load call must specify the <em>start</em>
19280      * and <em>limit</em> properties in the options.params property to establish the initial
19281      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19282      * <p>
19283      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19284      * and this call will return before the new data has been loaded. Perform any post-processing
19285      * in a callback function, or in a "load" event handler.</strong>
19286      * <p>
19287      * @param {Object} options An object containing properties which control loading options:<ul>
19288      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19289      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19290      * passed the following arguments:<ul>
19291      * <li>r : Roo.data.Record[]</li>
19292      * <li>options: Options object from the load call</li>
19293      * <li>success: Boolean success indicator</li></ul></li>
19294      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19295      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19296      * </ul>
19297      */
19298     load : function(options){
19299         options = options || {};
19300         if(this.fireEvent("beforeload", this, options) !== false){
19301             this.storeOptions(options);
19302             var p = Roo.apply(options.params || {}, this.baseParams);
19303             // if meta was not loaded from remote source.. try requesting it.
19304             if (!this.reader.metaFromRemote) {
19305                 p._requestMeta = 1;
19306             }
19307             if(this.sortInfo && this.remoteSort){
19308                 var pn = this.paramNames;
19309                 p[pn["sort"]] = this.sortInfo.field;
19310                 p[pn["dir"]] = this.sortInfo.direction;
19311             }
19312             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19313         }
19314     },
19315
19316     /**
19317      * Reloads the Record cache from the configured Proxy using the configured Reader and
19318      * the options from the last load operation performed.
19319      * @param {Object} options (optional) An object containing properties which may override the options
19320      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19321      * the most recently used options are reused).
19322      */
19323     reload : function(options){
19324         this.load(Roo.applyIf(options||{}, this.lastOptions));
19325     },
19326
19327     // private
19328     // Called as a callback by the Reader during a load operation.
19329     loadRecords : function(o, options, success){
19330         if(!o || success === false){
19331             if(success !== false){
19332                 this.fireEvent("load", this, [], options);
19333             }
19334             if(options.callback){
19335                 options.callback.call(options.scope || this, [], options, false);
19336             }
19337             return;
19338         }
19339         // if data returned failure - throw an exception.
19340         if (o.success === false) {
19341             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19342             return;
19343         }
19344         var r = o.records, t = o.totalRecords || r.length;
19345         if(!options || options.add !== true){
19346             if(this.pruneModifiedRecords){
19347                 this.modified = [];
19348             }
19349             for(var i = 0, len = r.length; i < len; i++){
19350                 r[i].join(this);
19351             }
19352             if(this.snapshot){
19353                 this.data = this.snapshot;
19354                 delete this.snapshot;
19355             }
19356             this.data.clear();
19357             this.data.addAll(r);
19358             this.totalLength = t;
19359             this.applySort();
19360             this.fireEvent("datachanged", this);
19361         }else{
19362             this.totalLength = Math.max(t, this.data.length+r.length);
19363             this.add(r);
19364         }
19365         this.fireEvent("load", this, r, options);
19366         if(options.callback){
19367             options.callback.call(options.scope || this, r, options, true);
19368         }
19369     },
19370
19371     /**
19372      * Loads data from a passed data block. A Reader which understands the format of the data
19373      * must have been configured in the constructor.
19374      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19375      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19376      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19377      */
19378     loadData : function(o, append){
19379         var r = this.reader.readRecords(o);
19380         this.loadRecords(r, {add: append}, true);
19381     },
19382
19383     /**
19384      * Gets the number of cached records.
19385      * <p>
19386      * <em>If using paging, this may not be the total size of the dataset. If the data object
19387      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19388      * the data set size</em>
19389      */
19390     getCount : function(){
19391         return this.data.length || 0;
19392     },
19393
19394     /**
19395      * Gets the total number of records in the dataset as returned by the server.
19396      * <p>
19397      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19398      * the dataset size</em>
19399      */
19400     getTotalCount : function(){
19401         return this.totalLength || 0;
19402     },
19403
19404     /**
19405      * Returns the sort state of the Store as an object with two properties:
19406      * <pre><code>
19407  field {String} The name of the field by which the Records are sorted
19408  direction {String} The sort order, "ASC" or "DESC"
19409      * </code></pre>
19410      */
19411     getSortState : function(){
19412         return this.sortInfo;
19413     },
19414
19415     // private
19416     applySort : function(){
19417         if(this.sortInfo && !this.remoteSort){
19418             var s = this.sortInfo, f = s.field;
19419             var st = this.fields.get(f).sortType;
19420             var fn = function(r1, r2){
19421                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19422                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19423             };
19424             this.data.sort(s.direction, fn);
19425             if(this.snapshot && this.snapshot != this.data){
19426                 this.snapshot.sort(s.direction, fn);
19427             }
19428         }
19429     },
19430
19431     /**
19432      * Sets the default sort column and order to be used by the next load operation.
19433      * @param {String} fieldName The name of the field to sort by.
19434      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19435      */
19436     setDefaultSort : function(field, dir){
19437         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19438     },
19439
19440     /**
19441      * Sort the Records.
19442      * If remote sorting is used, the sort is performed on the server, and the cache is
19443      * reloaded. If local sorting is used, the cache is sorted internally.
19444      * @param {String} fieldName The name of the field to sort by.
19445      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19446      */
19447     sort : function(fieldName, dir){
19448         var f = this.fields.get(fieldName);
19449         if(!dir){
19450             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19451                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19452             }else{
19453                 dir = f.sortDir;
19454             }
19455         }
19456         this.sortToggle[f.name] = dir;
19457         this.sortInfo = {field: f.name, direction: dir};
19458         if(!this.remoteSort){
19459             this.applySort();
19460             this.fireEvent("datachanged", this);
19461         }else{
19462             this.load(this.lastOptions);
19463         }
19464     },
19465
19466     /**
19467      * Calls the specified function for each of the Records in the cache.
19468      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19469      * Returning <em>false</em> aborts and exits the iteration.
19470      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19471      */
19472     each : function(fn, scope){
19473         this.data.each(fn, scope);
19474     },
19475
19476     /**
19477      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19478      * (e.g., during paging).
19479      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19480      */
19481     getModifiedRecords : function(){
19482         return this.modified;
19483     },
19484
19485     // private
19486     createFilterFn : function(property, value, anyMatch){
19487         if(!value.exec){ // not a regex
19488             value = String(value);
19489             if(value.length == 0){
19490                 return false;
19491             }
19492             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19493         }
19494         return function(r){
19495             return value.test(r.data[property]);
19496         };
19497     },
19498
19499     /**
19500      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19501      * @param {String} property A field on your records
19502      * @param {Number} start The record index to start at (defaults to 0)
19503      * @param {Number} end The last record index to include (defaults to length - 1)
19504      * @return {Number} The sum
19505      */
19506     sum : function(property, start, end){
19507         var rs = this.data.items, v = 0;
19508         start = start || 0;
19509         end = (end || end === 0) ? end : rs.length-1;
19510
19511         for(var i = start; i <= end; i++){
19512             v += (rs[i].data[property] || 0);
19513         }
19514         return v;
19515     },
19516
19517     /**
19518      * Filter the records by a specified property.
19519      * @param {String} field A field on your records
19520      * @param {String/RegExp} value Either a string that the field
19521      * should start with or a RegExp to test against the field
19522      * @param {Boolean} anyMatch True to match any part not just the beginning
19523      */
19524     filter : function(property, value, anyMatch){
19525         var fn = this.createFilterFn(property, value, anyMatch);
19526         return fn ? this.filterBy(fn) : this.clearFilter();
19527     },
19528
19529     /**
19530      * Filter by a function. The specified function will be called with each
19531      * record in this data source. If the function returns true the record is included,
19532      * otherwise it is filtered.
19533      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19534      * @param {Object} scope (optional) The scope of the function (defaults to this)
19535      */
19536     filterBy : function(fn, scope){
19537         this.snapshot = this.snapshot || this.data;
19538         this.data = this.queryBy(fn, scope||this);
19539         this.fireEvent("datachanged", this);
19540     },
19541
19542     /**
19543      * Query the records by a specified property.
19544      * @param {String} field A field on your records
19545      * @param {String/RegExp} value Either a string that the field
19546      * should start with or a RegExp to test against the field
19547      * @param {Boolean} anyMatch True to match any part not just the beginning
19548      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19549      */
19550     query : function(property, value, anyMatch){
19551         var fn = this.createFilterFn(property, value, anyMatch);
19552         return fn ? this.queryBy(fn) : this.data.clone();
19553     },
19554
19555     /**
19556      * Query by a function. The specified function will be called with each
19557      * record in this data source. If the function returns true the record is included
19558      * in the results.
19559      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19560      * @param {Object} scope (optional) The scope of the function (defaults to this)
19561       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19562      **/
19563     queryBy : function(fn, scope){
19564         var data = this.snapshot || this.data;
19565         return data.filterBy(fn, scope||this);
19566     },
19567
19568     /**
19569      * Collects unique values for a particular dataIndex from this store.
19570      * @param {String} dataIndex The property to collect
19571      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19572      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19573      * @return {Array} An array of the unique values
19574      **/
19575     collect : function(dataIndex, allowNull, bypassFilter){
19576         var d = (bypassFilter === true && this.snapshot) ?
19577                 this.snapshot.items : this.data.items;
19578         var v, sv, r = [], l = {};
19579         for(var i = 0, len = d.length; i < len; i++){
19580             v = d[i].data[dataIndex];
19581             sv = String(v);
19582             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19583                 l[sv] = true;
19584                 r[r.length] = v;
19585             }
19586         }
19587         return r;
19588     },
19589
19590     /**
19591      * Revert to a view of the Record cache with no filtering applied.
19592      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19593      */
19594     clearFilter : function(suppressEvent){
19595         if(this.snapshot && this.snapshot != this.data){
19596             this.data = this.snapshot;
19597             delete this.snapshot;
19598             if(suppressEvent !== true){
19599                 this.fireEvent("datachanged", this);
19600             }
19601         }
19602     },
19603
19604     // private
19605     afterEdit : function(record){
19606         if(this.modified.indexOf(record) == -1){
19607             this.modified.push(record);
19608         }
19609         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19610     },
19611
19612     // private
19613     afterReject : function(record){
19614         this.modified.remove(record);
19615         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19616     },
19617
19618     // private
19619     afterCommit : function(record){
19620         this.modified.remove(record);
19621         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19622     },
19623
19624     /**
19625      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19626      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19627      */
19628     commitChanges : function(){
19629         var m = this.modified.slice(0);
19630         this.modified = [];
19631         for(var i = 0, len = m.length; i < len; i++){
19632             m[i].commit();
19633         }
19634     },
19635
19636     /**
19637      * Cancel outstanding changes on all changed records.
19638      */
19639     rejectChanges : function(){
19640         var m = this.modified.slice(0);
19641         this.modified = [];
19642         for(var i = 0, len = m.length; i < len; i++){
19643             m[i].reject();
19644         }
19645     },
19646
19647     onMetaChange : function(meta, rtype, o){
19648         this.recordType = rtype;
19649         this.fields = rtype.prototype.fields;
19650         delete this.snapshot;
19651         this.sortInfo = meta.sortInfo || this.sortInfo;
19652         this.modified = [];
19653         this.fireEvent('metachange', this, this.reader.meta);
19654     }
19655 });/*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665
19666 /**
19667  * @class Roo.data.SimpleStore
19668  * @extends Roo.data.Store
19669  * Small helper class to make creating Stores from Array data easier.
19670  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19671  * @cfg {Array} fields An array of field definition objects, or field name strings.
19672  * @cfg {Array} data The multi-dimensional array of data
19673  * @constructor
19674  * @param {Object} config
19675  */
19676 Roo.data.SimpleStore = function(config){
19677     Roo.data.SimpleStore.superclass.constructor.call(this, {
19678         isLocal : true,
19679         reader: new Roo.data.ArrayReader({
19680                 id: config.id
19681             },
19682             Roo.data.Record.create(config.fields)
19683         ),
19684         proxy : new Roo.data.MemoryProxy(config.data)
19685     });
19686     this.load();
19687 };
19688 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19689  * Based on:
19690  * Ext JS Library 1.1.1
19691  * Copyright(c) 2006-2007, Ext JS, LLC.
19692  *
19693  * Originally Released Under LGPL - original licence link has changed is not relivant.
19694  *
19695  * Fork - LGPL
19696  * <script type="text/javascript">
19697  */
19698
19699 /**
19700 /**
19701  * @extends Roo.data.Store
19702  * @class Roo.data.JsonStore
19703  * Small helper class to make creating Stores for JSON data easier. <br/>
19704 <pre><code>
19705 var store = new Roo.data.JsonStore({
19706     url: 'get-images.php',
19707     root: 'images',
19708     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19709 });
19710 </code></pre>
19711  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19712  * JsonReader and HttpProxy (unless inline data is provided).</b>
19713  * @cfg {Array} fields An array of field definition objects, or field name strings.
19714  * @constructor
19715  * @param {Object} config
19716  */
19717 Roo.data.JsonStore = function(c){
19718     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19719         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19720         reader: new Roo.data.JsonReader(c, c.fields)
19721     }));
19722 };
19723 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19724  * Based on:
19725  * Ext JS Library 1.1.1
19726  * Copyright(c) 2006-2007, Ext JS, LLC.
19727  *
19728  * Originally Released Under LGPL - original licence link has changed is not relivant.
19729  *
19730  * Fork - LGPL
19731  * <script type="text/javascript">
19732  */
19733
19734  
19735 Roo.data.Field = function(config){
19736     if(typeof config == "string"){
19737         config = {name: config};
19738     }
19739     Roo.apply(this, config);
19740     
19741     if(!this.type){
19742         this.type = "auto";
19743     }
19744     
19745     var st = Roo.data.SortTypes;
19746     // named sortTypes are supported, here we look them up
19747     if(typeof this.sortType == "string"){
19748         this.sortType = st[this.sortType];
19749     }
19750     
19751     // set default sortType for strings and dates
19752     if(!this.sortType){
19753         switch(this.type){
19754             case "string":
19755                 this.sortType = st.asUCString;
19756                 break;
19757             case "date":
19758                 this.sortType = st.asDate;
19759                 break;
19760             default:
19761                 this.sortType = st.none;
19762         }
19763     }
19764
19765     // define once
19766     var stripRe = /[\$,%]/g;
19767
19768     // prebuilt conversion function for this field, instead of
19769     // switching every time we're reading a value
19770     if(!this.convert){
19771         var cv, dateFormat = this.dateFormat;
19772         switch(this.type){
19773             case "":
19774             case "auto":
19775             case undefined:
19776                 cv = function(v){ return v; };
19777                 break;
19778             case "string":
19779                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19780                 break;
19781             case "int":
19782                 cv = function(v){
19783                     return v !== undefined && v !== null && v !== '' ?
19784                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19785                     };
19786                 break;
19787             case "float":
19788                 cv = function(v){
19789                     return v !== undefined && v !== null && v !== '' ?
19790                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19791                     };
19792                 break;
19793             case "bool":
19794             case "boolean":
19795                 cv = function(v){ return v === true || v === "true" || v == 1; };
19796                 break;
19797             case "date":
19798                 cv = function(v){
19799                     if(!v){
19800                         return '';
19801                     }
19802                     if(v instanceof Date){
19803                         return v;
19804                     }
19805                     if(dateFormat){
19806                         if(dateFormat == "timestamp"){
19807                             return new Date(v*1000);
19808                         }
19809                         return Date.parseDate(v, dateFormat);
19810                     }
19811                     var parsed = Date.parse(v);
19812                     return parsed ? new Date(parsed) : null;
19813                 };
19814              break;
19815             
19816         }
19817         this.convert = cv;
19818     }
19819 };
19820
19821 Roo.data.Field.prototype = {
19822     dateFormat: null,
19823     defaultValue: "",
19824     mapping: null,
19825     sortType : null,
19826     sortDir : "ASC"
19827 };/*
19828  * Based on:
19829  * Ext JS Library 1.1.1
19830  * Copyright(c) 2006-2007, Ext JS, LLC.
19831  *
19832  * Originally Released Under LGPL - original licence link has changed is not relivant.
19833  *
19834  * Fork - LGPL
19835  * <script type="text/javascript">
19836  */
19837  
19838 // Base class for reading structured data from a data source.  This class is intended to be
19839 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19840
19841 /**
19842  * @class Roo.data.DataReader
19843  * Base class for reading structured data from a data source.  This class is intended to be
19844  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19845  */
19846
19847 Roo.data.DataReader = function(meta, recordType){
19848     
19849     this.meta = meta;
19850     
19851     this.recordType = recordType instanceof Array ? 
19852         Roo.data.Record.create(recordType) : recordType;
19853 };
19854
19855 Roo.data.DataReader.prototype = {
19856      /**
19857      * Create an empty record
19858      * @param {Object} data (optional) - overlay some values
19859      * @return {Roo.data.Record} record created.
19860      */
19861     newRow :  function(d) {
19862         var da =  {};
19863         this.recordType.prototype.fields.each(function(c) {
19864             switch( c.type) {
19865                 case 'int' : da[c.name] = 0; break;
19866                 case 'date' : da[c.name] = new Date(); break;
19867                 case 'float' : da[c.name] = 0.0; break;
19868                 case 'boolean' : da[c.name] = false; break;
19869                 default : da[c.name] = ""; break;
19870             }
19871             
19872         });
19873         return new this.recordType(Roo.apply(da, d));
19874     }
19875     
19876 };/*
19877  * Based on:
19878  * Ext JS Library 1.1.1
19879  * Copyright(c) 2006-2007, Ext JS, LLC.
19880  *
19881  * Originally Released Under LGPL - original licence link has changed is not relivant.
19882  *
19883  * Fork - LGPL
19884  * <script type="text/javascript">
19885  */
19886
19887 /**
19888  * @class Roo.data.DataProxy
19889  * @extends Roo.data.Observable
19890  * This class is an abstract base class for implementations which provide retrieval of
19891  * unformatted data objects.<br>
19892  * <p>
19893  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19894  * (of the appropriate type which knows how to parse the data object) to provide a block of
19895  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19896  * <p>
19897  * Custom implementations must implement the load method as described in
19898  * {@link Roo.data.HttpProxy#load}.
19899  */
19900 Roo.data.DataProxy = function(){
19901     this.addEvents({
19902         /**
19903          * @event beforeload
19904          * Fires before a network request is made to retrieve a data object.
19905          * @param {Object} This DataProxy object.
19906          * @param {Object} params The params parameter to the load function.
19907          */
19908         beforeload : true,
19909         /**
19910          * @event load
19911          * Fires before the load method's callback is called.
19912          * @param {Object} This DataProxy object.
19913          * @param {Object} o The data object.
19914          * @param {Object} arg The callback argument object passed to the load function.
19915          */
19916         load : true,
19917         /**
19918          * @event loadexception
19919          * Fires if an Exception occurs during data retrieval.
19920          * @param {Object} This DataProxy object.
19921          * @param {Object} o The data object.
19922          * @param {Object} arg The callback argument object passed to the load function.
19923          * @param {Object} e The Exception.
19924          */
19925         loadexception : true
19926     });
19927     Roo.data.DataProxy.superclass.constructor.call(this);
19928 };
19929
19930 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19931
19932     /**
19933      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19934      */
19935 /*
19936  * Based on:
19937  * Ext JS Library 1.1.1
19938  * Copyright(c) 2006-2007, Ext JS, LLC.
19939  *
19940  * Originally Released Under LGPL - original licence link has changed is not relivant.
19941  *
19942  * Fork - LGPL
19943  * <script type="text/javascript">
19944  */
19945 /**
19946  * @class Roo.data.MemoryProxy
19947  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19948  * to the Reader when its load method is called.
19949  * @constructor
19950  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19951  */
19952 Roo.data.MemoryProxy = function(data){
19953     if (data.data) {
19954         data = data.data;
19955     }
19956     Roo.data.MemoryProxy.superclass.constructor.call(this);
19957     this.data = data;
19958 };
19959
19960 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19961     /**
19962      * Load data from the requested source (in this case an in-memory
19963      * data object passed to the constructor), read the data object into
19964      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19965      * process that block using the passed callback.
19966      * @param {Object} params This parameter is not used by the MemoryProxy class.
19967      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19968      * object into a block of Roo.data.Records.
19969      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19970      * The function must be passed <ul>
19971      * <li>The Record block object</li>
19972      * <li>The "arg" argument from the load function</li>
19973      * <li>A boolean success indicator</li>
19974      * </ul>
19975      * @param {Object} scope The scope in which to call the callback
19976      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19977      */
19978     load : function(params, reader, callback, scope, arg){
19979         params = params || {};
19980         var result;
19981         try {
19982             result = reader.readRecords(this.data);
19983         }catch(e){
19984             this.fireEvent("loadexception", this, arg, null, e);
19985             callback.call(scope, null, arg, false);
19986             return;
19987         }
19988         callback.call(scope, result, arg, true);
19989     },
19990     
19991     // private
19992     update : function(params, records){
19993         
19994     }
19995 });/*
19996  * Based on:
19997  * Ext JS Library 1.1.1
19998  * Copyright(c) 2006-2007, Ext JS, LLC.
19999  *
20000  * Originally Released Under LGPL - original licence link has changed is not relivant.
20001  *
20002  * Fork - LGPL
20003  * <script type="text/javascript">
20004  */
20005 /**
20006  * @class Roo.data.HttpProxy
20007  * @extends Roo.data.DataProxy
20008  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20009  * configured to reference a certain URL.<br><br>
20010  * <p>
20011  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20012  * from which the running page was served.<br><br>
20013  * <p>
20014  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20015  * <p>
20016  * Be aware that to enable the browser to parse an XML document, the server must set
20017  * the Content-Type header in the HTTP response to "text/xml".
20018  * @constructor
20019  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20020  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20021  * will be used to make the request.
20022  */
20023 Roo.data.HttpProxy = function(conn){
20024     Roo.data.HttpProxy.superclass.constructor.call(this);
20025     // is conn a conn config or a real conn?
20026     this.conn = conn;
20027     this.useAjax = !conn || !conn.events;
20028   
20029 };
20030
20031 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20032     // thse are take from connection...
20033     
20034     /**
20035      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20036      */
20037     /**
20038      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20039      * extra parameters to each request made by this object. (defaults to undefined)
20040      */
20041     /**
20042      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20043      *  to each request made by this object. (defaults to undefined)
20044      */
20045     /**
20046      * @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)
20047      */
20048     /**
20049      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20050      */
20051      /**
20052      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20053      * @type Boolean
20054      */
20055   
20056
20057     /**
20058      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20059      * @type Boolean
20060      */
20061     /**
20062      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20063      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20064      * a finer-grained basis than the DataProxy events.
20065      */
20066     getConnection : function(){
20067         return this.useAjax ? Roo.Ajax : this.conn;
20068     },
20069
20070     /**
20071      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20072      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20073      * process that block using the passed callback.
20074      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20075      * for the request to the remote server.
20076      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20077      * object into a block of Roo.data.Records.
20078      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20079      * The function must be passed <ul>
20080      * <li>The Record block object</li>
20081      * <li>The "arg" argument from the load function</li>
20082      * <li>A boolean success indicator</li>
20083      * </ul>
20084      * @param {Object} scope The scope in which to call the callback
20085      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20086      */
20087     load : function(params, reader, callback, scope, arg){
20088         if(this.fireEvent("beforeload", this, params) !== false){
20089             var  o = {
20090                 params : params || {},
20091                 request: {
20092                     callback : callback,
20093                     scope : scope,
20094                     arg : arg
20095                 },
20096                 reader: reader,
20097                 callback : this.loadResponse,
20098                 scope: this
20099             };
20100             if(this.useAjax){
20101                 Roo.applyIf(o, this.conn);
20102                 if(this.activeRequest){
20103                     Roo.Ajax.abort(this.activeRequest);
20104                 }
20105                 this.activeRequest = Roo.Ajax.request(o);
20106             }else{
20107                 this.conn.request(o);
20108             }
20109         }else{
20110             callback.call(scope||this, null, arg, false);
20111         }
20112     },
20113
20114     // private
20115     loadResponse : function(o, success, response){
20116         delete this.activeRequest;
20117         if(!success){
20118             this.fireEvent("loadexception", this, o, response);
20119             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20120             return;
20121         }
20122         var result;
20123         try {
20124             result = o.reader.read(response);
20125         }catch(e){
20126             this.fireEvent("loadexception", this, o, response, e);
20127             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20128             return;
20129         }
20130         
20131         this.fireEvent("load", this, o, o.request.arg);
20132         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20133     },
20134
20135     // private
20136     update : function(dataSet){
20137
20138     },
20139
20140     // private
20141     updateResponse : function(dataSet){
20142
20143     }
20144 });/*
20145  * Based on:
20146  * Ext JS Library 1.1.1
20147  * Copyright(c) 2006-2007, Ext JS, LLC.
20148  *
20149  * Originally Released Under LGPL - original licence link has changed is not relivant.
20150  *
20151  * Fork - LGPL
20152  * <script type="text/javascript">
20153  */
20154
20155 /**
20156  * @class Roo.data.ScriptTagProxy
20157  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20158  * other than the originating domain of the running page.<br><br>
20159  * <p>
20160  * <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
20161  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20162  * <p>
20163  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20164  * source code that is used as the source inside a &lt;script> tag.<br><br>
20165  * <p>
20166  * In order for the browser to process the returned data, the server must wrap the data object
20167  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20168  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20169  * depending on whether the callback name was passed:
20170  * <p>
20171  * <pre><code>
20172 boolean scriptTag = false;
20173 String cb = request.getParameter("callback");
20174 if (cb != null) {
20175     scriptTag = true;
20176     response.setContentType("text/javascript");
20177 } else {
20178     response.setContentType("application/x-json");
20179 }
20180 Writer out = response.getWriter();
20181 if (scriptTag) {
20182     out.write(cb + "(");
20183 }
20184 out.print(dataBlock.toJsonString());
20185 if (scriptTag) {
20186     out.write(");");
20187 }
20188 </pre></code>
20189  *
20190  * @constructor
20191  * @param {Object} config A configuration object.
20192  */
20193 Roo.data.ScriptTagProxy = function(config){
20194     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20195     Roo.apply(this, config);
20196     this.head = document.getElementsByTagName("head")[0];
20197 };
20198
20199 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20200
20201 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20202     /**
20203      * @cfg {String} url The URL from which to request the data object.
20204      */
20205     /**
20206      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20207      */
20208     timeout : 30000,
20209     /**
20210      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20211      * the server the name of the callback function set up by the load call to process the returned data object.
20212      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20213      * javascript output which calls this named function passing the data object as its only parameter.
20214      */
20215     callbackParam : "callback",
20216     /**
20217      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20218      * name to the request.
20219      */
20220     nocache : true,
20221
20222     /**
20223      * Load data from the configured URL, read the data object into
20224      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20225      * process that block using the passed callback.
20226      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20227      * for the request to the remote server.
20228      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20229      * object into a block of Roo.data.Records.
20230      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20231      * The function must be passed <ul>
20232      * <li>The Record block object</li>
20233      * <li>The "arg" argument from the load function</li>
20234      * <li>A boolean success indicator</li>
20235      * </ul>
20236      * @param {Object} scope The scope in which to call the callback
20237      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20238      */
20239     load : function(params, reader, callback, scope, arg){
20240         if(this.fireEvent("beforeload", this, params) !== false){
20241
20242             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20243
20244             var url = this.url;
20245             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20246             if(this.nocache){
20247                 url += "&_dc=" + (new Date().getTime());
20248             }
20249             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20250             var trans = {
20251                 id : transId,
20252                 cb : "stcCallback"+transId,
20253                 scriptId : "stcScript"+transId,
20254                 params : params,
20255                 arg : arg,
20256                 url : url,
20257                 callback : callback,
20258                 scope : scope,
20259                 reader : reader
20260             };
20261             var conn = this;
20262
20263             window[trans.cb] = function(o){
20264                 conn.handleResponse(o, trans);
20265             };
20266
20267             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20268
20269             if(this.autoAbort !== false){
20270                 this.abort();
20271             }
20272
20273             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20274
20275             var script = document.createElement("script");
20276             script.setAttribute("src", url);
20277             script.setAttribute("type", "text/javascript");
20278             script.setAttribute("id", trans.scriptId);
20279             this.head.appendChild(script);
20280
20281             this.trans = trans;
20282         }else{
20283             callback.call(scope||this, null, arg, false);
20284         }
20285     },
20286
20287     // private
20288     isLoading : function(){
20289         return this.trans ? true : false;
20290     },
20291
20292     /**
20293      * Abort the current server request.
20294      */
20295     abort : function(){
20296         if(this.isLoading()){
20297             this.destroyTrans(this.trans);
20298         }
20299     },
20300
20301     // private
20302     destroyTrans : function(trans, isLoaded){
20303         this.head.removeChild(document.getElementById(trans.scriptId));
20304         clearTimeout(trans.timeoutId);
20305         if(isLoaded){
20306             window[trans.cb] = undefined;
20307             try{
20308                 delete window[trans.cb];
20309             }catch(e){}
20310         }else{
20311             // if hasn't been loaded, wait for load to remove it to prevent script error
20312             window[trans.cb] = function(){
20313                 window[trans.cb] = undefined;
20314                 try{
20315                     delete window[trans.cb];
20316                 }catch(e){}
20317             };
20318         }
20319     },
20320
20321     // private
20322     handleResponse : function(o, trans){
20323         this.trans = false;
20324         this.destroyTrans(trans, true);
20325         var result;
20326         try {
20327             result = trans.reader.readRecords(o);
20328         }catch(e){
20329             this.fireEvent("loadexception", this, o, trans.arg, e);
20330             trans.callback.call(trans.scope||window, null, trans.arg, false);
20331             return;
20332         }
20333         this.fireEvent("load", this, o, trans.arg);
20334         trans.callback.call(trans.scope||window, result, trans.arg, true);
20335     },
20336
20337     // private
20338     handleFailure : function(trans){
20339         this.trans = false;
20340         this.destroyTrans(trans, false);
20341         this.fireEvent("loadexception", this, null, trans.arg);
20342         trans.callback.call(trans.scope||window, null, trans.arg, false);
20343     }
20344 });/*
20345  * Based on:
20346  * Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  *
20349  * Originally Released Under LGPL - original licence link has changed is not relivant.
20350  *
20351  * Fork - LGPL
20352  * <script type="text/javascript">
20353  */
20354
20355 /**
20356  * @class Roo.data.JsonReader
20357  * @extends Roo.data.DataReader
20358  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20359  * based on mappings in a provided Roo.data.Record constructor.
20360  * 
20361  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20362  * in the reply previously. 
20363  * 
20364  * <p>
20365  * Example code:
20366  * <pre><code>
20367 var RecordDef = Roo.data.Record.create([
20368     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20369     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20370 ]);
20371 var myReader = new Roo.data.JsonReader({
20372     totalProperty: "results",    // The property which contains the total dataset size (optional)
20373     root: "rows",                // The property which contains an Array of row objects
20374     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20375 }, RecordDef);
20376 </code></pre>
20377  * <p>
20378  * This would consume a JSON file like this:
20379  * <pre><code>
20380 { 'results': 2, 'rows': [
20381     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20382     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20383 }
20384 </code></pre>
20385  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20386  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20387  * paged from the remote server.
20388  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20389  * @cfg {String} root name of the property which contains the Array of row objects.
20390  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20391  * @constructor
20392  * Create a new JsonReader
20393  * @param {Object} meta Metadata configuration options
20394  * @param {Object} recordType Either an Array of field definition objects,
20395  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20396  */
20397 Roo.data.JsonReader = function(meta, recordType){
20398     
20399     meta = meta || {};
20400     // set some defaults:
20401     Roo.applyIf(meta, {
20402         totalProperty: 'total',
20403         successProperty : 'success',
20404         root : 'data',
20405         id : 'id'
20406     });
20407     
20408     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20409 };
20410 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20411     
20412     /**
20413      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20414      * Used by Store query builder to append _requestMeta to params.
20415      * 
20416      */
20417     metaFromRemote : false,
20418     /**
20419      * This method is only used by a DataProxy which has retrieved data from a remote server.
20420      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20421      * @return {Object} data A data block which is used by an Roo.data.Store object as
20422      * a cache of Roo.data.Records.
20423      */
20424     read : function(response){
20425         var json = response.responseText;
20426        
20427         var o = /* eval:var:o */ eval("("+json+")");
20428         if(!o) {
20429             throw {message: "JsonReader.read: Json object not found"};
20430         }
20431         
20432         if(o.metaData){
20433             
20434             delete this.ef;
20435             this.metaFromRemote = true;
20436             this.meta = o.metaData;
20437             this.recordType = Roo.data.Record.create(o.metaData.fields);
20438             this.onMetaChange(this.meta, this.recordType, o);
20439         }
20440         return this.readRecords(o);
20441     },
20442
20443     // private function a store will implement
20444     onMetaChange : function(meta, recordType, o){
20445
20446     },
20447
20448     /**
20449          * @ignore
20450          */
20451     simpleAccess: function(obj, subsc) {
20452         return obj[subsc];
20453     },
20454
20455         /**
20456          * @ignore
20457          */
20458     getJsonAccessor: function(){
20459         var re = /[\[\.]/;
20460         return function(expr) {
20461             try {
20462                 return(re.test(expr))
20463                     ? new Function("obj", "return obj." + expr)
20464                     : function(obj){
20465                         return obj[expr];
20466                     };
20467             } catch(e){}
20468             return Roo.emptyFn;
20469         };
20470     }(),
20471
20472     /**
20473      * Create a data block containing Roo.data.Records from an XML document.
20474      * @param {Object} o An object which contains an Array of row objects in the property specified
20475      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20476      * which contains the total size of the dataset.
20477      * @return {Object} data A data block which is used by an Roo.data.Store object as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(o){
20481         /**
20482          * After any data loads, the raw JSON data is available for further custom processing.
20483          * @type Object
20484          */
20485         this.jsonData = o;
20486         var s = this.meta, Record = this.recordType,
20487             f = Record.prototype.fields, fi = f.items, fl = f.length;
20488
20489 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20490         if (!this.ef) {
20491             if(s.totalProperty) {
20492                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20493                 }
20494                 if(s.successProperty) {
20495                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20496                 }
20497                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20498                 if (s.id) {
20499                         var g = this.getJsonAccessor(s.id);
20500                         this.getId = function(rec) {
20501                                 var r = g(rec);
20502                                 return (r === undefined || r === "") ? null : r;
20503                         };
20504                 } else {
20505                         this.getId = function(){return null;};
20506                 }
20507             this.ef = [];
20508             for(var jj = 0; jj < fl; jj++){
20509                 f = fi[jj];
20510                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20511                 this.ef[jj] = this.getJsonAccessor(map);
20512             }
20513         }
20514
20515         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20516         if(s.totalProperty){
20517             var vt = parseInt(this.getTotal(o), 10);
20518             if(!isNaN(vt)){
20519                 totalRecords = vt;
20520             }
20521         }
20522         if(s.successProperty){
20523             var vs = this.getSuccess(o);
20524             if(vs === false || vs === 'false'){
20525                 success = false;
20526             }
20527         }
20528         var records = [];
20529             for(var i = 0; i < c; i++){
20530                     var n = root[i];
20531                 var values = {};
20532                 var id = this.getId(n);
20533                 for(var j = 0; j < fl; j++){
20534                     f = fi[j];
20535                 var v = this.ef[j](n);
20536                 if (!f.convert) {
20537                     Roo.log('missing convert for ' + f.name);
20538                     Roo.log(f);
20539                     continue;
20540                 }
20541                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20542                 }
20543                 var record = new Record(values, id);
20544                 record.json = n;
20545                 records[i] = record;
20546             }
20547             return {
20548                 success : success,
20549                 records : records,
20550                 totalRecords : totalRecords
20551             };
20552     }
20553 });/*
20554  * Based on:
20555  * Ext JS Library 1.1.1
20556  * Copyright(c) 2006-2007, Ext JS, LLC.
20557  *
20558  * Originally Released Under LGPL - original licence link has changed is not relivant.
20559  *
20560  * Fork - LGPL
20561  * <script type="text/javascript">
20562  */
20563
20564 /**
20565  * @class Roo.data.XmlReader
20566  * @extends Roo.data.DataReader
20567  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20568  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20569  * <p>
20570  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20571  * header in the HTTP response must be set to "text/xml".</em>
20572  * <p>
20573  * Example code:
20574  * <pre><code>
20575 var RecordDef = Roo.data.Record.create([
20576    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20577    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20578 ]);
20579 var myReader = new Roo.data.XmlReader({
20580    totalRecords: "results", // The element which contains the total dataset size (optional)
20581    record: "row",           // The repeated element which contains row information
20582    id: "id"                 // The element within the row that provides an ID for the record (optional)
20583 }, RecordDef);
20584 </code></pre>
20585  * <p>
20586  * This would consume an XML file like this:
20587  * <pre><code>
20588 &lt;?xml?>
20589 &lt;dataset>
20590  &lt;results>2&lt;/results>
20591  &lt;row>
20592    &lt;id>1&lt;/id>
20593    &lt;name>Bill&lt;/name>
20594    &lt;occupation>Gardener&lt;/occupation>
20595  &lt;/row>
20596  &lt;row>
20597    &lt;id>2&lt;/id>
20598    &lt;name>Ben&lt;/name>
20599    &lt;occupation>Horticulturalist&lt;/occupation>
20600  &lt;/row>
20601 &lt;/dataset>
20602 </code></pre>
20603  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20604  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20605  * paged from the remote server.
20606  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20607  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20608  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20609  * a record identifier value.
20610  * @constructor
20611  * Create a new XmlReader
20612  * @param {Object} meta Metadata configuration options
20613  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20614  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20615  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20616  */
20617 Roo.data.XmlReader = function(meta, recordType){
20618     meta = meta || {};
20619     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20620 };
20621 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20622     /**
20623      * This method is only used by a DataProxy which has retrieved data from a remote server.
20624          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20625          * to contain a method called 'responseXML' that returns an XML document object.
20626      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20627      * a cache of Roo.data.Records.
20628      */
20629     read : function(response){
20630         var doc = response.responseXML;
20631         if(!doc) {
20632             throw {message: "XmlReader.read: XML Document not available"};
20633         }
20634         return this.readRecords(doc);
20635     },
20636
20637     /**
20638      * Create a data block containing Roo.data.Records from an XML document.
20639          * @param {Object} doc A parsed XML document.
20640      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20641      * a cache of Roo.data.Records.
20642      */
20643     readRecords : function(doc){
20644         /**
20645          * After any data loads/reads, the raw XML Document is available for further custom processing.
20646          * @type XMLDocument
20647          */
20648         this.xmlData = doc;
20649         var root = doc.documentElement || doc;
20650         var q = Roo.DomQuery;
20651         var recordType = this.recordType, fields = recordType.prototype.fields;
20652         var sid = this.meta.id;
20653         var totalRecords = 0, success = true;
20654         if(this.meta.totalRecords){
20655             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20656         }
20657         
20658         if(this.meta.success){
20659             var sv = q.selectValue(this.meta.success, root, true);
20660             success = sv !== false && sv !== 'false';
20661         }
20662         var records = [];
20663         var ns = q.select(this.meta.record, root);
20664         for(var i = 0, len = ns.length; i < len; i++) {
20665                 var n = ns[i];
20666                 var values = {};
20667                 var id = sid ? q.selectValue(sid, n) : undefined;
20668                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20669                     var f = fields.items[j];
20670                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20671                     v = f.convert(v);
20672                     values[f.name] = v;
20673                 }
20674                 var record = new recordType(values, id);
20675                 record.node = n;
20676                 records[records.length] = record;
20677             }
20678
20679             return {
20680                 success : success,
20681                 records : records,
20682                 totalRecords : totalRecords || records.length
20683             };
20684     }
20685 });/*
20686  * Based on:
20687  * Ext JS Library 1.1.1
20688  * Copyright(c) 2006-2007, Ext JS, LLC.
20689  *
20690  * Originally Released Under LGPL - original licence link has changed is not relivant.
20691  *
20692  * Fork - LGPL
20693  * <script type="text/javascript">
20694  */
20695
20696 /**
20697  * @class Roo.data.ArrayReader
20698  * @extends Roo.data.DataReader
20699  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20700  * Each element of that Array represents a row of data fields. The
20701  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20702  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20703  * <p>
20704  * Example code:.
20705  * <pre><code>
20706 var RecordDef = Roo.data.Record.create([
20707     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20708     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20709 ]);
20710 var myReader = new Roo.data.ArrayReader({
20711     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20712 }, RecordDef);
20713 </code></pre>
20714  * <p>
20715  * This would consume an Array like this:
20716  * <pre><code>
20717 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20718   </code></pre>
20719  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20720  * @constructor
20721  * Create a new JsonReader
20722  * @param {Object} meta Metadata configuration options.
20723  * @param {Object} recordType Either an Array of field definition objects
20724  * as specified to {@link Roo.data.Record#create},
20725  * or an {@link Roo.data.Record} object
20726  * created using {@link Roo.data.Record#create}.
20727  */
20728 Roo.data.ArrayReader = function(meta, recordType){
20729     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20730 };
20731
20732 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20733     /**
20734      * Create a data block containing Roo.data.Records from an XML document.
20735      * @param {Object} o An Array of row objects which represents the dataset.
20736      * @return {Object} data A data block which is used by an Roo.data.Store object as
20737      * a cache of Roo.data.Records.
20738      */
20739     readRecords : function(o){
20740         var sid = this.meta ? this.meta.id : null;
20741         var recordType = this.recordType, fields = recordType.prototype.fields;
20742         var records = [];
20743         var root = o;
20744             for(var i = 0; i < root.length; i++){
20745                     var n = root[i];
20746                 var values = {};
20747                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20748                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20749                 var f = fields.items[j];
20750                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20751                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20752                 v = f.convert(v);
20753                 values[f.name] = v;
20754             }
20755                 var record = new recordType(values, id);
20756                 record.json = n;
20757                 records[records.length] = record;
20758             }
20759             return {
20760                 records : records,
20761                 totalRecords : records.length
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775
20776 /**
20777  * @class Roo.data.Tree
20778  * @extends Roo.util.Observable
20779  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20780  * in the tree have most standard DOM functionality.
20781  * @constructor
20782  * @param {Node} root (optional) The root node
20783  */
20784 Roo.data.Tree = function(root){
20785    this.nodeHash = {};
20786    /**
20787     * The root node for this tree
20788     * @type Node
20789     */
20790    this.root = null;
20791    if(root){
20792        this.setRootNode(root);
20793    }
20794    this.addEvents({
20795        /**
20796         * @event append
20797         * Fires when a new child node is appended to a node in this tree.
20798         * @param {Tree} tree The owner tree
20799         * @param {Node} parent The parent node
20800         * @param {Node} node The newly appended node
20801         * @param {Number} index The index of the newly appended node
20802         */
20803        "append" : true,
20804        /**
20805         * @event remove
20806         * Fires when a child node is removed from a node in this tree.
20807         * @param {Tree} tree The owner tree
20808         * @param {Node} parent The parent node
20809         * @param {Node} node The child node removed
20810         */
20811        "remove" : true,
20812        /**
20813         * @event move
20814         * Fires when a node is moved to a new location in the tree
20815         * @param {Tree} tree The owner tree
20816         * @param {Node} node The node moved
20817         * @param {Node} oldParent The old parent of this node
20818         * @param {Node} newParent The new parent of this node
20819         * @param {Number} index The index it was moved to
20820         */
20821        "move" : true,
20822        /**
20823         * @event insert
20824         * Fires when a new child node is inserted in a node in this tree.
20825         * @param {Tree} tree The owner tree
20826         * @param {Node} parent The parent node
20827         * @param {Node} node The child node inserted
20828         * @param {Node} refNode The child node the node was inserted before
20829         */
20830        "insert" : true,
20831        /**
20832         * @event beforeappend
20833         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20834         * @param {Tree} tree The owner tree
20835         * @param {Node} parent The parent node
20836         * @param {Node} node The child node to be appended
20837         */
20838        "beforeappend" : true,
20839        /**
20840         * @event beforeremove
20841         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20842         * @param {Tree} tree The owner tree
20843         * @param {Node} parent The parent node
20844         * @param {Node} node The child node to be removed
20845         */
20846        "beforeremove" : true,
20847        /**
20848         * @event beforemove
20849         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20850         * @param {Tree} tree The owner tree
20851         * @param {Node} node The node being moved
20852         * @param {Node} oldParent The parent of the node
20853         * @param {Node} newParent The new parent the node is moving to
20854         * @param {Number} index The index it is being moved to
20855         */
20856        "beforemove" : true,
20857        /**
20858         * @event beforeinsert
20859         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20860         * @param {Tree} tree The owner tree
20861         * @param {Node} parent The parent node
20862         * @param {Node} node The child node to be inserted
20863         * @param {Node} refNode The child node the node is being inserted before
20864         */
20865        "beforeinsert" : true
20866    });
20867
20868     Roo.data.Tree.superclass.constructor.call(this);
20869 };
20870
20871 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20872     pathSeparator: "/",
20873
20874     proxyNodeEvent : function(){
20875         return this.fireEvent.apply(this, arguments);
20876     },
20877
20878     /**
20879      * Returns the root node for this tree.
20880      * @return {Node}
20881      */
20882     getRootNode : function(){
20883         return this.root;
20884     },
20885
20886     /**
20887      * Sets the root node for this tree.
20888      * @param {Node} node
20889      * @return {Node}
20890      */
20891     setRootNode : function(node){
20892         this.root = node;
20893         node.ownerTree = this;
20894         node.isRoot = true;
20895         this.registerNode(node);
20896         return node;
20897     },
20898
20899     /**
20900      * Gets a node in this tree by its id.
20901      * @param {String} id
20902      * @return {Node}
20903      */
20904     getNodeById : function(id){
20905         return this.nodeHash[id];
20906     },
20907
20908     registerNode : function(node){
20909         this.nodeHash[node.id] = node;
20910     },
20911
20912     unregisterNode : function(node){
20913         delete this.nodeHash[node.id];
20914     },
20915
20916     toString : function(){
20917         return "[Tree"+(this.id?" "+this.id:"")+"]";
20918     }
20919 });
20920
20921 /**
20922  * @class Roo.data.Node
20923  * @extends Roo.util.Observable
20924  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20925  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20926  * @constructor
20927  * @param {Object} attributes The attributes/config for the node
20928  */
20929 Roo.data.Node = function(attributes){
20930     /**
20931      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20932      * @type {Object}
20933      */
20934     this.attributes = attributes || {};
20935     this.leaf = this.attributes.leaf;
20936     /**
20937      * The node id. @type String
20938      */
20939     this.id = this.attributes.id;
20940     if(!this.id){
20941         this.id = Roo.id(null, "ynode-");
20942         this.attributes.id = this.id;
20943     }
20944     /**
20945      * All child nodes of this node. @type Array
20946      */
20947     this.childNodes = [];
20948     if(!this.childNodes.indexOf){ // indexOf is a must
20949         this.childNodes.indexOf = function(o){
20950             for(var i = 0, len = this.length; i < len; i++){
20951                 if(this[i] == o) {
20952                     return i;
20953                 }
20954             }
20955             return -1;
20956         };
20957     }
20958     /**
20959      * The parent node for this node. @type Node
20960      */
20961     this.parentNode = null;
20962     /**
20963      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20964      */
20965     this.firstChild = null;
20966     /**
20967      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20968      */
20969     this.lastChild = null;
20970     /**
20971      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20972      */
20973     this.previousSibling = null;
20974     /**
20975      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20976      */
20977     this.nextSibling = null;
20978
20979     this.addEvents({
20980        /**
20981         * @event append
20982         * Fires when a new child node is appended
20983         * @param {Tree} tree The owner tree
20984         * @param {Node} this This node
20985         * @param {Node} node The newly appended node
20986         * @param {Number} index The index of the newly appended node
20987         */
20988        "append" : true,
20989        /**
20990         * @event remove
20991         * Fires when a child node is removed
20992         * @param {Tree} tree The owner tree
20993         * @param {Node} this This node
20994         * @param {Node} node The removed node
20995         */
20996        "remove" : true,
20997        /**
20998         * @event move
20999         * Fires when this node is moved to a new location in the tree
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} this This node
21002         * @param {Node} oldParent The old parent of this node
21003         * @param {Node} newParent The new parent of this node
21004         * @param {Number} index The index it was moved to
21005         */
21006        "move" : true,
21007        /**
21008         * @event insert
21009         * Fires when a new child node is inserted.
21010         * @param {Tree} tree The owner tree
21011         * @param {Node} this This node
21012         * @param {Node} node The child node inserted
21013         * @param {Node} refNode The child node the node was inserted before
21014         */
21015        "insert" : true,
21016        /**
21017         * @event beforeappend
21018         * Fires before a new child is appended, return false to cancel the append.
21019         * @param {Tree} tree The owner tree
21020         * @param {Node} this This node
21021         * @param {Node} node The child node to be appended
21022         */
21023        "beforeappend" : true,
21024        /**
21025         * @event beforeremove
21026         * Fires before a child is removed, return false to cancel the remove.
21027         * @param {Tree} tree The owner tree
21028         * @param {Node} this This node
21029         * @param {Node} node The child node to be removed
21030         */
21031        "beforeremove" : true,
21032        /**
21033         * @event beforemove
21034         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} this This node
21037         * @param {Node} oldParent The parent of this node
21038         * @param {Node} newParent The new parent this node is moving to
21039         * @param {Number} index The index it is being moved to
21040         */
21041        "beforemove" : true,
21042        /**
21043         * @event beforeinsert
21044         * Fires before a new child is inserted, return false to cancel the insert.
21045         * @param {Tree} tree The owner tree
21046         * @param {Node} this This node
21047         * @param {Node} node The child node to be inserted
21048         * @param {Node} refNode The child node the node is being inserted before
21049         */
21050        "beforeinsert" : true
21051    });
21052     this.listeners = this.attributes.listeners;
21053     Roo.data.Node.superclass.constructor.call(this);
21054 };
21055
21056 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21057     fireEvent : function(evtName){
21058         // first do standard event for this node
21059         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21060             return false;
21061         }
21062         // then bubble it up to the tree if the event wasn't cancelled
21063         var ot = this.getOwnerTree();
21064         if(ot){
21065             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21066                 return false;
21067             }
21068         }
21069         return true;
21070     },
21071
21072     /**
21073      * Returns true if this node is a leaf
21074      * @return {Boolean}
21075      */
21076     isLeaf : function(){
21077         return this.leaf === true;
21078     },
21079
21080     // private
21081     setFirstChild : function(node){
21082         this.firstChild = node;
21083     },
21084
21085     //private
21086     setLastChild : function(node){
21087         this.lastChild = node;
21088     },
21089
21090
21091     /**
21092      * Returns true if this node is the last child of its parent
21093      * @return {Boolean}
21094      */
21095     isLast : function(){
21096        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21097     },
21098
21099     /**
21100      * Returns true if this node is the first child of its parent
21101      * @return {Boolean}
21102      */
21103     isFirst : function(){
21104        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21105     },
21106
21107     hasChildNodes : function(){
21108         return !this.isLeaf() && this.childNodes.length > 0;
21109     },
21110
21111     /**
21112      * Insert node(s) as the last child node of this node.
21113      * @param {Node/Array} node The node or Array of nodes to append
21114      * @return {Node} The appended node if single append, or null if an array was passed
21115      */
21116     appendChild : function(node){
21117         var multi = false;
21118         if(node instanceof Array){
21119             multi = node;
21120         }else if(arguments.length > 1){
21121             multi = arguments;
21122         }
21123         // if passed an array or multiple args do them one by one
21124         if(multi){
21125             for(var i = 0, len = multi.length; i < len; i++) {
21126                 this.appendChild(multi[i]);
21127             }
21128         }else{
21129             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21130                 return false;
21131             }
21132             var index = this.childNodes.length;
21133             var oldParent = node.parentNode;
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) === false){
21137                     return false;
21138                 }
21139                 oldParent.removeChild(node);
21140             }
21141             index = this.childNodes.length;
21142             if(index == 0){
21143                 this.setFirstChild(node);
21144             }
21145             this.childNodes.push(node);
21146             node.parentNode = this;
21147             var ps = this.childNodes[index-1];
21148             if(ps){
21149                 node.previousSibling = ps;
21150                 ps.nextSibling = node;
21151             }else{
21152                 node.previousSibling = null;
21153             }
21154             node.nextSibling = null;
21155             this.setLastChild(node);
21156             node.setOwnerTree(this.getOwnerTree());
21157             this.fireEvent("append", this.ownerTree, this, node, index);
21158             if(oldParent){
21159                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21160             }
21161             return node;
21162         }
21163     },
21164
21165     /**
21166      * Removes a child node from this node.
21167      * @param {Node} node The node to remove
21168      * @return {Node} The removed node
21169      */
21170     removeChild : function(node){
21171         var index = this.childNodes.indexOf(node);
21172         if(index == -1){
21173             return false;
21174         }
21175         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21176             return false;
21177         }
21178
21179         // remove it from childNodes collection
21180         this.childNodes.splice(index, 1);
21181
21182         // update siblings
21183         if(node.previousSibling){
21184             node.previousSibling.nextSibling = node.nextSibling;
21185         }
21186         if(node.nextSibling){
21187             node.nextSibling.previousSibling = node.previousSibling;
21188         }
21189
21190         // update child refs
21191         if(this.firstChild == node){
21192             this.setFirstChild(node.nextSibling);
21193         }
21194         if(this.lastChild == node){
21195             this.setLastChild(node.previousSibling);
21196         }
21197
21198         node.setOwnerTree(null);
21199         // clear any references from the node
21200         node.parentNode = null;
21201         node.previousSibling = null;
21202         node.nextSibling = null;
21203         this.fireEvent("remove", this.ownerTree, this, node);
21204         return node;
21205     },
21206
21207     /**
21208      * Inserts the first node before the second node in this nodes childNodes collection.
21209      * @param {Node} node The node to insert
21210      * @param {Node} refNode The node to insert before (if null the node is appended)
21211      * @return {Node} The inserted node
21212      */
21213     insertBefore : function(node, refNode){
21214         if(!refNode){ // like standard Dom, refNode can be null for append
21215             return this.appendChild(node);
21216         }
21217         // nothing to do
21218         if(node == refNode){
21219             return false;
21220         }
21221
21222         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21223             return false;
21224         }
21225         var index = this.childNodes.indexOf(refNode);
21226         var oldParent = node.parentNode;
21227         var refIndex = index;
21228
21229         // when moving internally, indexes will change after remove
21230         if(oldParent == this && this.childNodes.indexOf(node) < index){
21231             refIndex--;
21232         }
21233
21234         // it's a move, make sure we move it cleanly
21235         if(oldParent){
21236             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21237                 return false;
21238             }
21239             oldParent.removeChild(node);
21240         }
21241         if(refIndex == 0){
21242             this.setFirstChild(node);
21243         }
21244         this.childNodes.splice(refIndex, 0, node);
21245         node.parentNode = this;
21246         var ps = this.childNodes[refIndex-1];
21247         if(ps){
21248             node.previousSibling = ps;
21249             ps.nextSibling = node;
21250         }else{
21251             node.previousSibling = null;
21252         }
21253         node.nextSibling = refNode;
21254         refNode.previousSibling = node;
21255         node.setOwnerTree(this.getOwnerTree());
21256         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21257         if(oldParent){
21258             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21259         }
21260         return node;
21261     },
21262
21263     /**
21264      * Returns the child node at the specified index.
21265      * @param {Number} index
21266      * @return {Node}
21267      */
21268     item : function(index){
21269         return this.childNodes[index];
21270     },
21271
21272     /**
21273      * Replaces one child node in this node with another.
21274      * @param {Node} newChild The replacement node
21275      * @param {Node} oldChild The node to replace
21276      * @return {Node} The replaced node
21277      */
21278     replaceChild : function(newChild, oldChild){
21279         this.insertBefore(newChild, oldChild);
21280         this.removeChild(oldChild);
21281         return oldChild;
21282     },
21283
21284     /**
21285      * Returns the index of a child node
21286      * @param {Node} node
21287      * @return {Number} The index of the node or -1 if it was not found
21288      */
21289     indexOf : function(child){
21290         return this.childNodes.indexOf(child);
21291     },
21292
21293     /**
21294      * Returns the tree this node is in.
21295      * @return {Tree}
21296      */
21297     getOwnerTree : function(){
21298         // if it doesn't have one, look for one
21299         if(!this.ownerTree){
21300             var p = this;
21301             while(p){
21302                 if(p.ownerTree){
21303                     this.ownerTree = p.ownerTree;
21304                     break;
21305                 }
21306                 p = p.parentNode;
21307             }
21308         }
21309         return this.ownerTree;
21310     },
21311
21312     /**
21313      * Returns depth of this node (the root node has a depth of 0)
21314      * @return {Number}
21315      */
21316     getDepth : function(){
21317         var depth = 0;
21318         var p = this;
21319         while(p.parentNode){
21320             ++depth;
21321             p = p.parentNode;
21322         }
21323         return depth;
21324     },
21325
21326     // private
21327     setOwnerTree : function(tree){
21328         // if it's move, we need to update everyone
21329         if(tree != this.ownerTree){
21330             if(this.ownerTree){
21331                 this.ownerTree.unregisterNode(this);
21332             }
21333             this.ownerTree = tree;
21334             var cs = this.childNodes;
21335             for(var i = 0, len = cs.length; i < len; i++) {
21336                 cs[i].setOwnerTree(tree);
21337             }
21338             if(tree){
21339                 tree.registerNode(this);
21340             }
21341         }
21342     },
21343
21344     /**
21345      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21346      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21347      * @return {String} The path
21348      */
21349     getPath : function(attr){
21350         attr = attr || "id";
21351         var p = this.parentNode;
21352         var b = [this.attributes[attr]];
21353         while(p){
21354             b.unshift(p.attributes[attr]);
21355             p = p.parentNode;
21356         }
21357         var sep = this.getOwnerTree().pathSeparator;
21358         return sep + b.join(sep);
21359     },
21360
21361     /**
21362      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21363      * function call will be the scope provided or the current node. The arguments to the function
21364      * will be the args provided or the current node. If the function returns false at any point,
21365      * the bubble is stopped.
21366      * @param {Function} fn The function to call
21367      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21368      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21369      */
21370     bubble : function(fn, scope, args){
21371         var p = this;
21372         while(p){
21373             if(fn.call(scope || p, args || p) === false){
21374                 break;
21375             }
21376             p = p.parentNode;
21377         }
21378     },
21379
21380     /**
21381      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21382      * function call will be the scope provided or the current node. The arguments to the function
21383      * will be the args provided or the current node. If the function returns false at any point,
21384      * the cascade is stopped on that branch.
21385      * @param {Function} fn The function to call
21386      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21387      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21388      */
21389     cascade : function(fn, scope, args){
21390         if(fn.call(scope || this, args || this) !== false){
21391             var cs = this.childNodes;
21392             for(var i = 0, len = cs.length; i < len; i++) {
21393                 cs[i].cascade(fn, scope, args);
21394             }
21395         }
21396     },
21397
21398     /**
21399      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21400      * function call will be the scope provided or the current node. The arguments to the function
21401      * will be the args provided or the current node. If the function returns false at any point,
21402      * the iteration stops.
21403      * @param {Function} fn The function to call
21404      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21405      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21406      */
21407     eachChild : function(fn, scope, args){
21408         var cs = this.childNodes;
21409         for(var i = 0, len = cs.length; i < len; i++) {
21410                 if(fn.call(scope || this, args || cs[i]) === false){
21411                     break;
21412                 }
21413         }
21414     },
21415
21416     /**
21417      * Finds the first child that has the attribute with the specified value.
21418      * @param {String} attribute The attribute name
21419      * @param {Mixed} value The value to search for
21420      * @return {Node} The found child or null if none was found
21421      */
21422     findChild : function(attribute, value){
21423         var cs = this.childNodes;
21424         for(var i = 0, len = cs.length; i < len; i++) {
21425                 if(cs[i].attributes[attribute] == value){
21426                     return cs[i];
21427                 }
21428         }
21429         return null;
21430     },
21431
21432     /**
21433      * Finds the first child by a custom function. The child matches if the function passed
21434      * returns true.
21435      * @param {Function} fn
21436      * @param {Object} scope (optional)
21437      * @return {Node} The found child or null if none was found
21438      */
21439     findChildBy : function(fn, scope){
21440         var cs = this.childNodes;
21441         for(var i = 0, len = cs.length; i < len; i++) {
21442                 if(fn.call(scope||cs[i], cs[i]) === true){
21443                     return cs[i];
21444                 }
21445         }
21446         return null;
21447     },
21448
21449     /**
21450      * Sorts this nodes children using the supplied sort function
21451      * @param {Function} fn
21452      * @param {Object} scope (optional)
21453      */
21454     sort : function(fn, scope){
21455         var cs = this.childNodes;
21456         var len = cs.length;
21457         if(len > 0){
21458             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21459             cs.sort(sortFn);
21460             for(var i = 0; i < len; i++){
21461                 var n = cs[i];
21462                 n.previousSibling = cs[i-1];
21463                 n.nextSibling = cs[i+1];
21464                 if(i == 0){
21465                     this.setFirstChild(n);
21466                 }
21467                 if(i == len-1){
21468                     this.setLastChild(n);
21469                 }
21470             }
21471         }
21472     },
21473
21474     /**
21475      * Returns true if this node is an ancestor (at any point) of the passed node.
21476      * @param {Node} node
21477      * @return {Boolean}
21478      */
21479     contains : function(node){
21480         return node.isAncestor(this);
21481     },
21482
21483     /**
21484      * Returns true if the passed node is an ancestor (at any point) of this node.
21485      * @param {Node} node
21486      * @return {Boolean}
21487      */
21488     isAncestor : function(node){
21489         var p = this.parentNode;
21490         while(p){
21491             if(p == node){
21492                 return true;
21493             }
21494             p = p.parentNode;
21495         }
21496         return false;
21497     },
21498
21499     toString : function(){
21500         return "[Node"+(this.id?" "+this.id:"")+"]";
21501     }
21502 });/*
21503  * Based on:
21504  * Ext JS Library 1.1.1
21505  * Copyright(c) 2006-2007, Ext JS, LLC.
21506  *
21507  * Originally Released Under LGPL - original licence link has changed is not relivant.
21508  *
21509  * Fork - LGPL
21510  * <script type="text/javascript">
21511  */
21512  
21513
21514 /**
21515  * @class Roo.ComponentMgr
21516  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21517  * @singleton
21518  */
21519 Roo.ComponentMgr = function(){
21520     var all = new Roo.util.MixedCollection();
21521
21522     return {
21523         /**
21524          * Registers a component.
21525          * @param {Roo.Component} c The component
21526          */
21527         register : function(c){
21528             all.add(c);
21529         },
21530
21531         /**
21532          * Unregisters a component.
21533          * @param {Roo.Component} c The component
21534          */
21535         unregister : function(c){
21536             all.remove(c);
21537         },
21538
21539         /**
21540          * Returns a component by id
21541          * @param {String} id The component id
21542          */
21543         get : function(id){
21544             return all.get(id);
21545         },
21546
21547         /**
21548          * Registers a function that will be called when a specified component is added to ComponentMgr
21549          * @param {String} id The component id
21550          * @param {Funtction} fn The callback function
21551          * @param {Object} scope The scope of the callback
21552          */
21553         onAvailable : function(id, fn, scope){
21554             all.on("add", function(index, o){
21555                 if(o.id == id){
21556                     fn.call(scope || o, o);
21557                     all.un("add", fn, scope);
21558                 }
21559             });
21560         }
21561     };
21562 }();/*
21563  * Based on:
21564  * Ext JS Library 1.1.1
21565  * Copyright(c) 2006-2007, Ext JS, LLC.
21566  *
21567  * Originally Released Under LGPL - original licence link has changed is not relivant.
21568  *
21569  * Fork - LGPL
21570  * <script type="text/javascript">
21571  */
21572  
21573 /**
21574  * @class Roo.Component
21575  * @extends Roo.util.Observable
21576  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21577  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21578  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21579  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21580  * All visual components (widgets) that require rendering into a layout should subclass Component.
21581  * @constructor
21582  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21583  * 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
21584  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21585  */
21586 Roo.Component = function(config){
21587     config = config || {};
21588     if(config.tagName || config.dom || typeof config == "string"){ // element object
21589         config = {el: config, id: config.id || config};
21590     }
21591     this.initialConfig = config;
21592
21593     Roo.apply(this, config);
21594     this.addEvents({
21595         /**
21596          * @event disable
21597          * Fires after the component is disabled.
21598              * @param {Roo.Component} this
21599              */
21600         disable : true,
21601         /**
21602          * @event enable
21603          * Fires after the component is enabled.
21604              * @param {Roo.Component} this
21605              */
21606         enable : true,
21607         /**
21608          * @event beforeshow
21609          * Fires before the component is shown.  Return false to stop the show.
21610              * @param {Roo.Component} this
21611              */
21612         beforeshow : true,
21613         /**
21614          * @event show
21615          * Fires after the component is shown.
21616              * @param {Roo.Component} this
21617              */
21618         show : true,
21619         /**
21620          * @event beforehide
21621          * Fires before the component is hidden. Return false to stop the hide.
21622              * @param {Roo.Component} this
21623              */
21624         beforehide : true,
21625         /**
21626          * @event hide
21627          * Fires after the component is hidden.
21628              * @param {Roo.Component} this
21629              */
21630         hide : true,
21631         /**
21632          * @event beforerender
21633          * Fires before the component is rendered. Return false to stop the render.
21634              * @param {Roo.Component} this
21635              */
21636         beforerender : true,
21637         /**
21638          * @event render
21639          * Fires after the component is rendered.
21640              * @param {Roo.Component} this
21641              */
21642         render : true,
21643         /**
21644          * @event beforedestroy
21645          * Fires before the component is destroyed. Return false to stop the destroy.
21646              * @param {Roo.Component} this
21647              */
21648         beforedestroy : true,
21649         /**
21650          * @event destroy
21651          * Fires after the component is destroyed.
21652              * @param {Roo.Component} this
21653              */
21654         destroy : true
21655     });
21656     if(!this.id){
21657         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21658     }
21659     Roo.ComponentMgr.register(this);
21660     Roo.Component.superclass.constructor.call(this);
21661     this.initComponent();
21662     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21663         this.render(this.renderTo);
21664         delete this.renderTo;
21665     }
21666 };
21667
21668 // private
21669 Roo.Component.AUTO_ID = 1000;
21670
21671 Roo.extend(Roo.Component, Roo.util.Observable, {
21672     /**
21673      * @property {Boolean} hidden
21674      * true if this component is hidden. Read-only.
21675      */
21676     hidden : false,
21677     /**
21678      * true if this component is disabled. Read-only.
21679      */
21680     disabled : false,
21681     /**
21682      * true if this component has been rendered. Read-only.
21683      */
21684     rendered : false,
21685     
21686     /** @cfg {String} disableClass
21687      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21688      */
21689     disabledClass : "x-item-disabled",
21690         /** @cfg {Boolean} allowDomMove
21691          * Whether the component can move the Dom node when rendering (defaults to true).
21692          */
21693     allowDomMove : true,
21694     /** @cfg {String} hideMode
21695      * How this component should hidden. Supported values are
21696      * "visibility" (css visibility), "offsets" (negative offset position) and
21697      * "display" (css display) - defaults to "display".
21698      */
21699     hideMode: 'display',
21700
21701     // private
21702     ctype : "Roo.Component",
21703
21704     /** @cfg {String} actionMode 
21705      * which property holds the element that used for  hide() / show() / disable() / enable()
21706      * default is 'el' 
21707      */
21708     actionMode : "el",
21709
21710     // private
21711     getActionEl : function(){
21712         return this[this.actionMode];
21713     },
21714
21715     initComponent : Roo.emptyFn,
21716     /**
21717      * If this is a lazy rendering component, render it to its container element.
21718      * @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.
21719      */
21720     render : function(container, position){
21721         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21722             if(!container && this.el){
21723                 this.el = Roo.get(this.el);
21724                 container = this.el.dom.parentNode;
21725                 this.allowDomMove = false;
21726             }
21727             this.container = Roo.get(container);
21728             this.rendered = true;
21729             if(position !== undefined){
21730                 if(typeof position == 'number'){
21731                     position = this.container.dom.childNodes[position];
21732                 }else{
21733                     position = Roo.getDom(position);
21734                 }
21735             }
21736             this.onRender(this.container, position || null);
21737             if(this.cls){
21738                 this.el.addClass(this.cls);
21739                 delete this.cls;
21740             }
21741             if(this.style){
21742                 this.el.applyStyles(this.style);
21743                 delete this.style;
21744             }
21745             this.fireEvent("render", this);
21746             this.afterRender(this.container);
21747             if(this.hidden){
21748                 this.hide();
21749             }
21750             if(this.disabled){
21751                 this.disable();
21752             }
21753         }
21754         return this;
21755     },
21756
21757     // private
21758     // default function is not really useful
21759     onRender : function(ct, position){
21760         if(this.el){
21761             this.el = Roo.get(this.el);
21762             if(this.allowDomMove !== false){
21763                 ct.dom.insertBefore(this.el.dom, position);
21764             }
21765         }
21766     },
21767
21768     // private
21769     getAutoCreate : function(){
21770         var cfg = typeof this.autoCreate == "object" ?
21771                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21772         if(this.id && !cfg.id){
21773             cfg.id = this.id;
21774         }
21775         return cfg;
21776     },
21777
21778     // private
21779     afterRender : Roo.emptyFn,
21780
21781     /**
21782      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21783      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21784      */
21785     destroy : function(){
21786         if(this.fireEvent("beforedestroy", this) !== false){
21787             this.purgeListeners();
21788             this.beforeDestroy();
21789             if(this.rendered){
21790                 this.el.removeAllListeners();
21791                 this.el.remove();
21792                 if(this.actionMode == "container"){
21793                     this.container.remove();
21794                 }
21795             }
21796             this.onDestroy();
21797             Roo.ComponentMgr.unregister(this);
21798             this.fireEvent("destroy", this);
21799         }
21800     },
21801
21802         // private
21803     beforeDestroy : function(){
21804
21805     },
21806
21807         // private
21808         onDestroy : function(){
21809
21810     },
21811
21812     /**
21813      * Returns the underlying {@link Roo.Element}.
21814      * @return {Roo.Element} The element
21815      */
21816     getEl : function(){
21817         return this.el;
21818     },
21819
21820     /**
21821      * Returns the id of this component.
21822      * @return {String}
21823      */
21824     getId : function(){
21825         return this.id;
21826     },
21827
21828     /**
21829      * Try to focus this component.
21830      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21831      * @return {Roo.Component} this
21832      */
21833     focus : function(selectText){
21834         if(this.rendered){
21835             this.el.focus();
21836             if(selectText === true){
21837                 this.el.dom.select();
21838             }
21839         }
21840         return this;
21841     },
21842
21843     // private
21844     blur : function(){
21845         if(this.rendered){
21846             this.el.blur();
21847         }
21848         return this;
21849     },
21850
21851     /**
21852      * Disable this component.
21853      * @return {Roo.Component} this
21854      */
21855     disable : function(){
21856         if(this.rendered){
21857             this.onDisable();
21858         }
21859         this.disabled = true;
21860         this.fireEvent("disable", this);
21861         return this;
21862     },
21863
21864         // private
21865     onDisable : function(){
21866         this.getActionEl().addClass(this.disabledClass);
21867         this.el.dom.disabled = true;
21868     },
21869
21870     /**
21871      * Enable this component.
21872      * @return {Roo.Component} this
21873      */
21874     enable : function(){
21875         if(this.rendered){
21876             this.onEnable();
21877         }
21878         this.disabled = false;
21879         this.fireEvent("enable", this);
21880         return this;
21881     },
21882
21883         // private
21884     onEnable : function(){
21885         this.getActionEl().removeClass(this.disabledClass);
21886         this.el.dom.disabled = false;
21887     },
21888
21889     /**
21890      * Convenience function for setting disabled/enabled by boolean.
21891      * @param {Boolean} disabled
21892      */
21893     setDisabled : function(disabled){
21894         this[disabled ? "disable" : "enable"]();
21895     },
21896
21897     /**
21898      * Show this component.
21899      * @return {Roo.Component} this
21900      */
21901     show: function(){
21902         if(this.fireEvent("beforeshow", this) !== false){
21903             this.hidden = false;
21904             if(this.rendered){
21905                 this.onShow();
21906             }
21907             this.fireEvent("show", this);
21908         }
21909         return this;
21910     },
21911
21912     // private
21913     onShow : function(){
21914         var ae = this.getActionEl();
21915         if(this.hideMode == 'visibility'){
21916             ae.dom.style.visibility = "visible";
21917         }else if(this.hideMode == 'offsets'){
21918             ae.removeClass('x-hidden');
21919         }else{
21920             ae.dom.style.display = "";
21921         }
21922     },
21923
21924     /**
21925      * Hide this component.
21926      * @return {Roo.Component} this
21927      */
21928     hide: function(){
21929         if(this.fireEvent("beforehide", this) !== false){
21930             this.hidden = true;
21931             if(this.rendered){
21932                 this.onHide();
21933             }
21934             this.fireEvent("hide", this);
21935         }
21936         return this;
21937     },
21938
21939     // private
21940     onHide : function(){
21941         var ae = this.getActionEl();
21942         if(this.hideMode == 'visibility'){
21943             ae.dom.style.visibility = "hidden";
21944         }else if(this.hideMode == 'offsets'){
21945             ae.addClass('x-hidden');
21946         }else{
21947             ae.dom.style.display = "none";
21948         }
21949     },
21950
21951     /**
21952      * Convenience function to hide or show this component by boolean.
21953      * @param {Boolean} visible True to show, false to hide
21954      * @return {Roo.Component} this
21955      */
21956     setVisible: function(visible){
21957         if(visible) {
21958             this.show();
21959         }else{
21960             this.hide();
21961         }
21962         return this;
21963     },
21964
21965     /**
21966      * Returns true if this component is visible.
21967      */
21968     isVisible : function(){
21969         return this.getActionEl().isVisible();
21970     },
21971
21972     cloneConfig : function(overrides){
21973         overrides = overrides || {};
21974         var id = overrides.id || Roo.id();
21975         var cfg = Roo.applyIf(overrides, this.initialConfig);
21976         cfg.id = id; // prevent dup id
21977         return new this.constructor(cfg);
21978     }
21979 });/*
21980  * Based on:
21981  * Ext JS Library 1.1.1
21982  * Copyright(c) 2006-2007, Ext JS, LLC.
21983  *
21984  * Originally Released Under LGPL - original licence link has changed is not relivant.
21985  *
21986  * Fork - LGPL
21987  * <script type="text/javascript">
21988  */
21989  (function(){ 
21990 /**
21991  * @class Roo.Layer
21992  * @extends Roo.Element
21993  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21994  * automatic maintaining of shadow/shim positions.
21995  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21996  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21997  * you can pass a string with a CSS class name. False turns off the shadow.
21998  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21999  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22000  * @cfg {String} cls CSS class to add to the element
22001  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22002  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22003  * @constructor
22004  * @param {Object} config An object with config options.
22005  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22006  */
22007
22008 Roo.Layer = function(config, existingEl){
22009     config = config || {};
22010     var dh = Roo.DomHelper;
22011     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22012     if(existingEl){
22013         this.dom = Roo.getDom(existingEl);
22014     }
22015     if(!this.dom){
22016         var o = config.dh || {tag: "div", cls: "x-layer"};
22017         this.dom = dh.append(pel, o);
22018     }
22019     if(config.cls){
22020         this.addClass(config.cls);
22021     }
22022     this.constrain = config.constrain !== false;
22023     this.visibilityMode = Roo.Element.VISIBILITY;
22024     if(config.id){
22025         this.id = this.dom.id = config.id;
22026     }else{
22027         this.id = Roo.id(this.dom);
22028     }
22029     this.zindex = config.zindex || this.getZIndex();
22030     this.position("absolute", this.zindex);
22031     if(config.shadow){
22032         this.shadowOffset = config.shadowOffset || 4;
22033         this.shadow = new Roo.Shadow({
22034             offset : this.shadowOffset,
22035             mode : config.shadow
22036         });
22037     }else{
22038         this.shadowOffset = 0;
22039     }
22040     this.useShim = config.shim !== false && Roo.useShims;
22041     this.useDisplay = config.useDisplay;
22042     this.hide();
22043 };
22044
22045 var supr = Roo.Element.prototype;
22046
22047 // shims are shared among layer to keep from having 100 iframes
22048 var shims = [];
22049
22050 Roo.extend(Roo.Layer, Roo.Element, {
22051
22052     getZIndex : function(){
22053         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22054     },
22055
22056     getShim : function(){
22057         if(!this.useShim){
22058             return null;
22059         }
22060         if(this.shim){
22061             return this.shim;
22062         }
22063         var shim = shims.shift();
22064         if(!shim){
22065             shim = this.createShim();
22066             shim.enableDisplayMode('block');
22067             shim.dom.style.display = 'none';
22068             shim.dom.style.visibility = 'visible';
22069         }
22070         var pn = this.dom.parentNode;
22071         if(shim.dom.parentNode != pn){
22072             pn.insertBefore(shim.dom, this.dom);
22073         }
22074         shim.setStyle('z-index', this.getZIndex()-2);
22075         this.shim = shim;
22076         return shim;
22077     },
22078
22079     hideShim : function(){
22080         if(this.shim){
22081             this.shim.setDisplayed(false);
22082             shims.push(this.shim);
22083             delete this.shim;
22084         }
22085     },
22086
22087     disableShadow : function(){
22088         if(this.shadow){
22089             this.shadowDisabled = true;
22090             this.shadow.hide();
22091             this.lastShadowOffset = this.shadowOffset;
22092             this.shadowOffset = 0;
22093         }
22094     },
22095
22096     enableShadow : function(show){
22097         if(this.shadow){
22098             this.shadowDisabled = false;
22099             this.shadowOffset = this.lastShadowOffset;
22100             delete this.lastShadowOffset;
22101             if(show){
22102                 this.sync(true);
22103             }
22104         }
22105     },
22106
22107     // private
22108     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22109     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22110     sync : function(doShow){
22111         var sw = this.shadow;
22112         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22113             var sh = this.getShim();
22114
22115             var w = this.getWidth(),
22116                 h = this.getHeight();
22117
22118             var l = this.getLeft(true),
22119                 t = this.getTop(true);
22120
22121             if(sw && !this.shadowDisabled){
22122                 if(doShow && !sw.isVisible()){
22123                     sw.show(this);
22124                 }else{
22125                     sw.realign(l, t, w, h);
22126                 }
22127                 if(sh){
22128                     if(doShow){
22129                        sh.show();
22130                     }
22131                     // fit the shim behind the shadow, so it is shimmed too
22132                     var a = sw.adjusts, s = sh.dom.style;
22133                     s.left = (Math.min(l, l+a.l))+"px";
22134                     s.top = (Math.min(t, t+a.t))+"px";
22135                     s.width = (w+a.w)+"px";
22136                     s.height = (h+a.h)+"px";
22137                 }
22138             }else if(sh){
22139                 if(doShow){
22140                    sh.show();
22141                 }
22142                 sh.setSize(w, h);
22143                 sh.setLeftTop(l, t);
22144             }
22145             
22146         }
22147     },
22148
22149     // private
22150     destroy : function(){
22151         this.hideShim();
22152         if(this.shadow){
22153             this.shadow.hide();
22154         }
22155         this.removeAllListeners();
22156         var pn = this.dom.parentNode;
22157         if(pn){
22158             pn.removeChild(this.dom);
22159         }
22160         Roo.Element.uncache(this.id);
22161     },
22162
22163     remove : function(){
22164         this.destroy();
22165     },
22166
22167     // private
22168     beginUpdate : function(){
22169         this.updating = true;
22170     },
22171
22172     // private
22173     endUpdate : function(){
22174         this.updating = false;
22175         this.sync(true);
22176     },
22177
22178     // private
22179     hideUnders : function(negOffset){
22180         if(this.shadow){
22181             this.shadow.hide();
22182         }
22183         this.hideShim();
22184     },
22185
22186     // private
22187     constrainXY : function(){
22188         if(this.constrain){
22189             var vw = Roo.lib.Dom.getViewWidth(),
22190                 vh = Roo.lib.Dom.getViewHeight();
22191             var s = Roo.get(document).getScroll();
22192
22193             var xy = this.getXY();
22194             var x = xy[0], y = xy[1];   
22195             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22196             // only move it if it needs it
22197             var moved = false;
22198             // first validate right/bottom
22199             if((x + w) > vw+s.left){
22200                 x = vw - w - this.shadowOffset;
22201                 moved = true;
22202             }
22203             if((y + h) > vh+s.top){
22204                 y = vh - h - this.shadowOffset;
22205                 moved = true;
22206             }
22207             // then make sure top/left isn't negative
22208             if(x < s.left){
22209                 x = s.left;
22210                 moved = true;
22211             }
22212             if(y < s.top){
22213                 y = s.top;
22214                 moved = true;
22215             }
22216             if(moved){
22217                 if(this.avoidY){
22218                     var ay = this.avoidY;
22219                     if(y <= ay && (y+h) >= ay){
22220                         y = ay-h-5;   
22221                     }
22222                 }
22223                 xy = [x, y];
22224                 this.storeXY(xy);
22225                 supr.setXY.call(this, xy);
22226                 this.sync();
22227             }
22228         }
22229     },
22230
22231     isVisible : function(){
22232         return this.visible;    
22233     },
22234
22235     // private
22236     showAction : function(){
22237         this.visible = true; // track visibility to prevent getStyle calls
22238         if(this.useDisplay === true){
22239             this.setDisplayed("");
22240         }else if(this.lastXY){
22241             supr.setXY.call(this, this.lastXY);
22242         }else if(this.lastLT){
22243             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22244         }
22245     },
22246
22247     // private
22248     hideAction : function(){
22249         this.visible = false;
22250         if(this.useDisplay === true){
22251             this.setDisplayed(false);
22252         }else{
22253             this.setLeftTop(-10000,-10000);
22254         }
22255     },
22256
22257     // overridden Element method
22258     setVisible : function(v, a, d, c, e){
22259         if(v){
22260             this.showAction();
22261         }
22262         if(a && v){
22263             var cb = function(){
22264                 this.sync(true);
22265                 if(c){
22266                     c();
22267                 }
22268             }.createDelegate(this);
22269             supr.setVisible.call(this, true, true, d, cb, e);
22270         }else{
22271             if(!v){
22272                 this.hideUnders(true);
22273             }
22274             var cb = c;
22275             if(a){
22276                 cb = function(){
22277                     this.hideAction();
22278                     if(c){
22279                         c();
22280                     }
22281                 }.createDelegate(this);
22282             }
22283             supr.setVisible.call(this, v, a, d, cb, e);
22284             if(v){
22285                 this.sync(true);
22286             }else if(!a){
22287                 this.hideAction();
22288             }
22289         }
22290     },
22291
22292     storeXY : function(xy){
22293         delete this.lastLT;
22294         this.lastXY = xy;
22295     },
22296
22297     storeLeftTop : function(left, top){
22298         delete this.lastXY;
22299         this.lastLT = [left, top];
22300     },
22301
22302     // private
22303     beforeFx : function(){
22304         this.beforeAction();
22305         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22306     },
22307
22308     // private
22309     afterFx : function(){
22310         Roo.Layer.superclass.afterFx.apply(this, arguments);
22311         this.sync(this.isVisible());
22312     },
22313
22314     // private
22315     beforeAction : function(){
22316         if(!this.updating && this.shadow){
22317             this.shadow.hide();
22318         }
22319     },
22320
22321     // overridden Element method
22322     setLeft : function(left){
22323         this.storeLeftTop(left, this.getTop(true));
22324         supr.setLeft.apply(this, arguments);
22325         this.sync();
22326     },
22327
22328     setTop : function(top){
22329         this.storeLeftTop(this.getLeft(true), top);
22330         supr.setTop.apply(this, arguments);
22331         this.sync();
22332     },
22333
22334     setLeftTop : function(left, top){
22335         this.storeLeftTop(left, top);
22336         supr.setLeftTop.apply(this, arguments);
22337         this.sync();
22338     },
22339
22340     setXY : function(xy, a, d, c, e){
22341         this.fixDisplay();
22342         this.beforeAction();
22343         this.storeXY(xy);
22344         var cb = this.createCB(c);
22345         supr.setXY.call(this, xy, a, d, cb, e);
22346         if(!a){
22347             cb();
22348         }
22349     },
22350
22351     // private
22352     createCB : function(c){
22353         var el = this;
22354         return function(){
22355             el.constrainXY();
22356             el.sync(true);
22357             if(c){
22358                 c();
22359             }
22360         };
22361     },
22362
22363     // overridden Element method
22364     setX : function(x, a, d, c, e){
22365         this.setXY([x, this.getY()], a, d, c, e);
22366     },
22367
22368     // overridden Element method
22369     setY : function(y, a, d, c, e){
22370         this.setXY([this.getX(), y], a, d, c, e);
22371     },
22372
22373     // overridden Element method
22374     setSize : function(w, h, a, d, c, e){
22375         this.beforeAction();
22376         var cb = this.createCB(c);
22377         supr.setSize.call(this, w, h, a, d, cb, e);
22378         if(!a){
22379             cb();
22380         }
22381     },
22382
22383     // overridden Element method
22384     setWidth : function(w, a, d, c, e){
22385         this.beforeAction();
22386         var cb = this.createCB(c);
22387         supr.setWidth.call(this, w, a, d, cb, e);
22388         if(!a){
22389             cb();
22390         }
22391     },
22392
22393     // overridden Element method
22394     setHeight : function(h, a, d, c, e){
22395         this.beforeAction();
22396         var cb = this.createCB(c);
22397         supr.setHeight.call(this, h, a, d, cb, e);
22398         if(!a){
22399             cb();
22400         }
22401     },
22402
22403     // overridden Element method
22404     setBounds : function(x, y, w, h, a, d, c, e){
22405         this.beforeAction();
22406         var cb = this.createCB(c);
22407         if(!a){
22408             this.storeXY([x, y]);
22409             supr.setXY.call(this, [x, y]);
22410             supr.setSize.call(this, w, h, a, d, cb, e);
22411             cb();
22412         }else{
22413             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22414         }
22415         return this;
22416     },
22417     
22418     /**
22419      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22420      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22421      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22422      * @param {Number} zindex The new z-index to set
22423      * @return {this} The Layer
22424      */
22425     setZIndex : function(zindex){
22426         this.zindex = zindex;
22427         this.setStyle("z-index", zindex + 2);
22428         if(this.shadow){
22429             this.shadow.setZIndex(zindex + 1);
22430         }
22431         if(this.shim){
22432             this.shim.setStyle("z-index", zindex);
22433         }
22434     }
22435 });
22436 })();/*
22437  * Based on:
22438  * Ext JS Library 1.1.1
22439  * Copyright(c) 2006-2007, Ext JS, LLC.
22440  *
22441  * Originally Released Under LGPL - original licence link has changed is not relivant.
22442  *
22443  * Fork - LGPL
22444  * <script type="text/javascript">
22445  */
22446
22447
22448 /**
22449  * @class Roo.Shadow
22450  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22451  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22452  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22453  * @constructor
22454  * Create a new Shadow
22455  * @param {Object} config The config object
22456  */
22457 Roo.Shadow = function(config){
22458     Roo.apply(this, config);
22459     if(typeof this.mode != "string"){
22460         this.mode = this.defaultMode;
22461     }
22462     var o = this.offset, a = {h: 0};
22463     var rad = Math.floor(this.offset/2);
22464     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22465         case "drop":
22466             a.w = 0;
22467             a.l = a.t = o;
22468             a.t -= 1;
22469             if(Roo.isIE){
22470                 a.l -= this.offset + rad;
22471                 a.t -= this.offset + rad;
22472                 a.w -= rad;
22473                 a.h -= rad;
22474                 a.t += 1;
22475             }
22476         break;
22477         case "sides":
22478             a.w = (o*2);
22479             a.l = -o;
22480             a.t = o-1;
22481             if(Roo.isIE){
22482                 a.l -= (this.offset - rad);
22483                 a.t -= this.offset + rad;
22484                 a.l += 1;
22485                 a.w -= (this.offset - rad)*2;
22486                 a.w -= rad + 1;
22487                 a.h -= 1;
22488             }
22489         break;
22490         case "frame":
22491             a.w = a.h = (o*2);
22492             a.l = a.t = -o;
22493             a.t += 1;
22494             a.h -= 2;
22495             if(Roo.isIE){
22496                 a.l -= (this.offset - rad);
22497                 a.t -= (this.offset - rad);
22498                 a.l += 1;
22499                 a.w -= (this.offset + rad + 1);
22500                 a.h -= (this.offset + rad);
22501                 a.h += 1;
22502             }
22503         break;
22504     };
22505
22506     this.adjusts = a;
22507 };
22508
22509 Roo.Shadow.prototype = {
22510     /**
22511      * @cfg {String} mode
22512      * The shadow display mode.  Supports the following options:<br />
22513      * sides: Shadow displays on both sides and bottom only<br />
22514      * frame: Shadow displays equally on all four sides<br />
22515      * drop: Traditional bottom-right drop shadow (default)
22516      */
22517     /**
22518      * @cfg {String} offset
22519      * The number of pixels to offset the shadow from the element (defaults to 4)
22520      */
22521     offset: 4,
22522
22523     // private
22524     defaultMode: "drop",
22525
22526     /**
22527      * Displays the shadow under the target element
22528      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22529      */
22530     show : function(target){
22531         target = Roo.get(target);
22532         if(!this.el){
22533             this.el = Roo.Shadow.Pool.pull();
22534             if(this.el.dom.nextSibling != target.dom){
22535                 this.el.insertBefore(target);
22536             }
22537         }
22538         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22539         if(Roo.isIE){
22540             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22541         }
22542         this.realign(
22543             target.getLeft(true),
22544             target.getTop(true),
22545             target.getWidth(),
22546             target.getHeight()
22547         );
22548         this.el.dom.style.display = "block";
22549     },
22550
22551     /**
22552      * Returns true if the shadow is visible, else false
22553      */
22554     isVisible : function(){
22555         return this.el ? true : false;  
22556     },
22557
22558     /**
22559      * Direct alignment when values are already available. Show must be called at least once before
22560      * calling this method to ensure it is initialized.
22561      * @param {Number} left The target element left position
22562      * @param {Number} top The target element top position
22563      * @param {Number} width The target element width
22564      * @param {Number} height The target element height
22565      */
22566     realign : function(l, t, w, h){
22567         if(!this.el){
22568             return;
22569         }
22570         var a = this.adjusts, d = this.el.dom, s = d.style;
22571         var iea = 0;
22572         s.left = (l+a.l)+"px";
22573         s.top = (t+a.t)+"px";
22574         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22575  
22576         if(s.width != sws || s.height != shs){
22577             s.width = sws;
22578             s.height = shs;
22579             if(!Roo.isIE){
22580                 var cn = d.childNodes;
22581                 var sww = Math.max(0, (sw-12))+"px";
22582                 cn[0].childNodes[1].style.width = sww;
22583                 cn[1].childNodes[1].style.width = sww;
22584                 cn[2].childNodes[1].style.width = sww;
22585                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22586             }
22587         }
22588     },
22589
22590     /**
22591      * Hides this shadow
22592      */
22593     hide : function(){
22594         if(this.el){
22595             this.el.dom.style.display = "none";
22596             Roo.Shadow.Pool.push(this.el);
22597             delete this.el;
22598         }
22599     },
22600
22601     /**
22602      * Adjust the z-index of this shadow
22603      * @param {Number} zindex The new z-index
22604      */
22605     setZIndex : function(z){
22606         this.zIndex = z;
22607         if(this.el){
22608             this.el.setStyle("z-index", z);
22609         }
22610     }
22611 };
22612
22613 // Private utility class that manages the internal Shadow cache
22614 Roo.Shadow.Pool = function(){
22615     var p = [];
22616     var markup = Roo.isIE ?
22617                  '<div class="x-ie-shadow"></div>' :
22618                  '<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>';
22619     return {
22620         pull : function(){
22621             var sh = p.shift();
22622             if(!sh){
22623                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22624                 sh.autoBoxAdjust = false;
22625             }
22626             return sh;
22627         },
22628
22629         push : function(sh){
22630             p.push(sh);
22631         }
22632     };
22633 }();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644 /**
22645  * @class Roo.BoxComponent
22646  * @extends Roo.Component
22647  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22648  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22649  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22650  * layout containers.
22651  * @constructor
22652  * @param {Roo.Element/String/Object} config The configuration options.
22653  */
22654 Roo.BoxComponent = function(config){
22655     Roo.Component.call(this, config);
22656     this.addEvents({
22657         /**
22658          * @event resize
22659          * Fires after the component is resized.
22660              * @param {Roo.Component} this
22661              * @param {Number} adjWidth The box-adjusted width that was set
22662              * @param {Number} adjHeight The box-adjusted height that was set
22663              * @param {Number} rawWidth The width that was originally specified
22664              * @param {Number} rawHeight The height that was originally specified
22665              */
22666         resize : true,
22667         /**
22668          * @event move
22669          * Fires after the component is moved.
22670              * @param {Roo.Component} this
22671              * @param {Number} x The new x position
22672              * @param {Number} y The new y position
22673              */
22674         move : true
22675     });
22676 };
22677
22678 Roo.extend(Roo.BoxComponent, Roo.Component, {
22679     // private, set in afterRender to signify that the component has been rendered
22680     boxReady : false,
22681     // private, used to defer height settings to subclasses
22682     deferHeight: false,
22683     /** @cfg {Number} width
22684      * width (optional) size of component
22685      */
22686      /** @cfg {Number} height
22687      * height (optional) size of component
22688      */
22689      
22690     /**
22691      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22692      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22693      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22694      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22695      * @return {Roo.BoxComponent} this
22696      */
22697     setSize : function(w, h){
22698         // support for standard size objects
22699         if(typeof w == 'object'){
22700             h = w.height;
22701             w = w.width;
22702         }
22703         // not rendered
22704         if(!this.boxReady){
22705             this.width = w;
22706             this.height = h;
22707             return this;
22708         }
22709
22710         // prevent recalcs when not needed
22711         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22712             return this;
22713         }
22714         this.lastSize = {width: w, height: h};
22715
22716         var adj = this.adjustSize(w, h);
22717         var aw = adj.width, ah = adj.height;
22718         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22719             var rz = this.getResizeEl();
22720             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22721                 rz.setSize(aw, ah);
22722             }else if(!this.deferHeight && ah !== undefined){
22723                 rz.setHeight(ah);
22724             }else if(aw !== undefined){
22725                 rz.setWidth(aw);
22726             }
22727             this.onResize(aw, ah, w, h);
22728             this.fireEvent('resize', this, aw, ah, w, h);
22729         }
22730         return this;
22731     },
22732
22733     /**
22734      * Gets the current size of the component's underlying element.
22735      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22736      */
22737     getSize : function(){
22738         return this.el.getSize();
22739     },
22740
22741     /**
22742      * Gets the current XY position of the component's underlying element.
22743      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22744      * @return {Array} The XY position of the element (e.g., [100, 200])
22745      */
22746     getPosition : function(local){
22747         if(local === true){
22748             return [this.el.getLeft(true), this.el.getTop(true)];
22749         }
22750         return this.xy || this.el.getXY();
22751     },
22752
22753     /**
22754      * Gets the current box measurements of the component's underlying element.
22755      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22756      * @returns {Object} box An object in the format {x, y, width, height}
22757      */
22758     getBox : function(local){
22759         var s = this.el.getSize();
22760         if(local){
22761             s.x = this.el.getLeft(true);
22762             s.y = this.el.getTop(true);
22763         }else{
22764             var xy = this.xy || this.el.getXY();
22765             s.x = xy[0];
22766             s.y = xy[1];
22767         }
22768         return s;
22769     },
22770
22771     /**
22772      * Sets the current box measurements of the component's underlying element.
22773      * @param {Object} box An object in the format {x, y, width, height}
22774      * @returns {Roo.BoxComponent} this
22775      */
22776     updateBox : function(box){
22777         this.setSize(box.width, box.height);
22778         this.setPagePosition(box.x, box.y);
22779         return this;
22780     },
22781
22782     // protected
22783     getResizeEl : function(){
22784         return this.resizeEl || this.el;
22785     },
22786
22787     // protected
22788     getPositionEl : function(){
22789         return this.positionEl || this.el;
22790     },
22791
22792     /**
22793      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22794      * This method fires the move event.
22795      * @param {Number} left The new left
22796      * @param {Number} top The new top
22797      * @returns {Roo.BoxComponent} this
22798      */
22799     setPosition : function(x, y){
22800         this.x = x;
22801         this.y = y;
22802         if(!this.boxReady){
22803             return this;
22804         }
22805         var adj = this.adjustPosition(x, y);
22806         var ax = adj.x, ay = adj.y;
22807
22808         var el = this.getPositionEl();
22809         if(ax !== undefined || ay !== undefined){
22810             if(ax !== undefined && ay !== undefined){
22811                 el.setLeftTop(ax, ay);
22812             }else if(ax !== undefined){
22813                 el.setLeft(ax);
22814             }else if(ay !== undefined){
22815                 el.setTop(ay);
22816             }
22817             this.onPosition(ax, ay);
22818             this.fireEvent('move', this, ax, ay);
22819         }
22820         return this;
22821     },
22822
22823     /**
22824      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22825      * This method fires the move event.
22826      * @param {Number} x The new x position
22827      * @param {Number} y The new y position
22828      * @returns {Roo.BoxComponent} this
22829      */
22830     setPagePosition : function(x, y){
22831         this.pageX = x;
22832         this.pageY = y;
22833         if(!this.boxReady){
22834             return;
22835         }
22836         if(x === undefined || y === undefined){ // cannot translate undefined points
22837             return;
22838         }
22839         var p = this.el.translatePoints(x, y);
22840         this.setPosition(p.left, p.top);
22841         return this;
22842     },
22843
22844     // private
22845     onRender : function(ct, position){
22846         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22847         if(this.resizeEl){
22848             this.resizeEl = Roo.get(this.resizeEl);
22849         }
22850         if(this.positionEl){
22851             this.positionEl = Roo.get(this.positionEl);
22852         }
22853     },
22854
22855     // private
22856     afterRender : function(){
22857         Roo.BoxComponent.superclass.afterRender.call(this);
22858         this.boxReady = true;
22859         this.setSize(this.width, this.height);
22860         if(this.x || this.y){
22861             this.setPosition(this.x, this.y);
22862         }
22863         if(this.pageX || this.pageY){
22864             this.setPagePosition(this.pageX, this.pageY);
22865         }
22866     },
22867
22868     /**
22869      * Force the component's size to recalculate based on the underlying element's current height and width.
22870      * @returns {Roo.BoxComponent} this
22871      */
22872     syncSize : function(){
22873         delete this.lastSize;
22874         this.setSize(this.el.getWidth(), this.el.getHeight());
22875         return this;
22876     },
22877
22878     /**
22879      * Called after the component is resized, this method is empty by default but can be implemented by any
22880      * subclass that needs to perform custom logic after a resize occurs.
22881      * @param {Number} adjWidth The box-adjusted width that was set
22882      * @param {Number} adjHeight The box-adjusted height that was set
22883      * @param {Number} rawWidth The width that was originally specified
22884      * @param {Number} rawHeight The height that was originally specified
22885      */
22886     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22887
22888     },
22889
22890     /**
22891      * Called after the component is moved, this method is empty by default but can be implemented by any
22892      * subclass that needs to perform custom logic after a move occurs.
22893      * @param {Number} x The new x position
22894      * @param {Number} y The new y position
22895      */
22896     onPosition : function(x, y){
22897
22898     },
22899
22900     // private
22901     adjustSize : function(w, h){
22902         if(this.autoWidth){
22903             w = 'auto';
22904         }
22905         if(this.autoHeight){
22906             h = 'auto';
22907         }
22908         return {width : w, height: h};
22909     },
22910
22911     // private
22912     adjustPosition : function(x, y){
22913         return {x : x, y: y};
22914     }
22915 });/*
22916  * Based on:
22917  * Ext JS Library 1.1.1
22918  * Copyright(c) 2006-2007, Ext JS, LLC.
22919  *
22920  * Originally Released Under LGPL - original licence link has changed is not relivant.
22921  *
22922  * Fork - LGPL
22923  * <script type="text/javascript">
22924  */
22925
22926
22927 /**
22928  * @class Roo.SplitBar
22929  * @extends Roo.util.Observable
22930  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22931  * <br><br>
22932  * Usage:
22933  * <pre><code>
22934 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22935                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22936 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22937 split.minSize = 100;
22938 split.maxSize = 600;
22939 split.animate = true;
22940 split.on('moved', splitterMoved);
22941 </code></pre>
22942  * @constructor
22943  * Create a new SplitBar
22944  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22945  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22946  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22947  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22948                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22949                         position of the SplitBar).
22950  */
22951 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22952     
22953     /** @private */
22954     this.el = Roo.get(dragElement, true);
22955     this.el.dom.unselectable = "on";
22956     /** @private */
22957     this.resizingEl = Roo.get(resizingElement, true);
22958
22959     /**
22960      * @private
22961      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22962      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22963      * @type Number
22964      */
22965     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22966     
22967     /**
22968      * The minimum size of the resizing element. (Defaults to 0)
22969      * @type Number
22970      */
22971     this.minSize = 0;
22972     
22973     /**
22974      * The maximum size of the resizing element. (Defaults to 2000)
22975      * @type Number
22976      */
22977     this.maxSize = 2000;
22978     
22979     /**
22980      * Whether to animate the transition to the new size
22981      * @type Boolean
22982      */
22983     this.animate = false;
22984     
22985     /**
22986      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22987      * @type Boolean
22988      */
22989     this.useShim = false;
22990     
22991     /** @private */
22992     this.shim = null;
22993     
22994     if(!existingProxy){
22995         /** @private */
22996         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22997     }else{
22998         this.proxy = Roo.get(existingProxy).dom;
22999     }
23000     /** @private */
23001     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23002     
23003     /** @private */
23004     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23005     
23006     /** @private */
23007     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23008     
23009     /** @private */
23010     this.dragSpecs = {};
23011     
23012     /**
23013      * @private The adapter to use to positon and resize elements
23014      */
23015     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23016     this.adapter.init(this);
23017     
23018     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23019         /** @private */
23020         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23021         this.el.addClass("x-splitbar-h");
23022     }else{
23023         /** @private */
23024         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23025         this.el.addClass("x-splitbar-v");
23026     }
23027     
23028     this.addEvents({
23029         /**
23030          * @event resize
23031          * Fires when the splitter is moved (alias for {@link #event-moved})
23032          * @param {Roo.SplitBar} this
23033          * @param {Number} newSize the new width or height
23034          */
23035         "resize" : true,
23036         /**
23037          * @event moved
23038          * Fires when the splitter is moved
23039          * @param {Roo.SplitBar} this
23040          * @param {Number} newSize the new width or height
23041          */
23042         "moved" : true,
23043         /**
23044          * @event beforeresize
23045          * Fires before the splitter is dragged
23046          * @param {Roo.SplitBar} this
23047          */
23048         "beforeresize" : true,
23049
23050         "beforeapply" : true
23051     });
23052
23053     Roo.util.Observable.call(this);
23054 };
23055
23056 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23057     onStartProxyDrag : function(x, y){
23058         this.fireEvent("beforeresize", this);
23059         if(!this.overlay){
23060             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23061             o.unselectable();
23062             o.enableDisplayMode("block");
23063             // all splitbars share the same overlay
23064             Roo.SplitBar.prototype.overlay = o;
23065         }
23066         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23067         this.overlay.show();
23068         Roo.get(this.proxy).setDisplayed("block");
23069         var size = this.adapter.getElementSize(this);
23070         this.activeMinSize = this.getMinimumSize();;
23071         this.activeMaxSize = this.getMaximumSize();;
23072         var c1 = size - this.activeMinSize;
23073         var c2 = Math.max(this.activeMaxSize - size, 0);
23074         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23075             this.dd.resetConstraints();
23076             this.dd.setXConstraint(
23077                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23078                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23079             );
23080             this.dd.setYConstraint(0, 0);
23081         }else{
23082             this.dd.resetConstraints();
23083             this.dd.setXConstraint(0, 0);
23084             this.dd.setYConstraint(
23085                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23086                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23087             );
23088          }
23089         this.dragSpecs.startSize = size;
23090         this.dragSpecs.startPoint = [x, y];
23091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23092     },
23093     
23094     /** 
23095      * @private Called after the drag operation by the DDProxy
23096      */
23097     onEndProxyDrag : function(e){
23098         Roo.get(this.proxy).setDisplayed(false);
23099         var endPoint = Roo.lib.Event.getXY(e);
23100         if(this.overlay){
23101             this.overlay.hide();
23102         }
23103         var newSize;
23104         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23105             newSize = this.dragSpecs.startSize + 
23106                 (this.placement == Roo.SplitBar.LEFT ?
23107                     endPoint[0] - this.dragSpecs.startPoint[0] :
23108                     this.dragSpecs.startPoint[0] - endPoint[0]
23109                 );
23110         }else{
23111             newSize = this.dragSpecs.startSize + 
23112                 (this.placement == Roo.SplitBar.TOP ?
23113                     endPoint[1] - this.dragSpecs.startPoint[1] :
23114                     this.dragSpecs.startPoint[1] - endPoint[1]
23115                 );
23116         }
23117         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23118         if(newSize != this.dragSpecs.startSize){
23119             if(this.fireEvent('beforeapply', this, newSize) !== false){
23120                 this.adapter.setElementSize(this, newSize);
23121                 this.fireEvent("moved", this, newSize);
23122                 this.fireEvent("resize", this, newSize);
23123             }
23124         }
23125     },
23126     
23127     /**
23128      * Get the adapter this SplitBar uses
23129      * @return The adapter object
23130      */
23131     getAdapter : function(){
23132         return this.adapter;
23133     },
23134     
23135     /**
23136      * Set the adapter this SplitBar uses
23137      * @param {Object} adapter A SplitBar adapter object
23138      */
23139     setAdapter : function(adapter){
23140         this.adapter = adapter;
23141         this.adapter.init(this);
23142     },
23143     
23144     /**
23145      * Gets the minimum size for the resizing element
23146      * @return {Number} The minimum size
23147      */
23148     getMinimumSize : function(){
23149         return this.minSize;
23150     },
23151     
23152     /**
23153      * Sets the minimum size for the resizing element
23154      * @param {Number} minSize The minimum size
23155      */
23156     setMinimumSize : function(minSize){
23157         this.minSize = minSize;
23158     },
23159     
23160     /**
23161      * Gets the maximum size for the resizing element
23162      * @return {Number} The maximum size
23163      */
23164     getMaximumSize : function(){
23165         return this.maxSize;
23166     },
23167     
23168     /**
23169      * Sets the maximum size for the resizing element
23170      * @param {Number} maxSize The maximum size
23171      */
23172     setMaximumSize : function(maxSize){
23173         this.maxSize = maxSize;
23174     },
23175     
23176     /**
23177      * Sets the initialize size for the resizing element
23178      * @param {Number} size The initial size
23179      */
23180     setCurrentSize : function(size){
23181         var oldAnimate = this.animate;
23182         this.animate = false;
23183         this.adapter.setElementSize(this, size);
23184         this.animate = oldAnimate;
23185     },
23186     
23187     /**
23188      * Destroy this splitbar. 
23189      * @param {Boolean} removeEl True to remove the element
23190      */
23191     destroy : function(removeEl){
23192         if(this.shim){
23193             this.shim.remove();
23194         }
23195         this.dd.unreg();
23196         this.proxy.parentNode.removeChild(this.proxy);
23197         if(removeEl){
23198             this.el.remove();
23199         }
23200     }
23201 });
23202
23203 /**
23204  * @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.
23205  */
23206 Roo.SplitBar.createProxy = function(dir){
23207     var proxy = new Roo.Element(document.createElement("div"));
23208     proxy.unselectable();
23209     var cls = 'x-splitbar-proxy';
23210     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23211     document.body.appendChild(proxy.dom);
23212     return proxy.dom;
23213 };
23214
23215 /** 
23216  * @class Roo.SplitBar.BasicLayoutAdapter
23217  * Default Adapter. It assumes the splitter and resizing element are not positioned
23218  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23219  */
23220 Roo.SplitBar.BasicLayoutAdapter = function(){
23221 };
23222
23223 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23224     // do nothing for now
23225     init : function(s){
23226     
23227     },
23228     /**
23229      * Called before drag operations to get the current size of the resizing element. 
23230      * @param {Roo.SplitBar} s The SplitBar using this adapter
23231      */
23232      getElementSize : function(s){
23233         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23234             return s.resizingEl.getWidth();
23235         }else{
23236             return s.resizingEl.getHeight();
23237         }
23238     },
23239     
23240     /**
23241      * Called after drag operations to set the size of the resizing element.
23242      * @param {Roo.SplitBar} s The SplitBar using this adapter
23243      * @param {Number} newSize The new size to set
23244      * @param {Function} onComplete A function to be invoked when resizing is complete
23245      */
23246     setElementSize : function(s, newSize, onComplete){
23247         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23248             if(!s.animate){
23249                 s.resizingEl.setWidth(newSize);
23250                 if(onComplete){
23251                     onComplete(s, newSize);
23252                 }
23253             }else{
23254                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23255             }
23256         }else{
23257             
23258             if(!s.animate){
23259                 s.resizingEl.setHeight(newSize);
23260                 if(onComplete){
23261                     onComplete(s, newSize);
23262                 }
23263             }else{
23264                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23265             }
23266         }
23267     }
23268 };
23269
23270 /** 
23271  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23272  * @extends Roo.SplitBar.BasicLayoutAdapter
23273  * Adapter that  moves the splitter element to align with the resized sizing element. 
23274  * Used with an absolute positioned SplitBar.
23275  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23276  * document.body, make sure you assign an id to the body element.
23277  */
23278 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23279     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23280     this.container = Roo.get(container);
23281 };
23282
23283 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23284     init : function(s){
23285         this.basic.init(s);
23286     },
23287     
23288     getElementSize : function(s){
23289         return this.basic.getElementSize(s);
23290     },
23291     
23292     setElementSize : function(s, newSize, onComplete){
23293         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23294     },
23295     
23296     moveSplitter : function(s){
23297         var yes = Roo.SplitBar;
23298         switch(s.placement){
23299             case yes.LEFT:
23300                 s.el.setX(s.resizingEl.getRight());
23301                 break;
23302             case yes.RIGHT:
23303                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23304                 break;
23305             case yes.TOP:
23306                 s.el.setY(s.resizingEl.getBottom());
23307                 break;
23308             case yes.BOTTOM:
23309                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23310                 break;
23311         }
23312     }
23313 };
23314
23315 /**
23316  * Orientation constant - Create a vertical SplitBar
23317  * @static
23318  * @type Number
23319  */
23320 Roo.SplitBar.VERTICAL = 1;
23321
23322 /**
23323  * Orientation constant - Create a horizontal SplitBar
23324  * @static
23325  * @type Number
23326  */
23327 Roo.SplitBar.HORIZONTAL = 2;
23328
23329 /**
23330  * Placement constant - The resizing element is to the left of the splitter element
23331  * @static
23332  * @type Number
23333  */
23334 Roo.SplitBar.LEFT = 1;
23335
23336 /**
23337  * Placement constant - The resizing element is to the right of the splitter element
23338  * @static
23339  * @type Number
23340  */
23341 Roo.SplitBar.RIGHT = 2;
23342
23343 /**
23344  * Placement constant - The resizing element is positioned above the splitter element
23345  * @static
23346  * @type Number
23347  */
23348 Roo.SplitBar.TOP = 3;
23349
23350 /**
23351  * Placement constant - The resizing element is positioned under splitter element
23352  * @static
23353  * @type Number
23354  */
23355 Roo.SplitBar.BOTTOM = 4;
23356 /*
23357  * Based on:
23358  * Ext JS Library 1.1.1
23359  * Copyright(c) 2006-2007, Ext JS, LLC.
23360  *
23361  * Originally Released Under LGPL - original licence link has changed is not relivant.
23362  *
23363  * Fork - LGPL
23364  * <script type="text/javascript">
23365  */
23366
23367 /**
23368  * @class Roo.View
23369  * @extends Roo.util.Observable
23370  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23371  * This class also supports single and multi selection modes. <br>
23372  * Create a data model bound view:
23373  <pre><code>
23374  var store = new Roo.data.Store(...);
23375
23376  var view = new Roo.View({
23377     el : "my-element",
23378     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23379  
23380     singleSelect: true,
23381     selectedClass: "ydataview-selected",
23382     store: store
23383  });
23384
23385  // listen for node click?
23386  view.on("click", function(vw, index, node, e){
23387  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23388  });
23389
23390  // load XML data
23391  dataModel.load("foobar.xml");
23392  </code></pre>
23393  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23394  * <br><br>
23395  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23396  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23397  * 
23398  * Note: old style constructor is still suported (container, template, config)
23399  * 
23400  * @constructor
23401  * Create a new View
23402  * @param {Object} config The config object
23403  * 
23404  */
23405 Roo.View = function(config, depreciated_tpl, depreciated_config){
23406     
23407     if (typeof(depreciated_tpl) == 'undefined') {
23408         // new way.. - universal constructor.
23409         Roo.apply(this, config);
23410         this.el  = Roo.get(this.el);
23411     } else {
23412         // old format..
23413         this.el  = Roo.get(config);
23414         this.tpl = depreciated_tpl;
23415         Roo.apply(this, depreciated_config);
23416     }
23417      
23418     
23419     if(typeof(this.tpl) == "string"){
23420         this.tpl = new Roo.Template(this.tpl);
23421     } else {
23422         // support xtype ctors..
23423         this.tpl = new Roo.factory(this.tpl, Roo);
23424     }
23425     
23426     
23427     this.tpl.compile();
23428    
23429
23430      
23431     /** @private */
23432     this.addEvents({
23433     /**
23434      * @event beforeclick
23435      * Fires before a click is processed. Returns false to cancel the default action.
23436      * @param {Roo.View} this
23437      * @param {Number} index The index of the target node
23438      * @param {HTMLElement} node The target node
23439      * @param {Roo.EventObject} e The raw event object
23440      */
23441         "beforeclick" : true,
23442     /**
23443      * @event click
23444      * Fires when a template node is clicked.
23445      * @param {Roo.View} this
23446      * @param {Number} index The index of the target node
23447      * @param {HTMLElement} node The target node
23448      * @param {Roo.EventObject} e The raw event object
23449      */
23450         "click" : true,
23451     /**
23452      * @event dblclick
23453      * Fires when a template node is double clicked.
23454      * @param {Roo.View} this
23455      * @param {Number} index The index of the target node
23456      * @param {HTMLElement} node The target node
23457      * @param {Roo.EventObject} e The raw event object
23458      */
23459         "dblclick" : true,
23460     /**
23461      * @event contextmenu
23462      * Fires when a template node is right clicked.
23463      * @param {Roo.View} this
23464      * @param {Number} index The index of the target node
23465      * @param {HTMLElement} node The target node
23466      * @param {Roo.EventObject} e The raw event object
23467      */
23468         "contextmenu" : true,
23469     /**
23470      * @event selectionchange
23471      * Fires when the selected nodes change.
23472      * @param {Roo.View} this
23473      * @param {Array} selections Array of the selected nodes
23474      */
23475         "selectionchange" : true,
23476
23477     /**
23478      * @event beforeselect
23479      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23480      * @param {Roo.View} this
23481      * @param {HTMLElement} node The node to be selected
23482      * @param {Array} selections Array of currently selected nodes
23483      */
23484         "beforeselect" : true
23485     });
23486
23487     this.el.on({
23488         "click": this.onClick,
23489         "dblclick": this.onDblClick,
23490         "contextmenu": this.onContextMenu,
23491         scope:this
23492     });
23493
23494     this.selections = [];
23495     this.nodes = [];
23496     this.cmp = new Roo.CompositeElementLite([]);
23497     if(this.store){
23498         this.store = Roo.factory(this.store, Roo.data);
23499         this.setStore(this.store, true);
23500     }
23501     Roo.View.superclass.constructor.call(this);
23502 };
23503
23504 Roo.extend(Roo.View, Roo.util.Observable, {
23505     
23506      /**
23507      * @cfg {Roo.data.Store} store Data store to load data from.
23508      */
23509     store : false,
23510     
23511     /**
23512      * @cfg {String|Roo.Element} el The container element.
23513      */
23514     el : '',
23515     
23516     /**
23517      * @cfg {String|Roo.Template} tpl The template used by this View 
23518      */
23519     tpl : false,
23520     
23521     /**
23522      * @cfg {String} selectedClass The css class to add to selected nodes
23523      */
23524     selectedClass : "x-view-selected",
23525      /**
23526      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23527      */
23528     emptyText : "",
23529     /**
23530      * @cfg {Boolean} multiSelect Allow multiple selection
23531      */
23532     
23533     multiSelect : false,
23534     /**
23535      * @cfg {Boolean} singleSelect Allow single selection
23536      */
23537     singleSelect:  false,
23538     
23539     /**
23540      * Returns the element this view is bound to.
23541      * @return {Roo.Element}
23542      */
23543     getEl : function(){
23544         return this.el;
23545     },
23546
23547     /**
23548      * Refreshes the view.
23549      */
23550     refresh : function(){
23551         var t = this.tpl;
23552         this.clearSelections();
23553         this.el.update("");
23554         var html = [];
23555         var records = this.store.getRange();
23556         if(records.length < 1){
23557             this.el.update(this.emptyText);
23558             return;
23559         }
23560         for(var i = 0, len = records.length; i < len; i++){
23561             var data = this.prepareData(records[i].data, i, records[i]);
23562             html[html.length] = t.apply(data);
23563         }
23564         this.el.update(html.join(""));
23565         this.nodes = this.el.dom.childNodes;
23566         this.updateIndexes(0);
23567     },
23568
23569     /**
23570      * Function to override to reformat the data that is sent to
23571      * the template for each node.
23572      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23573      * a JSON object for an UpdateManager bound view).
23574      */
23575     prepareData : function(data){
23576         return data;
23577     },
23578
23579     onUpdate : function(ds, record){
23580         this.clearSelections();
23581         var index = this.store.indexOf(record);
23582         var n = this.nodes[index];
23583         this.tpl.insertBefore(n, this.prepareData(record.data));
23584         n.parentNode.removeChild(n);
23585         this.updateIndexes(index, index);
23586     },
23587
23588     onAdd : function(ds, records, index){
23589         this.clearSelections();
23590         if(this.nodes.length == 0){
23591             this.refresh();
23592             return;
23593         }
23594         var n = this.nodes[index];
23595         for(var i = 0, len = records.length; i < len; i++){
23596             var d = this.prepareData(records[i].data);
23597             if(n){
23598                 this.tpl.insertBefore(n, d);
23599             }else{
23600                 this.tpl.append(this.el, d);
23601             }
23602         }
23603         this.updateIndexes(index);
23604     },
23605
23606     onRemove : function(ds, record, index){
23607         this.clearSelections();
23608         this.el.dom.removeChild(this.nodes[index]);
23609         this.updateIndexes(index);
23610     },
23611
23612     /**
23613      * Refresh an individual node.
23614      * @param {Number} index
23615      */
23616     refreshNode : function(index){
23617         this.onUpdate(this.store, this.store.getAt(index));
23618     },
23619
23620     updateIndexes : function(startIndex, endIndex){
23621         var ns = this.nodes;
23622         startIndex = startIndex || 0;
23623         endIndex = endIndex || ns.length - 1;
23624         for(var i = startIndex; i <= endIndex; i++){
23625             ns[i].nodeIndex = i;
23626         }
23627     },
23628
23629     /**
23630      * Changes the data store this view uses and refresh the view.
23631      * @param {Store} store
23632      */
23633     setStore : function(store, initial){
23634         if(!initial && this.store){
23635             this.store.un("datachanged", this.refresh);
23636             this.store.un("add", this.onAdd);
23637             this.store.un("remove", this.onRemove);
23638             this.store.un("update", this.onUpdate);
23639             this.store.un("clear", this.refresh);
23640         }
23641         if(store){
23642           
23643             store.on("datachanged", this.refresh, this);
23644             store.on("add", this.onAdd, this);
23645             store.on("remove", this.onRemove, this);
23646             store.on("update", this.onUpdate, this);
23647             store.on("clear", this.refresh, this);
23648         }
23649         
23650         if(store){
23651             this.refresh();
23652         }
23653     },
23654
23655     /**
23656      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23657      * @param {HTMLElement} node
23658      * @return {HTMLElement} The template node
23659      */
23660     findItemFromChild : function(node){
23661         var el = this.el.dom;
23662         if(!node || node.parentNode == el){
23663                     return node;
23664             }
23665             var p = node.parentNode;
23666             while(p && p != el){
23667             if(p.parentNode == el){
23668                 return p;
23669             }
23670             p = p.parentNode;
23671         }
23672             return null;
23673     },
23674
23675     /** @ignore */
23676     onClick : function(e){
23677         var item = this.findItemFromChild(e.getTarget());
23678         if(item){
23679             var index = this.indexOf(item);
23680             if(this.onItemClick(item, index, e) !== false){
23681                 this.fireEvent("click", this, index, item, e);
23682             }
23683         }else{
23684             this.clearSelections();
23685         }
23686     },
23687
23688     /** @ignore */
23689     onContextMenu : function(e){
23690         var item = this.findItemFromChild(e.getTarget());
23691         if(item){
23692             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23693         }
23694     },
23695
23696     /** @ignore */
23697     onDblClick : function(e){
23698         var item = this.findItemFromChild(e.getTarget());
23699         if(item){
23700             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23701         }
23702     },
23703
23704     onItemClick : function(item, index, e){
23705         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23706             return false;
23707         }
23708         if(this.multiSelect || this.singleSelect){
23709             if(this.multiSelect && e.shiftKey && this.lastSelection){
23710                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23711             }else{
23712                 this.select(item, this.multiSelect && e.ctrlKey);
23713                 this.lastSelection = item;
23714             }
23715             e.preventDefault();
23716         }
23717         return true;
23718     },
23719
23720     /**
23721      * Get the number of selected nodes.
23722      * @return {Number}
23723      */
23724     getSelectionCount : function(){
23725         return this.selections.length;
23726     },
23727
23728     /**
23729      * Get the currently selected nodes.
23730      * @return {Array} An array of HTMLElements
23731      */
23732     getSelectedNodes : function(){
23733         return this.selections;
23734     },
23735
23736     /**
23737      * Get the indexes of the selected nodes.
23738      * @return {Array}
23739      */
23740     getSelectedIndexes : function(){
23741         var indexes = [], s = this.selections;
23742         for(var i = 0, len = s.length; i < len; i++){
23743             indexes.push(s[i].nodeIndex);
23744         }
23745         return indexes;
23746     },
23747
23748     /**
23749      * Clear all selections
23750      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23751      */
23752     clearSelections : function(suppressEvent){
23753         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23754             this.cmp.elements = this.selections;
23755             this.cmp.removeClass(this.selectedClass);
23756             this.selections = [];
23757             if(!suppressEvent){
23758                 this.fireEvent("selectionchange", this, this.selections);
23759             }
23760         }
23761     },
23762
23763     /**
23764      * Returns true if the passed node is selected
23765      * @param {HTMLElement/Number} node The node or node index
23766      * @return {Boolean}
23767      */
23768     isSelected : function(node){
23769         var s = this.selections;
23770         if(s.length < 1){
23771             return false;
23772         }
23773         node = this.getNode(node);
23774         return s.indexOf(node) !== -1;
23775     },
23776
23777     /**
23778      * Selects nodes.
23779      * @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
23780      * @param {Boolean} keepExisting (optional) true to keep existing selections
23781      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23782      */
23783     select : function(nodeInfo, keepExisting, suppressEvent){
23784         if(nodeInfo instanceof Array){
23785             if(!keepExisting){
23786                 this.clearSelections(true);
23787             }
23788             for(var i = 0, len = nodeInfo.length; i < len; i++){
23789                 this.select(nodeInfo[i], true, true);
23790             }
23791         } else{
23792             var node = this.getNode(nodeInfo);
23793             if(node && !this.isSelected(node)){
23794                 if(!keepExisting){
23795                     this.clearSelections(true);
23796                 }
23797                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23798                     Roo.fly(node).addClass(this.selectedClass);
23799                     this.selections.push(node);
23800                     if(!suppressEvent){
23801                         this.fireEvent("selectionchange", this, this.selections);
23802                     }
23803                 }
23804             }
23805         }
23806     },
23807
23808     /**
23809      * Gets a template node.
23810      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23811      * @return {HTMLElement} The node or null if it wasn't found
23812      */
23813     getNode : function(nodeInfo){
23814         if(typeof nodeInfo == "string"){
23815             return document.getElementById(nodeInfo);
23816         }else if(typeof nodeInfo == "number"){
23817             return this.nodes[nodeInfo];
23818         }
23819         return nodeInfo;
23820     },
23821
23822     /**
23823      * Gets a range template nodes.
23824      * @param {Number} startIndex
23825      * @param {Number} endIndex
23826      * @return {Array} An array of nodes
23827      */
23828     getNodes : function(start, end){
23829         var ns = this.nodes;
23830         start = start || 0;
23831         end = typeof end == "undefined" ? ns.length - 1 : end;
23832         var nodes = [];
23833         if(start <= end){
23834             for(var i = start; i <= end; i++){
23835                 nodes.push(ns[i]);
23836             }
23837         } else{
23838             for(var i = start; i >= end; i--){
23839                 nodes.push(ns[i]);
23840             }
23841         }
23842         return nodes;
23843     },
23844
23845     /**
23846      * Finds the index of the passed node
23847      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23848      * @return {Number} The index of the node or -1
23849      */
23850     indexOf : function(node){
23851         node = this.getNode(node);
23852         if(typeof node.nodeIndex == "number"){
23853             return node.nodeIndex;
23854         }
23855         var ns = this.nodes;
23856         for(var i = 0, len = ns.length; i < len; i++){
23857             if(ns[i] == node){
23858                 return i;
23859             }
23860         }
23861         return -1;
23862     }
23863 });
23864 /*
23865  * Based on:
23866  * Ext JS Library 1.1.1
23867  * Copyright(c) 2006-2007, Ext JS, LLC.
23868  *
23869  * Originally Released Under LGPL - original licence link has changed is not relivant.
23870  *
23871  * Fork - LGPL
23872  * <script type="text/javascript">
23873  */
23874
23875 /**
23876  * @class Roo.JsonView
23877  * @extends Roo.View
23878  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23879 <pre><code>
23880 var view = new Roo.JsonView({
23881     container: "my-element",
23882     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23883     multiSelect: true, 
23884     jsonRoot: "data" 
23885 });
23886
23887 // listen for node click?
23888 view.on("click", function(vw, index, node, e){
23889     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23890 });
23891
23892 // direct load of JSON data
23893 view.load("foobar.php");
23894
23895 // Example from my blog list
23896 var tpl = new Roo.Template(
23897     '&lt;div class="entry"&gt;' +
23898     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23899     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23900     "&lt;/div&gt;&lt;hr /&gt;"
23901 );
23902
23903 var moreView = new Roo.JsonView({
23904     container :  "entry-list", 
23905     template : tpl,
23906     jsonRoot: "posts"
23907 });
23908 moreView.on("beforerender", this.sortEntries, this);
23909 moreView.load({
23910     url: "/blog/get-posts.php",
23911     params: "allposts=true",
23912     text: "Loading Blog Entries..."
23913 });
23914 </code></pre>
23915
23916 * Note: old code is supported with arguments : (container, template, config)
23917
23918
23919  * @constructor
23920  * Create a new JsonView
23921  * 
23922  * @param {Object} config The config object
23923  * 
23924  */
23925 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23926     
23927     
23928     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23929
23930     var um = this.el.getUpdateManager();
23931     um.setRenderer(this);
23932     um.on("update", this.onLoad, this);
23933     um.on("failure", this.onLoadException, this);
23934
23935     /**
23936      * @event beforerender
23937      * Fires before rendering of the downloaded JSON data.
23938      * @param {Roo.JsonView} this
23939      * @param {Object} data The JSON data loaded
23940      */
23941     /**
23942      * @event load
23943      * Fires when data is loaded.
23944      * @param {Roo.JsonView} this
23945      * @param {Object} data The JSON data loaded
23946      * @param {Object} response The raw Connect response object
23947      */
23948     /**
23949      * @event loadexception
23950      * Fires when loading fails.
23951      * @param {Roo.JsonView} this
23952      * @param {Object} response The raw Connect response object
23953      */
23954     this.addEvents({
23955         'beforerender' : true,
23956         'load' : true,
23957         'loadexception' : true
23958     });
23959 };
23960 Roo.extend(Roo.JsonView, Roo.View, {
23961     /**
23962      * @type {String} The root property in the loaded JSON object that contains the data
23963      */
23964     jsonRoot : "",
23965
23966     /**
23967      * Refreshes the view.
23968      */
23969     refresh : function(){
23970         this.clearSelections();
23971         this.el.update("");
23972         var html = [];
23973         var o = this.jsonData;
23974         if(o && o.length > 0){
23975             for(var i = 0, len = o.length; i < len; i++){
23976                 var data = this.prepareData(o[i], i, o);
23977                 html[html.length] = this.tpl.apply(data);
23978             }
23979         }else{
23980             html.push(this.emptyText);
23981         }
23982         this.el.update(html.join(""));
23983         this.nodes = this.el.dom.childNodes;
23984         this.updateIndexes(0);
23985     },
23986
23987     /**
23988      * 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.
23989      * @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:
23990      <pre><code>
23991      view.load({
23992          url: "your-url.php",
23993          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23994          callback: yourFunction,
23995          scope: yourObject, //(optional scope)
23996          discardUrl: false,
23997          nocache: false,
23998          text: "Loading...",
23999          timeout: 30,
24000          scripts: false
24001      });
24002      </code></pre>
24003      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24004      * 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.
24005      * @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}
24006      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24007      * @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.
24008      */
24009     load : function(){
24010         var um = this.el.getUpdateManager();
24011         um.update.apply(um, arguments);
24012     },
24013
24014     render : function(el, response){
24015         this.clearSelections();
24016         this.el.update("");
24017         var o;
24018         try{
24019             o = Roo.util.JSON.decode(response.responseText);
24020             if(this.jsonRoot){
24021                 
24022                 o = o[this.jsonRoot];
24023             }
24024         } catch(e){
24025         }
24026         /**
24027          * The current JSON data or null
24028          */
24029         this.jsonData = o;
24030         this.beforeRender();
24031         this.refresh();
24032     },
24033
24034 /**
24035  * Get the number of records in the current JSON dataset
24036  * @return {Number}
24037  */
24038     getCount : function(){
24039         return this.jsonData ? this.jsonData.length : 0;
24040     },
24041
24042 /**
24043  * Returns the JSON object for the specified node(s)
24044  * @param {HTMLElement/Array} node The node or an array of nodes
24045  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24046  * you get the JSON object for the node
24047  */
24048     getNodeData : function(node){
24049         if(node instanceof Array){
24050             var data = [];
24051             for(var i = 0, len = node.length; i < len; i++){
24052                 data.push(this.getNodeData(node[i]));
24053             }
24054             return data;
24055         }
24056         return this.jsonData[this.indexOf(node)] || null;
24057     },
24058
24059     beforeRender : function(){
24060         this.snapshot = this.jsonData;
24061         if(this.sortInfo){
24062             this.sort.apply(this, this.sortInfo);
24063         }
24064         this.fireEvent("beforerender", this, this.jsonData);
24065     },
24066
24067     onLoad : function(el, o){
24068         this.fireEvent("load", this, this.jsonData, o);
24069     },
24070
24071     onLoadException : function(el, o){
24072         this.fireEvent("loadexception", this, o);
24073     },
24074
24075 /**
24076  * Filter the data by a specific property.
24077  * @param {String} property A property on your JSON objects
24078  * @param {String/RegExp} value Either string that the property values
24079  * should start with, or a RegExp to test against the property
24080  */
24081     filter : function(property, value){
24082         if(this.jsonData){
24083             var data = [];
24084             var ss = this.snapshot;
24085             if(typeof value == "string"){
24086                 var vlen = value.length;
24087                 if(vlen == 0){
24088                     this.clearFilter();
24089                     return;
24090                 }
24091                 value = value.toLowerCase();
24092                 for(var i = 0, len = ss.length; i < len; i++){
24093                     var o = ss[i];
24094                     if(o[property].substr(0, vlen).toLowerCase() == value){
24095                         data.push(o);
24096                     }
24097                 }
24098             } else if(value.exec){ // regex?
24099                 for(var i = 0, len = ss.length; i < len; i++){
24100                     var o = ss[i];
24101                     if(value.test(o[property])){
24102                         data.push(o);
24103                     }
24104                 }
24105             } else{
24106                 return;
24107             }
24108             this.jsonData = data;
24109             this.refresh();
24110         }
24111     },
24112
24113 /**
24114  * Filter by a function. The passed function will be called with each
24115  * object in the current dataset. If the function returns true the value is kept,
24116  * otherwise it is filtered.
24117  * @param {Function} fn
24118  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24119  */
24120     filterBy : function(fn, scope){
24121         if(this.jsonData){
24122             var data = [];
24123             var ss = this.snapshot;
24124             for(var i = 0, len = ss.length; i < len; i++){
24125                 var o = ss[i];
24126                 if(fn.call(scope || this, o)){
24127                     data.push(o);
24128                 }
24129             }
24130             this.jsonData = data;
24131             this.refresh();
24132         }
24133     },
24134
24135 /**
24136  * Clears the current filter.
24137  */
24138     clearFilter : function(){
24139         if(this.snapshot && this.jsonData != this.snapshot){
24140             this.jsonData = this.snapshot;
24141             this.refresh();
24142         }
24143     },
24144
24145
24146 /**
24147  * Sorts the data for this view and refreshes it.
24148  * @param {String} property A property on your JSON objects to sort on
24149  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24150  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24151  */
24152     sort : function(property, dir, sortType){
24153         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24154         if(this.jsonData){
24155             var p = property;
24156             var dsc = dir && dir.toLowerCase() == "desc";
24157             var f = function(o1, o2){
24158                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24159                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24160                 ;
24161                 if(v1 < v2){
24162                     return dsc ? +1 : -1;
24163                 } else if(v1 > v2){
24164                     return dsc ? -1 : +1;
24165                 } else{
24166                     return 0;
24167                 }
24168             };
24169             this.jsonData.sort(f);
24170             this.refresh();
24171             if(this.jsonData != this.snapshot){
24172                 this.snapshot.sort(f);
24173             }
24174         }
24175     }
24176 });/*
24177  * Based on:
24178  * Ext JS Library 1.1.1
24179  * Copyright(c) 2006-2007, Ext JS, LLC.
24180  *
24181  * Originally Released Under LGPL - original licence link has changed is not relivant.
24182  *
24183  * Fork - LGPL
24184  * <script type="text/javascript">
24185  */
24186  
24187
24188 /**
24189  * @class Roo.ColorPalette
24190  * @extends Roo.Component
24191  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24192  * Here's an example of typical usage:
24193  * <pre><code>
24194 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24195 cp.render('my-div');
24196
24197 cp.on('select', function(palette, selColor){
24198     // do something with selColor
24199 });
24200 </code></pre>
24201  * @constructor
24202  * Create a new ColorPalette
24203  * @param {Object} config The config object
24204  */
24205 Roo.ColorPalette = function(config){
24206     Roo.ColorPalette.superclass.constructor.call(this, config);
24207     this.addEvents({
24208         /**
24209              * @event select
24210              * Fires when a color is selected
24211              * @param {ColorPalette} this
24212              * @param {String} color The 6-digit color hex code (without the # symbol)
24213              */
24214         select: true
24215     });
24216
24217     if(this.handler){
24218         this.on("select", this.handler, this.scope, true);
24219     }
24220 };
24221 Roo.extend(Roo.ColorPalette, Roo.Component, {
24222     /**
24223      * @cfg {String} itemCls
24224      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24225      */
24226     itemCls : "x-color-palette",
24227     /**
24228      * @cfg {String} value
24229      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24230      * the hex codes are case-sensitive.
24231      */
24232     value : null,
24233     clickEvent:'click',
24234     // private
24235     ctype: "Roo.ColorPalette",
24236
24237     /**
24238      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24239      */
24240     allowReselect : false,
24241
24242     /**
24243      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24244      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24245      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24246      * of colors with the width setting until the box is symmetrical.</p>
24247      * <p>You can override individual colors if needed:</p>
24248      * <pre><code>
24249 var cp = new Roo.ColorPalette();
24250 cp.colors[0] = "FF0000";  // change the first box to red
24251 </code></pre>
24252
24253 Or you can provide a custom array of your own for complete control:
24254 <pre><code>
24255 var cp = new Roo.ColorPalette();
24256 cp.colors = ["000000", "993300", "333300"];
24257 </code></pre>
24258      * @type Array
24259      */
24260     colors : [
24261         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24262         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24263         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24264         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24265         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24266     ],
24267
24268     // private
24269     onRender : function(container, position){
24270         var t = new Roo.MasterTemplate(
24271             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24272         );
24273         var c = this.colors;
24274         for(var i = 0, len = c.length; i < len; i++){
24275             t.add([c[i]]);
24276         }
24277         var el = document.createElement("div");
24278         el.className = this.itemCls;
24279         t.overwrite(el);
24280         container.dom.insertBefore(el, position);
24281         this.el = Roo.get(el);
24282         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24283         if(this.clickEvent != 'click'){
24284             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24285         }
24286     },
24287
24288     // private
24289     afterRender : function(){
24290         Roo.ColorPalette.superclass.afterRender.call(this);
24291         if(this.value){
24292             var s = this.value;
24293             this.value = null;
24294             this.select(s);
24295         }
24296     },
24297
24298     // private
24299     handleClick : function(e, t){
24300         e.preventDefault();
24301         if(!this.disabled){
24302             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24303             this.select(c.toUpperCase());
24304         }
24305     },
24306
24307     /**
24308      * Selects the specified color in the palette (fires the select event)
24309      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24310      */
24311     select : function(color){
24312         color = color.replace("#", "");
24313         if(color != this.value || this.allowReselect){
24314             var el = this.el;
24315             if(this.value){
24316                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24317             }
24318             el.child("a.color-"+color).addClass("x-color-palette-sel");
24319             this.value = color;
24320             this.fireEvent("select", this, color);
24321         }
24322     }
24323 });/*
24324  * Based on:
24325  * Ext JS Library 1.1.1
24326  * Copyright(c) 2006-2007, Ext JS, LLC.
24327  *
24328  * Originally Released Under LGPL - original licence link has changed is not relivant.
24329  *
24330  * Fork - LGPL
24331  * <script type="text/javascript">
24332  */
24333  
24334 /**
24335  * @class Roo.DatePicker
24336  * @extends Roo.Component
24337  * Simple date picker class.
24338  * @constructor
24339  * Create a new DatePicker
24340  * @param {Object} config The config object
24341  */
24342 Roo.DatePicker = function(config){
24343     Roo.DatePicker.superclass.constructor.call(this, config);
24344
24345     this.value = config && config.value ?
24346                  config.value.clearTime() : new Date().clearTime();
24347
24348     this.addEvents({
24349         /**
24350              * @event select
24351              * Fires when a date is selected
24352              * @param {DatePicker} this
24353              * @param {Date} date The selected date
24354              */
24355         select: true
24356     });
24357
24358     if(this.handler){
24359         this.on("select", this.handler,  this.scope || this);
24360     }
24361     // build the disabledDatesRE
24362     if(!this.disabledDatesRE && this.disabledDates){
24363         var dd = this.disabledDates;
24364         var re = "(?:";
24365         for(var i = 0; i < dd.length; i++){
24366             re += dd[i];
24367             if(i != dd.length-1) re += "|";
24368         }
24369         this.disabledDatesRE = new RegExp(re + ")");
24370     }
24371 };
24372
24373 Roo.extend(Roo.DatePicker, Roo.Component, {
24374     /**
24375      * @cfg {String} todayText
24376      * The text to display on the button that selects the current date (defaults to "Today")
24377      */
24378     todayText : "Today",
24379     /**
24380      * @cfg {String} okText
24381      * The text to display on the ok button
24382      */
24383     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24384     /**
24385      * @cfg {String} cancelText
24386      * The text to display on the cancel button
24387      */
24388     cancelText : "Cancel",
24389     /**
24390      * @cfg {String} todayTip
24391      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24392      */
24393     todayTip : "{0} (Spacebar)",
24394     /**
24395      * @cfg {Date} minDate
24396      * Minimum allowable date (JavaScript date object, defaults to null)
24397      */
24398     minDate : null,
24399     /**
24400      * @cfg {Date} maxDate
24401      * Maximum allowable date (JavaScript date object, defaults to null)
24402      */
24403     maxDate : null,
24404     /**
24405      * @cfg {String} minText
24406      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24407      */
24408     minText : "This date is before the minimum date",
24409     /**
24410      * @cfg {String} maxText
24411      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24412      */
24413     maxText : "This date is after the maximum date",
24414     /**
24415      * @cfg {String} format
24416      * The default date format string which can be overriden for localization support.  The format must be
24417      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24418      */
24419     format : "m/d/y",
24420     /**
24421      * @cfg {Array} disabledDays
24422      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24423      */
24424     disabledDays : null,
24425     /**
24426      * @cfg {String} disabledDaysText
24427      * The tooltip to display when the date falls on a disabled day (defaults to "")
24428      */
24429     disabledDaysText : "",
24430     /**
24431      * @cfg {RegExp} disabledDatesRE
24432      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24433      */
24434     disabledDatesRE : null,
24435     /**
24436      * @cfg {String} disabledDatesText
24437      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24438      */
24439     disabledDatesText : "",
24440     /**
24441      * @cfg {Boolean} constrainToViewport
24442      * True to constrain the date picker to the viewport (defaults to true)
24443      */
24444     constrainToViewport : true,
24445     /**
24446      * @cfg {Array} monthNames
24447      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24448      */
24449     monthNames : Date.monthNames,
24450     /**
24451      * @cfg {Array} dayNames
24452      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24453      */
24454     dayNames : Date.dayNames,
24455     /**
24456      * @cfg {String} nextText
24457      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24458      */
24459     nextText: 'Next Month (Control+Right)',
24460     /**
24461      * @cfg {String} prevText
24462      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24463      */
24464     prevText: 'Previous Month (Control+Left)',
24465     /**
24466      * @cfg {String} monthYearText
24467      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24468      */
24469     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24470     /**
24471      * @cfg {Number} startDay
24472      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24473      */
24474     startDay : 0,
24475     /**
24476      * @cfg {Bool} showClear
24477      * Show a clear button (usefull for date form elements that can be blank.)
24478      */
24479     
24480     showClear: false,
24481     
24482     /**
24483      * Sets the value of the date field
24484      * @param {Date} value The date to set
24485      */
24486     setValue : function(value){
24487         var old = this.value;
24488         this.value = value.clearTime(true);
24489         if(this.el){
24490             this.update(this.value);
24491         }
24492     },
24493
24494     /**
24495      * Gets the current selected value of the date field
24496      * @return {Date} The selected date
24497      */
24498     getValue : function(){
24499         return this.value;
24500     },
24501
24502     // private
24503     focus : function(){
24504         if(this.el){
24505             this.update(this.activeDate);
24506         }
24507     },
24508
24509     // private
24510     onRender : function(container, position){
24511         var m = [
24512              '<table cellspacing="0">',
24513                 '<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>',
24514                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24515         var dn = this.dayNames;
24516         for(var i = 0; i < 7; i++){
24517             var d = this.startDay+i;
24518             if(d > 6){
24519                 d = d-7;
24520             }
24521             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24522         }
24523         m[m.length] = "</tr></thead><tbody><tr>";
24524         for(var i = 0; i < 42; i++) {
24525             if(i % 7 == 0 && i != 0){
24526                 m[m.length] = "</tr><tr>";
24527             }
24528             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24529         }
24530         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24531             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24532
24533         var el = document.createElement("div");
24534         el.className = "x-date-picker";
24535         el.innerHTML = m.join("");
24536
24537         container.dom.insertBefore(el, position);
24538
24539         this.el = Roo.get(el);
24540         this.eventEl = Roo.get(el.firstChild);
24541
24542         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24543             handler: this.showPrevMonth,
24544             scope: this,
24545             preventDefault:true,
24546             stopDefault:true
24547         });
24548
24549         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24550             handler: this.showNextMonth,
24551             scope: this,
24552             preventDefault:true,
24553             stopDefault:true
24554         });
24555
24556         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24557
24558         this.monthPicker = this.el.down('div.x-date-mp');
24559         this.monthPicker.enableDisplayMode('block');
24560         
24561         var kn = new Roo.KeyNav(this.eventEl, {
24562             "left" : function(e){
24563                 e.ctrlKey ?
24564                     this.showPrevMonth() :
24565                     this.update(this.activeDate.add("d", -1));
24566             },
24567
24568             "right" : function(e){
24569                 e.ctrlKey ?
24570                     this.showNextMonth() :
24571                     this.update(this.activeDate.add("d", 1));
24572             },
24573
24574             "up" : function(e){
24575                 e.ctrlKey ?
24576                     this.showNextYear() :
24577                     this.update(this.activeDate.add("d", -7));
24578             },
24579
24580             "down" : function(e){
24581                 e.ctrlKey ?
24582                     this.showPrevYear() :
24583                     this.update(this.activeDate.add("d", 7));
24584             },
24585
24586             "pageUp" : function(e){
24587                 this.showNextMonth();
24588             },
24589
24590             "pageDown" : function(e){
24591                 this.showPrevMonth();
24592             },
24593
24594             "enter" : function(e){
24595                 e.stopPropagation();
24596                 return true;
24597             },
24598
24599             scope : this
24600         });
24601
24602         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24603
24604         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24605
24606         this.el.unselectable();
24607         
24608         this.cells = this.el.select("table.x-date-inner tbody td");
24609         this.textNodes = this.el.query("table.x-date-inner tbody span");
24610
24611         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24612             text: "&#160;",
24613             tooltip: this.monthYearText
24614         });
24615
24616         this.mbtn.on('click', this.showMonthPicker, this);
24617         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24618
24619
24620         var today = (new Date()).dateFormat(this.format);
24621         
24622         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24623         if (this.showClear) {
24624             baseTb.add( new Roo.Toolbar.Fill());
24625         }
24626         baseTb.add({
24627             text: String.format(this.todayText, today),
24628             tooltip: String.format(this.todayTip, today),
24629             handler: this.selectToday,
24630             scope: this
24631         });
24632         
24633         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24634             
24635         //});
24636         if (this.showClear) {
24637             
24638             baseTb.add( new Roo.Toolbar.Fill());
24639             baseTb.add({
24640                 text: '&#160;',
24641                 cls: 'x-btn-icon x-btn-clear',
24642                 handler: function() {
24643                     //this.value = '';
24644                     this.fireEvent("select", this, '');
24645                 },
24646                 scope: this
24647             });
24648         }
24649         
24650         
24651         if(Roo.isIE){
24652             this.el.repaint();
24653         }
24654         this.update(this.value);
24655     },
24656
24657     createMonthPicker : function(){
24658         if(!this.monthPicker.dom.firstChild){
24659             var buf = ['<table border="0" cellspacing="0">'];
24660             for(var i = 0; i < 6; i++){
24661                 buf.push(
24662                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24663                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24664                     i == 0 ?
24665                     '<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>' :
24666                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24667                 );
24668             }
24669             buf.push(
24670                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24671                     this.okText,
24672                     '</button><button type="button" class="x-date-mp-cancel">',
24673                     this.cancelText,
24674                     '</button></td></tr>',
24675                 '</table>'
24676             );
24677             this.monthPicker.update(buf.join(''));
24678             this.monthPicker.on('click', this.onMonthClick, this);
24679             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24680
24681             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24682             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24683
24684             this.mpMonths.each(function(m, a, i){
24685                 i += 1;
24686                 if((i%2) == 0){
24687                     m.dom.xmonth = 5 + Math.round(i * .5);
24688                 }else{
24689                     m.dom.xmonth = Math.round((i-1) * .5);
24690                 }
24691             });
24692         }
24693     },
24694
24695     showMonthPicker : function(){
24696         this.createMonthPicker();
24697         var size = this.el.getSize();
24698         this.monthPicker.setSize(size);
24699         this.monthPicker.child('table').setSize(size);
24700
24701         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24702         this.updateMPMonth(this.mpSelMonth);
24703         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24704         this.updateMPYear(this.mpSelYear);
24705
24706         this.monthPicker.slideIn('t', {duration:.2});
24707     },
24708
24709     updateMPYear : function(y){
24710         this.mpyear = y;
24711         var ys = this.mpYears.elements;
24712         for(var i = 1; i <= 10; i++){
24713             var td = ys[i-1], y2;
24714             if((i%2) == 0){
24715                 y2 = y + Math.round(i * .5);
24716                 td.firstChild.innerHTML = y2;
24717                 td.xyear = y2;
24718             }else{
24719                 y2 = y - (5-Math.round(i * .5));
24720                 td.firstChild.innerHTML = y2;
24721                 td.xyear = y2;
24722             }
24723             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24724         }
24725     },
24726
24727     updateMPMonth : function(sm){
24728         this.mpMonths.each(function(m, a, i){
24729             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24730         });
24731     },
24732
24733     selectMPMonth: function(m){
24734         
24735     },
24736
24737     onMonthClick : function(e, t){
24738         e.stopEvent();
24739         var el = new Roo.Element(t), pn;
24740         if(el.is('button.x-date-mp-cancel')){
24741             this.hideMonthPicker();
24742         }
24743         else if(el.is('button.x-date-mp-ok')){
24744             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24745             this.hideMonthPicker();
24746         }
24747         else if(pn = el.up('td.x-date-mp-month', 2)){
24748             this.mpMonths.removeClass('x-date-mp-sel');
24749             pn.addClass('x-date-mp-sel');
24750             this.mpSelMonth = pn.dom.xmonth;
24751         }
24752         else if(pn = el.up('td.x-date-mp-year', 2)){
24753             this.mpYears.removeClass('x-date-mp-sel');
24754             pn.addClass('x-date-mp-sel');
24755             this.mpSelYear = pn.dom.xyear;
24756         }
24757         else if(el.is('a.x-date-mp-prev')){
24758             this.updateMPYear(this.mpyear-10);
24759         }
24760         else if(el.is('a.x-date-mp-next')){
24761             this.updateMPYear(this.mpyear+10);
24762         }
24763     },
24764
24765     onMonthDblClick : function(e, t){
24766         e.stopEvent();
24767         var el = new Roo.Element(t), pn;
24768         if(pn = el.up('td.x-date-mp-month', 2)){
24769             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24770             this.hideMonthPicker();
24771         }
24772         else if(pn = el.up('td.x-date-mp-year', 2)){
24773             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24774             this.hideMonthPicker();
24775         }
24776     },
24777
24778     hideMonthPicker : function(disableAnim){
24779         if(this.monthPicker){
24780             if(disableAnim === true){
24781                 this.monthPicker.hide();
24782             }else{
24783                 this.monthPicker.slideOut('t', {duration:.2});
24784             }
24785         }
24786     },
24787
24788     // private
24789     showPrevMonth : function(e){
24790         this.update(this.activeDate.add("mo", -1));
24791     },
24792
24793     // private
24794     showNextMonth : function(e){
24795         this.update(this.activeDate.add("mo", 1));
24796     },
24797
24798     // private
24799     showPrevYear : function(){
24800         this.update(this.activeDate.add("y", -1));
24801     },
24802
24803     // private
24804     showNextYear : function(){
24805         this.update(this.activeDate.add("y", 1));
24806     },
24807
24808     // private
24809     handleMouseWheel : function(e){
24810         var delta = e.getWheelDelta();
24811         if(delta > 0){
24812             this.showPrevMonth();
24813             e.stopEvent();
24814         } else if(delta < 0){
24815             this.showNextMonth();
24816             e.stopEvent();
24817         }
24818     },
24819
24820     // private
24821     handleDateClick : function(e, t){
24822         e.stopEvent();
24823         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24824             this.setValue(new Date(t.dateValue));
24825             this.fireEvent("select", this, this.value);
24826         }
24827     },
24828
24829     // private
24830     selectToday : function(){
24831         this.setValue(new Date().clearTime());
24832         this.fireEvent("select", this, this.value);
24833     },
24834
24835     // private
24836     update : function(date){
24837         var vd = this.activeDate;
24838         this.activeDate = date;
24839         if(vd && this.el){
24840             var t = date.getTime();
24841             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24842                 this.cells.removeClass("x-date-selected");
24843                 this.cells.each(function(c){
24844                    if(c.dom.firstChild.dateValue == t){
24845                        c.addClass("x-date-selected");
24846                        setTimeout(function(){
24847                             try{c.dom.firstChild.focus();}catch(e){}
24848                        }, 50);
24849                        return false;
24850                    }
24851                 });
24852                 return;
24853             }
24854         }
24855         var days = date.getDaysInMonth();
24856         var firstOfMonth = date.getFirstDateOfMonth();
24857         var startingPos = firstOfMonth.getDay()-this.startDay;
24858
24859         if(startingPos <= this.startDay){
24860             startingPos += 7;
24861         }
24862
24863         var pm = date.add("mo", -1);
24864         var prevStart = pm.getDaysInMonth()-startingPos;
24865
24866         var cells = this.cells.elements;
24867         var textEls = this.textNodes;
24868         days += startingPos;
24869
24870         // convert everything to numbers so it's fast
24871         var day = 86400000;
24872         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24873         var today = new Date().clearTime().getTime();
24874         var sel = date.clearTime().getTime();
24875         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24876         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24877         var ddMatch = this.disabledDatesRE;
24878         var ddText = this.disabledDatesText;
24879         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24880         var ddaysText = this.disabledDaysText;
24881         var format = this.format;
24882
24883         var setCellClass = function(cal, cell){
24884             cell.title = "";
24885             var t = d.getTime();
24886             cell.firstChild.dateValue = t;
24887             if(t == today){
24888                 cell.className += " x-date-today";
24889                 cell.title = cal.todayText;
24890             }
24891             if(t == sel){
24892                 cell.className += " x-date-selected";
24893                 setTimeout(function(){
24894                     try{cell.firstChild.focus();}catch(e){}
24895                 }, 50);
24896             }
24897             // disabling
24898             if(t < min) {
24899                 cell.className = " x-date-disabled";
24900                 cell.title = cal.minText;
24901                 return;
24902             }
24903             if(t > max) {
24904                 cell.className = " x-date-disabled";
24905                 cell.title = cal.maxText;
24906                 return;
24907             }
24908             if(ddays){
24909                 if(ddays.indexOf(d.getDay()) != -1){
24910                     cell.title = ddaysText;
24911                     cell.className = " x-date-disabled";
24912                 }
24913             }
24914             if(ddMatch && format){
24915                 var fvalue = d.dateFormat(format);
24916                 if(ddMatch.test(fvalue)){
24917                     cell.title = ddText.replace("%0", fvalue);
24918                     cell.className = " x-date-disabled";
24919                 }
24920             }
24921         };
24922
24923         var i = 0;
24924         for(; i < startingPos; i++) {
24925             textEls[i].innerHTML = (++prevStart);
24926             d.setDate(d.getDate()+1);
24927             cells[i].className = "x-date-prevday";
24928             setCellClass(this, cells[i]);
24929         }
24930         for(; i < days; i++){
24931             intDay = i - startingPos + 1;
24932             textEls[i].innerHTML = (intDay);
24933             d.setDate(d.getDate()+1);
24934             cells[i].className = "x-date-active";
24935             setCellClass(this, cells[i]);
24936         }
24937         var extraDays = 0;
24938         for(; i < 42; i++) {
24939              textEls[i].innerHTML = (++extraDays);
24940              d.setDate(d.getDate()+1);
24941              cells[i].className = "x-date-nextday";
24942              setCellClass(this, cells[i]);
24943         }
24944
24945         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24946
24947         if(!this.internalRender){
24948             var main = this.el.dom.firstChild;
24949             var w = main.offsetWidth;
24950             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24951             Roo.fly(main).setWidth(w);
24952             this.internalRender = true;
24953             // opera does not respect the auto grow header center column
24954             // then, after it gets a width opera refuses to recalculate
24955             // without a second pass
24956             if(Roo.isOpera && !this.secondPass){
24957                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24958                 this.secondPass = true;
24959                 this.update.defer(10, this, [date]);
24960             }
24961         }
24962     }
24963 });/*
24964  * Based on:
24965  * Ext JS Library 1.1.1
24966  * Copyright(c) 2006-2007, Ext JS, LLC.
24967  *
24968  * Originally Released Under LGPL - original licence link has changed is not relivant.
24969  *
24970  * Fork - LGPL
24971  * <script type="text/javascript">
24972  */
24973 /**
24974  * @class Roo.TabPanel
24975  * @extends Roo.util.Observable
24976  * A lightweight tab container.
24977  * <br><br>
24978  * Usage:
24979  * <pre><code>
24980 // basic tabs 1, built from existing content
24981 var tabs = new Roo.TabPanel("tabs1");
24982 tabs.addTab("script", "View Script");
24983 tabs.addTab("markup", "View Markup");
24984 tabs.activate("script");
24985
24986 // more advanced tabs, built from javascript
24987 var jtabs = new Roo.TabPanel("jtabs");
24988 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24989
24990 // set up the UpdateManager
24991 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24992 var updater = tab2.getUpdateManager();
24993 updater.setDefaultUrl("ajax1.htm");
24994 tab2.on('activate', updater.refresh, updater, true);
24995
24996 // Use setUrl for Ajax loading
24997 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24998 tab3.setUrl("ajax2.htm", null, true);
24999
25000 // Disabled tab
25001 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25002 tab4.disable();
25003
25004 jtabs.activate("jtabs-1");
25005  * </code></pre>
25006  * @constructor
25007  * Create a new TabPanel.
25008  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25009  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25010  */
25011 Roo.TabPanel = function(container, config){
25012     /**
25013     * The container element for this TabPanel.
25014     * @type Roo.Element
25015     */
25016     this.el = Roo.get(container, true);
25017     if(config){
25018         if(typeof config == "boolean"){
25019             this.tabPosition = config ? "bottom" : "top";
25020         }else{
25021             Roo.apply(this, config);
25022         }
25023     }
25024     if(this.tabPosition == "bottom"){
25025         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25026         this.el.addClass("x-tabs-bottom");
25027     }
25028     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25029     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25030     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25031     if(Roo.isIE){
25032         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25033     }
25034     if(this.tabPosition != "bottom"){
25035     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25036      * @type Roo.Element
25037      */
25038       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25039       this.el.addClass("x-tabs-top");
25040     }
25041     this.items = [];
25042
25043     this.bodyEl.setStyle("position", "relative");
25044
25045     this.active = null;
25046     this.activateDelegate = this.activate.createDelegate(this);
25047
25048     this.addEvents({
25049         /**
25050          * @event tabchange
25051          * Fires when the active tab changes
25052          * @param {Roo.TabPanel} this
25053          * @param {Roo.TabPanelItem} activePanel The new active tab
25054          */
25055         "tabchange": true,
25056         /**
25057          * @event beforetabchange
25058          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25059          * @param {Roo.TabPanel} this
25060          * @param {Object} e Set cancel to true on this object to cancel the tab change
25061          * @param {Roo.TabPanelItem} tab The tab being changed to
25062          */
25063         "beforetabchange" : true
25064     });
25065
25066     Roo.EventManager.onWindowResize(this.onResize, this);
25067     this.cpad = this.el.getPadding("lr");
25068     this.hiddenCount = 0;
25069
25070     Roo.TabPanel.superclass.constructor.call(this);
25071 };
25072
25073 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25074         /*
25075          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25076          */
25077     tabPosition : "top",
25078         /*
25079          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25080          */
25081     currentTabWidth : 0,
25082         /*
25083          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25084          */
25085     minTabWidth : 40,
25086         /*
25087          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25088          */
25089     maxTabWidth : 250,
25090         /*
25091          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25092          */
25093     preferredTabWidth : 175,
25094         /*
25095          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25096          */
25097     resizeTabs : false,
25098         /*
25099          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25100          */
25101     monitorResize : true,
25102
25103     /**
25104      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25105      * @param {String} id The id of the div to use <b>or create</b>
25106      * @param {String} text The text for the tab
25107      * @param {String} content (optional) Content to put in the TabPanelItem body
25108      * @param {Boolean} closable (optional) True to create a close icon on the tab
25109      * @return {Roo.TabPanelItem} The created TabPanelItem
25110      */
25111     addTab : function(id, text, content, closable){
25112         var item = new Roo.TabPanelItem(this, id, text, closable);
25113         this.addTabItem(item);
25114         if(content){
25115             item.setContent(content);
25116         }
25117         return item;
25118     },
25119
25120     /**
25121      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25122      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25123      * @return {Roo.TabPanelItem}
25124      */
25125     getTab : function(id){
25126         return this.items[id];
25127     },
25128
25129     /**
25130      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25131      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25132      */
25133     hideTab : function(id){
25134         var t = this.items[id];
25135         if(!t.isHidden()){
25136            t.setHidden(true);
25137            this.hiddenCount++;
25138            this.autoSizeTabs();
25139         }
25140     },
25141
25142     /**
25143      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25144      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25145      */
25146     unhideTab : function(id){
25147         var t = this.items[id];
25148         if(t.isHidden()){
25149            t.setHidden(false);
25150            this.hiddenCount--;
25151            this.autoSizeTabs();
25152         }
25153     },
25154
25155     /**
25156      * Adds an existing {@link Roo.TabPanelItem}.
25157      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25158      */
25159     addTabItem : function(item){
25160         this.items[item.id] = item;
25161         this.items.push(item);
25162         if(this.resizeTabs){
25163            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25164            this.autoSizeTabs();
25165         }else{
25166             item.autoSize();
25167         }
25168     },
25169
25170     /**
25171      * Removes a {@link Roo.TabPanelItem}.
25172      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25173      */
25174     removeTab : function(id){
25175         var items = this.items;
25176         var tab = items[id];
25177         if(!tab) { return; }
25178         var index = items.indexOf(tab);
25179         if(this.active == tab && items.length > 1){
25180             var newTab = this.getNextAvailable(index);
25181             if(newTab) {
25182                 newTab.activate();
25183             }
25184         }
25185         this.stripEl.dom.removeChild(tab.pnode.dom);
25186         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25187             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25188         }
25189         items.splice(index, 1);
25190         delete this.items[tab.id];
25191         tab.fireEvent("close", tab);
25192         tab.purgeListeners();
25193         this.autoSizeTabs();
25194     },
25195
25196     getNextAvailable : function(start){
25197         var items = this.items;
25198         var index = start;
25199         // look for a next tab that will slide over to
25200         // replace the one being removed
25201         while(index < items.length){
25202             var item = items[++index];
25203             if(item && !item.isHidden()){
25204                 return item;
25205             }
25206         }
25207         // if one isn't found select the previous tab (on the left)
25208         index = start;
25209         while(index >= 0){
25210             var item = items[--index];
25211             if(item && !item.isHidden()){
25212                 return item;
25213             }
25214         }
25215         return null;
25216     },
25217
25218     /**
25219      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25220      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25221      */
25222     disableTab : function(id){
25223         var tab = this.items[id];
25224         if(tab && this.active != tab){
25225             tab.disable();
25226         }
25227     },
25228
25229     /**
25230      * Enables a {@link Roo.TabPanelItem} that is disabled.
25231      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25232      */
25233     enableTab : function(id){
25234         var tab = this.items[id];
25235         tab.enable();
25236     },
25237
25238     /**
25239      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25240      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25241      * @return {Roo.TabPanelItem} The TabPanelItem.
25242      */
25243     activate : function(id){
25244         var tab = this.items[id];
25245         if(!tab){
25246             return null;
25247         }
25248         if(tab == this.active || tab.disabled){
25249             return tab;
25250         }
25251         var e = {};
25252         this.fireEvent("beforetabchange", this, e, tab);
25253         if(e.cancel !== true && !tab.disabled){
25254             if(this.active){
25255                 this.active.hide();
25256             }
25257             this.active = this.items[id];
25258             this.active.show();
25259             this.fireEvent("tabchange", this, this.active);
25260         }
25261         return tab;
25262     },
25263
25264     /**
25265      * Gets the active {@link Roo.TabPanelItem}.
25266      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25267      */
25268     getActiveTab : function(){
25269         return this.active;
25270     },
25271
25272     /**
25273      * Updates the tab body element to fit the height of the container element
25274      * for overflow scrolling
25275      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25276      */
25277     syncHeight : function(targetHeight){
25278         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25279         var bm = this.bodyEl.getMargins();
25280         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25281         this.bodyEl.setHeight(newHeight);
25282         return newHeight;
25283     },
25284
25285     onResize : function(){
25286         if(this.monitorResize){
25287             this.autoSizeTabs();
25288         }
25289     },
25290
25291     /**
25292      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25293      */
25294     beginUpdate : function(){
25295         this.updating = true;
25296     },
25297
25298     /**
25299      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25300      */
25301     endUpdate : function(){
25302         this.updating = false;
25303         this.autoSizeTabs();
25304     },
25305
25306     /**
25307      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25308      */
25309     autoSizeTabs : function(){
25310         var count = this.items.length;
25311         var vcount = count - this.hiddenCount;
25312         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25313         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25314         var availWidth = Math.floor(w / vcount);
25315         var b = this.stripBody;
25316         if(b.getWidth() > w){
25317             var tabs = this.items;
25318             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25319             if(availWidth < this.minTabWidth){
25320                 /*if(!this.sleft){    // incomplete scrolling code
25321                     this.createScrollButtons();
25322                 }
25323                 this.showScroll();
25324                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25325             }
25326         }else{
25327             if(this.currentTabWidth < this.preferredTabWidth){
25328                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25329             }
25330         }
25331     },
25332
25333     /**
25334      * Returns the number of tabs in this TabPanel.
25335      * @return {Number}
25336      */
25337      getCount : function(){
25338          return this.items.length;
25339      },
25340
25341     /**
25342      * Resizes all the tabs to the passed width
25343      * @param {Number} The new width
25344      */
25345     setTabWidth : function(width){
25346         this.currentTabWidth = width;
25347         for(var i = 0, len = this.items.length; i < len; i++) {
25348                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25349         }
25350     },
25351
25352     /**
25353      * Destroys this TabPanel
25354      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25355      */
25356     destroy : function(removeEl){
25357         Roo.EventManager.removeResizeListener(this.onResize, this);
25358         for(var i = 0, len = this.items.length; i < len; i++){
25359             this.items[i].purgeListeners();
25360         }
25361         if(removeEl === true){
25362             this.el.update("");
25363             this.el.remove();
25364         }
25365     }
25366 });
25367
25368 /**
25369  * @class Roo.TabPanelItem
25370  * @extends Roo.util.Observable
25371  * Represents an individual item (tab plus body) in a TabPanel.
25372  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25373  * @param {String} id The id of this TabPanelItem
25374  * @param {String} text The text for the tab of this TabPanelItem
25375  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25376  */
25377 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25378     /**
25379      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25380      * @type Roo.TabPanel
25381      */
25382     this.tabPanel = tabPanel;
25383     /**
25384      * The id for this TabPanelItem
25385      * @type String
25386      */
25387     this.id = id;
25388     /** @private */
25389     this.disabled = false;
25390     /** @private */
25391     this.text = text;
25392     /** @private */
25393     this.loaded = false;
25394     this.closable = closable;
25395
25396     /**
25397      * The body element for this TabPanelItem.
25398      * @type Roo.Element
25399      */
25400     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25401     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25402     this.bodyEl.setStyle("display", "block");
25403     this.bodyEl.setStyle("zoom", "1");
25404     this.hideAction();
25405
25406     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25407     /** @private */
25408     this.el = Roo.get(els.el, true);
25409     this.inner = Roo.get(els.inner, true);
25410     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25411     this.pnode = Roo.get(els.el.parentNode, true);
25412     this.el.on("mousedown", this.onTabMouseDown, this);
25413     this.el.on("click", this.onTabClick, this);
25414     /** @private */
25415     if(closable){
25416         var c = Roo.get(els.close, true);
25417         c.dom.title = this.closeText;
25418         c.addClassOnOver("close-over");
25419         c.on("click", this.closeClick, this);
25420      }
25421
25422     this.addEvents({
25423          /**
25424          * @event activate
25425          * Fires when this tab becomes the active tab.
25426          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25427          * @param {Roo.TabPanelItem} this
25428          */
25429         "activate": true,
25430         /**
25431          * @event beforeclose
25432          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25433          * @param {Roo.TabPanelItem} this
25434          * @param {Object} e Set cancel to true on this object to cancel the close.
25435          */
25436         "beforeclose": true,
25437         /**
25438          * @event close
25439          * Fires when this tab is closed.
25440          * @param {Roo.TabPanelItem} this
25441          */
25442          "close": true,
25443         /**
25444          * @event deactivate
25445          * Fires when this tab is no longer the active tab.
25446          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25447          * @param {Roo.TabPanelItem} this
25448          */
25449          "deactivate" : true
25450     });
25451     this.hidden = false;
25452
25453     Roo.TabPanelItem.superclass.constructor.call(this);
25454 };
25455
25456 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25457     purgeListeners : function(){
25458        Roo.util.Observable.prototype.purgeListeners.call(this);
25459        this.el.removeAllListeners();
25460     },
25461     /**
25462      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25463      */
25464     show : function(){
25465         this.pnode.addClass("on");
25466         this.showAction();
25467         if(Roo.isOpera){
25468             this.tabPanel.stripWrap.repaint();
25469         }
25470         this.fireEvent("activate", this.tabPanel, this);
25471     },
25472
25473     /**
25474      * Returns true if this tab is the active tab.
25475      * @return {Boolean}
25476      */
25477     isActive : function(){
25478         return this.tabPanel.getActiveTab() == this;
25479     },
25480
25481     /**
25482      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25483      */
25484     hide : function(){
25485         this.pnode.removeClass("on");
25486         this.hideAction();
25487         this.fireEvent("deactivate", this.tabPanel, this);
25488     },
25489
25490     hideAction : function(){
25491         this.bodyEl.hide();
25492         this.bodyEl.setStyle("position", "absolute");
25493         this.bodyEl.setLeft("-20000px");
25494         this.bodyEl.setTop("-20000px");
25495     },
25496
25497     showAction : function(){
25498         this.bodyEl.setStyle("position", "relative");
25499         this.bodyEl.setTop("");
25500         this.bodyEl.setLeft("");
25501         this.bodyEl.show();
25502     },
25503
25504     /**
25505      * Set the tooltip for the tab.
25506      * @param {String} tooltip The tab's tooltip
25507      */
25508     setTooltip : function(text){
25509         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25510             this.textEl.dom.qtip = text;
25511             this.textEl.dom.removeAttribute('title');
25512         }else{
25513             this.textEl.dom.title = text;
25514         }
25515     },
25516
25517     onTabClick : function(e){
25518         e.preventDefault();
25519         this.tabPanel.activate(this.id);
25520     },
25521
25522     onTabMouseDown : function(e){
25523         e.preventDefault();
25524         this.tabPanel.activate(this.id);
25525     },
25526
25527     getWidth : function(){
25528         return this.inner.getWidth();
25529     },
25530
25531     setWidth : function(width){
25532         var iwidth = width - this.pnode.getPadding("lr");
25533         this.inner.setWidth(iwidth);
25534         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25535         this.pnode.setWidth(width);
25536     },
25537
25538     /**
25539      * Show or hide the tab
25540      * @param {Boolean} hidden True to hide or false to show.
25541      */
25542     setHidden : function(hidden){
25543         this.hidden = hidden;
25544         this.pnode.setStyle("display", hidden ? "none" : "");
25545     },
25546
25547     /**
25548      * Returns true if this tab is "hidden"
25549      * @return {Boolean}
25550      */
25551     isHidden : function(){
25552         return this.hidden;
25553     },
25554
25555     /**
25556      * Returns the text for this tab
25557      * @return {String}
25558      */
25559     getText : function(){
25560         return this.text;
25561     },
25562
25563     autoSize : function(){
25564         //this.el.beginMeasure();
25565         this.textEl.setWidth(1);
25566         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25567         //this.el.endMeasure();
25568     },
25569
25570     /**
25571      * Sets the text for the tab (Note: this also sets the tooltip text)
25572      * @param {String} text The tab's text and tooltip
25573      */
25574     setText : function(text){
25575         this.text = text;
25576         this.textEl.update(text);
25577         this.setTooltip(text);
25578         if(!this.tabPanel.resizeTabs){
25579             this.autoSize();
25580         }
25581     },
25582     /**
25583      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25584      */
25585     activate : function(){
25586         this.tabPanel.activate(this.id);
25587     },
25588
25589     /**
25590      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25591      */
25592     disable : function(){
25593         if(this.tabPanel.active != this){
25594             this.disabled = true;
25595             this.pnode.addClass("disabled");
25596         }
25597     },
25598
25599     /**
25600      * Enables this TabPanelItem if it was previously disabled.
25601      */
25602     enable : function(){
25603         this.disabled = false;
25604         this.pnode.removeClass("disabled");
25605     },
25606
25607     /**
25608      * Sets the content for this TabPanelItem.
25609      * @param {String} content The content
25610      * @param {Boolean} loadScripts true to look for and load scripts
25611      */
25612     setContent : function(content, loadScripts){
25613         this.bodyEl.update(content, loadScripts);
25614     },
25615
25616     /**
25617      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25618      * @return {Roo.UpdateManager} The UpdateManager
25619      */
25620     getUpdateManager : function(){
25621         return this.bodyEl.getUpdateManager();
25622     },
25623
25624     /**
25625      * Set a URL to be used to load the content for this TabPanelItem.
25626      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25627      * @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)
25628      * @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)
25629      * @return {Roo.UpdateManager} The UpdateManager
25630      */
25631     setUrl : function(url, params, loadOnce){
25632         if(this.refreshDelegate){
25633             this.un('activate', this.refreshDelegate);
25634         }
25635         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25636         this.on("activate", this.refreshDelegate);
25637         return this.bodyEl.getUpdateManager();
25638     },
25639
25640     /** @private */
25641     _handleRefresh : function(url, params, loadOnce){
25642         if(!loadOnce || !this.loaded){
25643             var updater = this.bodyEl.getUpdateManager();
25644             updater.update(url, params, this._setLoaded.createDelegate(this));
25645         }
25646     },
25647
25648     /**
25649      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25650      *   Will fail silently if the setUrl method has not been called.
25651      *   This does not activate the panel, just updates its content.
25652      */
25653     refresh : function(){
25654         if(this.refreshDelegate){
25655            this.loaded = false;
25656            this.refreshDelegate();
25657         }
25658     },
25659
25660     /** @private */
25661     _setLoaded : function(){
25662         this.loaded = true;
25663     },
25664
25665     /** @private */
25666     closeClick : function(e){
25667         var o = {};
25668         e.stopEvent();
25669         this.fireEvent("beforeclose", this, o);
25670         if(o.cancel !== true){
25671             this.tabPanel.removeTab(this.id);
25672         }
25673     },
25674     /**
25675      * The text displayed in the tooltip for the close icon.
25676      * @type String
25677      */
25678     closeText : "Close this tab"
25679 });
25680
25681 /** @private */
25682 Roo.TabPanel.prototype.createStrip = function(container){
25683     var strip = document.createElement("div");
25684     strip.className = "x-tabs-wrap";
25685     container.appendChild(strip);
25686     return strip;
25687 };
25688 /** @private */
25689 Roo.TabPanel.prototype.createStripList = function(strip){
25690     // div wrapper for retard IE
25691     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>';
25692     return strip.firstChild.firstChild.firstChild.firstChild;
25693 };
25694 /** @private */
25695 Roo.TabPanel.prototype.createBody = function(container){
25696     var body = document.createElement("div");
25697     Roo.id(body, "tab-body");
25698     Roo.fly(body).addClass("x-tabs-body");
25699     container.appendChild(body);
25700     return body;
25701 };
25702 /** @private */
25703 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25704     var body = Roo.getDom(id);
25705     if(!body){
25706         body = document.createElement("div");
25707         body.id = id;
25708     }
25709     Roo.fly(body).addClass("x-tabs-item-body");
25710     bodyEl.insertBefore(body, bodyEl.firstChild);
25711     return body;
25712 };
25713 /** @private */
25714 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25715     var td = document.createElement("td");
25716     stripEl.appendChild(td);
25717     if(closable){
25718         td.className = "x-tabs-closable";
25719         if(!this.closeTpl){
25720             this.closeTpl = new Roo.Template(
25721                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25722                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25723                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25724             );
25725         }
25726         var el = this.closeTpl.overwrite(td, {"text": text});
25727         var close = el.getElementsByTagName("div")[0];
25728         var inner = el.getElementsByTagName("em")[0];
25729         return {"el": el, "close": close, "inner": inner};
25730     } else {
25731         if(!this.tabTpl){
25732             this.tabTpl = new Roo.Template(
25733                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25734                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25735             );
25736         }
25737         var el = this.tabTpl.overwrite(td, {"text": text});
25738         var inner = el.getElementsByTagName("em")[0];
25739         return {"el": el, "inner": inner};
25740     }
25741 };/*
25742  * Based on:
25743  * Ext JS Library 1.1.1
25744  * Copyright(c) 2006-2007, Ext JS, LLC.
25745  *
25746  * Originally Released Under LGPL - original licence link has changed is not relivant.
25747  *
25748  * Fork - LGPL
25749  * <script type="text/javascript">
25750  */
25751
25752 /**
25753  * @class Roo.Button
25754  * @extends Roo.util.Observable
25755  * Simple Button class
25756  * @cfg {String} text The button text
25757  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25758  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25759  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25760  * @cfg {Object} scope The scope of the handler
25761  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25762  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25763  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25764  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25765  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25766  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25767    applies if enableToggle = true)
25768  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25769  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25770   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25771  * @constructor
25772  * Create a new button
25773  * @param {Object} config The config object
25774  */
25775 Roo.Button = function(renderTo, config)
25776 {
25777     if (!config) {
25778         config = renderTo;
25779         renderTo = config.renderTo || false;
25780     }
25781     
25782     Roo.apply(this, config);
25783     this.addEvents({
25784         /**
25785              * @event click
25786              * Fires when this button is clicked
25787              * @param {Button} this
25788              * @param {EventObject} e The click event
25789              */
25790             "click" : true,
25791         /**
25792              * @event toggle
25793              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25794              * @param {Button} this
25795              * @param {Boolean} pressed
25796              */
25797             "toggle" : true,
25798         /**
25799              * @event mouseover
25800              * Fires when the mouse hovers over the button
25801              * @param {Button} this
25802              * @param {Event} e The event object
25803              */
25804         'mouseover' : true,
25805         /**
25806              * @event mouseout
25807              * Fires when the mouse exits the button
25808              * @param {Button} this
25809              * @param {Event} e The event object
25810              */
25811         'mouseout': true,
25812          /**
25813              * @event render
25814              * Fires when the button is rendered
25815              * @param {Button} this
25816              */
25817         'render': true
25818     });
25819     if(this.menu){
25820         this.menu = Roo.menu.MenuMgr.get(this.menu);
25821     }
25822     // register listeners first!!  - so render can be captured..
25823     Roo.util.Observable.call(this);
25824     if(renderTo){
25825         this.render(renderTo);
25826     }
25827     
25828   
25829 };
25830
25831 Roo.extend(Roo.Button, Roo.util.Observable, {
25832     /**
25833      * 
25834      */
25835     
25836     /**
25837      * Read-only. True if this button is hidden
25838      * @type Boolean
25839      */
25840     hidden : false,
25841     /**
25842      * Read-only. True if this button is disabled
25843      * @type Boolean
25844      */
25845     disabled : false,
25846     /**
25847      * Read-only. True if this button is pressed (only if enableToggle = true)
25848      * @type Boolean
25849      */
25850     pressed : false,
25851
25852     /**
25853      * @cfg {Number} tabIndex 
25854      * The DOM tabIndex for this button (defaults to undefined)
25855      */
25856     tabIndex : undefined,
25857
25858     /**
25859      * @cfg {Boolean} enableToggle
25860      * True to enable pressed/not pressed toggling (defaults to false)
25861      */
25862     enableToggle: false,
25863     /**
25864      * @cfg {Mixed} menu
25865      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25866      */
25867     menu : undefined,
25868     /**
25869      * @cfg {String} menuAlign
25870      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25871      */
25872     menuAlign : "tl-bl?",
25873
25874     /**
25875      * @cfg {String} iconCls
25876      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25877      */
25878     iconCls : undefined,
25879     /**
25880      * @cfg {String} type
25881      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25882      */
25883     type : 'button',
25884
25885     // private
25886     menuClassTarget: 'tr',
25887
25888     /**
25889      * @cfg {String} clickEvent
25890      * The type of event to map to the button's event handler (defaults to 'click')
25891      */
25892     clickEvent : 'click',
25893
25894     /**
25895      * @cfg {Boolean} handleMouseEvents
25896      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25897      */
25898     handleMouseEvents : true,
25899
25900     /**
25901      * @cfg {String} tooltipType
25902      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25903      */
25904     tooltipType : 'qtip',
25905
25906     /**
25907      * @cfg {String} cls
25908      * A CSS class to apply to the button's main element.
25909      */
25910     
25911     /**
25912      * @cfg {Roo.Template} template (Optional)
25913      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25914      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25915      * require code modifications if required elements (e.g. a button) aren't present.
25916      */
25917
25918     // private
25919     render : function(renderTo){
25920         var btn;
25921         if(this.hideParent){
25922             this.parentEl = Roo.get(renderTo);
25923         }
25924         if(!this.dhconfig){
25925             if(!this.template){
25926                 if(!Roo.Button.buttonTemplate){
25927                     // hideous table template
25928                     Roo.Button.buttonTemplate = new Roo.Template(
25929                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25930                         '<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>',
25931                         "</tr></tbody></table>");
25932                 }
25933                 this.template = Roo.Button.buttonTemplate;
25934             }
25935             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25936             var btnEl = btn.child("button:first");
25937             btnEl.on('focus', this.onFocus, this);
25938             btnEl.on('blur', this.onBlur, this);
25939             if(this.cls){
25940                 btn.addClass(this.cls);
25941             }
25942             if(this.icon){
25943                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25944             }
25945             if(this.iconCls){
25946                 btnEl.addClass(this.iconCls);
25947                 if(!this.cls){
25948                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25949                 }
25950             }
25951             if(this.tabIndex !== undefined){
25952                 btnEl.dom.tabIndex = this.tabIndex;
25953             }
25954             if(this.tooltip){
25955                 if(typeof this.tooltip == 'object'){
25956                     Roo.QuickTips.tips(Roo.apply({
25957                           target: btnEl.id
25958                     }, this.tooltip));
25959                 } else {
25960                     btnEl.dom[this.tooltipType] = this.tooltip;
25961                 }
25962             }
25963         }else{
25964             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25965         }
25966         this.el = btn;
25967         if(this.id){
25968             this.el.dom.id = this.el.id = this.id;
25969         }
25970         if(this.menu){
25971             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25972             this.menu.on("show", this.onMenuShow, this);
25973             this.menu.on("hide", this.onMenuHide, this);
25974         }
25975         btn.addClass("x-btn");
25976         if(Roo.isIE && !Roo.isIE7){
25977             this.autoWidth.defer(1, this);
25978         }else{
25979             this.autoWidth();
25980         }
25981         if(this.handleMouseEvents){
25982             btn.on("mouseover", this.onMouseOver, this);
25983             btn.on("mouseout", this.onMouseOut, this);
25984             btn.on("mousedown", this.onMouseDown, this);
25985         }
25986         btn.on(this.clickEvent, this.onClick, this);
25987         //btn.on("mouseup", this.onMouseUp, this);
25988         if(this.hidden){
25989             this.hide();
25990         }
25991         if(this.disabled){
25992             this.disable();
25993         }
25994         Roo.ButtonToggleMgr.register(this);
25995         if(this.pressed){
25996             this.el.addClass("x-btn-pressed");
25997         }
25998         if(this.repeat){
25999             var repeater = new Roo.util.ClickRepeater(btn,
26000                 typeof this.repeat == "object" ? this.repeat : {}
26001             );
26002             repeater.on("click", this.onClick,  this);
26003         }
26004         
26005         this.fireEvent('render', this);
26006         
26007     },
26008     /**
26009      * Returns the button's underlying element
26010      * @return {Roo.Element} The element
26011      */
26012     getEl : function(){
26013         return this.el;  
26014     },
26015     
26016     /**
26017      * Destroys this Button and removes any listeners.
26018      */
26019     destroy : function(){
26020         Roo.ButtonToggleMgr.unregister(this);
26021         this.el.removeAllListeners();
26022         this.purgeListeners();
26023         this.el.remove();
26024     },
26025
26026     // private
26027     autoWidth : function(){
26028         if(this.el){
26029             this.el.setWidth("auto");
26030             if(Roo.isIE7 && Roo.isStrict){
26031                 var ib = this.el.child('button');
26032                 if(ib && ib.getWidth() > 20){
26033                     ib.clip();
26034                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26035                 }
26036             }
26037             if(this.minWidth){
26038                 if(this.hidden){
26039                     this.el.beginMeasure();
26040                 }
26041                 if(this.el.getWidth() < this.minWidth){
26042                     this.el.setWidth(this.minWidth);
26043                 }
26044                 if(this.hidden){
26045                     this.el.endMeasure();
26046                 }
26047             }
26048         }
26049     },
26050
26051     /**
26052      * Assigns this button's click handler
26053      * @param {Function} handler The function to call when the button is clicked
26054      * @param {Object} scope (optional) Scope for the function passed in
26055      */
26056     setHandler : function(handler, scope){
26057         this.handler = handler;
26058         this.scope = scope;  
26059     },
26060     
26061     /**
26062      * Sets this button's text
26063      * @param {String} text The button text
26064      */
26065     setText : function(text){
26066         this.text = text;
26067         if(this.el){
26068             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26069         }
26070         this.autoWidth();
26071     },
26072     
26073     /**
26074      * Gets the text for this button
26075      * @return {String} The button text
26076      */
26077     getText : function(){
26078         return this.text;  
26079     },
26080     
26081     /**
26082      * Show this button
26083      */
26084     show: function(){
26085         this.hidden = false;
26086         if(this.el){
26087             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26088         }
26089     },
26090     
26091     /**
26092      * Hide this button
26093      */
26094     hide: function(){
26095         this.hidden = true;
26096         if(this.el){
26097             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26098         }
26099     },
26100     
26101     /**
26102      * Convenience function for boolean show/hide
26103      * @param {Boolean} visible True to show, false to hide
26104      */
26105     setVisible: function(visible){
26106         if(visible) {
26107             this.show();
26108         }else{
26109             this.hide();
26110         }
26111     },
26112     
26113     /**
26114      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26115      * @param {Boolean} state (optional) Force a particular state
26116      */
26117     toggle : function(state){
26118         state = state === undefined ? !this.pressed : state;
26119         if(state != this.pressed){
26120             if(state){
26121                 this.el.addClass("x-btn-pressed");
26122                 this.pressed = true;
26123                 this.fireEvent("toggle", this, true);
26124             }else{
26125                 this.el.removeClass("x-btn-pressed");
26126                 this.pressed = false;
26127                 this.fireEvent("toggle", this, false);
26128             }
26129             if(this.toggleHandler){
26130                 this.toggleHandler.call(this.scope || this, this, state);
26131             }
26132         }
26133     },
26134     
26135     /**
26136      * Focus the button
26137      */
26138     focus : function(){
26139         this.el.child('button:first').focus();
26140     },
26141     
26142     /**
26143      * Disable this button
26144      */
26145     disable : function(){
26146         if(this.el){
26147             this.el.addClass("x-btn-disabled");
26148         }
26149         this.disabled = true;
26150     },
26151     
26152     /**
26153      * Enable this button
26154      */
26155     enable : function(){
26156         if(this.el){
26157             this.el.removeClass("x-btn-disabled");
26158         }
26159         this.disabled = false;
26160     },
26161
26162     /**
26163      * Convenience function for boolean enable/disable
26164      * @param {Boolean} enabled True to enable, false to disable
26165      */
26166     setDisabled : function(v){
26167         this[v !== true ? "enable" : "disable"]();
26168     },
26169
26170     // private
26171     onClick : function(e){
26172         if(e){
26173             e.preventDefault();
26174         }
26175         if(e.button != 0){
26176             return;
26177         }
26178         if(!this.disabled){
26179             if(this.enableToggle){
26180                 this.toggle();
26181             }
26182             if(this.menu && !this.menu.isVisible()){
26183                 this.menu.show(this.el, this.menuAlign);
26184             }
26185             this.fireEvent("click", this, e);
26186             if(this.handler){
26187                 this.el.removeClass("x-btn-over");
26188                 this.handler.call(this.scope || this, this, e);
26189             }
26190         }
26191     },
26192     // private
26193     onMouseOver : function(e){
26194         if(!this.disabled){
26195             this.el.addClass("x-btn-over");
26196             this.fireEvent('mouseover', this, e);
26197         }
26198     },
26199     // private
26200     onMouseOut : function(e){
26201         if(!e.within(this.el,  true)){
26202             this.el.removeClass("x-btn-over");
26203             this.fireEvent('mouseout', this, e);
26204         }
26205     },
26206     // private
26207     onFocus : function(e){
26208         if(!this.disabled){
26209             this.el.addClass("x-btn-focus");
26210         }
26211     },
26212     // private
26213     onBlur : function(e){
26214         this.el.removeClass("x-btn-focus");
26215     },
26216     // private
26217     onMouseDown : function(e){
26218         if(!this.disabled && e.button == 0){
26219             this.el.addClass("x-btn-click");
26220             Roo.get(document).on('mouseup', this.onMouseUp, this);
26221         }
26222     },
26223     // private
26224     onMouseUp : function(e){
26225         if(e.button == 0){
26226             this.el.removeClass("x-btn-click");
26227             Roo.get(document).un('mouseup', this.onMouseUp, this);
26228         }
26229     },
26230     // private
26231     onMenuShow : function(e){
26232         this.el.addClass("x-btn-menu-active");
26233     },
26234     // private
26235     onMenuHide : function(e){
26236         this.el.removeClass("x-btn-menu-active");
26237     }   
26238 });
26239
26240 // Private utility class used by Button
26241 Roo.ButtonToggleMgr = function(){
26242    var groups = {};
26243    
26244    function toggleGroup(btn, state){
26245        if(state){
26246            var g = groups[btn.toggleGroup];
26247            for(var i = 0, l = g.length; i < l; i++){
26248                if(g[i] != btn){
26249                    g[i].toggle(false);
26250                }
26251            }
26252        }
26253    }
26254    
26255    return {
26256        register : function(btn){
26257            if(!btn.toggleGroup){
26258                return;
26259            }
26260            var g = groups[btn.toggleGroup];
26261            if(!g){
26262                g = groups[btn.toggleGroup] = [];
26263            }
26264            g.push(btn);
26265            btn.on("toggle", toggleGroup);
26266        },
26267        
26268        unregister : function(btn){
26269            if(!btn.toggleGroup){
26270                return;
26271            }
26272            var g = groups[btn.toggleGroup];
26273            if(g){
26274                g.remove(btn);
26275                btn.un("toggle", toggleGroup);
26276            }
26277        }
26278    };
26279 }();/*
26280  * Based on:
26281  * Ext JS Library 1.1.1
26282  * Copyright(c) 2006-2007, Ext JS, LLC.
26283  *
26284  * Originally Released Under LGPL - original licence link has changed is not relivant.
26285  *
26286  * Fork - LGPL
26287  * <script type="text/javascript">
26288  */
26289  
26290 /**
26291  * @class Roo.SplitButton
26292  * @extends Roo.Button
26293  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26294  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26295  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26296  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26297  * @cfg {String} arrowTooltip The title attribute of the arrow
26298  * @constructor
26299  * Create a new menu button
26300  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26301  * @param {Object} config The config object
26302  */
26303 Roo.SplitButton = function(renderTo, config){
26304     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26305     /**
26306      * @event arrowclick
26307      * Fires when this button's arrow is clicked
26308      * @param {SplitButton} this
26309      * @param {EventObject} e The click event
26310      */
26311     this.addEvents({"arrowclick":true});
26312 };
26313
26314 Roo.extend(Roo.SplitButton, Roo.Button, {
26315     render : function(renderTo){
26316         // this is one sweet looking template!
26317         var tpl = new Roo.Template(
26318             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26319             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26320             '<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>',
26321             "</tbody></table></td><td>",
26322             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26323             '<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>',
26324             "</tbody></table></td></tr></table>"
26325         );
26326         var btn = tpl.append(renderTo, [this.text, this.type], true);
26327         var btnEl = btn.child("button");
26328         if(this.cls){
26329             btn.addClass(this.cls);
26330         }
26331         if(this.icon){
26332             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26333         }
26334         if(this.iconCls){
26335             btnEl.addClass(this.iconCls);
26336             if(!this.cls){
26337                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26338             }
26339         }
26340         this.el = btn;
26341         if(this.handleMouseEvents){
26342             btn.on("mouseover", this.onMouseOver, this);
26343             btn.on("mouseout", this.onMouseOut, this);
26344             btn.on("mousedown", this.onMouseDown, this);
26345             btn.on("mouseup", this.onMouseUp, this);
26346         }
26347         btn.on(this.clickEvent, this.onClick, this);
26348         if(this.tooltip){
26349             if(typeof this.tooltip == 'object'){
26350                 Roo.QuickTips.tips(Roo.apply({
26351                       target: btnEl.id
26352                 }, this.tooltip));
26353             } else {
26354                 btnEl.dom[this.tooltipType] = this.tooltip;
26355             }
26356         }
26357         if(this.arrowTooltip){
26358             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26359         }
26360         if(this.hidden){
26361             this.hide();
26362         }
26363         if(this.disabled){
26364             this.disable();
26365         }
26366         if(this.pressed){
26367             this.el.addClass("x-btn-pressed");
26368         }
26369         if(Roo.isIE && !Roo.isIE7){
26370             this.autoWidth.defer(1, this);
26371         }else{
26372             this.autoWidth();
26373         }
26374         if(this.menu){
26375             this.menu.on("show", this.onMenuShow, this);
26376             this.menu.on("hide", this.onMenuHide, this);
26377         }
26378         this.fireEvent('render', this);
26379     },
26380
26381     // private
26382     autoWidth : function(){
26383         if(this.el){
26384             var tbl = this.el.child("table:first");
26385             var tbl2 = this.el.child("table:last");
26386             this.el.setWidth("auto");
26387             tbl.setWidth("auto");
26388             if(Roo.isIE7 && Roo.isStrict){
26389                 var ib = this.el.child('button:first');
26390                 if(ib && ib.getWidth() > 20){
26391                     ib.clip();
26392                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26393                 }
26394             }
26395             if(this.minWidth){
26396                 if(this.hidden){
26397                     this.el.beginMeasure();
26398                 }
26399                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26400                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26401                 }
26402                 if(this.hidden){
26403                     this.el.endMeasure();
26404                 }
26405             }
26406             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26407         } 
26408     },
26409     /**
26410      * Sets this button's click handler
26411      * @param {Function} handler The function to call when the button is clicked
26412      * @param {Object} scope (optional) Scope for the function passed above
26413      */
26414     setHandler : function(handler, scope){
26415         this.handler = handler;
26416         this.scope = scope;  
26417     },
26418     
26419     /**
26420      * Sets this button's arrow click handler
26421      * @param {Function} handler The function to call when the arrow is clicked
26422      * @param {Object} scope (optional) Scope for the function passed above
26423      */
26424     setArrowHandler : function(handler, scope){
26425         this.arrowHandler = handler;
26426         this.scope = scope;  
26427     },
26428     
26429     /**
26430      * Focus the button
26431      */
26432     focus : function(){
26433         if(this.el){
26434             this.el.child("button:first").focus();
26435         }
26436     },
26437
26438     // private
26439     onClick : function(e){
26440         e.preventDefault();
26441         if(!this.disabled){
26442             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26443                 if(this.menu && !this.menu.isVisible()){
26444                     this.menu.show(this.el, this.menuAlign);
26445                 }
26446                 this.fireEvent("arrowclick", this, e);
26447                 if(this.arrowHandler){
26448                     this.arrowHandler.call(this.scope || this, this, e);
26449                 }
26450             }else{
26451                 this.fireEvent("click", this, e);
26452                 if(this.handler){
26453                     this.handler.call(this.scope || this, this, e);
26454                 }
26455             }
26456         }
26457     },
26458     // private
26459     onMouseDown : function(e){
26460         if(!this.disabled){
26461             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26462         }
26463     },
26464     // private
26465     onMouseUp : function(e){
26466         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26467     }   
26468 });
26469
26470
26471 // backwards compat
26472 Roo.MenuButton = Roo.SplitButton;/*
26473  * Based on:
26474  * Ext JS Library 1.1.1
26475  * Copyright(c) 2006-2007, Ext JS, LLC.
26476  *
26477  * Originally Released Under LGPL - original licence link has changed is not relivant.
26478  *
26479  * Fork - LGPL
26480  * <script type="text/javascript">
26481  */
26482
26483 /**
26484  * @class Roo.Toolbar
26485  * Basic Toolbar class.
26486  * @constructor
26487  * Creates a new Toolbar
26488  * @param {Object} config The config object
26489  */ 
26490 Roo.Toolbar = function(container, buttons, config)
26491 {
26492     /// old consturctor format still supported..
26493     if(container instanceof Array){ // omit the container for later rendering
26494         buttons = container;
26495         config = buttons;
26496         container = null;
26497     }
26498     if (typeof(container) == 'object' && container.xtype) {
26499         config = container;
26500         container = config.container;
26501         buttons = config.buttons; // not really - use items!!
26502     }
26503     var xitems = [];
26504     if (config && config.items) {
26505         xitems = config.items;
26506         delete config.items;
26507     }
26508     Roo.apply(this, config);
26509     this.buttons = buttons;
26510     
26511     if(container){
26512         this.render(container);
26513     }
26514     Roo.each(xitems, function(b) {
26515         this.add(b);
26516     }, this);
26517     
26518 };
26519
26520 Roo.Toolbar.prototype = {
26521     /**
26522      * @cfg {Roo.data.Store} items
26523      * array of button configs or elements to add
26524      */
26525     
26526     /**
26527      * @cfg {String/HTMLElement/Element} container
26528      * The id or element that will contain the toolbar
26529      */
26530     // private
26531     render : function(ct){
26532         this.el = Roo.get(ct);
26533         if(this.cls){
26534             this.el.addClass(this.cls);
26535         }
26536         // using a table allows for vertical alignment
26537         // 100% width is needed by Safari...
26538         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26539         this.tr = this.el.child("tr", true);
26540         var autoId = 0;
26541         this.items = new Roo.util.MixedCollection(false, function(o){
26542             return o.id || ("item" + (++autoId));
26543         });
26544         if(this.buttons){
26545             this.add.apply(this, this.buttons);
26546             delete this.buttons;
26547         }
26548     },
26549
26550     /**
26551      * Adds element(s) to the toolbar -- this function takes a variable number of 
26552      * arguments of mixed type and adds them to the toolbar.
26553      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26554      * <ul>
26555      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26556      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26557      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26558      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26559      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26560      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26561      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26562      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26563      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26564      * </ul>
26565      * @param {Mixed} arg2
26566      * @param {Mixed} etc.
26567      */
26568     add : function(){
26569         var a = arguments, l = a.length;
26570         for(var i = 0; i < l; i++){
26571             this._add(a[i]);
26572         }
26573     },
26574     // private..
26575     _add : function(el) {
26576         
26577         if (el.xtype) {
26578             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26579         }
26580         
26581         if (el.applyTo){ // some kind of form field
26582             return this.addField(el);
26583         } 
26584         if (el.render){ // some kind of Toolbar.Item
26585             return this.addItem(el);
26586         }
26587         if (typeof el == "string"){ // string
26588             if(el == "separator" || el == "-"){
26589                 return this.addSeparator();
26590             }
26591             if (el == " "){
26592                 return this.addSpacer();
26593             }
26594             if(el == "->"){
26595                 return this.addFill();
26596             }
26597             return this.addText(el);
26598             
26599         }
26600         if(el.tagName){ // element
26601             return this.addElement(el);
26602         }
26603         if(typeof el == "object"){ // must be button config?
26604             return this.addButton(el);
26605         }
26606         // and now what?!?!
26607         return false;
26608         
26609     },
26610     
26611     /**
26612      * Add an Xtype element
26613      * @param {Object} xtype Xtype Object
26614      * @return {Object} created Object
26615      */
26616     addxtype : function(e){
26617         return this.add(e);  
26618     },
26619     
26620     /**
26621      * Returns the Element for this toolbar.
26622      * @return {Roo.Element}
26623      */
26624     getEl : function(){
26625         return this.el;  
26626     },
26627     
26628     /**
26629      * Adds a separator
26630      * @return {Roo.Toolbar.Item} The separator item
26631      */
26632     addSeparator : function(){
26633         return this.addItem(new Roo.Toolbar.Separator());
26634     },
26635
26636     /**
26637      * Adds a spacer element
26638      * @return {Roo.Toolbar.Spacer} The spacer item
26639      */
26640     addSpacer : function(){
26641         return this.addItem(new Roo.Toolbar.Spacer());
26642     },
26643
26644     /**
26645      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26646      * @return {Roo.Toolbar.Fill} The fill item
26647      */
26648     addFill : function(){
26649         return this.addItem(new Roo.Toolbar.Fill());
26650     },
26651
26652     /**
26653      * Adds any standard HTML element to the toolbar
26654      * @param {String/HTMLElement/Element} el The element or id of the element to add
26655      * @return {Roo.Toolbar.Item} The element's item
26656      */
26657     addElement : function(el){
26658         return this.addItem(new Roo.Toolbar.Item(el));
26659     },
26660     /**
26661      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26662      * @type Roo.util.MixedCollection  
26663      */
26664     items : false,
26665      
26666     /**
26667      * Adds any Toolbar.Item or subclass
26668      * @param {Roo.Toolbar.Item} item
26669      * @return {Roo.Toolbar.Item} The item
26670      */
26671     addItem : function(item){
26672         var td = this.nextBlock();
26673         item.render(td);
26674         this.items.add(item);
26675         return item;
26676     },
26677     
26678     /**
26679      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26680      * @param {Object/Array} config A button config or array of configs
26681      * @return {Roo.Toolbar.Button/Array}
26682      */
26683     addButton : function(config){
26684         if(config instanceof Array){
26685             var buttons = [];
26686             for(var i = 0, len = config.length; i < len; i++) {
26687                 buttons.push(this.addButton(config[i]));
26688             }
26689             return buttons;
26690         }
26691         var b = config;
26692         if(!(config instanceof Roo.Toolbar.Button)){
26693             b = config.split ?
26694                 new Roo.Toolbar.SplitButton(config) :
26695                 new Roo.Toolbar.Button(config);
26696         }
26697         var td = this.nextBlock();
26698         b.render(td);
26699         this.items.add(b);
26700         return b;
26701     },
26702     
26703     /**
26704      * Adds text to the toolbar
26705      * @param {String} text The text to add
26706      * @return {Roo.Toolbar.Item} The element's item
26707      */
26708     addText : function(text){
26709         return this.addItem(new Roo.Toolbar.TextItem(text));
26710     },
26711     
26712     /**
26713      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26714      * @param {Number} index The index where the item is to be inserted
26715      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26716      * @return {Roo.Toolbar.Button/Item}
26717      */
26718     insertButton : function(index, item){
26719         if(item instanceof Array){
26720             var buttons = [];
26721             for(var i = 0, len = item.length; i < len; i++) {
26722                buttons.push(this.insertButton(index + i, item[i]));
26723             }
26724             return buttons;
26725         }
26726         if (!(item instanceof Roo.Toolbar.Button)){
26727            item = new Roo.Toolbar.Button(item);
26728         }
26729         var td = document.createElement("td");
26730         this.tr.insertBefore(td, this.tr.childNodes[index]);
26731         item.render(td);
26732         this.items.insert(index, item);
26733         return item;
26734     },
26735     
26736     /**
26737      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26738      * @param {Object} config
26739      * @return {Roo.Toolbar.Item} The element's item
26740      */
26741     addDom : function(config, returnEl){
26742         var td = this.nextBlock();
26743         Roo.DomHelper.overwrite(td, config);
26744         var ti = new Roo.Toolbar.Item(td.firstChild);
26745         ti.render(td);
26746         this.items.add(ti);
26747         return ti;
26748     },
26749
26750     /**
26751      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26752      * @type Roo.util.MixedCollection  
26753      */
26754     fields : false,
26755     
26756     /**
26757      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26758      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26759      * @param {Roo.form.Field} field
26760      * @return {Roo.ToolbarItem}
26761      */
26762      
26763       
26764     addField : function(field) {
26765         if (!this.fields) {
26766             var autoId = 0;
26767             this.fields = new Roo.util.MixedCollection(false, function(o){
26768                 return o.id || ("item" + (++autoId));
26769             });
26770
26771         }
26772         
26773         var td = this.nextBlock();
26774         field.render(td);
26775         var ti = new Roo.Toolbar.Item(td.firstChild);
26776         ti.render(td);
26777         this.items.add(ti);
26778         this.fields.add(field);
26779         return ti;
26780     },
26781     /**
26782      * Hide the toolbar
26783      * @method hide
26784      */
26785      
26786       
26787     hide : function()
26788     {
26789         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26790         this.el.child('div').hide();
26791     },
26792     /**
26793      * Show the toolbar
26794      * @method show
26795      */
26796     show : function()
26797     {
26798         this.el.child('div').show();
26799     },
26800       
26801     // private
26802     nextBlock : function(){
26803         var td = document.createElement("td");
26804         this.tr.appendChild(td);
26805         return td;
26806     },
26807
26808     // private
26809     destroy : function(){
26810         if(this.items){ // rendered?
26811             Roo.destroy.apply(Roo, this.items.items);
26812         }
26813         if(this.fields){ // rendered?
26814             Roo.destroy.apply(Roo, this.fields.items);
26815         }
26816         Roo.Element.uncache(this.el, this.tr);
26817     }
26818 };
26819
26820 /**
26821  * @class Roo.Toolbar.Item
26822  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26823  * @constructor
26824  * Creates a new Item
26825  * @param {HTMLElement} el 
26826  */
26827 Roo.Toolbar.Item = function(el){
26828     this.el = Roo.getDom(el);
26829     this.id = Roo.id(this.el);
26830     this.hidden = false;
26831 };
26832
26833 Roo.Toolbar.Item.prototype = {
26834     
26835     /**
26836      * Get this item's HTML Element
26837      * @return {HTMLElement}
26838      */
26839     getEl : function(){
26840        return this.el;  
26841     },
26842
26843     // private
26844     render : function(td){
26845         this.td = td;
26846         td.appendChild(this.el);
26847     },
26848     
26849     /**
26850      * Removes and destroys this item.
26851      */
26852     destroy : function(){
26853         this.td.parentNode.removeChild(this.td);
26854     },
26855     
26856     /**
26857      * Shows this item.
26858      */
26859     show: function(){
26860         this.hidden = false;
26861         this.td.style.display = "";
26862     },
26863     
26864     /**
26865      * Hides this item.
26866      */
26867     hide: function(){
26868         this.hidden = true;
26869         this.td.style.display = "none";
26870     },
26871     
26872     /**
26873      * Convenience function for boolean show/hide.
26874      * @param {Boolean} visible true to show/false to hide
26875      */
26876     setVisible: function(visible){
26877         if(visible) {
26878             this.show();
26879         }else{
26880             this.hide();
26881         }
26882     },
26883     
26884     /**
26885      * Try to focus this item.
26886      */
26887     focus : function(){
26888         Roo.fly(this.el).focus();
26889     },
26890     
26891     /**
26892      * Disables this item.
26893      */
26894     disable : function(){
26895         Roo.fly(this.td).addClass("x-item-disabled");
26896         this.disabled = true;
26897         this.el.disabled = true;
26898     },
26899     
26900     /**
26901      * Enables this item.
26902      */
26903     enable : function(){
26904         Roo.fly(this.td).removeClass("x-item-disabled");
26905         this.disabled = false;
26906         this.el.disabled = false;
26907     }
26908 };
26909
26910
26911 /**
26912  * @class Roo.Toolbar.Separator
26913  * @extends Roo.Toolbar.Item
26914  * A simple toolbar separator class
26915  * @constructor
26916  * Creates a new Separator
26917  */
26918 Roo.Toolbar.Separator = function(){
26919     var s = document.createElement("span");
26920     s.className = "ytb-sep";
26921     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26922 };
26923 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26924     enable:Roo.emptyFn,
26925     disable:Roo.emptyFn,
26926     focus:Roo.emptyFn
26927 });
26928
26929 /**
26930  * @class Roo.Toolbar.Spacer
26931  * @extends Roo.Toolbar.Item
26932  * A simple element that adds extra horizontal space to a toolbar.
26933  * @constructor
26934  * Creates a new Spacer
26935  */
26936 Roo.Toolbar.Spacer = function(){
26937     var s = document.createElement("div");
26938     s.className = "ytb-spacer";
26939     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26940 };
26941 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26942     enable:Roo.emptyFn,
26943     disable:Roo.emptyFn,
26944     focus:Roo.emptyFn
26945 });
26946
26947 /**
26948  * @class Roo.Toolbar.Fill
26949  * @extends Roo.Toolbar.Spacer
26950  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26951  * @constructor
26952  * Creates a new Spacer
26953  */
26954 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26955     // private
26956     render : function(td){
26957         td.style.width = '100%';
26958         Roo.Toolbar.Fill.superclass.render.call(this, td);
26959     }
26960 });
26961
26962 /**
26963  * @class Roo.Toolbar.TextItem
26964  * @extends Roo.Toolbar.Item
26965  * A simple class that renders text directly into a toolbar.
26966  * @constructor
26967  * Creates a new TextItem
26968  * @param {String} text
26969  */
26970 Roo.Toolbar.TextItem = function(text){
26971     if (typeof(text) == 'object') {
26972         text = text.text;
26973     }
26974     var s = document.createElement("span");
26975     s.className = "ytb-text";
26976     s.innerHTML = text;
26977     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26978 };
26979 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26980     enable:Roo.emptyFn,
26981     disable:Roo.emptyFn,
26982     focus:Roo.emptyFn
26983 });
26984
26985 /**
26986  * @class Roo.Toolbar.Button
26987  * @extends Roo.Button
26988  * A button that renders into a toolbar.
26989  * @constructor
26990  * Creates a new Button
26991  * @param {Object} config A standard {@link Roo.Button} config object
26992  */
26993 Roo.Toolbar.Button = function(config){
26994     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26995 };
26996 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26997     render : function(td){
26998         this.td = td;
26999         Roo.Toolbar.Button.superclass.render.call(this, td);
27000     },
27001     
27002     /**
27003      * Removes and destroys this button
27004      */
27005     destroy : function(){
27006         Roo.Toolbar.Button.superclass.destroy.call(this);
27007         this.td.parentNode.removeChild(this.td);
27008     },
27009     
27010     /**
27011      * Shows this button
27012      */
27013     show: function(){
27014         this.hidden = false;
27015         this.td.style.display = "";
27016     },
27017     
27018     /**
27019      * Hides this button
27020      */
27021     hide: function(){
27022         this.hidden = true;
27023         this.td.style.display = "none";
27024     },
27025
27026     /**
27027      * Disables this item
27028      */
27029     disable : function(){
27030         Roo.fly(this.td).addClass("x-item-disabled");
27031         this.disabled = true;
27032     },
27033
27034     /**
27035      * Enables this item
27036      */
27037     enable : function(){
27038         Roo.fly(this.td).removeClass("x-item-disabled");
27039         this.disabled = false;
27040     }
27041 });
27042 // backwards compat
27043 Roo.ToolbarButton = Roo.Toolbar.Button;
27044
27045 /**
27046  * @class Roo.Toolbar.SplitButton
27047  * @extends Roo.SplitButton
27048  * A menu button that renders into a toolbar.
27049  * @constructor
27050  * Creates a new SplitButton
27051  * @param {Object} config A standard {@link Roo.SplitButton} config object
27052  */
27053 Roo.Toolbar.SplitButton = function(config){
27054     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27055 };
27056 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27057     render : function(td){
27058         this.td = td;
27059         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27060     },
27061     
27062     /**
27063      * Removes and destroys this button
27064      */
27065     destroy : function(){
27066         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27067         this.td.parentNode.removeChild(this.td);
27068     },
27069     
27070     /**
27071      * Shows this button
27072      */
27073     show: function(){
27074         this.hidden = false;
27075         this.td.style.display = "";
27076     },
27077     
27078     /**
27079      * Hides this button
27080      */
27081     hide: function(){
27082         this.hidden = true;
27083         this.td.style.display = "none";
27084     }
27085 });
27086
27087 // backwards compat
27088 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27089  * Based on:
27090  * Ext JS Library 1.1.1
27091  * Copyright(c) 2006-2007, Ext JS, LLC.
27092  *
27093  * Originally Released Under LGPL - original licence link has changed is not relivant.
27094  *
27095  * Fork - LGPL
27096  * <script type="text/javascript">
27097  */
27098  
27099 /**
27100  * @class Roo.PagingToolbar
27101  * @extends Roo.Toolbar
27102  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27103  * @constructor
27104  * Create a new PagingToolbar
27105  * @param {Object} config The config object
27106  */
27107 Roo.PagingToolbar = function(el, ds, config)
27108 {
27109     // old args format still supported... - xtype is prefered..
27110     if (typeof(el) == 'object' && el.xtype) {
27111         // created from xtype...
27112         config = el;
27113         ds = el.dataSource;
27114         el = config.container;
27115     }
27116     
27117     
27118     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27119     this.ds = ds;
27120     this.cursor = 0;
27121     this.renderButtons(this.el);
27122     this.bind(ds);
27123 };
27124
27125 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27126     /**
27127      * @cfg {Roo.data.Store} dataSource
27128      * The underlying data store providing the paged data
27129      */
27130     /**
27131      * @cfg {String/HTMLElement/Element} container
27132      * container The id or element that will contain the toolbar
27133      */
27134     /**
27135      * @cfg {Boolean} displayInfo
27136      * True to display the displayMsg (defaults to false)
27137      */
27138     /**
27139      * @cfg {Number} pageSize
27140      * The number of records to display per page (defaults to 20)
27141      */
27142     pageSize: 20,
27143     /**
27144      * @cfg {String} displayMsg
27145      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27146      */
27147     displayMsg : 'Displaying {0} - {1} of {2}',
27148     /**
27149      * @cfg {String} emptyMsg
27150      * The message to display when no records are found (defaults to "No data to display")
27151      */
27152     emptyMsg : 'No data to display',
27153     /**
27154      * Customizable piece of the default paging text (defaults to "Page")
27155      * @type String
27156      */
27157     beforePageText : "Page",
27158     /**
27159      * Customizable piece of the default paging text (defaults to "of %0")
27160      * @type String
27161      */
27162     afterPageText : "of {0}",
27163     /**
27164      * Customizable piece of the default paging text (defaults to "First Page")
27165      * @type String
27166      */
27167     firstText : "First Page",
27168     /**
27169      * Customizable piece of the default paging text (defaults to "Previous Page")
27170      * @type String
27171      */
27172     prevText : "Previous Page",
27173     /**
27174      * Customizable piece of the default paging text (defaults to "Next Page")
27175      * @type String
27176      */
27177     nextText : "Next Page",
27178     /**
27179      * Customizable piece of the default paging text (defaults to "Last Page")
27180      * @type String
27181      */
27182     lastText : "Last Page",
27183     /**
27184      * Customizable piece of the default paging text (defaults to "Refresh")
27185      * @type String
27186      */
27187     refreshText : "Refresh",
27188
27189     // private
27190     renderButtons : function(el){
27191         Roo.PagingToolbar.superclass.render.call(this, el);
27192         this.first = this.addButton({
27193             tooltip: this.firstText,
27194             cls: "x-btn-icon x-grid-page-first",
27195             disabled: true,
27196             handler: this.onClick.createDelegate(this, ["first"])
27197         });
27198         this.prev = this.addButton({
27199             tooltip: this.prevText,
27200             cls: "x-btn-icon x-grid-page-prev",
27201             disabled: true,
27202             handler: this.onClick.createDelegate(this, ["prev"])
27203         });
27204         //this.addSeparator();
27205         this.add(this.beforePageText);
27206         this.field = Roo.get(this.addDom({
27207            tag: "input",
27208            type: "text",
27209            size: "3",
27210            value: "1",
27211            cls: "x-grid-page-number"
27212         }).el);
27213         this.field.on("keydown", this.onPagingKeydown, this);
27214         this.field.on("focus", function(){this.dom.select();});
27215         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27216         this.field.setHeight(18);
27217         //this.addSeparator();
27218         this.next = this.addButton({
27219             tooltip: this.nextText,
27220             cls: "x-btn-icon x-grid-page-next",
27221             disabled: true,
27222             handler: this.onClick.createDelegate(this, ["next"])
27223         });
27224         this.last = this.addButton({
27225             tooltip: this.lastText,
27226             cls: "x-btn-icon x-grid-page-last",
27227             disabled: true,
27228             handler: this.onClick.createDelegate(this, ["last"])
27229         });
27230         //this.addSeparator();
27231         this.loading = this.addButton({
27232             tooltip: this.refreshText,
27233             cls: "x-btn-icon x-grid-loading",
27234             handler: this.onClick.createDelegate(this, ["refresh"])
27235         });
27236
27237         if(this.displayInfo){
27238             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27239         }
27240     },
27241
27242     // private
27243     updateInfo : function(){
27244         if(this.displayEl){
27245             var count = this.ds.getCount();
27246             var msg = count == 0 ?
27247                 this.emptyMsg :
27248                 String.format(
27249                     this.displayMsg,
27250                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27251                 );
27252             this.displayEl.update(msg);
27253         }
27254     },
27255
27256     // private
27257     onLoad : function(ds, r, o){
27258        this.cursor = o.params ? o.params.start : 0;
27259        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27260
27261        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27262        this.field.dom.value = ap;
27263        this.first.setDisabled(ap == 1);
27264        this.prev.setDisabled(ap == 1);
27265        this.next.setDisabled(ap == ps);
27266        this.last.setDisabled(ap == ps);
27267        this.loading.enable();
27268        this.updateInfo();
27269     },
27270
27271     // private
27272     getPageData : function(){
27273         var total = this.ds.getTotalCount();
27274         return {
27275             total : total,
27276             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27277             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27278         };
27279     },
27280
27281     // private
27282     onLoadError : function(){
27283         this.loading.enable();
27284     },
27285
27286     // private
27287     onPagingKeydown : function(e){
27288         var k = e.getKey();
27289         var d = this.getPageData();
27290         if(k == e.RETURN){
27291             var v = this.field.dom.value, pageNum;
27292             if(!v || isNaN(pageNum = parseInt(v, 10))){
27293                 this.field.dom.value = d.activePage;
27294                 return;
27295             }
27296             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27297             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27298             e.stopEvent();
27299         }
27300         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))
27301         {
27302           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27303           this.field.dom.value = pageNum;
27304           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27305           e.stopEvent();
27306         }
27307         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27308         {
27309           var v = this.field.dom.value, pageNum; 
27310           var increment = (e.shiftKey) ? 10 : 1;
27311           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27312             increment *= -1;
27313           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27314             this.field.dom.value = d.activePage;
27315             return;
27316           }
27317           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27318           {
27319             this.field.dom.value = parseInt(v, 10) + increment;
27320             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27321             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27322           }
27323           e.stopEvent();
27324         }
27325     },
27326
27327     // private
27328     beforeLoad : function(){
27329         if(this.loading){
27330             this.loading.disable();
27331         }
27332     },
27333
27334     // private
27335     onClick : function(which){
27336         var ds = this.ds;
27337         switch(which){
27338             case "first":
27339                 ds.load({params:{start: 0, limit: this.pageSize}});
27340             break;
27341             case "prev":
27342                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27343             break;
27344             case "next":
27345                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27346             break;
27347             case "last":
27348                 var total = ds.getTotalCount();
27349                 var extra = total % this.pageSize;
27350                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27351                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27352             break;
27353             case "refresh":
27354                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27355             break;
27356         }
27357     },
27358
27359     /**
27360      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27361      * @param {Roo.data.Store} store The data store to unbind
27362      */
27363     unbind : function(ds){
27364         ds.un("beforeload", this.beforeLoad, this);
27365         ds.un("load", this.onLoad, this);
27366         ds.un("loadexception", this.onLoadError, this);
27367         ds.un("remove", this.updateInfo, this);
27368         ds.un("add", this.updateInfo, this);
27369         this.ds = undefined;
27370     },
27371
27372     /**
27373      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27374      * @param {Roo.data.Store} store The data store to bind
27375      */
27376     bind : function(ds){
27377         ds.on("beforeload", this.beforeLoad, this);
27378         ds.on("load", this.onLoad, this);
27379         ds.on("loadexception", this.onLoadError, this);
27380         ds.on("remove", this.updateInfo, this);
27381         ds.on("add", this.updateInfo, this);
27382         this.ds = ds;
27383     }
27384 });/*
27385  * Based on:
27386  * Ext JS Library 1.1.1
27387  * Copyright(c) 2006-2007, Ext JS, LLC.
27388  *
27389  * Originally Released Under LGPL - original licence link has changed is not relivant.
27390  *
27391  * Fork - LGPL
27392  * <script type="text/javascript">
27393  */
27394
27395 /**
27396  * @class Roo.Resizable
27397  * @extends Roo.util.Observable
27398  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27399  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27400  * 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
27401  * the element will be wrapped for you automatically.</p>
27402  * <p>Here is the list of valid resize handles:</p>
27403  * <pre>
27404 Value   Description
27405 ------  -------------------
27406  'n'     north
27407  's'     south
27408  'e'     east
27409  'w'     west
27410  'nw'    northwest
27411  'sw'    southwest
27412  'se'    southeast
27413  'ne'    northeast
27414  'hd'    horizontal drag
27415  'all'   all
27416 </pre>
27417  * <p>Here's an example showing the creation of a typical Resizable:</p>
27418  * <pre><code>
27419 var resizer = new Roo.Resizable("element-id", {
27420     handles: 'all',
27421     minWidth: 200,
27422     minHeight: 100,
27423     maxWidth: 500,
27424     maxHeight: 400,
27425     pinned: true
27426 });
27427 resizer.on("resize", myHandler);
27428 </code></pre>
27429  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27430  * resizer.east.setDisplayed(false);</p>
27431  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27432  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27433  * resize operation's new size (defaults to [0, 0])
27434  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27435  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27436  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27437  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27438  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27439  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27440  * @cfg {Number} width The width of the element in pixels (defaults to null)
27441  * @cfg {Number} height The height of the element in pixels (defaults to null)
27442  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27443  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27444  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27445  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27446  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27447  * in favor of the handles config option (defaults to false)
27448  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27449  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27450  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27451  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27452  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27453  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27454  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27455  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27456  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27457  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27458  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27459  * @constructor
27460  * Create a new resizable component
27461  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27462  * @param {Object} config configuration options
27463   */
27464 Roo.Resizable = function(el, config)
27465 {
27466     this.el = Roo.get(el);
27467
27468     if(config && config.wrap){
27469         config.resizeChild = this.el;
27470         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27471         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27472         this.el.setStyle("overflow", "hidden");
27473         this.el.setPositioning(config.resizeChild.getPositioning());
27474         config.resizeChild.clearPositioning();
27475         if(!config.width || !config.height){
27476             var csize = config.resizeChild.getSize();
27477             this.el.setSize(csize.width, csize.height);
27478         }
27479         if(config.pinned && !config.adjustments){
27480             config.adjustments = "auto";
27481         }
27482     }
27483
27484     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27485     this.proxy.unselectable();
27486     this.proxy.enableDisplayMode('block');
27487
27488     Roo.apply(this, config);
27489
27490     if(this.pinned){
27491         this.disableTrackOver = true;
27492         this.el.addClass("x-resizable-pinned");
27493     }
27494     // if the element isn't positioned, make it relative
27495     var position = this.el.getStyle("position");
27496     if(position != "absolute" && position != "fixed"){
27497         this.el.setStyle("position", "relative");
27498     }
27499     if(!this.handles){ // no handles passed, must be legacy style
27500         this.handles = 's,e,se';
27501         if(this.multiDirectional){
27502             this.handles += ',n,w';
27503         }
27504     }
27505     if(this.handles == "all"){
27506         this.handles = "n s e w ne nw se sw";
27507     }
27508     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27509     var ps = Roo.Resizable.positions;
27510     for(var i = 0, len = hs.length; i < len; i++){
27511         if(hs[i] && ps[hs[i]]){
27512             var pos = ps[hs[i]];
27513             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27514         }
27515     }
27516     // legacy
27517     this.corner = this.southeast;
27518     
27519     // updateBox = the box can move..
27520     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27521         this.updateBox = true;
27522     }
27523
27524     this.activeHandle = null;
27525
27526     if(this.resizeChild){
27527         if(typeof this.resizeChild == "boolean"){
27528             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27529         }else{
27530             this.resizeChild = Roo.get(this.resizeChild, true);
27531         }
27532     }
27533     
27534     if(this.adjustments == "auto"){
27535         var rc = this.resizeChild;
27536         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27537         if(rc && (hw || hn)){
27538             rc.position("relative");
27539             rc.setLeft(hw ? hw.el.getWidth() : 0);
27540             rc.setTop(hn ? hn.el.getHeight() : 0);
27541         }
27542         this.adjustments = [
27543             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27544             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27545         ];
27546     }
27547
27548     if(this.draggable){
27549         this.dd = this.dynamic ?
27550             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27551         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27552     }
27553
27554     // public events
27555     this.addEvents({
27556         /**
27557          * @event beforeresize
27558          * Fired before resize is allowed. Set enabled to false to cancel resize.
27559          * @param {Roo.Resizable} this
27560          * @param {Roo.EventObject} e The mousedown event
27561          */
27562         "beforeresize" : true,
27563         /**
27564          * @event resize
27565          * Fired after a resize.
27566          * @param {Roo.Resizable} this
27567          * @param {Number} width The new width
27568          * @param {Number} height The new height
27569          * @param {Roo.EventObject} e The mouseup event
27570          */
27571         "resize" : true
27572     });
27573
27574     if(this.width !== null && this.height !== null){
27575         this.resizeTo(this.width, this.height);
27576     }else{
27577         this.updateChildSize();
27578     }
27579     if(Roo.isIE){
27580         this.el.dom.style.zoom = 1;
27581     }
27582     Roo.Resizable.superclass.constructor.call(this);
27583 };
27584
27585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27586         resizeChild : false,
27587         adjustments : [0, 0],
27588         minWidth : 5,
27589         minHeight : 5,
27590         maxWidth : 10000,
27591         maxHeight : 10000,
27592         enabled : true,
27593         animate : false,
27594         duration : .35,
27595         dynamic : false,
27596         handles : false,
27597         multiDirectional : false,
27598         disableTrackOver : false,
27599         easing : 'easeOutStrong',
27600         widthIncrement : 0,
27601         heightIncrement : 0,
27602         pinned : false,
27603         width : null,
27604         height : null,
27605         preserveRatio : false,
27606         transparent: false,
27607         minX: 0,
27608         minY: 0,
27609         draggable: false,
27610
27611         /**
27612          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27613          */
27614         constrainTo: undefined,
27615         /**
27616          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27617          */
27618         resizeRegion: undefined,
27619
27620
27621     /**
27622      * Perform a manual resize
27623      * @param {Number} width
27624      * @param {Number} height
27625      */
27626     resizeTo : function(width, height){
27627         this.el.setSize(width, height);
27628         this.updateChildSize();
27629         this.fireEvent("resize", this, width, height, null);
27630     },
27631
27632     // private
27633     startSizing : function(e, handle){
27634         this.fireEvent("beforeresize", this, e);
27635         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27636
27637             if(!this.overlay){
27638                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27639                 this.overlay.unselectable();
27640                 this.overlay.enableDisplayMode("block");
27641                 this.overlay.on("mousemove", this.onMouseMove, this);
27642                 this.overlay.on("mouseup", this.onMouseUp, this);
27643             }
27644             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27645
27646             this.resizing = true;
27647             this.startBox = this.el.getBox();
27648             this.startPoint = e.getXY();
27649             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27650                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27651
27652             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27653             this.overlay.show();
27654
27655             if(this.constrainTo) {
27656                 var ct = Roo.get(this.constrainTo);
27657                 this.resizeRegion = ct.getRegion().adjust(
27658                     ct.getFrameWidth('t'),
27659                     ct.getFrameWidth('l'),
27660                     -ct.getFrameWidth('b'),
27661                     -ct.getFrameWidth('r')
27662                 );
27663             }
27664
27665             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27666             this.proxy.show();
27667             this.proxy.setBox(this.startBox);
27668             if(!this.dynamic){
27669                 this.proxy.setStyle('visibility', 'visible');
27670             }
27671         }
27672     },
27673
27674     // private
27675     onMouseDown : function(handle, e){
27676         if(this.enabled){
27677             e.stopEvent();
27678             this.activeHandle = handle;
27679             this.startSizing(e, handle);
27680         }
27681     },
27682
27683     // private
27684     onMouseUp : function(e){
27685         var size = this.resizeElement();
27686         this.resizing = false;
27687         this.handleOut();
27688         this.overlay.hide();
27689         this.proxy.hide();
27690         this.fireEvent("resize", this, size.width, size.height, e);
27691     },
27692
27693     // private
27694     updateChildSize : function(){
27695         if(this.resizeChild){
27696             var el = this.el;
27697             var child = this.resizeChild;
27698             var adj = this.adjustments;
27699             if(el.dom.offsetWidth){
27700                 var b = el.getSize(true);
27701                 child.setSize(b.width+adj[0], b.height+adj[1]);
27702             }
27703             // Second call here for IE
27704             // The first call enables instant resizing and
27705             // the second call corrects scroll bars if they
27706             // exist
27707             if(Roo.isIE){
27708                 setTimeout(function(){
27709                     if(el.dom.offsetWidth){
27710                         var b = el.getSize(true);
27711                         child.setSize(b.width+adj[0], b.height+adj[1]);
27712                     }
27713                 }, 10);
27714             }
27715         }
27716     },
27717
27718     // private
27719     snap : function(value, inc, min){
27720         if(!inc || !value) return value;
27721         var newValue = value;
27722         var m = value % inc;
27723         if(m > 0){
27724             if(m > (inc/2)){
27725                 newValue = value + (inc-m);
27726             }else{
27727                 newValue = value - m;
27728             }
27729         }
27730         return Math.max(min, newValue);
27731     },
27732
27733     // private
27734     resizeElement : function(){
27735         var box = this.proxy.getBox();
27736         if(this.updateBox){
27737             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27738         }else{
27739             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27740         }
27741         this.updateChildSize();
27742         if(!this.dynamic){
27743             this.proxy.hide();
27744         }
27745         return box;
27746     },
27747
27748     // private
27749     constrain : function(v, diff, m, mx){
27750         if(v - diff < m){
27751             diff = v - m;
27752         }else if(v - diff > mx){
27753             diff = mx - v;
27754         }
27755         return diff;
27756     },
27757
27758     // private
27759     onMouseMove : function(e){
27760         if(this.enabled){
27761             try{// try catch so if something goes wrong the user doesn't get hung
27762
27763             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27764                 return;
27765             }
27766
27767             //var curXY = this.startPoint;
27768             var curSize = this.curSize || this.startBox;
27769             var x = this.startBox.x, y = this.startBox.y;
27770             var ox = x, oy = y;
27771             var w = curSize.width, h = curSize.height;
27772             var ow = w, oh = h;
27773             var mw = this.minWidth, mh = this.minHeight;
27774             var mxw = this.maxWidth, mxh = this.maxHeight;
27775             var wi = this.widthIncrement;
27776             var hi = this.heightIncrement;
27777
27778             var eventXY = e.getXY();
27779             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27780             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27781
27782             var pos = this.activeHandle.position;
27783
27784             switch(pos){
27785                 case "east":
27786                     w += diffX;
27787                     w = Math.min(Math.max(mw, w), mxw);
27788                     break;
27789              
27790                 case "south":
27791                     h += diffY;
27792                     h = Math.min(Math.max(mh, h), mxh);
27793                     break;
27794                 case "southeast":
27795                     w += diffX;
27796                     h += diffY;
27797                     w = Math.min(Math.max(mw, w), mxw);
27798                     h = Math.min(Math.max(mh, h), mxh);
27799                     break;
27800                 case "north":
27801                     diffY = this.constrain(h, diffY, mh, mxh);
27802                     y += diffY;
27803                     h -= diffY;
27804                     break;
27805                 case "hdrag":
27806                     
27807                     if (wi) {
27808                         var adiffX = Math.abs(diffX);
27809                         var sub = (adiffX % wi); // how much 
27810                         if (sub > (wi/2)) { // far enough to snap
27811                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27812                         } else {
27813                             // remove difference.. 
27814                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27815                         }
27816                     }
27817                     x += diffX;
27818                     x = Math.max(this.minX, x);
27819                     break;
27820                 case "west":
27821                     diffX = this.constrain(w, diffX, mw, mxw);
27822                     x += diffX;
27823                     w -= diffX;
27824                     break;
27825                 case "northeast":
27826                     w += diffX;
27827                     w = Math.min(Math.max(mw, w), mxw);
27828                     diffY = this.constrain(h, diffY, mh, mxh);
27829                     y += diffY;
27830                     h -= diffY;
27831                     break;
27832                 case "northwest":
27833                     diffX = this.constrain(w, diffX, mw, mxw);
27834                     diffY = this.constrain(h, diffY, mh, mxh);
27835                     y += diffY;
27836                     h -= diffY;
27837                     x += diffX;
27838                     w -= diffX;
27839                     break;
27840                case "southwest":
27841                     diffX = this.constrain(w, diffX, mw, mxw);
27842                     h += diffY;
27843                     h = Math.min(Math.max(mh, h), mxh);
27844                     x += diffX;
27845                     w -= diffX;
27846                     break;
27847             }
27848
27849             var sw = this.snap(w, wi, mw);
27850             var sh = this.snap(h, hi, mh);
27851             if(sw != w || sh != h){
27852                 switch(pos){
27853                     case "northeast":
27854                         y -= sh - h;
27855                     break;
27856                     case "north":
27857                         y -= sh - h;
27858                         break;
27859                     case "southwest":
27860                         x -= sw - w;
27861                     break;
27862                     case "west":
27863                         x -= sw - w;
27864                         break;
27865                     case "northwest":
27866                         x -= sw - w;
27867                         y -= sh - h;
27868                     break;
27869                 }
27870                 w = sw;
27871                 h = sh;
27872             }
27873
27874             if(this.preserveRatio){
27875                 switch(pos){
27876                     case "southeast":
27877                     case "east":
27878                         h = oh * (w/ow);
27879                         h = Math.min(Math.max(mh, h), mxh);
27880                         w = ow * (h/oh);
27881                        break;
27882                     case "south":
27883                         w = ow * (h/oh);
27884                         w = Math.min(Math.max(mw, w), mxw);
27885                         h = oh * (w/ow);
27886                         break;
27887                     case "northeast":
27888                         w = ow * (h/oh);
27889                         w = Math.min(Math.max(mw, w), mxw);
27890                         h = oh * (w/ow);
27891                     break;
27892                     case "north":
27893                         var tw = w;
27894                         w = ow * (h/oh);
27895                         w = Math.min(Math.max(mw, w), mxw);
27896                         h = oh * (w/ow);
27897                         x += (tw - w) / 2;
27898                         break;
27899                     case "southwest":
27900                         h = oh * (w/ow);
27901                         h = Math.min(Math.max(mh, h), mxh);
27902                         var tw = w;
27903                         w = ow * (h/oh);
27904                         x += tw - w;
27905                         break;
27906                     case "west":
27907                         var th = h;
27908                         h = oh * (w/ow);
27909                         h = Math.min(Math.max(mh, h), mxh);
27910                         y += (th - h) / 2;
27911                         var tw = w;
27912                         w = ow * (h/oh);
27913                         x += tw - w;
27914                        break;
27915                     case "northwest":
27916                         var tw = w;
27917                         var th = h;
27918                         h = oh * (w/ow);
27919                         h = Math.min(Math.max(mh, h), mxh);
27920                         w = ow * (h/oh);
27921                         y += th - h;
27922                         x += tw - w;
27923                        break;
27924
27925                 }
27926             }
27927             if (pos == 'hdrag') {
27928                 w = ow;
27929             }
27930             this.proxy.setBounds(x, y, w, h);
27931             if(this.dynamic){
27932                 this.resizeElement();
27933             }
27934             }catch(e){}
27935         }
27936     },
27937
27938     // private
27939     handleOver : function(){
27940         if(this.enabled){
27941             this.el.addClass("x-resizable-over");
27942         }
27943     },
27944
27945     // private
27946     handleOut : function(){
27947         if(!this.resizing){
27948             this.el.removeClass("x-resizable-over");
27949         }
27950     },
27951
27952     /**
27953      * Returns the element this component is bound to.
27954      * @return {Roo.Element}
27955      */
27956     getEl : function(){
27957         return this.el;
27958     },
27959
27960     /**
27961      * Returns the resizeChild element (or null).
27962      * @return {Roo.Element}
27963      */
27964     getResizeChild : function(){
27965         return this.resizeChild;
27966     },
27967
27968     /**
27969      * Destroys this resizable. If the element was wrapped and
27970      * removeEl is not true then the element remains.
27971      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27972      */
27973     destroy : function(removeEl){
27974         this.proxy.remove();
27975         if(this.overlay){
27976             this.overlay.removeAllListeners();
27977             this.overlay.remove();
27978         }
27979         var ps = Roo.Resizable.positions;
27980         for(var k in ps){
27981             if(typeof ps[k] != "function" && this[ps[k]]){
27982                 var h = this[ps[k]];
27983                 h.el.removeAllListeners();
27984                 h.el.remove();
27985             }
27986         }
27987         if(removeEl){
27988             this.el.update("");
27989             this.el.remove();
27990         }
27991     }
27992 });
27993
27994 // private
27995 // hash to map config positions to true positions
27996 Roo.Resizable.positions = {
27997     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
27998     hd: "hdrag"
27999 };
28000
28001 // private
28002 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28003     if(!this.tpl){
28004         // only initialize the template if resizable is used
28005         var tpl = Roo.DomHelper.createTemplate(
28006             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28007         );
28008         tpl.compile();
28009         Roo.Resizable.Handle.prototype.tpl = tpl;
28010     }
28011     this.position = pos;
28012     this.rz = rz;
28013     // show north drag fro topdra
28014     var handlepos = pos == 'hdrag' ? 'north' : pos;
28015     
28016     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28017     if (pos == 'hdrag') {
28018         this.el.setStyle('cursor', 'pointer');
28019     }
28020     this.el.unselectable();
28021     if(transparent){
28022         this.el.setOpacity(0);
28023     }
28024     this.el.on("mousedown", this.onMouseDown, this);
28025     if(!disableTrackOver){
28026         this.el.on("mouseover", this.onMouseOver, this);
28027         this.el.on("mouseout", this.onMouseOut, this);
28028     }
28029 };
28030
28031 // private
28032 Roo.Resizable.Handle.prototype = {
28033     afterResize : function(rz){
28034         // do nothing
28035     },
28036     // private
28037     onMouseDown : function(e){
28038         this.rz.onMouseDown(this, e);
28039     },
28040     // private
28041     onMouseOver : function(e){
28042         this.rz.handleOver(this, e);
28043     },
28044     // private
28045     onMouseOut : function(e){
28046         this.rz.handleOut(this, e);
28047     }
28048 };/*
28049  * Based on:
28050  * Ext JS Library 1.1.1
28051  * Copyright(c) 2006-2007, Ext JS, LLC.
28052  *
28053  * Originally Released Under LGPL - original licence link has changed is not relivant.
28054  *
28055  * Fork - LGPL
28056  * <script type="text/javascript">
28057  */
28058
28059 /**
28060  * @class Roo.Editor
28061  * @extends Roo.Component
28062  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28063  * @constructor
28064  * Create a new Editor
28065  * @param {Roo.form.Field} field The Field object (or descendant)
28066  * @param {Object} config The config object
28067  */
28068 Roo.Editor = function(field, config){
28069     Roo.Editor.superclass.constructor.call(this, config);
28070     this.field = field;
28071     this.addEvents({
28072         /**
28073              * @event beforestartedit
28074              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28075              * false from the handler of this event.
28076              * @param {Editor} this
28077              * @param {Roo.Element} boundEl The underlying element bound to this editor
28078              * @param {Mixed} value The field value being set
28079              */
28080         "beforestartedit" : true,
28081         /**
28082              * @event startedit
28083              * Fires when this editor is displayed
28084              * @param {Roo.Element} boundEl The underlying element bound to this editor
28085              * @param {Mixed} value The starting field value
28086              */
28087         "startedit" : true,
28088         /**
28089              * @event beforecomplete
28090              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28091              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28092              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28093              * event will not fire since no edit actually occurred.
28094              * @param {Editor} this
28095              * @param {Mixed} value The current field value
28096              * @param {Mixed} startValue The original field value
28097              */
28098         "beforecomplete" : true,
28099         /**
28100              * @event complete
28101              * Fires after editing is complete and any changed value has been written to the underlying field.
28102              * @param {Editor} this
28103              * @param {Mixed} value The current field value
28104              * @param {Mixed} startValue The original field value
28105              */
28106         "complete" : true,
28107         /**
28108          * @event specialkey
28109          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28110          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28111          * @param {Roo.form.Field} this
28112          * @param {Roo.EventObject} e The event object
28113          */
28114         "specialkey" : true
28115     });
28116 };
28117
28118 Roo.extend(Roo.Editor, Roo.Component, {
28119     /**
28120      * @cfg {Boolean/String} autosize
28121      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28122      * or "height" to adopt the height only (defaults to false)
28123      */
28124     /**
28125      * @cfg {Boolean} revertInvalid
28126      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28127      * validation fails (defaults to true)
28128      */
28129     /**
28130      * @cfg {Boolean} ignoreNoChange
28131      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28132      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28133      * will never be ignored.
28134      */
28135     /**
28136      * @cfg {Boolean} hideEl
28137      * False to keep the bound element visible while the editor is displayed (defaults to true)
28138      */
28139     /**
28140      * @cfg {Mixed} value
28141      * The data value of the underlying field (defaults to "")
28142      */
28143     value : "",
28144     /**
28145      * @cfg {String} alignment
28146      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28147      */
28148     alignment: "c-c?",
28149     /**
28150      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28151      * for bottom-right shadow (defaults to "frame")
28152      */
28153     shadow : "frame",
28154     /**
28155      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28156      */
28157     constrain : false,
28158     /**
28159      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28160      */
28161     completeOnEnter : false,
28162     /**
28163      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28164      */
28165     cancelOnEsc : false,
28166     /**
28167      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28168      */
28169     updateEl : false,
28170
28171     // private
28172     onRender : function(ct, position){
28173         this.el = new Roo.Layer({
28174             shadow: this.shadow,
28175             cls: "x-editor",
28176             parentEl : ct,
28177             shim : this.shim,
28178             shadowOffset:4,
28179             id: this.id,
28180             constrain: this.constrain
28181         });
28182         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28183         if(this.field.msgTarget != 'title'){
28184             this.field.msgTarget = 'qtip';
28185         }
28186         this.field.render(this.el);
28187         if(Roo.isGecko){
28188             this.field.el.dom.setAttribute('autocomplete', 'off');
28189         }
28190         this.field.on("specialkey", this.onSpecialKey, this);
28191         if(this.swallowKeys){
28192             this.field.el.swallowEvent(['keydown','keypress']);
28193         }
28194         this.field.show();
28195         this.field.on("blur", this.onBlur, this);
28196         if(this.field.grow){
28197             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28198         }
28199     },
28200
28201     onSpecialKey : function(field, e){
28202         //Roo.log('editor onSpecialKey');
28203         if(this.completeOnEnter && e.getKey() == e.ENTER){
28204             e.stopEvent();
28205             this.completeEdit();
28206         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28207             this.cancelEdit();
28208         }else{
28209             this.fireEvent('specialkey', field, e);
28210         }
28211     },
28212
28213     /**
28214      * Starts the editing process and shows the editor.
28215      * @param {String/HTMLElement/Element} el The element to edit
28216      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28217       * to the innerHTML of el.
28218      */
28219     startEdit : function(el, value){
28220         if(this.editing){
28221             this.completeEdit();
28222         }
28223         this.boundEl = Roo.get(el);
28224         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28225         if(!this.rendered){
28226             this.render(this.parentEl || document.body);
28227         }
28228         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28229             return;
28230         }
28231         this.startValue = v;
28232         this.field.setValue(v);
28233         if(this.autoSize){
28234             var sz = this.boundEl.getSize();
28235             switch(this.autoSize){
28236                 case "width":
28237                 this.setSize(sz.width,  "");
28238                 break;
28239                 case "height":
28240                 this.setSize("",  sz.height);
28241                 break;
28242                 default:
28243                 this.setSize(sz.width,  sz.height);
28244             }
28245         }
28246         this.el.alignTo(this.boundEl, this.alignment);
28247         this.editing = true;
28248         if(Roo.QuickTips){
28249             Roo.QuickTips.disable();
28250         }
28251         this.show();
28252     },
28253
28254     /**
28255      * Sets the height and width of this editor.
28256      * @param {Number} width The new width
28257      * @param {Number} height The new height
28258      */
28259     setSize : function(w, h){
28260         this.field.setSize(w, h);
28261         if(this.el){
28262             this.el.sync();
28263         }
28264     },
28265
28266     /**
28267      * Realigns the editor to the bound field based on the current alignment config value.
28268      */
28269     realign : function(){
28270         this.el.alignTo(this.boundEl, this.alignment);
28271     },
28272
28273     /**
28274      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28275      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28276      */
28277     completeEdit : function(remainVisible){
28278         if(!this.editing){
28279             return;
28280         }
28281         var v = this.getValue();
28282         if(this.revertInvalid !== false && !this.field.isValid()){
28283             v = this.startValue;
28284             this.cancelEdit(true);
28285         }
28286         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28287             this.editing = false;
28288             this.hide();
28289             return;
28290         }
28291         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28292             this.editing = false;
28293             if(this.updateEl && this.boundEl){
28294                 this.boundEl.update(v);
28295             }
28296             if(remainVisible !== true){
28297                 this.hide();
28298             }
28299             this.fireEvent("complete", this, v, this.startValue);
28300         }
28301     },
28302
28303     // private
28304     onShow : function(){
28305         this.el.show();
28306         if(this.hideEl !== false){
28307             this.boundEl.hide();
28308         }
28309         this.field.show();
28310         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28311             this.fixIEFocus = true;
28312             this.deferredFocus.defer(50, this);
28313         }else{
28314             this.field.focus();
28315         }
28316         this.fireEvent("startedit", this.boundEl, this.startValue);
28317     },
28318
28319     deferredFocus : function(){
28320         if(this.editing){
28321             this.field.focus();
28322         }
28323     },
28324
28325     /**
28326      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28327      * reverted to the original starting value.
28328      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28329      * cancel (defaults to false)
28330      */
28331     cancelEdit : function(remainVisible){
28332         if(this.editing){
28333             this.setValue(this.startValue);
28334             if(remainVisible !== true){
28335                 this.hide();
28336             }
28337         }
28338     },
28339
28340     // private
28341     onBlur : function(){
28342         if(this.allowBlur !== true && this.editing){
28343             this.completeEdit();
28344         }
28345     },
28346
28347     // private
28348     onHide : function(){
28349         if(this.editing){
28350             this.completeEdit();
28351             return;
28352         }
28353         this.field.blur();
28354         if(this.field.collapse){
28355             this.field.collapse();
28356         }
28357         this.el.hide();
28358         if(this.hideEl !== false){
28359             this.boundEl.show();
28360         }
28361         if(Roo.QuickTips){
28362             Roo.QuickTips.enable();
28363         }
28364     },
28365
28366     /**
28367      * Sets the data value of the editor
28368      * @param {Mixed} value Any valid value supported by the underlying field
28369      */
28370     setValue : function(v){
28371         this.field.setValue(v);
28372     },
28373
28374     /**
28375      * Gets the data value of the editor
28376      * @return {Mixed} The data value
28377      */
28378     getValue : function(){
28379         return this.field.getValue();
28380     }
28381 });/*
28382  * Based on:
28383  * Ext JS Library 1.1.1
28384  * Copyright(c) 2006-2007, Ext JS, LLC.
28385  *
28386  * Originally Released Under LGPL - original licence link has changed is not relivant.
28387  *
28388  * Fork - LGPL
28389  * <script type="text/javascript">
28390  */
28391  
28392 /**
28393  * @class Roo.BasicDialog
28394  * @extends Roo.util.Observable
28395  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28396  * <pre><code>
28397 var dlg = new Roo.BasicDialog("my-dlg", {
28398     height: 200,
28399     width: 300,
28400     minHeight: 100,
28401     minWidth: 150,
28402     modal: true,
28403     proxyDrag: true,
28404     shadow: true
28405 });
28406 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28407 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28408 dlg.addButton('Cancel', dlg.hide, dlg);
28409 dlg.show();
28410 </code></pre>
28411   <b>A Dialog should always be a direct child of the body element.</b>
28412  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28413  * @cfg {String} title Default text to display in the title bar (defaults to null)
28414  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28415  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28416  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28417  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28418  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28419  * (defaults to null with no animation)
28420  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28421  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28422  * property for valid values (defaults to 'all')
28423  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28424  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28425  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28426  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28427  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28428  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28429  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28430  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28431  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28432  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28433  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28434  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28435  * draggable = true (defaults to false)
28436  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28437  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28438  * shadow (defaults to false)
28439  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28440  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28441  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28442  * @cfg {Array} buttons Array of buttons
28443  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28444  * @constructor
28445  * Create a new BasicDialog.
28446  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28447  * @param {Object} config Configuration options
28448  */
28449 Roo.BasicDialog = function(el, config){
28450     this.el = Roo.get(el);
28451     var dh = Roo.DomHelper;
28452     if(!this.el && config && config.autoCreate){
28453         if(typeof config.autoCreate == "object"){
28454             if(!config.autoCreate.id){
28455                 config.autoCreate.id = el;
28456             }
28457             this.el = dh.append(document.body,
28458                         config.autoCreate, true);
28459         }else{
28460             this.el = dh.append(document.body,
28461                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28462         }
28463     }
28464     el = this.el;
28465     el.setDisplayed(true);
28466     el.hide = this.hideAction;
28467     this.id = el.id;
28468     el.addClass("x-dlg");
28469
28470     Roo.apply(this, config);
28471
28472     this.proxy = el.createProxy("x-dlg-proxy");
28473     this.proxy.hide = this.hideAction;
28474     this.proxy.setOpacity(.5);
28475     this.proxy.hide();
28476
28477     if(config.width){
28478         el.setWidth(config.width);
28479     }
28480     if(config.height){
28481         el.setHeight(config.height);
28482     }
28483     this.size = el.getSize();
28484     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28485         this.xy = [config.x,config.y];
28486     }else{
28487         this.xy = el.getCenterXY(true);
28488     }
28489     /** The header element @type Roo.Element */
28490     this.header = el.child("> .x-dlg-hd");
28491     /** The body element @type Roo.Element */
28492     this.body = el.child("> .x-dlg-bd");
28493     /** The footer element @type Roo.Element */
28494     this.footer = el.child("> .x-dlg-ft");
28495
28496     if(!this.header){
28497         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28498     }
28499     if(!this.body){
28500         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28501     }
28502
28503     this.header.unselectable();
28504     if(this.title){
28505         this.header.update(this.title);
28506     }
28507     // this element allows the dialog to be focused for keyboard event
28508     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28509     this.focusEl.swallowEvent("click", true);
28510
28511     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28512
28513     // wrap the body and footer for special rendering
28514     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28515     if(this.footer){
28516         this.bwrap.dom.appendChild(this.footer.dom);
28517     }
28518
28519     this.bg = this.el.createChild({
28520         tag: "div", cls:"x-dlg-bg",
28521         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28522     });
28523     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28524
28525
28526     if(this.autoScroll !== false && !this.autoTabs){
28527         this.body.setStyle("overflow", "auto");
28528     }
28529
28530     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28531
28532     if(this.closable !== false){
28533         this.el.addClass("x-dlg-closable");
28534         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28535         this.close.on("click", this.closeClick, this);
28536         this.close.addClassOnOver("x-dlg-close-over");
28537     }
28538     if(this.collapsible !== false){
28539         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28540         this.collapseBtn.on("click", this.collapseClick, this);
28541         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28542         this.header.on("dblclick", this.collapseClick, this);
28543     }
28544     if(this.resizable !== false){
28545         this.el.addClass("x-dlg-resizable");
28546         this.resizer = new Roo.Resizable(el, {
28547             minWidth: this.minWidth || 80,
28548             minHeight:this.minHeight || 80,
28549             handles: this.resizeHandles || "all",
28550             pinned: true
28551         });
28552         this.resizer.on("beforeresize", this.beforeResize, this);
28553         this.resizer.on("resize", this.onResize, this);
28554     }
28555     if(this.draggable !== false){
28556         el.addClass("x-dlg-draggable");
28557         if (!this.proxyDrag) {
28558             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28559         }
28560         else {
28561             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28562         }
28563         dd.setHandleElId(this.header.id);
28564         dd.endDrag = this.endMove.createDelegate(this);
28565         dd.startDrag = this.startMove.createDelegate(this);
28566         dd.onDrag = this.onDrag.createDelegate(this);
28567         dd.scroll = false;
28568         this.dd = dd;
28569     }
28570     if(this.modal){
28571         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28572         this.mask.enableDisplayMode("block");
28573         this.mask.hide();
28574         this.el.addClass("x-dlg-modal");
28575     }
28576     if(this.shadow){
28577         this.shadow = new Roo.Shadow({
28578             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28579             offset : this.shadowOffset
28580         });
28581     }else{
28582         this.shadowOffset = 0;
28583     }
28584     if(Roo.useShims && this.shim !== false){
28585         this.shim = this.el.createShim();
28586         this.shim.hide = this.hideAction;
28587         this.shim.hide();
28588     }else{
28589         this.shim = false;
28590     }
28591     if(this.autoTabs){
28592         this.initTabs();
28593     }
28594     if (this.buttons) { 
28595         var bts= this.buttons;
28596         this.buttons = [];
28597         Roo.each(bts, function(b) {
28598             this.addButton(b);
28599         }, this);
28600     }
28601     
28602     
28603     this.addEvents({
28604         /**
28605          * @event keydown
28606          * Fires when a key is pressed
28607          * @param {Roo.BasicDialog} this
28608          * @param {Roo.EventObject} e
28609          */
28610         "keydown" : true,
28611         /**
28612          * @event move
28613          * Fires when this dialog is moved by the user.
28614          * @param {Roo.BasicDialog} this
28615          * @param {Number} x The new page X
28616          * @param {Number} y The new page Y
28617          */
28618         "move" : true,
28619         /**
28620          * @event resize
28621          * Fires when this dialog is resized by the user.
28622          * @param {Roo.BasicDialog} this
28623          * @param {Number} width The new width
28624          * @param {Number} height The new height
28625          */
28626         "resize" : true,
28627         /**
28628          * @event beforehide
28629          * Fires before this dialog is hidden.
28630          * @param {Roo.BasicDialog} this
28631          */
28632         "beforehide" : true,
28633         /**
28634          * @event hide
28635          * Fires when this dialog is hidden.
28636          * @param {Roo.BasicDialog} this
28637          */
28638         "hide" : true,
28639         /**
28640          * @event beforeshow
28641          * Fires before this dialog is shown.
28642          * @param {Roo.BasicDialog} this
28643          */
28644         "beforeshow" : true,
28645         /**
28646          * @event show
28647          * Fires when this dialog is shown.
28648          * @param {Roo.BasicDialog} this
28649          */
28650         "show" : true
28651     });
28652     el.on("keydown", this.onKeyDown, this);
28653     el.on("mousedown", this.toFront, this);
28654     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28655     this.el.hide();
28656     Roo.DialogManager.register(this);
28657     Roo.BasicDialog.superclass.constructor.call(this);
28658 };
28659
28660 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28661     shadowOffset: Roo.isIE ? 6 : 5,
28662     minHeight: 80,
28663     minWidth: 200,
28664     minButtonWidth: 75,
28665     defaultButton: null,
28666     buttonAlign: "right",
28667     tabTag: 'div',
28668     firstShow: true,
28669
28670     /**
28671      * Sets the dialog title text
28672      * @param {String} text The title text to display
28673      * @return {Roo.BasicDialog} this
28674      */
28675     setTitle : function(text){
28676         this.header.update(text);
28677         return this;
28678     },
28679
28680     // private
28681     closeClick : function(){
28682         this.hide();
28683     },
28684
28685     // private
28686     collapseClick : function(){
28687         this[this.collapsed ? "expand" : "collapse"]();
28688     },
28689
28690     /**
28691      * Collapses the dialog to its minimized state (only the title bar is visible).
28692      * Equivalent to the user clicking the collapse dialog button.
28693      */
28694     collapse : function(){
28695         if(!this.collapsed){
28696             this.collapsed = true;
28697             this.el.addClass("x-dlg-collapsed");
28698             this.restoreHeight = this.el.getHeight();
28699             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28700         }
28701     },
28702
28703     /**
28704      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28705      * clicking the expand dialog button.
28706      */
28707     expand : function(){
28708         if(this.collapsed){
28709             this.collapsed = false;
28710             this.el.removeClass("x-dlg-collapsed");
28711             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28712         }
28713     },
28714
28715     /**
28716      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28717      * @return {Roo.TabPanel} The tabs component
28718      */
28719     initTabs : function(){
28720         var tabs = this.getTabs();
28721         while(tabs.getTab(0)){
28722             tabs.removeTab(0);
28723         }
28724         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28725             var dom = el.dom;
28726             tabs.addTab(Roo.id(dom), dom.title);
28727             dom.title = "";
28728         });
28729         tabs.activate(0);
28730         return tabs;
28731     },
28732
28733     // private
28734     beforeResize : function(){
28735         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28736     },
28737
28738     // private
28739     onResize : function(){
28740         this.refreshSize();
28741         this.syncBodyHeight();
28742         this.adjustAssets();
28743         this.focus();
28744         this.fireEvent("resize", this, this.size.width, this.size.height);
28745     },
28746
28747     // private
28748     onKeyDown : function(e){
28749         if(this.isVisible()){
28750             this.fireEvent("keydown", this, e);
28751         }
28752     },
28753
28754     /**
28755      * Resizes the dialog.
28756      * @param {Number} width
28757      * @param {Number} height
28758      * @return {Roo.BasicDialog} this
28759      */
28760     resizeTo : function(width, height){
28761         this.el.setSize(width, height);
28762         this.size = {width: width, height: height};
28763         this.syncBodyHeight();
28764         if(this.fixedcenter){
28765             this.center();
28766         }
28767         if(this.isVisible()){
28768             this.constrainXY();
28769             this.adjustAssets();
28770         }
28771         this.fireEvent("resize", this, width, height);
28772         return this;
28773     },
28774
28775
28776     /**
28777      * Resizes the dialog to fit the specified content size.
28778      * @param {Number} width
28779      * @param {Number} height
28780      * @return {Roo.BasicDialog} this
28781      */
28782     setContentSize : function(w, h){
28783         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28784         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28785         //if(!this.el.isBorderBox()){
28786             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28787             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28788         //}
28789         if(this.tabs){
28790             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28791             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28792         }
28793         this.resizeTo(w, h);
28794         return this;
28795     },
28796
28797     /**
28798      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28799      * executed in response to a particular key being pressed while the dialog is active.
28800      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28801      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28802      * @param {Function} fn The function to call
28803      * @param {Object} scope (optional) The scope of the function
28804      * @return {Roo.BasicDialog} this
28805      */
28806     addKeyListener : function(key, fn, scope){
28807         var keyCode, shift, ctrl, alt;
28808         if(typeof key == "object" && !(key instanceof Array)){
28809             keyCode = key["key"];
28810             shift = key["shift"];
28811             ctrl = key["ctrl"];
28812             alt = key["alt"];
28813         }else{
28814             keyCode = key;
28815         }
28816         var handler = function(dlg, e){
28817             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28818                 var k = e.getKey();
28819                 if(keyCode instanceof Array){
28820                     for(var i = 0, len = keyCode.length; i < len; i++){
28821                         if(keyCode[i] == k){
28822                           fn.call(scope || window, dlg, k, e);
28823                           return;
28824                         }
28825                     }
28826                 }else{
28827                     if(k == keyCode){
28828                         fn.call(scope || window, dlg, k, e);
28829                     }
28830                 }
28831             }
28832         };
28833         this.on("keydown", handler);
28834         return this;
28835     },
28836
28837     /**
28838      * Returns the TabPanel component (creates it if it doesn't exist).
28839      * Note: If you wish to simply check for the existence of tabs without creating them,
28840      * check for a null 'tabs' property.
28841      * @return {Roo.TabPanel} The tabs component
28842      */
28843     getTabs : function(){
28844         if(!this.tabs){
28845             this.el.addClass("x-dlg-auto-tabs");
28846             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28847             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28848         }
28849         return this.tabs;
28850     },
28851
28852     /**
28853      * Adds a button to the footer section of the dialog.
28854      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28855      * object or a valid Roo.DomHelper element config
28856      * @param {Function} handler The function called when the button is clicked
28857      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28858      * @return {Roo.Button} The new button
28859      */
28860     addButton : function(config, handler, scope){
28861         var dh = Roo.DomHelper;
28862         if(!this.footer){
28863             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28864         }
28865         if(!this.btnContainer){
28866             var tb = this.footer.createChild({
28867
28868                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28869                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28870             }, null, true);
28871             this.btnContainer = tb.firstChild.firstChild.firstChild;
28872         }
28873         var bconfig = {
28874             handler: handler,
28875             scope: scope,
28876             minWidth: this.minButtonWidth,
28877             hideParent:true
28878         };
28879         if(typeof config == "string"){
28880             bconfig.text = config;
28881         }else{
28882             if(config.tag){
28883                 bconfig.dhconfig = config;
28884             }else{
28885                 Roo.apply(bconfig, config);
28886             }
28887         }
28888         var fc = false;
28889         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28890             bconfig.position = Math.max(0, bconfig.position);
28891             fc = this.btnContainer.childNodes[bconfig.position];
28892         }
28893          
28894         var btn = new Roo.Button(
28895             fc ? 
28896                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28897                 : this.btnContainer.appendChild(document.createElement("td")),
28898             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28899             bconfig
28900         );
28901         this.syncBodyHeight();
28902         if(!this.buttons){
28903             /**
28904              * Array of all the buttons that have been added to this dialog via addButton
28905              * @type Array
28906              */
28907             this.buttons = [];
28908         }
28909         this.buttons.push(btn);
28910         return btn;
28911     },
28912
28913     /**
28914      * Sets the default button to be focused when the dialog is displayed.
28915      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28916      * @return {Roo.BasicDialog} this
28917      */
28918     setDefaultButton : function(btn){
28919         this.defaultButton = btn;
28920         return this;
28921     },
28922
28923     // private
28924     getHeaderFooterHeight : function(safe){
28925         var height = 0;
28926         if(this.header){
28927            height += this.header.getHeight();
28928         }
28929         if(this.footer){
28930            var fm = this.footer.getMargins();
28931             height += (this.footer.getHeight()+fm.top+fm.bottom);
28932         }
28933         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28934         height += this.centerBg.getPadding("tb");
28935         return height;
28936     },
28937
28938     // private
28939     syncBodyHeight : function(){
28940         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28941         var height = this.size.height - this.getHeaderFooterHeight(false);
28942         bd.setHeight(height-bd.getMargins("tb"));
28943         var hh = this.header.getHeight();
28944         var h = this.size.height-hh;
28945         cb.setHeight(h);
28946         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28947         bw.setHeight(h-cb.getPadding("tb"));
28948         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28949         bd.setWidth(bw.getWidth(true));
28950         if(this.tabs){
28951             this.tabs.syncHeight();
28952             if(Roo.isIE){
28953                 this.tabs.el.repaint();
28954             }
28955         }
28956     },
28957
28958     /**
28959      * Restores the previous state of the dialog if Roo.state is configured.
28960      * @return {Roo.BasicDialog} this
28961      */
28962     restoreState : function(){
28963         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28964         if(box && box.width){
28965             this.xy = [box.x, box.y];
28966             this.resizeTo(box.width, box.height);
28967         }
28968         return this;
28969     },
28970
28971     // private
28972     beforeShow : function(){
28973         this.expand();
28974         if(this.fixedcenter){
28975             this.xy = this.el.getCenterXY(true);
28976         }
28977         if(this.modal){
28978             Roo.get(document.body).addClass("x-body-masked");
28979             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28980             this.mask.show();
28981         }
28982         this.constrainXY();
28983     },
28984
28985     // private
28986     animShow : function(){
28987         var b = Roo.get(this.animateTarget).getBox();
28988         this.proxy.setSize(b.width, b.height);
28989         this.proxy.setLocation(b.x, b.y);
28990         this.proxy.show();
28991         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28992                     true, .35, this.showEl.createDelegate(this));
28993     },
28994
28995     /**
28996      * Shows the dialog.
28997      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28998      * @return {Roo.BasicDialog} this
28999      */
29000     show : function(animateTarget){
29001         if (this.fireEvent("beforeshow", this) === false){
29002             return;
29003         }
29004         if(this.syncHeightBeforeShow){
29005             this.syncBodyHeight();
29006         }else if(this.firstShow){
29007             this.firstShow = false;
29008             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29009         }
29010         this.animateTarget = animateTarget || this.animateTarget;
29011         if(!this.el.isVisible()){
29012             this.beforeShow();
29013             if(this.animateTarget && Roo.get(this.animateTarget)){
29014                 this.animShow();
29015             }else{
29016                 this.showEl();
29017             }
29018         }
29019         return this;
29020     },
29021
29022     // private
29023     showEl : function(){
29024         this.proxy.hide();
29025         this.el.setXY(this.xy);
29026         this.el.show();
29027         this.adjustAssets(true);
29028         this.toFront();
29029         this.focus();
29030         // IE peekaboo bug - fix found by Dave Fenwick
29031         if(Roo.isIE){
29032             this.el.repaint();
29033         }
29034         this.fireEvent("show", this);
29035     },
29036
29037     /**
29038      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29039      * dialog itself will receive focus.
29040      */
29041     focus : function(){
29042         if(this.defaultButton){
29043             this.defaultButton.focus();
29044         }else{
29045             this.focusEl.focus();
29046         }
29047     },
29048
29049     // private
29050     constrainXY : function(){
29051         if(this.constraintoviewport !== false){
29052             if(!this.viewSize){
29053                 if(this.container){
29054                     var s = this.container.getSize();
29055                     this.viewSize = [s.width, s.height];
29056                 }else{
29057                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29058                 }
29059             }
29060             var s = Roo.get(this.container||document).getScroll();
29061
29062             var x = this.xy[0], y = this.xy[1];
29063             var w = this.size.width, h = this.size.height;
29064             var vw = this.viewSize[0], vh = this.viewSize[1];
29065             // only move it if it needs it
29066             var moved = false;
29067             // first validate right/bottom
29068             if(x + w > vw+s.left){
29069                 x = vw - w;
29070                 moved = true;
29071             }
29072             if(y + h > vh+s.top){
29073                 y = vh - h;
29074                 moved = true;
29075             }
29076             // then make sure top/left isn't negative
29077             if(x < s.left){
29078                 x = s.left;
29079                 moved = true;
29080             }
29081             if(y < s.top){
29082                 y = s.top;
29083                 moved = true;
29084             }
29085             if(moved){
29086                 // cache xy
29087                 this.xy = [x, y];
29088                 if(this.isVisible()){
29089                     this.el.setLocation(x, y);
29090                     this.adjustAssets();
29091                 }
29092             }
29093         }
29094     },
29095
29096     // private
29097     onDrag : function(){
29098         if(!this.proxyDrag){
29099             this.xy = this.el.getXY();
29100             this.adjustAssets();
29101         }
29102     },
29103
29104     // private
29105     adjustAssets : function(doShow){
29106         var x = this.xy[0], y = this.xy[1];
29107         var w = this.size.width, h = this.size.height;
29108         if(doShow === true){
29109             if(this.shadow){
29110                 this.shadow.show(this.el);
29111             }
29112             if(this.shim){
29113                 this.shim.show();
29114             }
29115         }
29116         if(this.shadow && this.shadow.isVisible()){
29117             this.shadow.show(this.el);
29118         }
29119         if(this.shim && this.shim.isVisible()){
29120             this.shim.setBounds(x, y, w, h);
29121         }
29122     },
29123
29124     // private
29125     adjustViewport : function(w, h){
29126         if(!w || !h){
29127             w = Roo.lib.Dom.getViewWidth();
29128             h = Roo.lib.Dom.getViewHeight();
29129         }
29130         // cache the size
29131         this.viewSize = [w, h];
29132         if(this.modal && this.mask.isVisible()){
29133             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29134             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29135         }
29136         if(this.isVisible()){
29137             this.constrainXY();
29138         }
29139     },
29140
29141     /**
29142      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29143      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29144      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29145      */
29146     destroy : function(removeEl){
29147         if(this.isVisible()){
29148             this.animateTarget = null;
29149             this.hide();
29150         }
29151         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29152         if(this.tabs){
29153             this.tabs.destroy(removeEl);
29154         }
29155         Roo.destroy(
29156              this.shim,
29157              this.proxy,
29158              this.resizer,
29159              this.close,
29160              this.mask
29161         );
29162         if(this.dd){
29163             this.dd.unreg();
29164         }
29165         if(this.buttons){
29166            for(var i = 0, len = this.buttons.length; i < len; i++){
29167                this.buttons[i].destroy();
29168            }
29169         }
29170         this.el.removeAllListeners();
29171         if(removeEl === true){
29172             this.el.update("");
29173             this.el.remove();
29174         }
29175         Roo.DialogManager.unregister(this);
29176     },
29177
29178     // private
29179     startMove : function(){
29180         if(this.proxyDrag){
29181             this.proxy.show();
29182         }
29183         if(this.constraintoviewport !== false){
29184             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29185         }
29186     },
29187
29188     // private
29189     endMove : function(){
29190         if(!this.proxyDrag){
29191             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29192         }else{
29193             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29194             this.proxy.hide();
29195         }
29196         this.refreshSize();
29197         this.adjustAssets();
29198         this.focus();
29199         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29200     },
29201
29202     /**
29203      * Brings this dialog to the front of any other visible dialogs
29204      * @return {Roo.BasicDialog} this
29205      */
29206     toFront : function(){
29207         Roo.DialogManager.bringToFront(this);
29208         return this;
29209     },
29210
29211     /**
29212      * Sends this dialog to the back (under) of any other visible dialogs
29213      * @return {Roo.BasicDialog} this
29214      */
29215     toBack : function(){
29216         Roo.DialogManager.sendToBack(this);
29217         return this;
29218     },
29219
29220     /**
29221      * Centers this dialog in the viewport
29222      * @return {Roo.BasicDialog} this
29223      */
29224     center : function(){
29225         var xy = this.el.getCenterXY(true);
29226         this.moveTo(xy[0], xy[1]);
29227         return this;
29228     },
29229
29230     /**
29231      * Moves the dialog's top-left corner to the specified point
29232      * @param {Number} x
29233      * @param {Number} y
29234      * @return {Roo.BasicDialog} this
29235      */
29236     moveTo : function(x, y){
29237         this.xy = [x,y];
29238         if(this.isVisible()){
29239             this.el.setXY(this.xy);
29240             this.adjustAssets();
29241         }
29242         return this;
29243     },
29244
29245     /**
29246      * Aligns the dialog to the specified element
29247      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29248      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29249      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29250      * @return {Roo.BasicDialog} this
29251      */
29252     alignTo : function(element, position, offsets){
29253         this.xy = this.el.getAlignToXY(element, position, offsets);
29254         if(this.isVisible()){
29255             this.el.setXY(this.xy);
29256             this.adjustAssets();
29257         }
29258         return this;
29259     },
29260
29261     /**
29262      * Anchors an element to another element and realigns it when the window is resized.
29263      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29264      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29265      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29266      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29267      * is a number, it is used as the buffer delay (defaults to 50ms).
29268      * @return {Roo.BasicDialog} this
29269      */
29270     anchorTo : function(el, alignment, offsets, monitorScroll){
29271         var action = function(){
29272             this.alignTo(el, alignment, offsets);
29273         };
29274         Roo.EventManager.onWindowResize(action, this);
29275         var tm = typeof monitorScroll;
29276         if(tm != 'undefined'){
29277             Roo.EventManager.on(window, 'scroll', action, this,
29278                 {buffer: tm == 'number' ? monitorScroll : 50});
29279         }
29280         action.call(this);
29281         return this;
29282     },
29283
29284     /**
29285      * Returns true if the dialog is visible
29286      * @return {Boolean}
29287      */
29288     isVisible : function(){
29289         return this.el.isVisible();
29290     },
29291
29292     // private
29293     animHide : function(callback){
29294         var b = Roo.get(this.animateTarget).getBox();
29295         this.proxy.show();
29296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29297         this.el.hide();
29298         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29299                     this.hideEl.createDelegate(this, [callback]));
29300     },
29301
29302     /**
29303      * Hides the dialog.
29304      * @param {Function} callback (optional) Function to call when the dialog is hidden
29305      * @return {Roo.BasicDialog} this
29306      */
29307     hide : function(callback){
29308         if (this.fireEvent("beforehide", this) === false){
29309             return;
29310         }
29311         if(this.shadow){
29312             this.shadow.hide();
29313         }
29314         if(this.shim) {
29315           this.shim.hide();
29316         }
29317         // sometimes animateTarget seems to get set.. causing problems...
29318         // this just double checks..
29319         if(this.animateTarget && Roo.get(this.animateTarget)) {
29320            this.animHide(callback);
29321         }else{
29322             this.el.hide();
29323             this.hideEl(callback);
29324         }
29325         return this;
29326     },
29327
29328     // private
29329     hideEl : function(callback){
29330         this.proxy.hide();
29331         if(this.modal){
29332             this.mask.hide();
29333             Roo.get(document.body).removeClass("x-body-masked");
29334         }
29335         this.fireEvent("hide", this);
29336         if(typeof callback == "function"){
29337             callback();
29338         }
29339     },
29340
29341     // private
29342     hideAction : function(){
29343         this.setLeft("-10000px");
29344         this.setTop("-10000px");
29345         this.setStyle("visibility", "hidden");
29346     },
29347
29348     // private
29349     refreshSize : function(){
29350         this.size = this.el.getSize();
29351         this.xy = this.el.getXY();
29352         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29353     },
29354
29355     // private
29356     // z-index is managed by the DialogManager and may be overwritten at any time
29357     setZIndex : function(index){
29358         if(this.modal){
29359             this.mask.setStyle("z-index", index);
29360         }
29361         if(this.shim){
29362             this.shim.setStyle("z-index", ++index);
29363         }
29364         if(this.shadow){
29365             this.shadow.setZIndex(++index);
29366         }
29367         this.el.setStyle("z-index", ++index);
29368         if(this.proxy){
29369             this.proxy.setStyle("z-index", ++index);
29370         }
29371         if(this.resizer){
29372             this.resizer.proxy.setStyle("z-index", ++index);
29373         }
29374
29375         this.lastZIndex = index;
29376     },
29377
29378     /**
29379      * Returns the element for this dialog
29380      * @return {Roo.Element} The underlying dialog Element
29381      */
29382     getEl : function(){
29383         return this.el;
29384     }
29385 });
29386
29387 /**
29388  * @class Roo.DialogManager
29389  * Provides global access to BasicDialogs that have been created and
29390  * support for z-indexing (layering) multiple open dialogs.
29391  */
29392 Roo.DialogManager = function(){
29393     var list = {};
29394     var accessList = [];
29395     var front = null;
29396
29397     // private
29398     var sortDialogs = function(d1, d2){
29399         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29400     };
29401
29402     // private
29403     var orderDialogs = function(){
29404         accessList.sort(sortDialogs);
29405         var seed = Roo.DialogManager.zseed;
29406         for(var i = 0, len = accessList.length; i < len; i++){
29407             var dlg = accessList[i];
29408             if(dlg){
29409                 dlg.setZIndex(seed + (i*10));
29410             }
29411         }
29412     };
29413
29414     return {
29415         /**
29416          * The starting z-index for BasicDialogs (defaults to 9000)
29417          * @type Number The z-index value
29418          */
29419         zseed : 9000,
29420
29421         // private
29422         register : function(dlg){
29423             list[dlg.id] = dlg;
29424             accessList.push(dlg);
29425         },
29426
29427         // private
29428         unregister : function(dlg){
29429             delete list[dlg.id];
29430             var i=0;
29431             var len=0;
29432             if(!accessList.indexOf){
29433                 for(  i = 0, len = accessList.length; i < len; i++){
29434                     if(accessList[i] == dlg){
29435                         accessList.splice(i, 1);
29436                         return;
29437                     }
29438                 }
29439             }else{
29440                  i = accessList.indexOf(dlg);
29441                 if(i != -1){
29442                     accessList.splice(i, 1);
29443                 }
29444             }
29445         },
29446
29447         /**
29448          * Gets a registered dialog by id
29449          * @param {String/Object} id The id of the dialog or a dialog
29450          * @return {Roo.BasicDialog} this
29451          */
29452         get : function(id){
29453             return typeof id == "object" ? id : list[id];
29454         },
29455
29456         /**
29457          * Brings the specified dialog to the front
29458          * @param {String/Object} dlg The id of the dialog or a dialog
29459          * @return {Roo.BasicDialog} this
29460          */
29461         bringToFront : function(dlg){
29462             dlg = this.get(dlg);
29463             if(dlg != front){
29464                 front = dlg;
29465                 dlg._lastAccess = new Date().getTime();
29466                 orderDialogs();
29467             }
29468             return dlg;
29469         },
29470
29471         /**
29472          * Sends the specified dialog to the back
29473          * @param {String/Object} dlg The id of the dialog or a dialog
29474          * @return {Roo.BasicDialog} this
29475          */
29476         sendToBack : function(dlg){
29477             dlg = this.get(dlg);
29478             dlg._lastAccess = -(new Date().getTime());
29479             orderDialogs();
29480             return dlg;
29481         },
29482
29483         /**
29484          * Hides all dialogs
29485          */
29486         hideAll : function(){
29487             for(var id in list){
29488                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29489                     list[id].hide();
29490                 }
29491             }
29492         }
29493     };
29494 }();
29495
29496 /**
29497  * @class Roo.LayoutDialog
29498  * @extends Roo.BasicDialog
29499  * Dialog which provides adjustments for working with a layout in a Dialog.
29500  * Add your necessary layout config options to the dialog's config.<br>
29501  * Example usage (including a nested layout):
29502  * <pre><code>
29503 if(!dialog){
29504     dialog = new Roo.LayoutDialog("download-dlg", {
29505         modal: true,
29506         width:600,
29507         height:450,
29508         shadow:true,
29509         minWidth:500,
29510         minHeight:350,
29511         autoTabs:true,
29512         proxyDrag:true,
29513         // layout config merges with the dialog config
29514         center:{
29515             tabPosition: "top",
29516             alwaysShowTabs: true
29517         }
29518     });
29519     dialog.addKeyListener(27, dialog.hide, dialog);
29520     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29521     dialog.addButton("Build It!", this.getDownload, this);
29522
29523     // we can even add nested layouts
29524     var innerLayout = new Roo.BorderLayout("dl-inner", {
29525         east: {
29526             initialSize: 200,
29527             autoScroll:true,
29528             split:true
29529         },
29530         center: {
29531             autoScroll:true
29532         }
29533     });
29534     innerLayout.beginUpdate();
29535     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29536     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29537     innerLayout.endUpdate(true);
29538
29539     var layout = dialog.getLayout();
29540     layout.beginUpdate();
29541     layout.add("center", new Roo.ContentPanel("standard-panel",
29542                         {title: "Download the Source", fitToFrame:true}));
29543     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29544                {title: "Build your own roo.js"}));
29545     layout.getRegion("center").showPanel(sp);
29546     layout.endUpdate();
29547 }
29548 </code></pre>
29549     * @constructor
29550     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29551     * @param {Object} config configuration options
29552   */
29553 Roo.LayoutDialog = function(el, cfg){
29554     
29555     var config=  cfg;
29556     if (typeof(cfg) == 'undefined') {
29557         config = Roo.apply({}, el);
29558         // not sure why we use documentElement here.. - it should always be body.
29559         // IE7 borks horribly if we use documentElement.
29560         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29561         //config.autoCreate = true;
29562     }
29563     
29564     
29565     config.autoTabs = false;
29566     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29567     this.body.setStyle({overflow:"hidden", position:"relative"});
29568     this.layout = new Roo.BorderLayout(this.body.dom, config);
29569     this.layout.monitorWindowResize = false;
29570     this.el.addClass("x-dlg-auto-layout");
29571     // fix case when center region overwrites center function
29572     this.center = Roo.BasicDialog.prototype.center;
29573     this.on("show", this.layout.layout, this.layout, true);
29574     if (config.items) {
29575         var xitems = config.items;
29576         delete config.items;
29577         Roo.each(xitems, this.addxtype, this);
29578     }
29579     
29580     
29581 };
29582 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29583     /**
29584      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29585      * @deprecated
29586      */
29587     endUpdate : function(){
29588         this.layout.endUpdate();
29589     },
29590
29591     /**
29592      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29593      *  @deprecated
29594      */
29595     beginUpdate : function(){
29596         this.layout.beginUpdate();
29597     },
29598
29599     /**
29600      * Get the BorderLayout for this dialog
29601      * @return {Roo.BorderLayout}
29602      */
29603     getLayout : function(){
29604         return this.layout;
29605     },
29606
29607     showEl : function(){
29608         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29609         if(Roo.isIE7){
29610             this.layout.layout();
29611         }
29612     },
29613
29614     // private
29615     // Use the syncHeightBeforeShow config option to control this automatically
29616     syncBodyHeight : function(){
29617         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29618         if(this.layout){this.layout.layout();}
29619     },
29620     
29621       /**
29622      * Add an xtype element (actually adds to the layout.)
29623      * @return {Object} xdata xtype object data.
29624      */
29625     
29626     addxtype : function(c) {
29627         return this.layout.addxtype(c);
29628     }
29629 });/*
29630  * Based on:
29631  * Ext JS Library 1.1.1
29632  * Copyright(c) 2006-2007, Ext JS, LLC.
29633  *
29634  * Originally Released Under LGPL - original licence link has changed is not relivant.
29635  *
29636  * Fork - LGPL
29637  * <script type="text/javascript">
29638  */
29639  
29640 /**
29641  * @class Roo.MessageBox
29642  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29643  * Example usage:
29644  *<pre><code>
29645 // Basic alert:
29646 Roo.Msg.alert('Status', 'Changes saved successfully.');
29647
29648 // Prompt for user data:
29649 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29650     if (btn == 'ok'){
29651         // process text value...
29652     }
29653 });
29654
29655 // Show a dialog using config options:
29656 Roo.Msg.show({
29657    title:'Save Changes?',
29658    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29659    buttons: Roo.Msg.YESNOCANCEL,
29660    fn: processResult,
29661    animEl: 'elId'
29662 });
29663 </code></pre>
29664  * @singleton
29665  */
29666 Roo.MessageBox = function(){
29667     var dlg, opt, mask, waitTimer;
29668     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29669     var buttons, activeTextEl, bwidth;
29670
29671     // private
29672     var handleButton = function(button){
29673         dlg.hide();
29674         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29675     };
29676
29677     // private
29678     var handleHide = function(){
29679         if(opt && opt.cls){
29680             dlg.el.removeClass(opt.cls);
29681         }
29682         if(waitTimer){
29683             Roo.TaskMgr.stop(waitTimer);
29684             waitTimer = null;
29685         }
29686     };
29687
29688     // private
29689     var updateButtons = function(b){
29690         var width = 0;
29691         if(!b){
29692             buttons["ok"].hide();
29693             buttons["cancel"].hide();
29694             buttons["yes"].hide();
29695             buttons["no"].hide();
29696             dlg.footer.dom.style.display = 'none';
29697             return width;
29698         }
29699         dlg.footer.dom.style.display = '';
29700         for(var k in buttons){
29701             if(typeof buttons[k] != "function"){
29702                 if(b[k]){
29703                     buttons[k].show();
29704                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29705                     width += buttons[k].el.getWidth()+15;
29706                 }else{
29707                     buttons[k].hide();
29708                 }
29709             }
29710         }
29711         return width;
29712     };
29713
29714     // private
29715     var handleEsc = function(d, k, e){
29716         if(opt && opt.closable !== false){
29717             dlg.hide();
29718         }
29719         if(e){
29720             e.stopEvent();
29721         }
29722     };
29723
29724     return {
29725         /**
29726          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29727          * @return {Roo.BasicDialog} The BasicDialog element
29728          */
29729         getDialog : function(){
29730            if(!dlg){
29731                 dlg = new Roo.BasicDialog("x-msg-box", {
29732                     autoCreate : true,
29733                     shadow: true,
29734                     draggable: true,
29735                     resizable:false,
29736                     constraintoviewport:false,
29737                     fixedcenter:true,
29738                     collapsible : false,
29739                     shim:true,
29740                     modal: true,
29741                     width:400, height:100,
29742                     buttonAlign:"center",
29743                     closeClick : function(){
29744                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29745                             handleButton("no");
29746                         }else{
29747                             handleButton("cancel");
29748                         }
29749                     }
29750                 });
29751                 dlg.on("hide", handleHide);
29752                 mask = dlg.mask;
29753                 dlg.addKeyListener(27, handleEsc);
29754                 buttons = {};
29755                 var bt = this.buttonText;
29756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29760                 bodyEl = dlg.body.createChild({
29761
29762                     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>'
29763                 });
29764                 msgEl = bodyEl.dom.firstChild;
29765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29766                 textboxEl.enableDisplayMode();
29767                 textboxEl.addKeyListener([10,13], function(){
29768                     if(dlg.isVisible() && opt && opt.buttons){
29769                         if(opt.buttons.ok){
29770                             handleButton("ok");
29771                         }else if(opt.buttons.yes){
29772                             handleButton("yes");
29773                         }
29774                     }
29775                 });
29776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29777                 textareaEl.enableDisplayMode();
29778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29779                 progressEl.enableDisplayMode();
29780                 var pf = progressEl.dom.firstChild;
29781                 if (pf) {
29782                     pp = Roo.get(pf.firstChild);
29783                     pp.setHeight(pf.offsetHeight);
29784                 }
29785                 
29786             }
29787             return dlg;
29788         },
29789
29790         /**
29791          * Updates the message box body text
29792          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29793          * the XHTML-compliant non-breaking space character '&amp;#160;')
29794          * @return {Roo.MessageBox} This message box
29795          */
29796         updateText : function(text){
29797             if(!dlg.isVisible() && !opt.width){
29798                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29799             }
29800             msgEl.innerHTML = text || '&#160;';
29801             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29802                         Math.max(opt.minWidth || this.minWidth, bwidth));
29803             if(opt.prompt){
29804                 activeTextEl.setWidth(w);
29805             }
29806             if(dlg.isVisible()){
29807                 dlg.fixedcenter = false;
29808             }
29809             dlg.setContentSize(w, bodyEl.getHeight());
29810             if(dlg.isVisible()){
29811                 dlg.fixedcenter = true;
29812             }
29813             return this;
29814         },
29815
29816         /**
29817          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29818          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29819          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29820          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29821          * @return {Roo.MessageBox} This message box
29822          */
29823         updateProgress : function(value, text){
29824             if(text){
29825                 this.updateText(text);
29826             }
29827             if (pp) { // weird bug on my firefox - for some reason this is not defined
29828                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29829             }
29830             return this;
29831         },        
29832
29833         /**
29834          * Returns true if the message box is currently displayed
29835          * @return {Boolean} True if the message box is visible, else false
29836          */
29837         isVisible : function(){
29838             return dlg && dlg.isVisible();  
29839         },
29840
29841         /**
29842          * Hides the message box if it is displayed
29843          */
29844         hide : function(){
29845             if(this.isVisible()){
29846                 dlg.hide();
29847             }  
29848         },
29849
29850         /**
29851          * Displays a new message box, or reinitializes an existing message box, based on the config options
29852          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29853          * The following config object properties are supported:
29854          * <pre>
29855 Property    Type             Description
29856 ----------  ---------------  ------------------------------------------------------------------------------------
29857 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29858                                    closes (defaults to undefined)
29859 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29860                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29861 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29862                                    progress and wait dialogs will ignore this property and always hide the
29863                                    close button as they can only be closed programmatically.
29864 cls               String           A custom CSS class to apply to the message box element
29865 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29866                                    displayed (defaults to 75)
29867 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29868                                    function will be btn (the name of the button that was clicked, if applicable,
29869                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29870                                    Progress and wait dialogs will ignore this option since they do not respond to
29871                                    user actions and can only be closed programmatically, so any required function
29872                                    should be called by the same code after it closes the dialog.
29873 icon              String           A CSS class that provides a background image to be used as an icon for
29874                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29875 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29876 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29877 modal             Boolean          False to allow user interaction with the page while the message box is
29878                                    displayed (defaults to true)
29879 msg               String           A string that will replace the existing message box body text (defaults
29880                                    to the XHTML-compliant non-breaking space character '&#160;')
29881 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29882 progress          Boolean          True to display a progress bar (defaults to false)
29883 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29884 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29885 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29886 title             String           The title text
29887 value             String           The string value to set into the active textbox element if displayed
29888 wait              Boolean          True to display a progress bar (defaults to false)
29889 width             Number           The width of the dialog in pixels
29890 </pre>
29891          *
29892          * Example usage:
29893          * <pre><code>
29894 Roo.Msg.show({
29895    title: 'Address',
29896    msg: 'Please enter your address:',
29897    width: 300,
29898    buttons: Roo.MessageBox.OKCANCEL,
29899    multiline: true,
29900    fn: saveAddress,
29901    animEl: 'addAddressBtn'
29902 });
29903 </code></pre>
29904          * @param {Object} config Configuration options
29905          * @return {Roo.MessageBox} This message box
29906          */
29907         show : function(options){
29908             if(this.isVisible()){
29909                 this.hide();
29910             }
29911             var d = this.getDialog();
29912             opt = options;
29913             d.setTitle(opt.title || "&#160;");
29914             d.close.setDisplayed(opt.closable !== false);
29915             activeTextEl = textboxEl;
29916             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29917             if(opt.prompt){
29918                 if(opt.multiline){
29919                     textboxEl.hide();
29920                     textareaEl.show();
29921                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29922                         opt.multiline : this.defaultTextHeight);
29923                     activeTextEl = textareaEl;
29924                 }else{
29925                     textboxEl.show();
29926                     textareaEl.hide();
29927                 }
29928             }else{
29929                 textboxEl.hide();
29930                 textareaEl.hide();
29931             }
29932             progressEl.setDisplayed(opt.progress === true);
29933             this.updateProgress(0);
29934             activeTextEl.dom.value = opt.value || "";
29935             if(opt.prompt){
29936                 dlg.setDefaultButton(activeTextEl);
29937             }else{
29938                 var bs = opt.buttons;
29939                 var db = null;
29940                 if(bs && bs.ok){
29941                     db = buttons["ok"];
29942                 }else if(bs && bs.yes){
29943                     db = buttons["yes"];
29944                 }
29945                 dlg.setDefaultButton(db);
29946             }
29947             bwidth = updateButtons(opt.buttons);
29948             this.updateText(opt.msg);
29949             if(opt.cls){
29950                 d.el.addClass(opt.cls);
29951             }
29952             d.proxyDrag = opt.proxyDrag === true;
29953             d.modal = opt.modal !== false;
29954             d.mask = opt.modal !== false ? mask : false;
29955             if(!d.isVisible()){
29956                 // force it to the end of the z-index stack so it gets a cursor in FF
29957                 document.body.appendChild(dlg.el.dom);
29958                 d.animateTarget = null;
29959                 d.show(options.animEl);
29960             }
29961             return this;
29962         },
29963
29964         /**
29965          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29966          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29967          * and closing the message box when the process is complete.
29968          * @param {String} title The title bar text
29969          * @param {String} msg The message box body text
29970          * @return {Roo.MessageBox} This message box
29971          */
29972         progress : function(title, msg){
29973             this.show({
29974                 title : title,
29975                 msg : msg,
29976                 buttons: false,
29977                 progress:true,
29978                 closable:false,
29979                 minWidth: this.minProgressWidth,
29980                 modal : true
29981             });
29982             return this;
29983         },
29984
29985         /**
29986          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29987          * If a callback function is passed it will be called after the user clicks the button, and the
29988          * id of the button that was clicked will be passed as the only parameter to the callback
29989          * (could also be the top-right close button).
29990          * @param {String} title The title bar text
29991          * @param {String} msg The message box body text
29992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29993          * @param {Object} scope (optional) The scope of the callback function
29994          * @return {Roo.MessageBox} This message box
29995          */
29996         alert : function(title, msg, fn, scope){
29997             this.show({
29998                 title : title,
29999                 msg : msg,
30000                 buttons: this.OK,
30001                 fn: fn,
30002                 scope : scope,
30003                 modal : true
30004             });
30005             return this;
30006         },
30007
30008         /**
30009          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30010          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30011          * You are responsible for closing the message box when the process is complete.
30012          * @param {String} msg The message box body text
30013          * @param {String} title (optional) The title bar text
30014          * @return {Roo.MessageBox} This message box
30015          */
30016         wait : function(msg, title){
30017             this.show({
30018                 title : title,
30019                 msg : msg,
30020                 buttons: false,
30021                 closable:false,
30022                 progress:true,
30023                 modal:true,
30024                 width:300,
30025                 wait:true
30026             });
30027             waitTimer = Roo.TaskMgr.start({
30028                 run: function(i){
30029                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30030                 },
30031                 interval: 1000
30032             });
30033             return this;
30034         },
30035
30036         /**
30037          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30038          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30039          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30040          * @param {String} title The title bar text
30041          * @param {String} msg The message box body text
30042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30043          * @param {Object} scope (optional) The scope of the callback function
30044          * @return {Roo.MessageBox} This message box
30045          */
30046         confirm : function(title, msg, fn, scope){
30047             this.show({
30048                 title : title,
30049                 msg : msg,
30050                 buttons: this.YESNO,
30051                 fn: fn,
30052                 scope : scope,
30053                 modal : true
30054             });
30055             return this;
30056         },
30057
30058         /**
30059          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30060          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30061          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30062          * (could also be the top-right close button) and the text that was entered will be passed as the two
30063          * parameters to the callback.
30064          * @param {String} title The title bar text
30065          * @param {String} msg The message box body text
30066          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30067          * @param {Object} scope (optional) The scope of the callback function
30068          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30069          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30070          * @return {Roo.MessageBox} This message box
30071          */
30072         prompt : function(title, msg, fn, scope, multiline){
30073             this.show({
30074                 title : title,
30075                 msg : msg,
30076                 buttons: this.OKCANCEL,
30077                 fn: fn,
30078                 minWidth:250,
30079                 scope : scope,
30080                 prompt:true,
30081                 multiline: multiline,
30082                 modal : true
30083             });
30084             return this;
30085         },
30086
30087         /**
30088          * Button config that displays a single OK button
30089          * @type Object
30090          */
30091         OK : {ok:true},
30092         /**
30093          * Button config that displays Yes and No buttons
30094          * @type Object
30095          */
30096         YESNO : {yes:true, no:true},
30097         /**
30098          * Button config that displays OK and Cancel buttons
30099          * @type Object
30100          */
30101         OKCANCEL : {ok:true, cancel:true},
30102         /**
30103          * Button config that displays Yes, No and Cancel buttons
30104          * @type Object
30105          */
30106         YESNOCANCEL : {yes:true, no:true, cancel:true},
30107
30108         /**
30109          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30110          * @type Number
30111          */
30112         defaultTextHeight : 75,
30113         /**
30114          * The maximum width in pixels of the message box (defaults to 600)
30115          * @type Number
30116          */
30117         maxWidth : 600,
30118         /**
30119          * The minimum width in pixels of the message box (defaults to 100)
30120          * @type Number
30121          */
30122         minWidth : 100,
30123         /**
30124          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30125          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30126          * @type Number
30127          */
30128         minProgressWidth : 250,
30129         /**
30130          * An object containing the default button text strings that can be overriden for localized language support.
30131          * Supported properties are: ok, cancel, yes and no.
30132          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30133          * @type Object
30134          */
30135         buttonText : {
30136             ok : "OK",
30137             cancel : "Cancel",
30138             yes : "Yes",
30139             no : "No"
30140         }
30141     };
30142 }();
30143
30144 /**
30145  * Shorthand for {@link Roo.MessageBox}
30146  */
30147 Roo.Msg = Roo.MessageBox;/*
30148  * Based on:
30149  * Ext JS Library 1.1.1
30150  * Copyright(c) 2006-2007, Ext JS, LLC.
30151  *
30152  * Originally Released Under LGPL - original licence link has changed is not relivant.
30153  *
30154  * Fork - LGPL
30155  * <script type="text/javascript">
30156  */
30157 /**
30158  * @class Roo.QuickTips
30159  * Provides attractive and customizable tooltips for any element.
30160  * @singleton
30161  */
30162 Roo.QuickTips = function(){
30163     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30164     var ce, bd, xy, dd;
30165     var visible = false, disabled = true, inited = false;
30166     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30167     
30168     var onOver = function(e){
30169         if(disabled){
30170             return;
30171         }
30172         var t = e.getTarget();
30173         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30174             return;
30175         }
30176         if(ce && t == ce.el){
30177             clearTimeout(hideProc);
30178             return;
30179         }
30180         if(t && tagEls[t.id]){
30181             tagEls[t.id].el = t;
30182             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30183             return;
30184         }
30185         var ttp, et = Roo.fly(t);
30186         var ns = cfg.namespace;
30187         if(tm.interceptTitles && t.title){
30188             ttp = t.title;
30189             t.qtip = ttp;
30190             t.removeAttribute("title");
30191             e.preventDefault();
30192         }else{
30193             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30194         }
30195         if(ttp){
30196             showProc = show.defer(tm.showDelay, tm, [{
30197                 el: t, 
30198                 text: ttp, 
30199                 width: et.getAttributeNS(ns, cfg.width),
30200                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30201                 title: et.getAttributeNS(ns, cfg.title),
30202                     cls: et.getAttributeNS(ns, cfg.cls)
30203             }]);
30204         }
30205     };
30206     
30207     var onOut = function(e){
30208         clearTimeout(showProc);
30209         var t = e.getTarget();
30210         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30211             hideProc = setTimeout(hide, tm.hideDelay);
30212         }
30213     };
30214     
30215     var onMove = function(e){
30216         if(disabled){
30217             return;
30218         }
30219         xy = e.getXY();
30220         xy[1] += 18;
30221         if(tm.trackMouse && ce){
30222             el.setXY(xy);
30223         }
30224     };
30225     
30226     var onDown = function(e){
30227         clearTimeout(showProc);
30228         clearTimeout(hideProc);
30229         if(!e.within(el)){
30230             if(tm.hideOnClick){
30231                 hide();
30232                 tm.disable();
30233                 tm.enable.defer(100, tm);
30234             }
30235         }
30236     };
30237     
30238     var getPad = function(){
30239         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30240     };
30241
30242     var show = function(o){
30243         if(disabled){
30244             return;
30245         }
30246         clearTimeout(dismissProc);
30247         ce = o;
30248         if(removeCls){ // in case manually hidden
30249             el.removeClass(removeCls);
30250             removeCls = null;
30251         }
30252         if(ce.cls){
30253             el.addClass(ce.cls);
30254             removeCls = ce.cls;
30255         }
30256         if(ce.title){
30257             tipTitle.update(ce.title);
30258             tipTitle.show();
30259         }else{
30260             tipTitle.update('');
30261             tipTitle.hide();
30262         }
30263         el.dom.style.width  = tm.maxWidth+'px';
30264         //tipBody.dom.style.width = '';
30265         tipBodyText.update(o.text);
30266         var p = getPad(), w = ce.width;
30267         if(!w){
30268             var td = tipBodyText.dom;
30269             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30270             if(aw > tm.maxWidth){
30271                 w = tm.maxWidth;
30272             }else if(aw < tm.minWidth){
30273                 w = tm.minWidth;
30274             }else{
30275                 w = aw;
30276             }
30277         }
30278         //tipBody.setWidth(w);
30279         el.setWidth(parseInt(w, 10) + p);
30280         if(ce.autoHide === false){
30281             close.setDisplayed(true);
30282             if(dd){
30283                 dd.unlock();
30284             }
30285         }else{
30286             close.setDisplayed(false);
30287             if(dd){
30288                 dd.lock();
30289             }
30290         }
30291         if(xy){
30292             el.avoidY = xy[1]-18;
30293             el.setXY(xy);
30294         }
30295         if(tm.animate){
30296             el.setOpacity(.1);
30297             el.setStyle("visibility", "visible");
30298             el.fadeIn({callback: afterShow});
30299         }else{
30300             afterShow();
30301         }
30302     };
30303     
30304     var afterShow = function(){
30305         if(ce){
30306             el.show();
30307             esc.enable();
30308             if(tm.autoDismiss && ce.autoHide !== false){
30309                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30310             }
30311         }
30312     };
30313     
30314     var hide = function(noanim){
30315         clearTimeout(dismissProc);
30316         clearTimeout(hideProc);
30317         ce = null;
30318         if(el.isVisible()){
30319             esc.disable();
30320             if(noanim !== true && tm.animate){
30321                 el.fadeOut({callback: afterHide});
30322             }else{
30323                 afterHide();
30324             } 
30325         }
30326     };
30327     
30328     var afterHide = function(){
30329         el.hide();
30330         if(removeCls){
30331             el.removeClass(removeCls);
30332             removeCls = null;
30333         }
30334     };
30335     
30336     return {
30337         /**
30338         * @cfg {Number} minWidth
30339         * The minimum width of the quick tip (defaults to 40)
30340         */
30341        minWidth : 40,
30342         /**
30343         * @cfg {Number} maxWidth
30344         * The maximum width of the quick tip (defaults to 300)
30345         */
30346        maxWidth : 300,
30347         /**
30348         * @cfg {Boolean} interceptTitles
30349         * True to automatically use the element's DOM title value if available (defaults to false)
30350         */
30351        interceptTitles : false,
30352         /**
30353         * @cfg {Boolean} trackMouse
30354         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30355         */
30356        trackMouse : false,
30357         /**
30358         * @cfg {Boolean} hideOnClick
30359         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30360         */
30361        hideOnClick : true,
30362         /**
30363         * @cfg {Number} showDelay
30364         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30365         */
30366        showDelay : 500,
30367         /**
30368         * @cfg {Number} hideDelay
30369         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30370         */
30371        hideDelay : 200,
30372         /**
30373         * @cfg {Boolean} autoHide
30374         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30375         * Used in conjunction with hideDelay.
30376         */
30377        autoHide : true,
30378         /**
30379         * @cfg {Boolean}
30380         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30381         * (defaults to true).  Used in conjunction with autoDismissDelay.
30382         */
30383        autoDismiss : true,
30384         /**
30385         * @cfg {Number}
30386         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30387         */
30388        autoDismissDelay : 5000,
30389        /**
30390         * @cfg {Boolean} animate
30391         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30392         */
30393        animate : false,
30394
30395        /**
30396         * @cfg {String} title
30397         * Title text to display (defaults to '').  This can be any valid HTML markup.
30398         */
30399         title: '',
30400        /**
30401         * @cfg {String} text
30402         * Body text to display (defaults to '').  This can be any valid HTML markup.
30403         */
30404         text : '',
30405        /**
30406         * @cfg {String} cls
30407         * A CSS class to apply to the base quick tip element (defaults to '').
30408         */
30409         cls : '',
30410        /**
30411         * @cfg {Number} width
30412         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30413         * minWidth or maxWidth.
30414         */
30415         width : null,
30416
30417     /**
30418      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30419      * or display QuickTips in a page.
30420      */
30421        init : function(){
30422           tm = Roo.QuickTips;
30423           cfg = tm.tagConfig;
30424           if(!inited){
30425               if(!Roo.isReady){ // allow calling of init() before onReady
30426                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30427                   return;
30428               }
30429               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30430               el.fxDefaults = {stopFx: true};
30431               // maximum custom styling
30432               //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>');
30433               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>');              
30434               tipTitle = el.child('h3');
30435               tipTitle.enableDisplayMode("block");
30436               tipBody = el.child('div.x-tip-bd');
30437               tipBodyText = el.child('div.x-tip-bd-inner');
30438               //bdLeft = el.child('div.x-tip-bd-left');
30439               //bdRight = el.child('div.x-tip-bd-right');
30440               close = el.child('div.x-tip-close');
30441               close.enableDisplayMode("block");
30442               close.on("click", hide);
30443               var d = Roo.get(document);
30444               d.on("mousedown", onDown);
30445               d.on("mouseover", onOver);
30446               d.on("mouseout", onOut);
30447               d.on("mousemove", onMove);
30448               esc = d.addKeyListener(27, hide);
30449               esc.disable();
30450               if(Roo.dd.DD){
30451                   dd = el.initDD("default", null, {
30452                       onDrag : function(){
30453                           el.sync();  
30454                       }
30455                   });
30456                   dd.setHandleElId(tipTitle.id);
30457                   dd.lock();
30458               }
30459               inited = true;
30460           }
30461           this.enable(); 
30462        },
30463
30464     /**
30465      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30466      * are supported:
30467      * <pre>
30468 Property    Type                   Description
30469 ----------  ---------------------  ------------------------------------------------------------------------
30470 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30471      * </ul>
30472      * @param {Object} config The config object
30473      */
30474        register : function(config){
30475            var cs = config instanceof Array ? config : arguments;
30476            for(var i = 0, len = cs.length; i < len; i++) {
30477                var c = cs[i];
30478                var target = c.target;
30479                if(target){
30480                    if(target instanceof Array){
30481                        for(var j = 0, jlen = target.length; j < jlen; j++){
30482                            tagEls[target[j]] = c;
30483                        }
30484                    }else{
30485                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30486                    }
30487                }
30488            }
30489        },
30490
30491     /**
30492      * Removes this quick tip from its element and destroys it.
30493      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30494      */
30495        unregister : function(el){
30496            delete tagEls[Roo.id(el)];
30497        },
30498
30499     /**
30500      * Enable this quick tip.
30501      */
30502        enable : function(){
30503            if(inited && disabled){
30504                locks.pop();
30505                if(locks.length < 1){
30506                    disabled = false;
30507                }
30508            }
30509        },
30510
30511     /**
30512      * Disable this quick tip.
30513      */
30514        disable : function(){
30515           disabled = true;
30516           clearTimeout(showProc);
30517           clearTimeout(hideProc);
30518           clearTimeout(dismissProc);
30519           if(ce){
30520               hide(true);
30521           }
30522           locks.push(1);
30523        },
30524
30525     /**
30526      * Returns true if the quick tip is enabled, else false.
30527      */
30528        isEnabled : function(){
30529             return !disabled;
30530        },
30531
30532         // private
30533        tagConfig : {
30534            namespace : "ext",
30535            attribute : "qtip",
30536            width : "width",
30537            target : "target",
30538            title : "qtitle",
30539            hide : "hide",
30540            cls : "qclass"
30541        }
30542    };
30543 }();
30544
30545 // backwards compat
30546 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30547  * Based on:
30548  * Ext JS Library 1.1.1
30549  * Copyright(c) 2006-2007, Ext JS, LLC.
30550  *
30551  * Originally Released Under LGPL - original licence link has changed is not relivant.
30552  *
30553  * Fork - LGPL
30554  * <script type="text/javascript">
30555  */
30556  
30557
30558 /**
30559  * @class Roo.tree.TreePanel
30560  * @extends Roo.data.Tree
30561
30562  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30563  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30564  * @cfg {Boolean} enableDD true to enable drag and drop
30565  * @cfg {Boolean} enableDrag true to enable just drag
30566  * @cfg {Boolean} enableDrop true to enable just drop
30567  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30568  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30569  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30570  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30571  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30572  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30573  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30574  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30575  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30576  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30577  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30578  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30579  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30580  * @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>
30581  * @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>
30582  * 
30583  * @constructor
30584  * @param {String/HTMLElement/Element} el The container element
30585  * @param {Object} config
30586  */
30587 Roo.tree.TreePanel = function(el, config){
30588     var root = false;
30589     var loader = false;
30590     if (config.root) {
30591         root = config.root;
30592         delete config.root;
30593     }
30594     if (config.loader) {
30595         loader = config.loader;
30596         delete config.loader;
30597     }
30598     
30599     Roo.apply(this, config);
30600     Roo.tree.TreePanel.superclass.constructor.call(this);
30601     this.el = Roo.get(el);
30602     this.el.addClass('x-tree');
30603     //console.log(root);
30604     if (root) {
30605         this.setRootNode( Roo.factory(root, Roo.tree));
30606     }
30607     if (loader) {
30608         this.loader = Roo.factory(loader, Roo.tree);
30609     }
30610    /**
30611     * Read-only. The id of the container element becomes this TreePanel's id.
30612     */
30613    this.id = this.el.id;
30614    this.addEvents({
30615         /**
30616         * @event beforeload
30617         * Fires before a node is loaded, return false to cancel
30618         * @param {Node} node The node being loaded
30619         */
30620         "beforeload" : true,
30621         /**
30622         * @event load
30623         * Fires when a node is loaded
30624         * @param {Node} node The node that was loaded
30625         */
30626         "load" : true,
30627         /**
30628         * @event textchange
30629         * Fires when the text for a node is changed
30630         * @param {Node} node The node
30631         * @param {String} text The new text
30632         * @param {String} oldText The old text
30633         */
30634         "textchange" : true,
30635         /**
30636         * @event beforeexpand
30637         * Fires before a node is expanded, return false to cancel.
30638         * @param {Node} node The node
30639         * @param {Boolean} deep
30640         * @param {Boolean} anim
30641         */
30642         "beforeexpand" : true,
30643         /**
30644         * @event beforecollapse
30645         * Fires before a node is collapsed, return false to cancel.
30646         * @param {Node} node The node
30647         * @param {Boolean} deep
30648         * @param {Boolean} anim
30649         */
30650         "beforecollapse" : true,
30651         /**
30652         * @event expand
30653         * Fires when a node is expanded
30654         * @param {Node} node The node
30655         */
30656         "expand" : true,
30657         /**
30658         * @event disabledchange
30659         * Fires when the disabled status of a node changes
30660         * @param {Node} node The node
30661         * @param {Boolean} disabled
30662         */
30663         "disabledchange" : true,
30664         /**
30665         * @event collapse
30666         * Fires when a node is collapsed
30667         * @param {Node} node The node
30668         */
30669         "collapse" : true,
30670         /**
30671         * @event beforeclick
30672         * Fires before click processing on a node. Return false to cancel the default action.
30673         * @param {Node} node The node
30674         * @param {Roo.EventObject} e The event object
30675         */
30676         "beforeclick":true,
30677         /**
30678         * @event checkchange
30679         * Fires when a node with a checkbox's checked property changes
30680         * @param {Node} this This node
30681         * @param {Boolean} checked
30682         */
30683         "checkchange":true,
30684         /**
30685         * @event click
30686         * Fires when a node is clicked
30687         * @param {Node} node The node
30688         * @param {Roo.EventObject} e The event object
30689         */
30690         "click":true,
30691         /**
30692         * @event dblclick
30693         * Fires when a node is double clicked
30694         * @param {Node} node The node
30695         * @param {Roo.EventObject} e The event object
30696         */
30697         "dblclick":true,
30698         /**
30699         * @event contextmenu
30700         * Fires when a node is right clicked
30701         * @param {Node} node The node
30702         * @param {Roo.EventObject} e The event object
30703         */
30704         "contextmenu":true,
30705         /**
30706         * @event beforechildrenrendered
30707         * Fires right before the child nodes for a node are rendered
30708         * @param {Node} node The node
30709         */
30710         "beforechildrenrendered":true,
30711        /**
30712              * @event startdrag
30713              * Fires when a node starts being dragged
30714              * @param {Roo.tree.TreePanel} this
30715              * @param {Roo.tree.TreeNode} node
30716              * @param {event} e The raw browser event
30717              */ 
30718             "startdrag" : true,
30719             /**
30720              * @event enddrag
30721              * Fires when a drag operation is complete
30722              * @param {Roo.tree.TreePanel} this
30723              * @param {Roo.tree.TreeNode} node
30724              * @param {event} e The raw browser event
30725              */
30726             "enddrag" : true,
30727             /**
30728              * @event dragdrop
30729              * Fires when a dragged node is dropped on a valid DD target
30730              * @param {Roo.tree.TreePanel} this
30731              * @param {Roo.tree.TreeNode} node
30732              * @param {DD} dd The dd it was dropped on
30733              * @param {event} e The raw browser event
30734              */
30735             "dragdrop" : true,
30736             /**
30737              * @event beforenodedrop
30738              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30739              * passed to handlers has the following properties:<br />
30740              * <ul style="padding:5px;padding-left:16px;">
30741              * <li>tree - The TreePanel</li>
30742              * <li>target - The node being targeted for the drop</li>
30743              * <li>data - The drag data from the drag source</li>
30744              * <li>point - The point of the drop - append, above or below</li>
30745              * <li>source - The drag source</li>
30746              * <li>rawEvent - Raw mouse event</li>
30747              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30748              * to be inserted by setting them on this object.</li>
30749              * <li>cancel - Set this to true to cancel the drop.</li>
30750              * </ul>
30751              * @param {Object} dropEvent
30752              */
30753             "beforenodedrop" : true,
30754             /**
30755              * @event nodedrop
30756              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30757              * passed to handlers has the following properties:<br />
30758              * <ul style="padding:5px;padding-left:16px;">
30759              * <li>tree - The TreePanel</li>
30760              * <li>target - The node being targeted for the drop</li>
30761              * <li>data - The drag data from the drag source</li>
30762              * <li>point - The point of the drop - append, above or below</li>
30763              * <li>source - The drag source</li>
30764              * <li>rawEvent - Raw mouse event</li>
30765              * <li>dropNode - Dropped node(s).</li>
30766              * </ul>
30767              * @param {Object} dropEvent
30768              */
30769             "nodedrop" : true,
30770              /**
30771              * @event nodedragover
30772              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30773              * passed to handlers has the following properties:<br />
30774              * <ul style="padding:5px;padding-left:16px;">
30775              * <li>tree - The TreePanel</li>
30776              * <li>target - The node being targeted for the drop</li>
30777              * <li>data - The drag data from the drag source</li>
30778              * <li>point - The point of the drop - append, above or below</li>
30779              * <li>source - The drag source</li>
30780              * <li>rawEvent - Raw mouse event</li>
30781              * <li>dropNode - Drop node(s) provided by the source.</li>
30782              * <li>cancel - Set this to true to signal drop not allowed.</li>
30783              * </ul>
30784              * @param {Object} dragOverEvent
30785              */
30786             "nodedragover" : true
30787         
30788    });
30789    if(this.singleExpand){
30790        this.on("beforeexpand", this.restrictExpand, this);
30791    }
30792 };
30793 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30794     rootVisible : true,
30795     animate: Roo.enableFx,
30796     lines : true,
30797     enableDD : false,
30798     hlDrop : Roo.enableFx,
30799   
30800     renderer: false,
30801     
30802     rendererTip: false,
30803     // private
30804     restrictExpand : function(node){
30805         var p = node.parentNode;
30806         if(p){
30807             if(p.expandedChild && p.expandedChild.parentNode == p){
30808                 p.expandedChild.collapse();
30809             }
30810             p.expandedChild = node;
30811         }
30812     },
30813
30814     // private override
30815     setRootNode : function(node){
30816         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30817         if(!this.rootVisible){
30818             node.ui = new Roo.tree.RootTreeNodeUI(node);
30819         }
30820         return node;
30821     },
30822
30823     /**
30824      * Returns the container element for this TreePanel
30825      */
30826     getEl : function(){
30827         return this.el;
30828     },
30829
30830     /**
30831      * Returns the default TreeLoader for this TreePanel
30832      */
30833     getLoader : function(){
30834         return this.loader;
30835     },
30836
30837     /**
30838      * Expand all nodes
30839      */
30840     expandAll : function(){
30841         this.root.expand(true);
30842     },
30843
30844     /**
30845      * Collapse all nodes
30846      */
30847     collapseAll : function(){
30848         this.root.collapse(true);
30849     },
30850
30851     /**
30852      * Returns the selection model used by this TreePanel
30853      */
30854     getSelectionModel : function(){
30855         if(!this.selModel){
30856             this.selModel = new Roo.tree.DefaultSelectionModel();
30857         }
30858         return this.selModel;
30859     },
30860
30861     /**
30862      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30863      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30864      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30865      * @return {Array}
30866      */
30867     getChecked : function(a, startNode){
30868         startNode = startNode || this.root;
30869         var r = [];
30870         var f = function(){
30871             if(this.attributes.checked){
30872                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30873             }
30874         }
30875         startNode.cascade(f);
30876         return r;
30877     },
30878
30879     /**
30880      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30881      * @param {String} path
30882      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30883      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30884      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30885      */
30886     expandPath : function(path, attr, callback){
30887         attr = attr || "id";
30888         var keys = path.split(this.pathSeparator);
30889         var curNode = this.root;
30890         if(curNode.attributes[attr] != keys[1]){ // invalid root
30891             if(callback){
30892                 callback(false, null);
30893             }
30894             return;
30895         }
30896         var index = 1;
30897         var f = function(){
30898             if(++index == keys.length){
30899                 if(callback){
30900                     callback(true, curNode);
30901                 }
30902                 return;
30903             }
30904             var c = curNode.findChild(attr, keys[index]);
30905             if(!c){
30906                 if(callback){
30907                     callback(false, curNode);
30908                 }
30909                 return;
30910             }
30911             curNode = c;
30912             c.expand(false, false, f);
30913         };
30914         curNode.expand(false, false, f);
30915     },
30916
30917     /**
30918      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30919      * @param {String} path
30920      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30921      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30922      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30923      */
30924     selectPath : function(path, attr, callback){
30925         attr = attr || "id";
30926         var keys = path.split(this.pathSeparator);
30927         var v = keys.pop();
30928         if(keys.length > 0){
30929             var f = function(success, node){
30930                 if(success && node){
30931                     var n = node.findChild(attr, v);
30932                     if(n){
30933                         n.select();
30934                         if(callback){
30935                             callback(true, n);
30936                         }
30937                     }else if(callback){
30938                         callback(false, n);
30939                     }
30940                 }else{
30941                     if(callback){
30942                         callback(false, n);
30943                     }
30944                 }
30945             };
30946             this.expandPath(keys.join(this.pathSeparator), attr, f);
30947         }else{
30948             this.root.select();
30949             if(callback){
30950                 callback(true, this.root);
30951             }
30952         }
30953     },
30954
30955     getTreeEl : function(){
30956         return this.el;
30957     },
30958
30959     /**
30960      * Trigger rendering of this TreePanel
30961      */
30962     render : function(){
30963         if (this.innerCt) {
30964             return this; // stop it rendering more than once!!
30965         }
30966         
30967         this.innerCt = this.el.createChild({tag:"ul",
30968                cls:"x-tree-root-ct " +
30969                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30970
30971         if(this.containerScroll){
30972             Roo.dd.ScrollManager.register(this.el);
30973         }
30974         if((this.enableDD || this.enableDrop) && !this.dropZone){
30975            /**
30976             * The dropZone used by this tree if drop is enabled
30977             * @type Roo.tree.TreeDropZone
30978             */
30979              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30980                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30981            });
30982         }
30983         if((this.enableDD || this.enableDrag) && !this.dragZone){
30984            /**
30985             * The dragZone used by this tree if drag is enabled
30986             * @type Roo.tree.TreeDragZone
30987             */
30988             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30989                ddGroup: this.ddGroup || "TreeDD",
30990                scroll: this.ddScroll
30991            });
30992         }
30993         this.getSelectionModel().init(this);
30994         if (!this.root) {
30995             console.log("ROOT not set in tree");
30996             return;
30997         }
30998         this.root.render();
30999         if(!this.rootVisible){
31000             this.root.renderChildren();
31001         }
31002         return this;
31003     }
31004 });/*
31005  * Based on:
31006  * Ext JS Library 1.1.1
31007  * Copyright(c) 2006-2007, Ext JS, LLC.
31008  *
31009  * Originally Released Under LGPL - original licence link has changed is not relivant.
31010  *
31011  * Fork - LGPL
31012  * <script type="text/javascript">
31013  */
31014  
31015
31016 /**
31017  * @class Roo.tree.DefaultSelectionModel
31018  * @extends Roo.util.Observable
31019  * The default single selection for a TreePanel.
31020  */
31021 Roo.tree.DefaultSelectionModel = function(){
31022    this.selNode = null;
31023    
31024    this.addEvents({
31025        /**
31026         * @event selectionchange
31027         * Fires when the selected node changes
31028         * @param {DefaultSelectionModel} this
31029         * @param {TreeNode} node the new selection
31030         */
31031        "selectionchange" : true,
31032
31033        /**
31034         * @event beforeselect
31035         * Fires before the selected node changes, return false to cancel the change
31036         * @param {DefaultSelectionModel} this
31037         * @param {TreeNode} node the new selection
31038         * @param {TreeNode} node the old selection
31039         */
31040        "beforeselect" : true
31041    });
31042 };
31043
31044 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31045     init : function(tree){
31046         this.tree = tree;
31047         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31048         tree.on("click", this.onNodeClick, this);
31049     },
31050     
31051     onNodeClick : function(node, e){
31052         if (e.ctrlKey && this.selNode == node)  {
31053             this.unselect(node);
31054             return;
31055         }
31056         this.select(node);
31057     },
31058     
31059     /**
31060      * Select a node.
31061      * @param {TreeNode} node The node to select
31062      * @return {TreeNode} The selected node
31063      */
31064     select : function(node){
31065         var last = this.selNode;
31066         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31067             if(last){
31068                 last.ui.onSelectedChange(false);
31069             }
31070             this.selNode = node;
31071             node.ui.onSelectedChange(true);
31072             this.fireEvent("selectionchange", this, node, last);
31073         }
31074         return node;
31075     },
31076     
31077     /**
31078      * Deselect a node.
31079      * @param {TreeNode} node The node to unselect
31080      */
31081     unselect : function(node){
31082         if(this.selNode == node){
31083             this.clearSelections();
31084         }    
31085     },
31086     
31087     /**
31088      * Clear all selections
31089      */
31090     clearSelections : function(){
31091         var n = this.selNode;
31092         if(n){
31093             n.ui.onSelectedChange(false);
31094             this.selNode = null;
31095             this.fireEvent("selectionchange", this, null);
31096         }
31097         return n;
31098     },
31099     
31100     /**
31101      * Get the selected node
31102      * @return {TreeNode} The selected node
31103      */
31104     getSelectedNode : function(){
31105         return this.selNode;    
31106     },
31107     
31108     /**
31109      * Returns true if the node is selected
31110      * @param {TreeNode} node The node to check
31111      * @return {Boolean}
31112      */
31113     isSelected : function(node){
31114         return this.selNode == node;  
31115     },
31116
31117     /**
31118      * Selects the node above the selected node in the tree, intelligently walking the nodes
31119      * @return TreeNode The new selection
31120      */
31121     selectPrevious : function(){
31122         var s = this.selNode || this.lastSelNode;
31123         if(!s){
31124             return null;
31125         }
31126         var ps = s.previousSibling;
31127         if(ps){
31128             if(!ps.isExpanded() || ps.childNodes.length < 1){
31129                 return this.select(ps);
31130             } else{
31131                 var lc = ps.lastChild;
31132                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31133                     lc = lc.lastChild;
31134                 }
31135                 return this.select(lc);
31136             }
31137         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31138             return this.select(s.parentNode);
31139         }
31140         return null;
31141     },
31142
31143     /**
31144      * Selects the node above the selected node in the tree, intelligently walking the nodes
31145      * @return TreeNode The new selection
31146      */
31147     selectNext : function(){
31148         var s = this.selNode || this.lastSelNode;
31149         if(!s){
31150             return null;
31151         }
31152         if(s.firstChild && s.isExpanded()){
31153              return this.select(s.firstChild);
31154          }else if(s.nextSibling){
31155              return this.select(s.nextSibling);
31156          }else if(s.parentNode){
31157             var newS = null;
31158             s.parentNode.bubble(function(){
31159                 if(this.nextSibling){
31160                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31161                     return false;
31162                 }
31163             });
31164             return newS;
31165          }
31166         return null;
31167     },
31168
31169     onKeyDown : function(e){
31170         var s = this.selNode || this.lastSelNode;
31171         // undesirable, but required
31172         var sm = this;
31173         if(!s){
31174             return;
31175         }
31176         var k = e.getKey();
31177         switch(k){
31178              case e.DOWN:
31179                  e.stopEvent();
31180                  this.selectNext();
31181              break;
31182              case e.UP:
31183                  e.stopEvent();
31184                  this.selectPrevious();
31185              break;
31186              case e.RIGHT:
31187                  e.preventDefault();
31188                  if(s.hasChildNodes()){
31189                      if(!s.isExpanded()){
31190                          s.expand();
31191                      }else if(s.firstChild){
31192                          this.select(s.firstChild, e);
31193                      }
31194                  }
31195              break;
31196              case e.LEFT:
31197                  e.preventDefault();
31198                  if(s.hasChildNodes() && s.isExpanded()){
31199                      s.collapse();
31200                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31201                      this.select(s.parentNode, e);
31202                  }
31203              break;
31204         };
31205     }
31206 });
31207
31208 /**
31209  * @class Roo.tree.MultiSelectionModel
31210  * @extends Roo.util.Observable
31211  * Multi selection for a TreePanel.
31212  */
31213 Roo.tree.MultiSelectionModel = function(){
31214    this.selNodes = [];
31215    this.selMap = {};
31216    this.addEvents({
31217        /**
31218         * @event selectionchange
31219         * Fires when the selected nodes change
31220         * @param {MultiSelectionModel} this
31221         * @param {Array} nodes Array of the selected nodes
31222         */
31223        "selectionchange" : true
31224    });
31225 };
31226
31227 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31228     init : function(tree){
31229         this.tree = tree;
31230         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31231         tree.on("click", this.onNodeClick, this);
31232     },
31233     
31234     onNodeClick : function(node, e){
31235         this.select(node, e, e.ctrlKey);
31236     },
31237     
31238     /**
31239      * Select a node.
31240      * @param {TreeNode} node The node to select
31241      * @param {EventObject} e (optional) An event associated with the selection
31242      * @param {Boolean} keepExisting True to retain existing selections
31243      * @return {TreeNode} The selected node
31244      */
31245     select : function(node, e, keepExisting){
31246         if(keepExisting !== true){
31247             this.clearSelections(true);
31248         }
31249         if(this.isSelected(node)){
31250             this.lastSelNode = node;
31251             return node;
31252         }
31253         this.selNodes.push(node);
31254         this.selMap[node.id] = node;
31255         this.lastSelNode = node;
31256         node.ui.onSelectedChange(true);
31257         this.fireEvent("selectionchange", this, this.selNodes);
31258         return node;
31259     },
31260     
31261     /**
31262      * Deselect a node.
31263      * @param {TreeNode} node The node to unselect
31264      */
31265     unselect : function(node){
31266         if(this.selMap[node.id]){
31267             node.ui.onSelectedChange(false);
31268             var sn = this.selNodes;
31269             var index = -1;
31270             if(sn.indexOf){
31271                 index = sn.indexOf(node);
31272             }else{
31273                 for(var i = 0, len = sn.length; i < len; i++){
31274                     if(sn[i] == node){
31275                         index = i;
31276                         break;
31277                     }
31278                 }
31279             }
31280             if(index != -1){
31281                 this.selNodes.splice(index, 1);
31282             }
31283             delete this.selMap[node.id];
31284             this.fireEvent("selectionchange", this, this.selNodes);
31285         }
31286     },
31287     
31288     /**
31289      * Clear all selections
31290      */
31291     clearSelections : function(suppressEvent){
31292         var sn = this.selNodes;
31293         if(sn.length > 0){
31294             for(var i = 0, len = sn.length; i < len; i++){
31295                 sn[i].ui.onSelectedChange(false);
31296             }
31297             this.selNodes = [];
31298             this.selMap = {};
31299             if(suppressEvent !== true){
31300                 this.fireEvent("selectionchange", this, this.selNodes);
31301             }
31302         }
31303     },
31304     
31305     /**
31306      * Returns true if the node is selected
31307      * @param {TreeNode} node The node to check
31308      * @return {Boolean}
31309      */
31310     isSelected : function(node){
31311         return this.selMap[node.id] ? true : false;  
31312     },
31313     
31314     /**
31315      * Returns an array of the selected nodes
31316      * @return {Array}
31317      */
31318     getSelectedNodes : function(){
31319         return this.selNodes;    
31320     },
31321
31322     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31323
31324     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31325
31326     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31327 });/*
31328  * Based on:
31329  * Ext JS Library 1.1.1
31330  * Copyright(c) 2006-2007, Ext JS, LLC.
31331  *
31332  * Originally Released Under LGPL - original licence link has changed is not relivant.
31333  *
31334  * Fork - LGPL
31335  * <script type="text/javascript">
31336  */
31337  
31338 /**
31339  * @class Roo.tree.TreeNode
31340  * @extends Roo.data.Node
31341  * @cfg {String} text The text for this node
31342  * @cfg {Boolean} expanded true to start the node expanded
31343  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31344  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31345  * @cfg {Boolean} disabled true to start the node disabled
31346  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31347  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31348  * @cfg {String} cls A css class to be added to the node
31349  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31350  * @cfg {String} href URL of the link used for the node (defaults to #)
31351  * @cfg {String} hrefTarget target frame for the link
31352  * @cfg {String} qtip An Ext QuickTip for the node
31353  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31354  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31355  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31356  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31357  * (defaults to undefined with no checkbox rendered)
31358  * @constructor
31359  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31360  */
31361 Roo.tree.TreeNode = function(attributes){
31362     attributes = attributes || {};
31363     if(typeof attributes == "string"){
31364         attributes = {text: attributes};
31365     }
31366     this.childrenRendered = false;
31367     this.rendered = false;
31368     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31369     this.expanded = attributes.expanded === true;
31370     this.isTarget = attributes.isTarget !== false;
31371     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31372     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31373
31374     /**
31375      * Read-only. The text for this node. To change it use setText().
31376      * @type String
31377      */
31378     this.text = attributes.text;
31379     /**
31380      * True if this node is disabled.
31381      * @type Boolean
31382      */
31383     this.disabled = attributes.disabled === true;
31384
31385     this.addEvents({
31386         /**
31387         * @event textchange
31388         * Fires when the text for this node is changed
31389         * @param {Node} this This node
31390         * @param {String} text The new text
31391         * @param {String} oldText The old text
31392         */
31393         "textchange" : true,
31394         /**
31395         * @event beforeexpand
31396         * Fires before this node is expanded, return false to cancel.
31397         * @param {Node} this This node
31398         * @param {Boolean} deep
31399         * @param {Boolean} anim
31400         */
31401         "beforeexpand" : true,
31402         /**
31403         * @event beforecollapse
31404         * Fires before this node is collapsed, return false to cancel.
31405         * @param {Node} this This node
31406         * @param {Boolean} deep
31407         * @param {Boolean} anim
31408         */
31409         "beforecollapse" : true,
31410         /**
31411         * @event expand
31412         * Fires when this node is expanded
31413         * @param {Node} this This node
31414         */
31415         "expand" : true,
31416         /**
31417         * @event disabledchange
31418         * Fires when the disabled status of this node changes
31419         * @param {Node} this This node
31420         * @param {Boolean} disabled
31421         */
31422         "disabledchange" : true,
31423         /**
31424         * @event collapse
31425         * Fires when this node is collapsed
31426         * @param {Node} this This node
31427         */
31428         "collapse" : true,
31429         /**
31430         * @event beforeclick
31431         * Fires before click processing. Return false to cancel the default action.
31432         * @param {Node} this This node
31433         * @param {Roo.EventObject} e The event object
31434         */
31435         "beforeclick":true,
31436         /**
31437         * @event checkchange
31438         * Fires when a node with a checkbox's checked property changes
31439         * @param {Node} this This node
31440         * @param {Boolean} checked
31441         */
31442         "checkchange":true,
31443         /**
31444         * @event click
31445         * Fires when this node is clicked
31446         * @param {Node} this This node
31447         * @param {Roo.EventObject} e The event object
31448         */
31449         "click":true,
31450         /**
31451         * @event dblclick
31452         * Fires when this node is double clicked
31453         * @param {Node} this This node
31454         * @param {Roo.EventObject} e The event object
31455         */
31456         "dblclick":true,
31457         /**
31458         * @event contextmenu
31459         * Fires when this node is right clicked
31460         * @param {Node} this This node
31461         * @param {Roo.EventObject} e The event object
31462         */
31463         "contextmenu":true,
31464         /**
31465         * @event beforechildrenrendered
31466         * Fires right before the child nodes for this node are rendered
31467         * @param {Node} this This node
31468         */
31469         "beforechildrenrendered":true
31470     });
31471
31472     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31473
31474     /**
31475      * Read-only. The UI for this node
31476      * @type TreeNodeUI
31477      */
31478     this.ui = new uiClass(this);
31479 };
31480 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31481     preventHScroll: true,
31482     /**
31483      * Returns true if this node is expanded
31484      * @return {Boolean}
31485      */
31486     isExpanded : function(){
31487         return this.expanded;
31488     },
31489
31490     /**
31491      * Returns the UI object for this node
31492      * @return {TreeNodeUI}
31493      */
31494     getUI : function(){
31495         return this.ui;
31496     },
31497
31498     // private override
31499     setFirstChild : function(node){
31500         var of = this.firstChild;
31501         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31502         if(this.childrenRendered && of && node != of){
31503             of.renderIndent(true, true);
31504         }
31505         if(this.rendered){
31506             this.renderIndent(true, true);
31507         }
31508     },
31509
31510     // private override
31511     setLastChild : function(node){
31512         var ol = this.lastChild;
31513         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31514         if(this.childrenRendered && ol && node != ol){
31515             ol.renderIndent(true, true);
31516         }
31517         if(this.rendered){
31518             this.renderIndent(true, true);
31519         }
31520     },
31521
31522     // these methods are overridden to provide lazy rendering support
31523     // private override
31524     appendChild : function(){
31525         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31526         if(node && this.childrenRendered){
31527             node.render();
31528         }
31529         this.ui.updateExpandIcon();
31530         return node;
31531     },
31532
31533     // private override
31534     removeChild : function(node){
31535         this.ownerTree.getSelectionModel().unselect(node);
31536         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31537         // if it's been rendered remove dom node
31538         if(this.childrenRendered){
31539             node.ui.remove();
31540         }
31541         if(this.childNodes.length < 1){
31542             this.collapse(false, false);
31543         }else{
31544             this.ui.updateExpandIcon();
31545         }
31546         if(!this.firstChild) {
31547             this.childrenRendered = false;
31548         }
31549         return node;
31550     },
31551
31552     // private override
31553     insertBefore : function(node, refNode){
31554         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31555         if(newNode && refNode && this.childrenRendered){
31556             node.render();
31557         }
31558         this.ui.updateExpandIcon();
31559         return newNode;
31560     },
31561
31562     /**
31563      * Sets the text for this node
31564      * @param {String} text
31565      */
31566     setText : function(text){
31567         var oldText = this.text;
31568         this.text = text;
31569         this.attributes.text = text;
31570         if(this.rendered){ // event without subscribing
31571             this.ui.onTextChange(this, text, oldText);
31572         }
31573         this.fireEvent("textchange", this, text, oldText);
31574     },
31575
31576     /**
31577      * Triggers selection of this node
31578      */
31579     select : function(){
31580         this.getOwnerTree().getSelectionModel().select(this);
31581     },
31582
31583     /**
31584      * Triggers deselection of this node
31585      */
31586     unselect : function(){
31587         this.getOwnerTree().getSelectionModel().unselect(this);
31588     },
31589
31590     /**
31591      * Returns true if this node is selected
31592      * @return {Boolean}
31593      */
31594     isSelected : function(){
31595         return this.getOwnerTree().getSelectionModel().isSelected(this);
31596     },
31597
31598     /**
31599      * Expand this node.
31600      * @param {Boolean} deep (optional) True to expand all children as well
31601      * @param {Boolean} anim (optional) false to cancel the default animation
31602      * @param {Function} callback (optional) A callback to be called when
31603      * expanding this node completes (does not wait for deep expand to complete).
31604      * Called with 1 parameter, this node.
31605      */
31606     expand : function(deep, anim, callback){
31607         if(!this.expanded){
31608             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31609                 return;
31610             }
31611             if(!this.childrenRendered){
31612                 this.renderChildren();
31613             }
31614             this.expanded = true;
31615             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31616                 this.ui.animExpand(function(){
31617                     this.fireEvent("expand", this);
31618                     if(typeof callback == "function"){
31619                         callback(this);
31620                     }
31621                     if(deep === true){
31622                         this.expandChildNodes(true);
31623                     }
31624                 }.createDelegate(this));
31625                 return;
31626             }else{
31627                 this.ui.expand();
31628                 this.fireEvent("expand", this);
31629                 if(typeof callback == "function"){
31630                     callback(this);
31631                 }
31632             }
31633         }else{
31634            if(typeof callback == "function"){
31635                callback(this);
31636            }
31637         }
31638         if(deep === true){
31639             this.expandChildNodes(true);
31640         }
31641     },
31642
31643     isHiddenRoot : function(){
31644         return this.isRoot && !this.getOwnerTree().rootVisible;
31645     },
31646
31647     /**
31648      * Collapse this node.
31649      * @param {Boolean} deep (optional) True to collapse all children as well
31650      * @param {Boolean} anim (optional) false to cancel the default animation
31651      */
31652     collapse : function(deep, anim){
31653         if(this.expanded && !this.isHiddenRoot()){
31654             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31655                 return;
31656             }
31657             this.expanded = false;
31658             if((this.getOwnerTree().animate && anim !== false) || anim){
31659                 this.ui.animCollapse(function(){
31660                     this.fireEvent("collapse", this);
31661                     if(deep === true){
31662                         this.collapseChildNodes(true);
31663                     }
31664                 }.createDelegate(this));
31665                 return;
31666             }else{
31667                 this.ui.collapse();
31668                 this.fireEvent("collapse", this);
31669             }
31670         }
31671         if(deep === true){
31672             var cs = this.childNodes;
31673             for(var i = 0, len = cs.length; i < len; i++) {
31674                 cs[i].collapse(true, false);
31675             }
31676         }
31677     },
31678
31679     // private
31680     delayedExpand : function(delay){
31681         if(!this.expandProcId){
31682             this.expandProcId = this.expand.defer(delay, this);
31683         }
31684     },
31685
31686     // private
31687     cancelExpand : function(){
31688         if(this.expandProcId){
31689             clearTimeout(this.expandProcId);
31690         }
31691         this.expandProcId = false;
31692     },
31693
31694     /**
31695      * Toggles expanded/collapsed state of the node
31696      */
31697     toggle : function(){
31698         if(this.expanded){
31699             this.collapse();
31700         }else{
31701             this.expand();
31702         }
31703     },
31704
31705     /**
31706      * Ensures all parent nodes are expanded
31707      */
31708     ensureVisible : function(callback){
31709         var tree = this.getOwnerTree();
31710         tree.expandPath(this.parentNode.getPath(), false, function(){
31711             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31712             Roo.callback(callback);
31713         }.createDelegate(this));
31714     },
31715
31716     /**
31717      * Expand all child nodes
31718      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31719      */
31720     expandChildNodes : function(deep){
31721         var cs = this.childNodes;
31722         for(var i = 0, len = cs.length; i < len; i++) {
31723                 cs[i].expand(deep);
31724         }
31725     },
31726
31727     /**
31728      * Collapse all child nodes
31729      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31730      */
31731     collapseChildNodes : function(deep){
31732         var cs = this.childNodes;
31733         for(var i = 0, len = cs.length; i < len; i++) {
31734                 cs[i].collapse(deep);
31735         }
31736     },
31737
31738     /**
31739      * Disables this node
31740      */
31741     disable : function(){
31742         this.disabled = true;
31743         this.unselect();
31744         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31745             this.ui.onDisableChange(this, true);
31746         }
31747         this.fireEvent("disabledchange", this, true);
31748     },
31749
31750     /**
31751      * Enables this node
31752      */
31753     enable : function(){
31754         this.disabled = false;
31755         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31756             this.ui.onDisableChange(this, false);
31757         }
31758         this.fireEvent("disabledchange", this, false);
31759     },
31760
31761     // private
31762     renderChildren : function(suppressEvent){
31763         if(suppressEvent !== false){
31764             this.fireEvent("beforechildrenrendered", this);
31765         }
31766         var cs = this.childNodes;
31767         for(var i = 0, len = cs.length; i < len; i++){
31768             cs[i].render(true);
31769         }
31770         this.childrenRendered = true;
31771     },
31772
31773     // private
31774     sort : function(fn, scope){
31775         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31776         if(this.childrenRendered){
31777             var cs = this.childNodes;
31778             for(var i = 0, len = cs.length; i < len; i++){
31779                 cs[i].render(true);
31780             }
31781         }
31782     },
31783
31784     // private
31785     render : function(bulkRender){
31786         this.ui.render(bulkRender);
31787         if(!this.rendered){
31788             this.rendered = true;
31789             if(this.expanded){
31790                 this.expanded = false;
31791                 this.expand(false, false);
31792             }
31793         }
31794     },
31795
31796     // private
31797     renderIndent : function(deep, refresh){
31798         if(refresh){
31799             this.ui.childIndent = null;
31800         }
31801         this.ui.renderIndent();
31802         if(deep === true && this.childrenRendered){
31803             var cs = this.childNodes;
31804             for(var i = 0, len = cs.length; i < len; i++){
31805                 cs[i].renderIndent(true, refresh);
31806             }
31807         }
31808     }
31809 });/*
31810  * Based on:
31811  * Ext JS Library 1.1.1
31812  * Copyright(c) 2006-2007, Ext JS, LLC.
31813  *
31814  * Originally Released Under LGPL - original licence link has changed is not relivant.
31815  *
31816  * Fork - LGPL
31817  * <script type="text/javascript">
31818  */
31819  
31820 /**
31821  * @class Roo.tree.AsyncTreeNode
31822  * @extends Roo.tree.TreeNode
31823  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31824  * @constructor
31825  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31826  */
31827  Roo.tree.AsyncTreeNode = function(config){
31828     this.loaded = false;
31829     this.loading = false;
31830     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31831     /**
31832     * @event beforeload
31833     * Fires before this node is loaded, return false to cancel
31834     * @param {Node} this This node
31835     */
31836     this.addEvents({'beforeload':true, 'load': true});
31837     /**
31838     * @event load
31839     * Fires when this node is loaded
31840     * @param {Node} this This node
31841     */
31842     /**
31843      * The loader used by this node (defaults to using the tree's defined loader)
31844      * @type TreeLoader
31845      * @property loader
31846      */
31847 };
31848 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31849     expand : function(deep, anim, callback){
31850         if(this.loading){ // if an async load is already running, waiting til it's done
31851             var timer;
31852             var f = function(){
31853                 if(!this.loading){ // done loading
31854                     clearInterval(timer);
31855                     this.expand(deep, anim, callback);
31856                 }
31857             }.createDelegate(this);
31858             timer = setInterval(f, 200);
31859             return;
31860         }
31861         if(!this.loaded){
31862             if(this.fireEvent("beforeload", this) === false){
31863                 return;
31864             }
31865             this.loading = true;
31866             this.ui.beforeLoad(this);
31867             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31868             if(loader){
31869                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31870                 return;
31871             }
31872         }
31873         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31874     },
31875     
31876     /**
31877      * Returns true if this node is currently loading
31878      * @return {Boolean}
31879      */
31880     isLoading : function(){
31881         return this.loading;  
31882     },
31883     
31884     loadComplete : function(deep, anim, callback){
31885         this.loading = false;
31886         this.loaded = true;
31887         this.ui.afterLoad(this);
31888         this.fireEvent("load", this);
31889         this.expand(deep, anim, callback);
31890     },
31891     
31892     /**
31893      * Returns true if this node has been loaded
31894      * @return {Boolean}
31895      */
31896     isLoaded : function(){
31897         return this.loaded;
31898     },
31899     
31900     hasChildNodes : function(){
31901         if(!this.isLeaf() && !this.loaded){
31902             return true;
31903         }else{
31904             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31905         }
31906     },
31907
31908     /**
31909      * Trigger a reload for this node
31910      * @param {Function} callback
31911      */
31912     reload : function(callback){
31913         this.collapse(false, false);
31914         while(this.firstChild){
31915             this.removeChild(this.firstChild);
31916         }
31917         this.childrenRendered = false;
31918         this.loaded = false;
31919         if(this.isHiddenRoot()){
31920             this.expanded = false;
31921         }
31922         this.expand(false, false, callback);
31923     }
31924 });/*
31925  * Based on:
31926  * Ext JS Library 1.1.1
31927  * Copyright(c) 2006-2007, Ext JS, LLC.
31928  *
31929  * Originally Released Under LGPL - original licence link has changed is not relivant.
31930  *
31931  * Fork - LGPL
31932  * <script type="text/javascript">
31933  */
31934  
31935 /**
31936  * @class Roo.tree.TreeNodeUI
31937  * @constructor
31938  * @param {Object} node The node to render
31939  * The TreeNode UI implementation is separate from the
31940  * tree implementation. Unless you are customizing the tree UI,
31941  * you should never have to use this directly.
31942  */
31943 Roo.tree.TreeNodeUI = function(node){
31944     this.node = node;
31945     this.rendered = false;
31946     this.animating = false;
31947     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31948 };
31949
31950 Roo.tree.TreeNodeUI.prototype = {
31951     removeChild : function(node){
31952         if(this.rendered){
31953             this.ctNode.removeChild(node.ui.getEl());
31954         }
31955     },
31956
31957     beforeLoad : function(){
31958          this.addClass("x-tree-node-loading");
31959     },
31960
31961     afterLoad : function(){
31962          this.removeClass("x-tree-node-loading");
31963     },
31964
31965     onTextChange : function(node, text, oldText){
31966         if(this.rendered){
31967             this.textNode.innerHTML = text;
31968         }
31969     },
31970
31971     onDisableChange : function(node, state){
31972         this.disabled = state;
31973         if(state){
31974             this.addClass("x-tree-node-disabled");
31975         }else{
31976             this.removeClass("x-tree-node-disabled");
31977         }
31978     },
31979
31980     onSelectedChange : function(state){
31981         if(state){
31982             this.focus();
31983             this.addClass("x-tree-selected");
31984         }else{
31985             //this.blur();
31986             this.removeClass("x-tree-selected");
31987         }
31988     },
31989
31990     onMove : function(tree, node, oldParent, newParent, index, refNode){
31991         this.childIndent = null;
31992         if(this.rendered){
31993             var targetNode = newParent.ui.getContainer();
31994             if(!targetNode){//target not rendered
31995                 this.holder = document.createElement("div");
31996                 this.holder.appendChild(this.wrap);
31997                 return;
31998             }
31999             var insertBefore = refNode ? refNode.ui.getEl() : null;
32000             if(insertBefore){
32001                 targetNode.insertBefore(this.wrap, insertBefore);
32002             }else{
32003                 targetNode.appendChild(this.wrap);
32004             }
32005             this.node.renderIndent(true);
32006         }
32007     },
32008
32009     addClass : function(cls){
32010         if(this.elNode){
32011             Roo.fly(this.elNode).addClass(cls);
32012         }
32013     },
32014
32015     removeClass : function(cls){
32016         if(this.elNode){
32017             Roo.fly(this.elNode).removeClass(cls);
32018         }
32019     },
32020
32021     remove : function(){
32022         if(this.rendered){
32023             this.holder = document.createElement("div");
32024             this.holder.appendChild(this.wrap);
32025         }
32026     },
32027
32028     fireEvent : function(){
32029         return this.node.fireEvent.apply(this.node, arguments);
32030     },
32031
32032     initEvents : function(){
32033         this.node.on("move", this.onMove, this);
32034         var E = Roo.EventManager;
32035         var a = this.anchor;
32036
32037         var el = Roo.fly(a, '_treeui');
32038
32039         if(Roo.isOpera){ // opera render bug ignores the CSS
32040             el.setStyle("text-decoration", "none");
32041         }
32042
32043         el.on("click", this.onClick, this);
32044         el.on("dblclick", this.onDblClick, this);
32045
32046         if(this.checkbox){
32047             Roo.EventManager.on(this.checkbox,
32048                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32049         }
32050
32051         el.on("contextmenu", this.onContextMenu, this);
32052
32053         var icon = Roo.fly(this.iconNode);
32054         icon.on("click", this.onClick, this);
32055         icon.on("dblclick", this.onDblClick, this);
32056         icon.on("contextmenu", this.onContextMenu, this);
32057         E.on(this.ecNode, "click", this.ecClick, this, true);
32058
32059         if(this.node.disabled){
32060             this.addClass("x-tree-node-disabled");
32061         }
32062         if(this.node.hidden){
32063             this.addClass("x-tree-node-disabled");
32064         }
32065         var ot = this.node.getOwnerTree();
32066         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32067         if(dd && (!this.node.isRoot || ot.rootVisible)){
32068             Roo.dd.Registry.register(this.elNode, {
32069                 node: this.node,
32070                 handles: this.getDDHandles(),
32071                 isHandle: false
32072             });
32073         }
32074     },
32075
32076     getDDHandles : function(){
32077         return [this.iconNode, this.textNode];
32078     },
32079
32080     hide : function(){
32081         if(this.rendered){
32082             this.wrap.style.display = "none";
32083         }
32084     },
32085
32086     show : function(){
32087         if(this.rendered){
32088             this.wrap.style.display = "";
32089         }
32090     },
32091
32092     onContextMenu : function(e){
32093         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32094             e.preventDefault();
32095             this.focus();
32096             this.fireEvent("contextmenu", this.node, e);
32097         }
32098     },
32099
32100     onClick : function(e){
32101         if(this.dropping){
32102             e.stopEvent();
32103             return;
32104         }
32105         if(this.fireEvent("beforeclick", this.node, e) !== false){
32106             if(!this.disabled && this.node.attributes.href){
32107                 this.fireEvent("click", this.node, e);
32108                 return;
32109             }
32110             e.preventDefault();
32111             if(this.disabled){
32112                 return;
32113             }
32114
32115             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32116                 this.node.toggle();
32117             }
32118
32119             this.fireEvent("click", this.node, e);
32120         }else{
32121             e.stopEvent();
32122         }
32123     },
32124
32125     onDblClick : function(e){
32126         e.preventDefault();
32127         if(this.disabled){
32128             return;
32129         }
32130         if(this.checkbox){
32131             this.toggleCheck();
32132         }
32133         if(!this.animating && this.node.hasChildNodes()){
32134             this.node.toggle();
32135         }
32136         this.fireEvent("dblclick", this.node, e);
32137     },
32138
32139     onCheckChange : function(){
32140         var checked = this.checkbox.checked;
32141         this.node.attributes.checked = checked;
32142         this.fireEvent('checkchange', this.node, checked);
32143     },
32144
32145     ecClick : function(e){
32146         if(!this.animating && this.node.hasChildNodes()){
32147             this.node.toggle();
32148         }
32149     },
32150
32151     startDrop : function(){
32152         this.dropping = true;
32153     },
32154
32155     // delayed drop so the click event doesn't get fired on a drop
32156     endDrop : function(){
32157        setTimeout(function(){
32158            this.dropping = false;
32159        }.createDelegate(this), 50);
32160     },
32161
32162     expand : function(){
32163         this.updateExpandIcon();
32164         this.ctNode.style.display = "";
32165     },
32166
32167     focus : function(){
32168         if(!this.node.preventHScroll){
32169             try{this.anchor.focus();
32170             }catch(e){}
32171         }else if(!Roo.isIE){
32172             try{
32173                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32174                 var l = noscroll.scrollLeft;
32175                 this.anchor.focus();
32176                 noscroll.scrollLeft = l;
32177             }catch(e){}
32178         }
32179     },
32180
32181     toggleCheck : function(value){
32182         var cb = this.checkbox;
32183         if(cb){
32184             cb.checked = (value === undefined ? !cb.checked : value);
32185         }
32186     },
32187
32188     blur : function(){
32189         try{
32190             this.anchor.blur();
32191         }catch(e){}
32192     },
32193
32194     animExpand : function(callback){
32195         var ct = Roo.get(this.ctNode);
32196         ct.stopFx();
32197         if(!this.node.hasChildNodes()){
32198             this.updateExpandIcon();
32199             this.ctNode.style.display = "";
32200             Roo.callback(callback);
32201             return;
32202         }
32203         this.animating = true;
32204         this.updateExpandIcon();
32205
32206         ct.slideIn('t', {
32207            callback : function(){
32208                this.animating = false;
32209                Roo.callback(callback);
32210             },
32211             scope: this,
32212             duration: this.node.ownerTree.duration || .25
32213         });
32214     },
32215
32216     highlight : function(){
32217         var tree = this.node.getOwnerTree();
32218         Roo.fly(this.wrap).highlight(
32219             tree.hlColor || "C3DAF9",
32220             {endColor: tree.hlBaseColor}
32221         );
32222     },
32223
32224     collapse : function(){
32225         this.updateExpandIcon();
32226         this.ctNode.style.display = "none";
32227     },
32228
32229     animCollapse : function(callback){
32230         var ct = Roo.get(this.ctNode);
32231         ct.enableDisplayMode('block');
32232         ct.stopFx();
32233
32234         this.animating = true;
32235         this.updateExpandIcon();
32236
32237         ct.slideOut('t', {
32238             callback : function(){
32239                this.animating = false;
32240                Roo.callback(callback);
32241             },
32242             scope: this,
32243             duration: this.node.ownerTree.duration || .25
32244         });
32245     },
32246
32247     getContainer : function(){
32248         return this.ctNode;
32249     },
32250
32251     getEl : function(){
32252         return this.wrap;
32253     },
32254
32255     appendDDGhost : function(ghostNode){
32256         ghostNode.appendChild(this.elNode.cloneNode(true));
32257     },
32258
32259     getDDRepairXY : function(){
32260         return Roo.lib.Dom.getXY(this.iconNode);
32261     },
32262
32263     onRender : function(){
32264         this.render();
32265     },
32266
32267     render : function(bulkRender){
32268         var n = this.node, a = n.attributes;
32269         var targetNode = n.parentNode ?
32270               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32271
32272         if(!this.rendered){
32273             this.rendered = true;
32274
32275             this.renderElements(n, a, targetNode, bulkRender);
32276
32277             if(a.qtip){
32278                if(this.textNode.setAttributeNS){
32279                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32280                    if(a.qtipTitle){
32281                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32282                    }
32283                }else{
32284                    this.textNode.setAttribute("ext:qtip", a.qtip);
32285                    if(a.qtipTitle){
32286                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32287                    }
32288                }
32289             }else if(a.qtipCfg){
32290                 a.qtipCfg.target = Roo.id(this.textNode);
32291                 Roo.QuickTips.register(a.qtipCfg);
32292             }
32293             this.initEvents();
32294             if(!this.node.expanded){
32295                 this.updateExpandIcon();
32296             }
32297         }else{
32298             if(bulkRender === true) {
32299                 targetNode.appendChild(this.wrap);
32300             }
32301         }
32302     },
32303
32304     renderElements : function(n, a, targetNode, bulkRender){
32305         // add some indent caching, this helps performance when rendering a large tree
32306         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32307         var t = n.getOwnerTree();
32308         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32309         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32310         var cb = typeof a.checked == 'boolean';
32311         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32312         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32313             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32314             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32315             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32316             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32317             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32318              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32319                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32320             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32321             "</li>"];
32322
32323         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32324             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32325                                 n.nextSibling.ui.getEl(), buf.join(""));
32326         }else{
32327             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32328         }
32329
32330         this.elNode = this.wrap.childNodes[0];
32331         this.ctNode = this.wrap.childNodes[1];
32332         var cs = this.elNode.childNodes;
32333         this.indentNode = cs[0];
32334         this.ecNode = cs[1];
32335         this.iconNode = cs[2];
32336         var index = 3;
32337         if(cb){
32338             this.checkbox = cs[3];
32339             index++;
32340         }
32341         this.anchor = cs[index];
32342         this.textNode = cs[index].firstChild;
32343     },
32344
32345     getAnchor : function(){
32346         return this.anchor;
32347     },
32348
32349     getTextEl : function(){
32350         return this.textNode;
32351     },
32352
32353     getIconEl : function(){
32354         return this.iconNode;
32355     },
32356
32357     isChecked : function(){
32358         return this.checkbox ? this.checkbox.checked : false;
32359     },
32360
32361     updateExpandIcon : function(){
32362         if(this.rendered){
32363             var n = this.node, c1, c2;
32364             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32365             var hasChild = n.hasChildNodes();
32366             if(hasChild){
32367                 if(n.expanded){
32368                     cls += "-minus";
32369                     c1 = "x-tree-node-collapsed";
32370                     c2 = "x-tree-node-expanded";
32371                 }else{
32372                     cls += "-plus";
32373                     c1 = "x-tree-node-expanded";
32374                     c2 = "x-tree-node-collapsed";
32375                 }
32376                 if(this.wasLeaf){
32377                     this.removeClass("x-tree-node-leaf");
32378                     this.wasLeaf = false;
32379                 }
32380                 if(this.c1 != c1 || this.c2 != c2){
32381                     Roo.fly(this.elNode).replaceClass(c1, c2);
32382                     this.c1 = c1; this.c2 = c2;
32383                 }
32384             }else{
32385                 if(!this.wasLeaf){
32386                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32387                     delete this.c1;
32388                     delete this.c2;
32389                     this.wasLeaf = true;
32390                 }
32391             }
32392             var ecc = "x-tree-ec-icon "+cls;
32393             if(this.ecc != ecc){
32394                 this.ecNode.className = ecc;
32395                 this.ecc = ecc;
32396             }
32397         }
32398     },
32399
32400     getChildIndent : function(){
32401         if(!this.childIndent){
32402             var buf = [];
32403             var p = this.node;
32404             while(p){
32405                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32406                     if(!p.isLast()) {
32407                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32408                     } else {
32409                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32410                     }
32411                 }
32412                 p = p.parentNode;
32413             }
32414             this.childIndent = buf.join("");
32415         }
32416         return this.childIndent;
32417     },
32418
32419     renderIndent : function(){
32420         if(this.rendered){
32421             var indent = "";
32422             var p = this.node.parentNode;
32423             if(p){
32424                 indent = p.ui.getChildIndent();
32425             }
32426             if(this.indentMarkup != indent){ // don't rerender if not required
32427                 this.indentNode.innerHTML = indent;
32428                 this.indentMarkup = indent;
32429             }
32430             this.updateExpandIcon();
32431         }
32432     }
32433 };
32434
32435 Roo.tree.RootTreeNodeUI = function(){
32436     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32437 };
32438 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32439     render : function(){
32440         if(!this.rendered){
32441             var targetNode = this.node.ownerTree.innerCt.dom;
32442             this.node.expanded = true;
32443             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32444             this.wrap = this.ctNode = targetNode.firstChild;
32445         }
32446     },
32447     collapse : function(){
32448     },
32449     expand : function(){
32450     }
32451 });/*
32452  * Based on:
32453  * Ext JS Library 1.1.1
32454  * Copyright(c) 2006-2007, Ext JS, LLC.
32455  *
32456  * Originally Released Under LGPL - original licence link has changed is not relivant.
32457  *
32458  * Fork - LGPL
32459  * <script type="text/javascript">
32460  */
32461 /**
32462  * @class Roo.tree.TreeLoader
32463  * @extends Roo.util.Observable
32464  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32465  * nodes from a specified URL. The response must be a javascript Array definition
32466  * who's elements are node definition objects. eg:
32467  * <pre><code>
32468    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32469     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32470 </code></pre>
32471  * <br><br>
32472  * A server request is sent, and child nodes are loaded only when a node is expanded.
32473  * The loading node's id is passed to the server under the parameter name "node" to
32474  * enable the server to produce the correct child nodes.
32475  * <br><br>
32476  * To pass extra parameters, an event handler may be attached to the "beforeload"
32477  * event, and the parameters specified in the TreeLoader's baseParams property:
32478  * <pre><code>
32479     myTreeLoader.on("beforeload", function(treeLoader, node) {
32480         this.baseParams.category = node.attributes.category;
32481     }, this);
32482 </code></pre><
32483  * This would pass an HTTP parameter called "category" to the server containing
32484  * the value of the Node's "category" attribute.
32485  * @constructor
32486  * Creates a new Treeloader.
32487  * @param {Object} config A config object containing config properties.
32488  */
32489 Roo.tree.TreeLoader = function(config){
32490     this.baseParams = {};
32491     this.requestMethod = "POST";
32492     Roo.apply(this, config);
32493
32494     this.addEvents({
32495     
32496         /**
32497          * @event beforeload
32498          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32499          * @param {Object} This TreeLoader object.
32500          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32501          * @param {Object} callback The callback function specified in the {@link #load} call.
32502          */
32503         beforeload : true,
32504         /**
32505          * @event load
32506          * Fires when the node has been successfuly loaded.
32507          * @param {Object} This TreeLoader object.
32508          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32509          * @param {Object} response The response object containing the data from the server.
32510          */
32511         load : true,
32512         /**
32513          * @event loadexception
32514          * Fires if the network request failed.
32515          * @param {Object} This TreeLoader object.
32516          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32517          * @param {Object} response The response object containing the data from the server.
32518          */
32519         loadexception : true,
32520         /**
32521          * @event create
32522          * Fires before a node is created, enabling you to return custom Node types 
32523          * @param {Object} This TreeLoader object.
32524          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32525          */
32526         create : true
32527     });
32528
32529     Roo.tree.TreeLoader.superclass.constructor.call(this);
32530 };
32531
32532 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32533     /**
32534     * @cfg {String} dataUrl The URL from which to request a Json string which
32535     * specifies an array of node definition object representing the child nodes
32536     * to be loaded.
32537     */
32538     /**
32539     * @cfg {Object} baseParams (optional) An object containing properties which
32540     * specify HTTP parameters to be passed to each request for child nodes.
32541     */
32542     /**
32543     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32544     * created by this loader. If the attributes sent by the server have an attribute in this object,
32545     * they take priority.
32546     */
32547     /**
32548     * @cfg {Object} uiProviders (optional) An object containing properties which
32549     * 
32550     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32551     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32552     * <i>uiProvider</i> attribute of a returned child node is a string rather
32553     * than a reference to a TreeNodeUI implementation, this that string value
32554     * is used as a property name in the uiProviders object. You can define the provider named
32555     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32556     */
32557     uiProviders : {},
32558
32559     /**
32560     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32561     * child nodes before loading.
32562     */
32563     clearOnLoad : true,
32564
32565     /**
32566     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32567     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32568     * Grid query { data : [ .....] }
32569     */
32570     
32571     root : false,
32572      /**
32573     * @cfg {String} queryParam (optional) 
32574     * Name of the query as it will be passed on the querystring (defaults to 'node')
32575     * eg. the request will be ?node=[id]
32576     */
32577     
32578     
32579     queryParam: false,
32580     
32581     /**
32582      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32583      * This is called automatically when a node is expanded, but may be used to reload
32584      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32585      * @param {Roo.tree.TreeNode} node
32586      * @param {Function} callback
32587      */
32588     load : function(node, callback){
32589         if(this.clearOnLoad){
32590             while(node.firstChild){
32591                 node.removeChild(node.firstChild);
32592             }
32593         }
32594         if(node.attributes.children){ // preloaded json children
32595             var cs = node.attributes.children;
32596             for(var i = 0, len = cs.length; i < len; i++){
32597                 node.appendChild(this.createNode(cs[i]));
32598             }
32599             if(typeof callback == "function"){
32600                 callback();
32601             }
32602         }else if(this.dataUrl){
32603             this.requestData(node, callback);
32604         }
32605     },
32606
32607     getParams: function(node){
32608         var buf = [], bp = this.baseParams;
32609         for(var key in bp){
32610             if(typeof bp[key] != "function"){
32611                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32612             }
32613         }
32614         var n = this.queryParam === false ? 'node' : this.queryParam;
32615         buf.push(n + "=", encodeURIComponent(node.id));
32616         return buf.join("");
32617     },
32618
32619     requestData : function(node, callback){
32620         if(this.fireEvent("beforeload", this, node, callback) !== false){
32621             this.transId = Roo.Ajax.request({
32622                 method:this.requestMethod,
32623                 url: this.dataUrl||this.url,
32624                 success: this.handleResponse,
32625                 failure: this.handleFailure,
32626                 scope: this,
32627                 argument: {callback: callback, node: node},
32628                 params: this.getParams(node)
32629             });
32630         }else{
32631             // if the load is cancelled, make sure we notify
32632             // the node that we are done
32633             if(typeof callback == "function"){
32634                 callback();
32635             }
32636         }
32637     },
32638
32639     isLoading : function(){
32640         return this.transId ? true : false;
32641     },
32642
32643     abort : function(){
32644         if(this.isLoading()){
32645             Roo.Ajax.abort(this.transId);
32646         }
32647     },
32648
32649     // private
32650     createNode : function(attr){
32651         // apply baseAttrs, nice idea Corey!
32652         if(this.baseAttrs){
32653             Roo.applyIf(attr, this.baseAttrs);
32654         }
32655         if(this.applyLoader !== false){
32656             attr.loader = this;
32657         }
32658         // uiProvider = depreciated..
32659         
32660         if(typeof(attr.uiProvider) == 'string'){
32661            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32662                 /**  eval:var:attr */ eval(attr.uiProvider);
32663         }
32664         if(typeof(this.uiProviders['default']) != 'undefined') {
32665             attr.uiProvider = this.uiProviders['default'];
32666         }
32667         
32668         this.fireEvent('create', this, attr);
32669         
32670         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32671         return(attr.leaf ?
32672                         new Roo.tree.TreeNode(attr) :
32673                         new Roo.tree.AsyncTreeNode(attr));
32674     },
32675
32676     processResponse : function(response, node, callback){
32677         var json = response.responseText;
32678         try {
32679             
32680             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32681             if (this.root !== false) {
32682                 o = o[this.root];
32683             }
32684             
32685             for(var i = 0, len = o.length; i < len; i++){
32686                 var n = this.createNode(o[i]);
32687                 if(n){
32688                     node.appendChild(n);
32689                 }
32690             }
32691             if(typeof callback == "function"){
32692                 callback(this, node);
32693             }
32694         }catch(e){
32695             this.handleFailure(response);
32696         }
32697     },
32698
32699     handleResponse : function(response){
32700         this.transId = false;
32701         var a = response.argument;
32702         this.processResponse(response, a.node, a.callback);
32703         this.fireEvent("load", this, a.node, response);
32704     },
32705
32706     handleFailure : function(response){
32707         this.transId = false;
32708         var a = response.argument;
32709         this.fireEvent("loadexception", this, a.node, response);
32710         if(typeof a.callback == "function"){
32711             a.callback(this, a.node);
32712         }
32713     }
32714 });/*
32715  * Based on:
32716  * Ext JS Library 1.1.1
32717  * Copyright(c) 2006-2007, Ext JS, LLC.
32718  *
32719  * Originally Released Under LGPL - original licence link has changed is not relivant.
32720  *
32721  * Fork - LGPL
32722  * <script type="text/javascript">
32723  */
32724
32725 /**
32726 * @class Roo.tree.TreeFilter
32727 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32728 * @param {TreePanel} tree
32729 * @param {Object} config (optional)
32730  */
32731 Roo.tree.TreeFilter = function(tree, config){
32732     this.tree = tree;
32733     this.filtered = {};
32734     Roo.apply(this, config);
32735 };
32736
32737 Roo.tree.TreeFilter.prototype = {
32738     clearBlank:false,
32739     reverse:false,
32740     autoClear:false,
32741     remove:false,
32742
32743      /**
32744      * Filter the data by a specific attribute.
32745      * @param {String/RegExp} value Either string that the attribute value
32746      * should start with or a RegExp to test against the attribute
32747      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32748      * @param {TreeNode} startNode (optional) The node to start the filter at.
32749      */
32750     filter : function(value, attr, startNode){
32751         attr = attr || "text";
32752         var f;
32753         if(typeof value == "string"){
32754             var vlen = value.length;
32755             // auto clear empty filter
32756             if(vlen == 0 && this.clearBlank){
32757                 this.clear();
32758                 return;
32759             }
32760             value = value.toLowerCase();
32761             f = function(n){
32762                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32763             };
32764         }else if(value.exec){ // regex?
32765             f = function(n){
32766                 return value.test(n.attributes[attr]);
32767             };
32768         }else{
32769             throw 'Illegal filter type, must be string or regex';
32770         }
32771         this.filterBy(f, null, startNode);
32772         },
32773
32774     /**
32775      * Filter by a function. The passed function will be called with each
32776      * node in the tree (or from the startNode). If the function returns true, the node is kept
32777      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32778      * @param {Function} fn The filter function
32779      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32780      */
32781     filterBy : function(fn, scope, startNode){
32782         startNode = startNode || this.tree.root;
32783         if(this.autoClear){
32784             this.clear();
32785         }
32786         var af = this.filtered, rv = this.reverse;
32787         var f = function(n){
32788             if(n == startNode){
32789                 return true;
32790             }
32791             if(af[n.id]){
32792                 return false;
32793             }
32794             var m = fn.call(scope || n, n);
32795             if(!m || rv){
32796                 af[n.id] = n;
32797                 n.ui.hide();
32798                 return false;
32799             }
32800             return true;
32801         };
32802         startNode.cascade(f);
32803         if(this.remove){
32804            for(var id in af){
32805                if(typeof id != "function"){
32806                    var n = af[id];
32807                    if(n && n.parentNode){
32808                        n.parentNode.removeChild(n);
32809                    }
32810                }
32811            }
32812         }
32813     },
32814
32815     /**
32816      * Clears the current filter. Note: with the "remove" option
32817      * set a filter cannot be cleared.
32818      */
32819     clear : function(){
32820         var t = this.tree;
32821         var af = this.filtered;
32822         for(var id in af){
32823             if(typeof id != "function"){
32824                 var n = af[id];
32825                 if(n){
32826                     n.ui.show();
32827                 }
32828             }
32829         }
32830         this.filtered = {};
32831     }
32832 };
32833 /*
32834  * Based on:
32835  * Ext JS Library 1.1.1
32836  * Copyright(c) 2006-2007, Ext JS, LLC.
32837  *
32838  * Originally Released Under LGPL - original licence link has changed is not relivant.
32839  *
32840  * Fork - LGPL
32841  * <script type="text/javascript">
32842  */
32843  
32844
32845 /**
32846  * @class Roo.tree.TreeSorter
32847  * Provides sorting of nodes in a TreePanel
32848  * 
32849  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32850  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32851  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32852  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32853  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32854  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32855  * @constructor
32856  * @param {TreePanel} tree
32857  * @param {Object} config
32858  */
32859 Roo.tree.TreeSorter = function(tree, config){
32860     Roo.apply(this, config);
32861     tree.on("beforechildrenrendered", this.doSort, this);
32862     tree.on("append", this.updateSort, this);
32863     tree.on("insert", this.updateSort, this);
32864     
32865     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32866     var p = this.property || "text";
32867     var sortType = this.sortType;
32868     var fs = this.folderSort;
32869     var cs = this.caseSensitive === true;
32870     var leafAttr = this.leafAttr || 'leaf';
32871
32872     this.sortFn = function(n1, n2){
32873         if(fs){
32874             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32875                 return 1;
32876             }
32877             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32878                 return -1;
32879             }
32880         }
32881         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32882         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32883         if(v1 < v2){
32884                         return dsc ? +1 : -1;
32885                 }else if(v1 > v2){
32886                         return dsc ? -1 : +1;
32887         }else{
32888                 return 0;
32889         }
32890     };
32891 };
32892
32893 Roo.tree.TreeSorter.prototype = {
32894     doSort : function(node){
32895         node.sort(this.sortFn);
32896     },
32897     
32898     compareNodes : function(n1, n2){
32899         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32900     },
32901     
32902     updateSort : function(tree, node){
32903         if(node.childrenRendered){
32904             this.doSort.defer(1, this, [node]);
32905         }
32906     }
32907 };/*
32908  * Based on:
32909  * Ext JS Library 1.1.1
32910  * Copyright(c) 2006-2007, Ext JS, LLC.
32911  *
32912  * Originally Released Under LGPL - original licence link has changed is not relivant.
32913  *
32914  * Fork - LGPL
32915  * <script type="text/javascript">
32916  */
32917
32918 if(Roo.dd.DropZone){
32919     
32920 Roo.tree.TreeDropZone = function(tree, config){
32921     this.allowParentInsert = false;
32922     this.allowContainerDrop = false;
32923     this.appendOnly = false;
32924     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32925     this.tree = tree;
32926     this.lastInsertClass = "x-tree-no-status";
32927     this.dragOverData = {};
32928 };
32929
32930 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32931     ddGroup : "TreeDD",
32932     
32933     expandDelay : 1000,
32934     
32935     expandNode : function(node){
32936         if(node.hasChildNodes() && !node.isExpanded()){
32937             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32938         }
32939     },
32940     
32941     queueExpand : function(node){
32942         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32943     },
32944     
32945     cancelExpand : function(){
32946         if(this.expandProcId){
32947             clearTimeout(this.expandProcId);
32948             this.expandProcId = false;
32949         }
32950     },
32951     
32952     isValidDropPoint : function(n, pt, dd, e, data){
32953         if(!n || !data){ return false; }
32954         var targetNode = n.node;
32955         var dropNode = data.node;
32956         // default drop rules
32957         if(!(targetNode && targetNode.isTarget && pt)){
32958             return false;
32959         }
32960         if(pt == "append" && targetNode.allowChildren === false){
32961             return false;
32962         }
32963         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32964             return false;
32965         }
32966         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32967             return false;
32968         }
32969         // reuse the object
32970         var overEvent = this.dragOverData;
32971         overEvent.tree = this.tree;
32972         overEvent.target = targetNode;
32973         overEvent.data = data;
32974         overEvent.point = pt;
32975         overEvent.source = dd;
32976         overEvent.rawEvent = e;
32977         overEvent.dropNode = dropNode;
32978         overEvent.cancel = false;  
32979         var result = this.tree.fireEvent("nodedragover", overEvent);
32980         return overEvent.cancel === false && result !== false;
32981     },
32982     
32983     getDropPoint : function(e, n, dd){
32984         var tn = n.node;
32985         if(tn.isRoot){
32986             return tn.allowChildren !== false ? "append" : false; // always append for root
32987         }
32988         var dragEl = n.ddel;
32989         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32990         var y = Roo.lib.Event.getPageY(e);
32991         //var noAppend = tn.allowChildren === false || tn.isLeaf();
32992         
32993         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
32994         var noAppend = tn.allowChildren === false;
32995         if(this.appendOnly || tn.parentNode.allowChildren === false){
32996             return noAppend ? false : "append";
32997         }
32998         var noBelow = false;
32999         if(!this.allowParentInsert){
33000             noBelow = tn.hasChildNodes() && tn.isExpanded();
33001         }
33002         var q = (b - t) / (noAppend ? 2 : 3);
33003         if(y >= t && y < (t + q)){
33004             return "above";
33005         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33006             return "below";
33007         }else{
33008             return "append";
33009         }
33010     },
33011     
33012     onNodeEnter : function(n, dd, e, data){
33013         this.cancelExpand();
33014     },
33015     
33016     onNodeOver : function(n, dd, e, data){
33017         var pt = this.getDropPoint(e, n, dd);
33018         var node = n.node;
33019         
33020         // auto node expand check
33021         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33022             this.queueExpand(node);
33023         }else if(pt != "append"){
33024             this.cancelExpand();
33025         }
33026         
33027         // set the insert point style on the target node
33028         var returnCls = this.dropNotAllowed;
33029         if(this.isValidDropPoint(n, pt, dd, e, data)){
33030            if(pt){
33031                var el = n.ddel;
33032                var cls;
33033                if(pt == "above"){
33034                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33035                    cls = "x-tree-drag-insert-above";
33036                }else if(pt == "below"){
33037                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33038                    cls = "x-tree-drag-insert-below";
33039                }else{
33040                    returnCls = "x-tree-drop-ok-append";
33041                    cls = "x-tree-drag-append";
33042                }
33043                if(this.lastInsertClass != cls){
33044                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33045                    this.lastInsertClass = cls;
33046                }
33047            }
33048        }
33049        return returnCls;
33050     },
33051     
33052     onNodeOut : function(n, dd, e, data){
33053         this.cancelExpand();
33054         this.removeDropIndicators(n);
33055     },
33056     
33057     onNodeDrop : function(n, dd, e, data){
33058         var point = this.getDropPoint(e, n, dd);
33059         var targetNode = n.node;
33060         targetNode.ui.startDrop();
33061         if(!this.isValidDropPoint(n, point, dd, e, data)){
33062             targetNode.ui.endDrop();
33063             return false;
33064         }
33065         // first try to find the drop node
33066         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33067         var dropEvent = {
33068             tree : this.tree,
33069             target: targetNode,
33070             data: data,
33071             point: point,
33072             source: dd,
33073             rawEvent: e,
33074             dropNode: dropNode,
33075             cancel: !dropNode   
33076         };
33077         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33078         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33079             targetNode.ui.endDrop();
33080             return false;
33081         }
33082         // allow target changing
33083         targetNode = dropEvent.target;
33084         if(point == "append" && !targetNode.isExpanded()){
33085             targetNode.expand(false, null, function(){
33086                 this.completeDrop(dropEvent);
33087             }.createDelegate(this));
33088         }else{
33089             this.completeDrop(dropEvent);
33090         }
33091         return true;
33092     },
33093     
33094     completeDrop : function(de){
33095         var ns = de.dropNode, p = de.point, t = de.target;
33096         if(!(ns instanceof Array)){
33097             ns = [ns];
33098         }
33099         var n;
33100         for(var i = 0, len = ns.length; i < len; i++){
33101             n = ns[i];
33102             if(p == "above"){
33103                 t.parentNode.insertBefore(n, t);
33104             }else if(p == "below"){
33105                 t.parentNode.insertBefore(n, t.nextSibling);
33106             }else{
33107                 t.appendChild(n);
33108             }
33109         }
33110         n.ui.focus();
33111         if(this.tree.hlDrop){
33112             n.ui.highlight();
33113         }
33114         t.ui.endDrop();
33115         this.tree.fireEvent("nodedrop", de);
33116     },
33117     
33118     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33119         if(this.tree.hlDrop){
33120             dropNode.ui.focus();
33121             dropNode.ui.highlight();
33122         }
33123         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33124     },
33125     
33126     getTree : function(){
33127         return this.tree;
33128     },
33129     
33130     removeDropIndicators : function(n){
33131         if(n && n.ddel){
33132             var el = n.ddel;
33133             Roo.fly(el).removeClass([
33134                     "x-tree-drag-insert-above",
33135                     "x-tree-drag-insert-below",
33136                     "x-tree-drag-append"]);
33137             this.lastInsertClass = "_noclass";
33138         }
33139     },
33140     
33141     beforeDragDrop : function(target, e, id){
33142         this.cancelExpand();
33143         return true;
33144     },
33145     
33146     afterRepair : function(data){
33147         if(data && Roo.enableFx){
33148             data.node.ui.highlight();
33149         }
33150         this.hideProxy();
33151     }    
33152 });
33153
33154 }
33155 /*
33156  * Based on:
33157  * Ext JS Library 1.1.1
33158  * Copyright(c) 2006-2007, Ext JS, LLC.
33159  *
33160  * Originally Released Under LGPL - original licence link has changed is not relivant.
33161  *
33162  * Fork - LGPL
33163  * <script type="text/javascript">
33164  */
33165  
33166
33167 if(Roo.dd.DragZone){
33168 Roo.tree.TreeDragZone = function(tree, config){
33169     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33170     this.tree = tree;
33171 };
33172
33173 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33174     ddGroup : "TreeDD",
33175     
33176     onBeforeDrag : function(data, e){
33177         var n = data.node;
33178         return n && n.draggable && !n.disabled;
33179     },
33180     
33181     onInitDrag : function(e){
33182         var data = this.dragData;
33183         this.tree.getSelectionModel().select(data.node);
33184         this.proxy.update("");
33185         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33186         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33187     },
33188     
33189     getRepairXY : function(e, data){
33190         return data.node.ui.getDDRepairXY();
33191     },
33192     
33193     onEndDrag : function(data, e){
33194         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33195     },
33196     
33197     onValidDrop : function(dd, e, id){
33198         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33199         this.hideProxy();
33200     },
33201     
33202     beforeInvalidDrop : function(e, id){
33203         // this scrolls the original position back into view
33204         var sm = this.tree.getSelectionModel();
33205         sm.clearSelections();
33206         sm.select(this.dragData.node);
33207     }
33208 });
33209 }/*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219 /**
33220  * @class Roo.tree.TreeEditor
33221  * @extends Roo.Editor
33222  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33223  * as the editor field.
33224  * @constructor
33225  * @param {TreePanel} tree
33226  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33227  */
33228 Roo.tree.TreeEditor = function(tree, config){
33229     config = config || {};
33230     var field = config.events ? config : new Roo.form.TextField(config);
33231     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33232
33233     this.tree = tree;
33234
33235     tree.on('beforeclick', this.beforeNodeClick, this);
33236     tree.getTreeEl().on('mousedown', this.hide, this);
33237     this.on('complete', this.updateNode, this);
33238     this.on('beforestartedit', this.fitToTree, this);
33239     this.on('startedit', this.bindScroll, this, {delay:10});
33240     this.on('specialkey', this.onSpecialKey, this);
33241 };
33242
33243 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33244     /**
33245      * @cfg {String} alignment
33246      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33247      */
33248     alignment: "l-l",
33249     // inherit
33250     autoSize: false,
33251     /**
33252      * @cfg {Boolean} hideEl
33253      * True to hide the bound element while the editor is displayed (defaults to false)
33254      */
33255     hideEl : false,
33256     /**
33257      * @cfg {String} cls
33258      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33259      */
33260     cls: "x-small-editor x-tree-editor",
33261     /**
33262      * @cfg {Boolean} shim
33263      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33264      */
33265     shim:false,
33266     // inherit
33267     shadow:"frame",
33268     /**
33269      * @cfg {Number} maxWidth
33270      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33271      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33272      * scroll and client offsets into account prior to each edit.
33273      */
33274     maxWidth: 250,
33275
33276     editDelay : 350,
33277
33278     // private
33279     fitToTree : function(ed, el){
33280         var td = this.tree.getTreeEl().dom, nd = el.dom;
33281         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33282             td.scrollLeft = nd.offsetLeft;
33283         }
33284         var w = Math.min(
33285                 this.maxWidth,
33286                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33287         this.setSize(w, '');
33288     },
33289
33290     // private
33291     triggerEdit : function(node){
33292         this.completeEdit();
33293         this.editNode = node;
33294         this.startEdit(node.ui.textNode, node.text);
33295     },
33296
33297     // private
33298     bindScroll : function(){
33299         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33300     },
33301
33302     // private
33303     beforeNodeClick : function(node, e){
33304         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33305         this.lastClick = new Date();
33306         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33307             e.stopEvent();
33308             this.triggerEdit(node);
33309             return false;
33310         }
33311     },
33312
33313     // private
33314     updateNode : function(ed, value){
33315         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33316         this.editNode.setText(value);
33317     },
33318
33319     // private
33320     onHide : function(){
33321         Roo.tree.TreeEditor.superclass.onHide.call(this);
33322         if(this.editNode){
33323             this.editNode.ui.focus();
33324         }
33325     },
33326
33327     // private
33328     onSpecialKey : function(field, e){
33329         var k = e.getKey();
33330         if(k == e.ESC){
33331             e.stopEvent();
33332             this.cancelEdit();
33333         }else if(k == e.ENTER && !e.hasModifier()){
33334             e.stopEvent();
33335             this.completeEdit();
33336         }
33337     }
33338 });//<Script type="text/javascript">
33339 /*
33340  * Based on:
33341  * Ext JS Library 1.1.1
33342  * Copyright(c) 2006-2007, Ext JS, LLC.
33343  *
33344  * Originally Released Under LGPL - original licence link has changed is not relivant.
33345  *
33346  * Fork - LGPL
33347  * <script type="text/javascript">
33348  */
33349  
33350 /**
33351  * Not documented??? - probably should be...
33352  */
33353
33354 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33355     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33356     
33357     renderElements : function(n, a, targetNode, bulkRender){
33358         //consel.log("renderElements?");
33359         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33360
33361         var t = n.getOwnerTree();
33362         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33363         
33364         var cols = t.columns;
33365         var bw = t.borderWidth;
33366         var c = cols[0];
33367         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33368          var cb = typeof a.checked == "boolean";
33369         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33370         var colcls = 'x-t-' + tid + '-c0';
33371         var buf = [
33372             '<li class="x-tree-node">',
33373             
33374                 
33375                 '<div class="x-tree-node-el ', a.cls,'">',
33376                     // extran...
33377                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33378                 
33379                 
33380                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33381                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33382                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33383                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33384                            (a.iconCls ? ' '+a.iconCls : ''),
33385                            '" unselectable="on" />',
33386                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33387                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33388                              
33389                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33390                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33391                             '<span unselectable="on" qtip="' + tx + '">',
33392                              tx,
33393                              '</span></a>' ,
33394                     '</div>',
33395                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33396                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33397                  ];
33398         for(var i = 1, len = cols.length; i < len; i++){
33399             c = cols[i];
33400             colcls = 'x-t-' + tid + '-c' +i;
33401             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33402             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33403                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33404                       "</div>");
33405          }
33406          
33407          buf.push(
33408             '</a>',
33409             '<div class="x-clear"></div></div>',
33410             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33411             "</li>");
33412         
33413         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33414             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33415                                 n.nextSibling.ui.getEl(), buf.join(""));
33416         }else{
33417             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33418         }
33419         var el = this.wrap.firstChild;
33420         this.elRow = el;
33421         this.elNode = el.firstChild;
33422         this.ranchor = el.childNodes[1];
33423         this.ctNode = this.wrap.childNodes[1];
33424         var cs = el.firstChild.childNodes;
33425         this.indentNode = cs[0];
33426         this.ecNode = cs[1];
33427         this.iconNode = cs[2];
33428         var index = 3;
33429         if(cb){
33430             this.checkbox = cs[3];
33431             index++;
33432         }
33433         this.anchor = cs[index];
33434         
33435         this.textNode = cs[index].firstChild;
33436         
33437         //el.on("click", this.onClick, this);
33438         //el.on("dblclick", this.onDblClick, this);
33439         
33440         
33441        // console.log(this);
33442     },
33443     initEvents : function(){
33444         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33445         
33446             
33447         var a = this.ranchor;
33448
33449         var el = Roo.get(a);
33450
33451         if(Roo.isOpera){ // opera render bug ignores the CSS
33452             el.setStyle("text-decoration", "none");
33453         }
33454
33455         el.on("click", this.onClick, this);
33456         el.on("dblclick", this.onDblClick, this);
33457         el.on("contextmenu", this.onContextMenu, this);
33458         
33459     },
33460     
33461     /*onSelectedChange : function(state){
33462         if(state){
33463             this.focus();
33464             this.addClass("x-tree-selected");
33465         }else{
33466             //this.blur();
33467             this.removeClass("x-tree-selected");
33468         }
33469     },*/
33470     addClass : function(cls){
33471         if(this.elRow){
33472             Roo.fly(this.elRow).addClass(cls);
33473         }
33474         
33475     },
33476     
33477     
33478     removeClass : function(cls){
33479         if(this.elRow){
33480             Roo.fly(this.elRow).removeClass(cls);
33481         }
33482     }
33483
33484     
33485     
33486 });//<Script type="text/javascript">
33487
33488 /*
33489  * Based on:
33490  * Ext JS Library 1.1.1
33491  * Copyright(c) 2006-2007, Ext JS, LLC.
33492  *
33493  * Originally Released Under LGPL - original licence link has changed is not relivant.
33494  *
33495  * Fork - LGPL
33496  * <script type="text/javascript">
33497  */
33498  
33499
33500 /**
33501  * @class Roo.tree.ColumnTree
33502  * @extends Roo.data.TreePanel
33503  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33504  * @cfg {int} borderWidth  compined right/left border allowance
33505  * @constructor
33506  * @param {String/HTMLElement/Element} el The container element
33507  * @param {Object} config
33508  */
33509 Roo.tree.ColumnTree =  function(el, config)
33510 {
33511    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33512    this.addEvents({
33513         /**
33514         * @event resize
33515         * Fire this event on a container when it resizes
33516         * @param {int} w Width
33517         * @param {int} h Height
33518         */
33519        "resize" : true
33520     });
33521     this.on('resize', this.onResize, this);
33522 };
33523
33524 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33525     //lines:false,
33526     
33527     
33528     borderWidth: Roo.isBorderBox ? 0 : 2, 
33529     headEls : false,
33530     
33531     render : function(){
33532         // add the header.....
33533        
33534         Roo.tree.ColumnTree.superclass.render.apply(this);
33535         
33536         this.el.addClass('x-column-tree');
33537         
33538         this.headers = this.el.createChild(
33539             {cls:'x-tree-headers'},this.innerCt.dom);
33540    
33541         var cols = this.columns, c;
33542         var totalWidth = 0;
33543         this.headEls = [];
33544         var  len = cols.length;
33545         for(var i = 0; i < len; i++){
33546              c = cols[i];
33547              totalWidth += c.width;
33548             this.headEls.push(this.headers.createChild({
33549                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33550                  cn: {
33551                      cls:'x-tree-hd-text',
33552                      html: c.header
33553                  },
33554                  style:'width:'+(c.width-this.borderWidth)+'px;'
33555              }));
33556         }
33557         this.headers.createChild({cls:'x-clear'});
33558         // prevent floats from wrapping when clipped
33559         this.headers.setWidth(totalWidth);
33560         //this.innerCt.setWidth(totalWidth);
33561         this.innerCt.setStyle({ overflow: 'auto' });
33562         this.onResize(this.width, this.height);
33563              
33564         
33565     },
33566     onResize : function(w,h)
33567     {
33568         this.height = h;
33569         this.width = w;
33570         // resize cols..
33571         this.innerCt.setWidth(this.width);
33572         this.innerCt.setHeight(this.height-20);
33573         
33574         // headers...
33575         var cols = this.columns, c;
33576         var totalWidth = 0;
33577         var expEl = false;
33578         var len = cols.length;
33579         for(var i = 0; i < len; i++){
33580             c = cols[i];
33581             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33582                 // it's the expander..
33583                 expEl  = this.headEls[i];
33584                 continue;
33585             }
33586             totalWidth += c.width;
33587             
33588         }
33589         if (expEl) {
33590             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33591         }
33592         this.headers.setWidth(w-20);
33593
33594         
33595         
33596         
33597     }
33598 });
33599 /*
33600  * Based on:
33601  * Ext JS Library 1.1.1
33602  * Copyright(c) 2006-2007, Ext JS, LLC.
33603  *
33604  * Originally Released Under LGPL - original licence link has changed is not relivant.
33605  *
33606  * Fork - LGPL
33607  * <script type="text/javascript">
33608  */
33609  
33610 /**
33611  * @class Roo.menu.Menu
33612  * @extends Roo.util.Observable
33613  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33614  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33615  * @constructor
33616  * Creates a new Menu
33617  * @param {Object} config Configuration options
33618  */
33619 Roo.menu.Menu = function(config){
33620     Roo.apply(this, config);
33621     this.id = this.id || Roo.id();
33622     this.addEvents({
33623         /**
33624          * @event beforeshow
33625          * Fires before this menu is displayed
33626          * @param {Roo.menu.Menu} this
33627          */
33628         beforeshow : true,
33629         /**
33630          * @event beforehide
33631          * Fires before this menu is hidden
33632          * @param {Roo.menu.Menu} this
33633          */
33634         beforehide : true,
33635         /**
33636          * @event show
33637          * Fires after this menu is displayed
33638          * @param {Roo.menu.Menu} this
33639          */
33640         show : true,
33641         /**
33642          * @event hide
33643          * Fires after this menu is hidden
33644          * @param {Roo.menu.Menu} this
33645          */
33646         hide : true,
33647         /**
33648          * @event click
33649          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33650          * @param {Roo.menu.Menu} this
33651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33652          * @param {Roo.EventObject} e
33653          */
33654         click : true,
33655         /**
33656          * @event mouseover
33657          * Fires when the mouse is hovering over this menu
33658          * @param {Roo.menu.Menu} this
33659          * @param {Roo.EventObject} e
33660          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33661          */
33662         mouseover : true,
33663         /**
33664          * @event mouseout
33665          * Fires when the mouse exits this menu
33666          * @param {Roo.menu.Menu} this
33667          * @param {Roo.EventObject} e
33668          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33669          */
33670         mouseout : true,
33671         /**
33672          * @event itemclick
33673          * Fires when a menu item contained in this menu is clicked
33674          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33675          * @param {Roo.EventObject} e
33676          */
33677         itemclick: true
33678     });
33679     if (this.registerMenu) {
33680         Roo.menu.MenuMgr.register(this);
33681     }
33682     
33683     var mis = this.items;
33684     this.items = new Roo.util.MixedCollection();
33685     if(mis){
33686         this.add.apply(this, mis);
33687     }
33688 };
33689
33690 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33691     /**
33692      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33693      */
33694     minWidth : 120,
33695     /**
33696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33697      * for bottom-right shadow (defaults to "sides")
33698      */
33699     shadow : "sides",
33700     /**
33701      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33702      * this menu (defaults to "tl-tr?")
33703      */
33704     subMenuAlign : "tl-tr?",
33705     /**
33706      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33707      * relative to its element of origin (defaults to "tl-bl?")
33708      */
33709     defaultAlign : "tl-bl?",
33710     /**
33711      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33712      */
33713     allowOtherMenus : false,
33714     /**
33715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33716      */
33717     registerMenu : true,
33718
33719     hidden:true,
33720
33721     // private
33722     render : function(){
33723         if(this.el){
33724             return;
33725         }
33726         var el = this.el = new Roo.Layer({
33727             cls: "x-menu",
33728             shadow:this.shadow,
33729             constrain: false,
33730             parentEl: this.parentEl || document.body,
33731             zindex:15000
33732         });
33733
33734         this.keyNav = new Roo.menu.MenuNav(this);
33735
33736         if(this.plain){
33737             el.addClass("x-menu-plain");
33738         }
33739         if(this.cls){
33740             el.addClass(this.cls);
33741         }
33742         // generic focus element
33743         this.focusEl = el.createChild({
33744             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33745         });
33746         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33747         ul.on("click", this.onClick, this);
33748         ul.on("mouseover", this.onMouseOver, this);
33749         ul.on("mouseout", this.onMouseOut, this);
33750         this.items.each(function(item){
33751             var li = document.createElement("li");
33752             li.className = "x-menu-list-item";
33753             ul.dom.appendChild(li);
33754             item.render(li, this);
33755         }, this);
33756         this.ul = ul;
33757         this.autoWidth();
33758     },
33759
33760     // private
33761     autoWidth : function(){
33762         var el = this.el, ul = this.ul;
33763         if(!el){
33764             return;
33765         }
33766         var w = this.width;
33767         if(w){
33768             el.setWidth(w);
33769         }else if(Roo.isIE){
33770             el.setWidth(this.minWidth);
33771             var t = el.dom.offsetWidth; // force recalc
33772             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33773         }
33774     },
33775
33776     // private
33777     delayAutoWidth : function(){
33778         if(this.rendered){
33779             if(!this.awTask){
33780                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33781             }
33782             this.awTask.delay(20);
33783         }
33784     },
33785
33786     // private
33787     findTargetItem : function(e){
33788         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33789         if(t && t.menuItemId){
33790             return this.items.get(t.menuItemId);
33791         }
33792     },
33793
33794     // private
33795     onClick : function(e){
33796         var t;
33797         if(t = this.findTargetItem(e)){
33798             t.onClick(e);
33799             this.fireEvent("click", this, t, e);
33800         }
33801     },
33802
33803     // private
33804     setActiveItem : function(item, autoExpand){
33805         if(item != this.activeItem){
33806             if(this.activeItem){
33807                 this.activeItem.deactivate();
33808             }
33809             this.activeItem = item;
33810             item.activate(autoExpand);
33811         }else if(autoExpand){
33812             item.expandMenu();
33813         }
33814     },
33815
33816     // private
33817     tryActivate : function(start, step){
33818         var items = this.items;
33819         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33820             var item = items.get(i);
33821             if(!item.disabled && item.canActivate){
33822                 this.setActiveItem(item, false);
33823                 return item;
33824             }
33825         }
33826         return false;
33827     },
33828
33829     // private
33830     onMouseOver : function(e){
33831         var t;
33832         if(t = this.findTargetItem(e)){
33833             if(t.canActivate && !t.disabled){
33834                 this.setActiveItem(t, true);
33835             }
33836         }
33837         this.fireEvent("mouseover", this, e, t);
33838     },
33839
33840     // private
33841     onMouseOut : function(e){
33842         var t;
33843         if(t = this.findTargetItem(e)){
33844             if(t == this.activeItem && t.shouldDeactivate(e)){
33845                 this.activeItem.deactivate();
33846                 delete this.activeItem;
33847             }
33848         }
33849         this.fireEvent("mouseout", this, e, t);
33850     },
33851
33852     /**
33853      * Read-only.  Returns true if the menu is currently displayed, else false.
33854      * @type Boolean
33855      */
33856     isVisible : function(){
33857         return this.el && !this.hidden;
33858     },
33859
33860     /**
33861      * Displays this menu relative to another element
33862      * @param {String/HTMLElement/Roo.Element} element The element to align to
33863      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33864      * the element (defaults to this.defaultAlign)
33865      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33866      */
33867     show : function(el, pos, parentMenu){
33868         this.parentMenu = parentMenu;
33869         if(!this.el){
33870             this.render();
33871         }
33872         this.fireEvent("beforeshow", this);
33873         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33874     },
33875
33876     /**
33877      * Displays this menu at a specific xy position
33878      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33879      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33880      */
33881     showAt : function(xy, parentMenu, /* private: */_e){
33882         this.parentMenu = parentMenu;
33883         if(!this.el){
33884             this.render();
33885         }
33886         if(_e !== false){
33887             this.fireEvent("beforeshow", this);
33888             xy = this.el.adjustForConstraints(xy);
33889         }
33890         this.el.setXY(xy);
33891         this.el.show();
33892         this.hidden = false;
33893         this.focus();
33894         this.fireEvent("show", this);
33895     },
33896
33897     focus : function(){
33898         if(!this.hidden){
33899             this.doFocus.defer(50, this);
33900         }
33901     },
33902
33903     doFocus : function(){
33904         if(!this.hidden){
33905             this.focusEl.focus();
33906         }
33907     },
33908
33909     /**
33910      * Hides this menu and optionally all parent menus
33911      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33912      */
33913     hide : function(deep){
33914         if(this.el && this.isVisible()){
33915             this.fireEvent("beforehide", this);
33916             if(this.activeItem){
33917                 this.activeItem.deactivate();
33918                 this.activeItem = null;
33919             }
33920             this.el.hide();
33921             this.hidden = true;
33922             this.fireEvent("hide", this);
33923         }
33924         if(deep === true && this.parentMenu){
33925             this.parentMenu.hide(true);
33926         }
33927     },
33928
33929     /**
33930      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33931      * Any of the following are valid:
33932      * <ul>
33933      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33934      * <li>An HTMLElement object which will be converted to a menu item</li>
33935      * <li>A menu item config object that will be created as a new menu item</li>
33936      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33937      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33938      * </ul>
33939      * Usage:
33940      * <pre><code>
33941 // Create the menu
33942 var menu = new Roo.menu.Menu();
33943
33944 // Create a menu item to add by reference
33945 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33946
33947 // Add a bunch of items at once using different methods.
33948 // Only the last item added will be returned.
33949 var item = menu.add(
33950     menuItem,                // add existing item by ref
33951     'Dynamic Item',          // new TextItem
33952     '-',                     // new separator
33953     { text: 'Config Item' }  // new item by config
33954 );
33955 </code></pre>
33956      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33957      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33958      */
33959     add : function(){
33960         var a = arguments, l = a.length, item;
33961         for(var i = 0; i < l; i++){
33962             var el = a[i];
33963             if ((typeof(el) == "object") && el.xtype && el.xns) {
33964                 el = Roo.factory(el, Roo.menu);
33965             }
33966             
33967             if(el.render){ // some kind of Item
33968                 item = this.addItem(el);
33969             }else if(typeof el == "string"){ // string
33970                 if(el == "separator" || el == "-"){
33971                     item = this.addSeparator();
33972                 }else{
33973                     item = this.addText(el);
33974                 }
33975             }else if(el.tagName || el.el){ // element
33976                 item = this.addElement(el);
33977             }else if(typeof el == "object"){ // must be menu item config?
33978                 item = this.addMenuItem(el);
33979             }
33980         }
33981         return item;
33982     },
33983
33984     /**
33985      * Returns this menu's underlying {@link Roo.Element} object
33986      * @return {Roo.Element} The element
33987      */
33988     getEl : function(){
33989         if(!this.el){
33990             this.render();
33991         }
33992         return this.el;
33993     },
33994
33995     /**
33996      * Adds a separator bar to the menu
33997      * @return {Roo.menu.Item} The menu item that was added
33998      */
33999     addSeparator : function(){
34000         return this.addItem(new Roo.menu.Separator());
34001     },
34002
34003     /**
34004      * Adds an {@link Roo.Element} object to the menu
34005      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34006      * @return {Roo.menu.Item} The menu item that was added
34007      */
34008     addElement : function(el){
34009         return this.addItem(new Roo.menu.BaseItem(el));
34010     },
34011
34012     /**
34013      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34014      * @param {Roo.menu.Item} item The menu item to add
34015      * @return {Roo.menu.Item} The menu item that was added
34016      */
34017     addItem : function(item){
34018         this.items.add(item);
34019         if(this.ul){
34020             var li = document.createElement("li");
34021             li.className = "x-menu-list-item";
34022             this.ul.dom.appendChild(li);
34023             item.render(li, this);
34024             this.delayAutoWidth();
34025         }
34026         return item;
34027     },
34028
34029     /**
34030      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34031      * @param {Object} config A MenuItem config object
34032      * @return {Roo.menu.Item} The menu item that was added
34033      */
34034     addMenuItem : function(config){
34035         if(!(config instanceof Roo.menu.Item)){
34036             if(typeof config.checked == "boolean"){ // must be check menu item config?
34037                 config = new Roo.menu.CheckItem(config);
34038             }else{
34039                 config = new Roo.menu.Item(config);
34040             }
34041         }
34042         return this.addItem(config);
34043     },
34044
34045     /**
34046      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34047      * @param {String} text The text to display in the menu item
34048      * @return {Roo.menu.Item} The menu item that was added
34049      */
34050     addText : function(text){
34051         return this.addItem(new Roo.menu.TextItem({ text : text }));
34052     },
34053
34054     /**
34055      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34056      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34057      * @param {Roo.menu.Item} item The menu item to add
34058      * @return {Roo.menu.Item} The menu item that was added
34059      */
34060     insert : function(index, item){
34061         this.items.insert(index, item);
34062         if(this.ul){
34063             var li = document.createElement("li");
34064             li.className = "x-menu-list-item";
34065             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34066             item.render(li, this);
34067             this.delayAutoWidth();
34068         }
34069         return item;
34070     },
34071
34072     /**
34073      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34074      * @param {Roo.menu.Item} item The menu item to remove
34075      */
34076     remove : function(item){
34077         this.items.removeKey(item.id);
34078         item.destroy();
34079     },
34080
34081     /**
34082      * Removes and destroys all items in the menu
34083      */
34084     removeAll : function(){
34085         var f;
34086         while(f = this.items.first()){
34087             this.remove(f);
34088         }
34089     }
34090 });
34091
34092 // MenuNav is a private utility class used internally by the Menu
34093 Roo.menu.MenuNav = function(menu){
34094     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34095     this.scope = this.menu = menu;
34096 };
34097
34098 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34099     doRelay : function(e, h){
34100         var k = e.getKey();
34101         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34102             this.menu.tryActivate(0, 1);
34103             return false;
34104         }
34105         return h.call(this.scope || this, e, this.menu);
34106     },
34107
34108     up : function(e, m){
34109         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34110             m.tryActivate(m.items.length-1, -1);
34111         }
34112     },
34113
34114     down : function(e, m){
34115         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34116             m.tryActivate(0, 1);
34117         }
34118     },
34119
34120     right : function(e, m){
34121         if(m.activeItem){
34122             m.activeItem.expandMenu(true);
34123         }
34124     },
34125
34126     left : function(e, m){
34127         m.hide();
34128         if(m.parentMenu && m.parentMenu.activeItem){
34129             m.parentMenu.activeItem.activate();
34130         }
34131     },
34132
34133     enter : function(e, m){
34134         if(m.activeItem){
34135             e.stopPropagation();
34136             m.activeItem.onClick(e);
34137             m.fireEvent("click", this, m.activeItem);
34138             return true;
34139         }
34140     }
34141 });/*
34142  * Based on:
34143  * Ext JS Library 1.1.1
34144  * Copyright(c) 2006-2007, Ext JS, LLC.
34145  *
34146  * Originally Released Under LGPL - original licence link has changed is not relivant.
34147  *
34148  * Fork - LGPL
34149  * <script type="text/javascript">
34150  */
34151  
34152 /**
34153  * @class Roo.menu.MenuMgr
34154  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34155  * @singleton
34156  */
34157 Roo.menu.MenuMgr = function(){
34158    var menus, active, groups = {}, attached = false, lastShow = new Date();
34159
34160    // private - called when first menu is created
34161    function init(){
34162        menus = {};
34163        active = new Roo.util.MixedCollection();
34164        Roo.get(document).addKeyListener(27, function(){
34165            if(active.length > 0){
34166                hideAll();
34167            }
34168        });
34169    }
34170
34171    // private
34172    function hideAll(){
34173        if(active && active.length > 0){
34174            var c = active.clone();
34175            c.each(function(m){
34176                m.hide();
34177            });
34178        }
34179    }
34180
34181    // private
34182    function onHide(m){
34183        active.remove(m);
34184        if(active.length < 1){
34185            Roo.get(document).un("mousedown", onMouseDown);
34186            attached = false;
34187        }
34188    }
34189
34190    // private
34191    function onShow(m){
34192        var last = active.last();
34193        lastShow = new Date();
34194        active.add(m);
34195        if(!attached){
34196            Roo.get(document).on("mousedown", onMouseDown);
34197            attached = true;
34198        }
34199        if(m.parentMenu){
34200           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34201           m.parentMenu.activeChild = m;
34202        }else if(last && last.isVisible()){
34203           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34204        }
34205    }
34206
34207    // private
34208    function onBeforeHide(m){
34209        if(m.activeChild){
34210            m.activeChild.hide();
34211        }
34212        if(m.autoHideTimer){
34213            clearTimeout(m.autoHideTimer);
34214            delete m.autoHideTimer;
34215        }
34216    }
34217
34218    // private
34219    function onBeforeShow(m){
34220        var pm = m.parentMenu;
34221        if(!pm && !m.allowOtherMenus){
34222            hideAll();
34223        }else if(pm && pm.activeChild && active != m){
34224            pm.activeChild.hide();
34225        }
34226    }
34227
34228    // private
34229    function onMouseDown(e){
34230        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34231            hideAll();
34232        }
34233    }
34234
34235    // private
34236    function onBeforeCheck(mi, state){
34237        if(state){
34238            var g = groups[mi.group];
34239            for(var i = 0, l = g.length; i < l; i++){
34240                if(g[i] != mi){
34241                    g[i].setChecked(false);
34242                }
34243            }
34244        }
34245    }
34246
34247    return {
34248
34249        /**
34250         * Hides all menus that are currently visible
34251         */
34252        hideAll : function(){
34253             hideAll();  
34254        },
34255
34256        // private
34257        register : function(menu){
34258            if(!menus){
34259                init();
34260            }
34261            menus[menu.id] = menu;
34262            menu.on("beforehide", onBeforeHide);
34263            menu.on("hide", onHide);
34264            menu.on("beforeshow", onBeforeShow);
34265            menu.on("show", onShow);
34266            var g = menu.group;
34267            if(g && menu.events["checkchange"]){
34268                if(!groups[g]){
34269                    groups[g] = [];
34270                }
34271                groups[g].push(menu);
34272                menu.on("checkchange", onCheck);
34273            }
34274        },
34275
34276         /**
34277          * Returns a {@link Roo.menu.Menu} object
34278          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34279          * be used to generate and return a new Menu instance.
34280          */
34281        get : function(menu){
34282            if(typeof menu == "string"){ // menu id
34283                return menus[menu];
34284            }else if(menu.events){  // menu instance
34285                return menu;
34286            }else if(typeof menu.length == 'number'){ // array of menu items?
34287                return new Roo.menu.Menu({items:menu});
34288            }else{ // otherwise, must be a config
34289                return new Roo.menu.Menu(menu);
34290            }
34291        },
34292
34293        // private
34294        unregister : function(menu){
34295            delete menus[menu.id];
34296            menu.un("beforehide", onBeforeHide);
34297            menu.un("hide", onHide);
34298            menu.un("beforeshow", onBeforeShow);
34299            menu.un("show", onShow);
34300            var g = menu.group;
34301            if(g && menu.events["checkchange"]){
34302                groups[g].remove(menu);
34303                menu.un("checkchange", onCheck);
34304            }
34305        },
34306
34307        // private
34308        registerCheckable : function(menuItem){
34309            var g = menuItem.group;
34310            if(g){
34311                if(!groups[g]){
34312                    groups[g] = [];
34313                }
34314                groups[g].push(menuItem);
34315                menuItem.on("beforecheckchange", onBeforeCheck);
34316            }
34317        },
34318
34319        // private
34320        unregisterCheckable : function(menuItem){
34321            var g = menuItem.group;
34322            if(g){
34323                groups[g].remove(menuItem);
34324                menuItem.un("beforecheckchange", onBeforeCheck);
34325            }
34326        }
34327    };
34328 }();/*
34329  * Based on:
34330  * Ext JS Library 1.1.1
34331  * Copyright(c) 2006-2007, Ext JS, LLC.
34332  *
34333  * Originally Released Under LGPL - original licence link has changed is not relivant.
34334  *
34335  * Fork - LGPL
34336  * <script type="text/javascript">
34337  */
34338  
34339
34340 /**
34341  * @class Roo.menu.BaseItem
34342  * @extends Roo.Component
34343  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34344  * management and base configuration options shared by all menu components.
34345  * @constructor
34346  * Creates a new BaseItem
34347  * @param {Object} config Configuration options
34348  */
34349 Roo.menu.BaseItem = function(config){
34350     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34351
34352     this.addEvents({
34353         /**
34354          * @event click
34355          * Fires when this item is clicked
34356          * @param {Roo.menu.BaseItem} this
34357          * @param {Roo.EventObject} e
34358          */
34359         click: true,
34360         /**
34361          * @event activate
34362          * Fires when this item is activated
34363          * @param {Roo.menu.BaseItem} this
34364          */
34365         activate : true,
34366         /**
34367          * @event deactivate
34368          * Fires when this item is deactivated
34369          * @param {Roo.menu.BaseItem} this
34370          */
34371         deactivate : true
34372     });
34373
34374     if(this.handler){
34375         this.on("click", this.handler, this.scope, true);
34376     }
34377 };
34378
34379 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34380     /**
34381      * @cfg {Function} handler
34382      * A function that will handle the click event of this menu item (defaults to undefined)
34383      */
34384     /**
34385      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34386      */
34387     canActivate : false,
34388     /**
34389      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34390      */
34391     activeClass : "x-menu-item-active",
34392     /**
34393      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34394      */
34395     hideOnClick : true,
34396     /**
34397      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34398      */
34399     hideDelay : 100,
34400
34401     // private
34402     ctype: "Roo.menu.BaseItem",
34403
34404     // private
34405     actionMode : "container",
34406
34407     // private
34408     render : function(container, parentMenu){
34409         this.parentMenu = parentMenu;
34410         Roo.menu.BaseItem.superclass.render.call(this, container);
34411         this.container.menuItemId = this.id;
34412     },
34413
34414     // private
34415     onRender : function(container, position){
34416         this.el = Roo.get(this.el);
34417         container.dom.appendChild(this.el.dom);
34418     },
34419
34420     // private
34421     onClick : function(e){
34422         if(!this.disabled && this.fireEvent("click", this, e) !== false
34423                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34424             this.handleClick(e);
34425         }else{
34426             e.stopEvent();
34427         }
34428     },
34429
34430     // private
34431     activate : function(){
34432         if(this.disabled){
34433             return false;
34434         }
34435         var li = this.container;
34436         li.addClass(this.activeClass);
34437         this.region = li.getRegion().adjust(2, 2, -2, -2);
34438         this.fireEvent("activate", this);
34439         return true;
34440     },
34441
34442     // private
34443     deactivate : function(){
34444         this.container.removeClass(this.activeClass);
34445         this.fireEvent("deactivate", this);
34446     },
34447
34448     // private
34449     shouldDeactivate : function(e){
34450         return !this.region || !this.region.contains(e.getPoint());
34451     },
34452
34453     // private
34454     handleClick : function(e){
34455         if(this.hideOnClick){
34456             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34457         }
34458     },
34459
34460     // private
34461     expandMenu : function(autoActivate){
34462         // do nothing
34463     },
34464
34465     // private
34466     hideMenu : function(){
34467         // do nothing
34468     }
34469 });/*
34470  * Based on:
34471  * Ext JS Library 1.1.1
34472  * Copyright(c) 2006-2007, Ext JS, LLC.
34473  *
34474  * Originally Released Under LGPL - original licence link has changed is not relivant.
34475  *
34476  * Fork - LGPL
34477  * <script type="text/javascript">
34478  */
34479  
34480 /**
34481  * @class Roo.menu.Adapter
34482  * @extends Roo.menu.BaseItem
34483  * 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.
34484  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34485  * @constructor
34486  * Creates a new Adapter
34487  * @param {Object} config Configuration options
34488  */
34489 Roo.menu.Adapter = function(component, config){
34490     Roo.menu.Adapter.superclass.constructor.call(this, config);
34491     this.component = component;
34492 };
34493 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34494     // private
34495     canActivate : true,
34496
34497     // private
34498     onRender : function(container, position){
34499         this.component.render(container);
34500         this.el = this.component.getEl();
34501     },
34502
34503     // private
34504     activate : function(){
34505         if(this.disabled){
34506             return false;
34507         }
34508         this.component.focus();
34509         this.fireEvent("activate", this);
34510         return true;
34511     },
34512
34513     // private
34514     deactivate : function(){
34515         this.fireEvent("deactivate", this);
34516     },
34517
34518     // private
34519     disable : function(){
34520         this.component.disable();
34521         Roo.menu.Adapter.superclass.disable.call(this);
34522     },
34523
34524     // private
34525     enable : function(){
34526         this.component.enable();
34527         Roo.menu.Adapter.superclass.enable.call(this);
34528     }
34529 });/*
34530  * Based on:
34531  * Ext JS Library 1.1.1
34532  * Copyright(c) 2006-2007, Ext JS, LLC.
34533  *
34534  * Originally Released Under LGPL - original licence link has changed is not relivant.
34535  *
34536  * Fork - LGPL
34537  * <script type="text/javascript">
34538  */
34539
34540 /**
34541  * @class Roo.menu.TextItem
34542  * @extends Roo.menu.BaseItem
34543  * Adds a static text string to a menu, usually used as either a heading or group separator.
34544  * Note: old style constructor with text is still supported.
34545  * 
34546  * @constructor
34547  * Creates a new TextItem
34548  * @param {Object} cfg Configuration
34549  */
34550 Roo.menu.TextItem = function(cfg){
34551     if (typeof(cfg) == 'string') {
34552         this.text = cfg;
34553     } else {
34554         Roo.apply(this,cfg);
34555     }
34556     
34557     Roo.menu.TextItem.superclass.constructor.call(this);
34558 };
34559
34560 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34561     /**
34562      * @cfg {Boolean} text Text to show on item.
34563      */
34564     text : '',
34565     
34566     /**
34567      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34568      */
34569     hideOnClick : false,
34570     /**
34571      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34572      */
34573     itemCls : "x-menu-text",
34574
34575     // private
34576     onRender : function(){
34577         var s = document.createElement("span");
34578         s.className = this.itemCls;
34579         s.innerHTML = this.text;
34580         this.el = s;
34581         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34582     }
34583 });/*
34584  * Based on:
34585  * Ext JS Library 1.1.1
34586  * Copyright(c) 2006-2007, Ext JS, LLC.
34587  *
34588  * Originally Released Under LGPL - original licence link has changed is not relivant.
34589  *
34590  * Fork - LGPL
34591  * <script type="text/javascript">
34592  */
34593
34594 /**
34595  * @class Roo.menu.Separator
34596  * @extends Roo.menu.BaseItem
34597  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34598  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34599  * @constructor
34600  * @param {Object} config Configuration options
34601  */
34602 Roo.menu.Separator = function(config){
34603     Roo.menu.Separator.superclass.constructor.call(this, config);
34604 };
34605
34606 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34607     /**
34608      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34609      */
34610     itemCls : "x-menu-sep",
34611     /**
34612      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34613      */
34614     hideOnClick : false,
34615
34616     // private
34617     onRender : function(li){
34618         var s = document.createElement("span");
34619         s.className = this.itemCls;
34620         s.innerHTML = "&#160;";
34621         this.el = s;
34622         li.addClass("x-menu-sep-li");
34623         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34624     }
34625 });/*
34626  * Based on:
34627  * Ext JS Library 1.1.1
34628  * Copyright(c) 2006-2007, Ext JS, LLC.
34629  *
34630  * Originally Released Under LGPL - original licence link has changed is not relivant.
34631  *
34632  * Fork - LGPL
34633  * <script type="text/javascript">
34634  */
34635 /**
34636  * @class Roo.menu.Item
34637  * @extends Roo.menu.BaseItem
34638  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34639  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34640  * activation and click handling.
34641  * @constructor
34642  * Creates a new Item
34643  * @param {Object} config Configuration options
34644  */
34645 Roo.menu.Item = function(config){
34646     Roo.menu.Item.superclass.constructor.call(this, config);
34647     if(this.menu){
34648         this.menu = Roo.menu.MenuMgr.get(this.menu);
34649     }
34650 };
34651 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34652     
34653     /**
34654      * @cfg {String} text
34655      * The text to show on the menu item.
34656      */
34657     text: '',
34658      /**
34659      * @cfg {String} HTML to render in menu
34660      * The text to show on the menu item (HTML version).
34661      */
34662     html: '',
34663     /**
34664      * @cfg {String} icon
34665      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34666      */
34667     icon: undefined,
34668     /**
34669      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34670      */
34671     itemCls : "x-menu-item",
34672     /**
34673      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34674      */
34675     canActivate : true,
34676     /**
34677      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34678      */
34679     showDelay: 200,
34680     // doc'd in BaseItem
34681     hideDelay: 200,
34682
34683     // private
34684     ctype: "Roo.menu.Item",
34685     
34686     // private
34687     onRender : function(container, position){
34688         var el = document.createElement("a");
34689         el.hideFocus = true;
34690         el.unselectable = "on";
34691         el.href = this.href || "#";
34692         if(this.hrefTarget){
34693             el.target = this.hrefTarget;
34694         }
34695         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34696         
34697         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34698         
34699         el.innerHTML = String.format(
34700                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34701                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34702         this.el = el;
34703         Roo.menu.Item.superclass.onRender.call(this, container, position);
34704     },
34705
34706     /**
34707      * Sets the text to display in this menu item
34708      * @param {String} text The text to display
34709      * @param {Boolean} isHTML true to indicate text is pure html.
34710      */
34711     setText : function(text, isHTML){
34712         if (isHTML) {
34713             this.html = text;
34714         } else {
34715             this.text = text;
34716             this.html = '';
34717         }
34718         if(this.rendered){
34719             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34720      
34721             this.el.update(String.format(
34722                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34723                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34724             this.parentMenu.autoWidth();
34725         }
34726     },
34727
34728     // private
34729     handleClick : function(e){
34730         if(!this.href){ // if no link defined, stop the event automatically
34731             e.stopEvent();
34732         }
34733         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34734     },
34735
34736     // private
34737     activate : function(autoExpand){
34738         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34739             this.focus();
34740             if(autoExpand){
34741                 this.expandMenu();
34742             }
34743         }
34744         return true;
34745     },
34746
34747     // private
34748     shouldDeactivate : function(e){
34749         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34750             if(this.menu && this.menu.isVisible()){
34751                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34752             }
34753             return true;
34754         }
34755         return false;
34756     },
34757
34758     // private
34759     deactivate : function(){
34760         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34761         this.hideMenu();
34762     },
34763
34764     // private
34765     expandMenu : function(autoActivate){
34766         if(!this.disabled && this.menu){
34767             clearTimeout(this.hideTimer);
34768             delete this.hideTimer;
34769             if(!this.menu.isVisible() && !this.showTimer){
34770                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34771             }else if (this.menu.isVisible() && autoActivate){
34772                 this.menu.tryActivate(0, 1);
34773             }
34774         }
34775     },
34776
34777     // private
34778     deferExpand : function(autoActivate){
34779         delete this.showTimer;
34780         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34781         if(autoActivate){
34782             this.menu.tryActivate(0, 1);
34783         }
34784     },
34785
34786     // private
34787     hideMenu : function(){
34788         clearTimeout(this.showTimer);
34789         delete this.showTimer;
34790         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34791             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34792         }
34793     },
34794
34795     // private
34796     deferHide : function(){
34797         delete this.hideTimer;
34798         this.menu.hide();
34799     }
34800 });/*
34801  * Based on:
34802  * Ext JS Library 1.1.1
34803  * Copyright(c) 2006-2007, Ext JS, LLC.
34804  *
34805  * Originally Released Under LGPL - original licence link has changed is not relivant.
34806  *
34807  * Fork - LGPL
34808  * <script type="text/javascript">
34809  */
34810  
34811 /**
34812  * @class Roo.menu.CheckItem
34813  * @extends Roo.menu.Item
34814  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34815  * @constructor
34816  * Creates a new CheckItem
34817  * @param {Object} config Configuration options
34818  */
34819 Roo.menu.CheckItem = function(config){
34820     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34821     this.addEvents({
34822         /**
34823          * @event beforecheckchange
34824          * Fires before the checked value is set, providing an opportunity to cancel if needed
34825          * @param {Roo.menu.CheckItem} this
34826          * @param {Boolean} checked The new checked value that will be set
34827          */
34828         "beforecheckchange" : true,
34829         /**
34830          * @event checkchange
34831          * Fires after the checked value has been set
34832          * @param {Roo.menu.CheckItem} this
34833          * @param {Boolean} checked The checked value that was set
34834          */
34835         "checkchange" : true
34836     });
34837     if(this.checkHandler){
34838         this.on('checkchange', this.checkHandler, this.scope);
34839     }
34840 };
34841 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34842     /**
34843      * @cfg {String} group
34844      * All check items with the same group name will automatically be grouped into a single-select
34845      * radio button group (defaults to '')
34846      */
34847     /**
34848      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34849      */
34850     itemCls : "x-menu-item x-menu-check-item",
34851     /**
34852      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34853      */
34854     groupClass : "x-menu-group-item",
34855
34856     /**
34857      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34858      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34859      * initialized with checked = true will be rendered as checked.
34860      */
34861     checked: false,
34862
34863     // private
34864     ctype: "Roo.menu.CheckItem",
34865
34866     // private
34867     onRender : function(c){
34868         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34869         if(this.group){
34870             this.el.addClass(this.groupClass);
34871         }
34872         Roo.menu.MenuMgr.registerCheckable(this);
34873         if(this.checked){
34874             this.checked = false;
34875             this.setChecked(true, true);
34876         }
34877     },
34878
34879     // private
34880     destroy : function(){
34881         if(this.rendered){
34882             Roo.menu.MenuMgr.unregisterCheckable(this);
34883         }
34884         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34885     },
34886
34887     /**
34888      * Set the checked state of this item
34889      * @param {Boolean} checked The new checked value
34890      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34891      */
34892     setChecked : function(state, suppressEvent){
34893         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34894             if(this.container){
34895                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34896             }
34897             this.checked = state;
34898             if(suppressEvent !== true){
34899                 this.fireEvent("checkchange", this, state);
34900             }
34901         }
34902     },
34903
34904     // private
34905     handleClick : function(e){
34906        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34907            this.setChecked(!this.checked);
34908        }
34909        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34910     }
34911 });/*
34912  * Based on:
34913  * Ext JS Library 1.1.1
34914  * Copyright(c) 2006-2007, Ext JS, LLC.
34915  *
34916  * Originally Released Under LGPL - original licence link has changed is not relivant.
34917  *
34918  * Fork - LGPL
34919  * <script type="text/javascript">
34920  */
34921  
34922 /**
34923  * @class Roo.menu.DateItem
34924  * @extends Roo.menu.Adapter
34925  * A menu item that wraps the {@link Roo.DatPicker} component.
34926  * @constructor
34927  * Creates a new DateItem
34928  * @param {Object} config Configuration options
34929  */
34930 Roo.menu.DateItem = function(config){
34931     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34932     /** The Roo.DatePicker object @type Roo.DatePicker */
34933     this.picker = this.component;
34934     this.addEvents({select: true});
34935     
34936     this.picker.on("render", function(picker){
34937         picker.getEl().swallowEvent("click");
34938         picker.container.addClass("x-menu-date-item");
34939     });
34940
34941     this.picker.on("select", this.onSelect, this);
34942 };
34943
34944 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34945     // private
34946     onSelect : function(picker, date){
34947         this.fireEvent("select", this, date, picker);
34948         Roo.menu.DateItem.superclass.handleClick.call(this);
34949     }
34950 });/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960  
34961 /**
34962  * @class Roo.menu.ColorItem
34963  * @extends Roo.menu.Adapter
34964  * A menu item that wraps the {@link Roo.ColorPalette} component.
34965  * @constructor
34966  * Creates a new ColorItem
34967  * @param {Object} config Configuration options
34968  */
34969 Roo.menu.ColorItem = function(config){
34970     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34971     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34972     this.palette = this.component;
34973     this.relayEvents(this.palette, ["select"]);
34974     if(this.selectHandler){
34975         this.on('select', this.selectHandler, this.scope);
34976     }
34977 };
34978 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34979  * Based on:
34980  * Ext JS Library 1.1.1
34981  * Copyright(c) 2006-2007, Ext JS, LLC.
34982  *
34983  * Originally Released Under LGPL - original licence link has changed is not relivant.
34984  *
34985  * Fork - LGPL
34986  * <script type="text/javascript">
34987  */
34988  
34989
34990 /**
34991  * @class Roo.menu.DateMenu
34992  * @extends Roo.menu.Menu
34993  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34994  * @constructor
34995  * Creates a new DateMenu
34996  * @param {Object} config Configuration options
34997  */
34998 Roo.menu.DateMenu = function(config){
34999     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35000     this.plain = true;
35001     var di = new Roo.menu.DateItem(config);
35002     this.add(di);
35003     /**
35004      * The {@link Roo.DatePicker} instance for this DateMenu
35005      * @type DatePicker
35006      */
35007     this.picker = di.picker;
35008     /**
35009      * @event select
35010      * @param {DatePicker} picker
35011      * @param {Date} date
35012      */
35013     this.relayEvents(di, ["select"]);
35014
35015     this.on('beforeshow', function(){
35016         if(this.picker){
35017             this.picker.hideMonthPicker(true);
35018         }
35019     }, this);
35020 };
35021 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35022     cls:'x-date-menu'
35023 });/*
35024  * Based on:
35025  * Ext JS Library 1.1.1
35026  * Copyright(c) 2006-2007, Ext JS, LLC.
35027  *
35028  * Originally Released Under LGPL - original licence link has changed is not relivant.
35029  *
35030  * Fork - LGPL
35031  * <script type="text/javascript">
35032  */
35033  
35034
35035 /**
35036  * @class Roo.menu.ColorMenu
35037  * @extends Roo.menu.Menu
35038  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35039  * @constructor
35040  * Creates a new ColorMenu
35041  * @param {Object} config Configuration options
35042  */
35043 Roo.menu.ColorMenu = function(config){
35044     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35045     this.plain = true;
35046     var ci = new Roo.menu.ColorItem(config);
35047     this.add(ci);
35048     /**
35049      * The {@link Roo.ColorPalette} instance for this ColorMenu
35050      * @type ColorPalette
35051      */
35052     this.palette = ci.palette;
35053     /**
35054      * @event select
35055      * @param {ColorPalette} palette
35056      * @param {String} color
35057      */
35058     this.relayEvents(ci, ["select"]);
35059 };
35060 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * @class Roo.form.Field
35073  * @extends Roo.BoxComponent
35074  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35075  * @constructor
35076  * Creates a new Field
35077  * @param {Object} config Configuration options
35078  */
35079 Roo.form.Field = function(config){
35080     Roo.form.Field.superclass.constructor.call(this, config);
35081 };
35082
35083 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35084     /**
35085      * @cfg {String} fieldLabel Label to use when rendering a form.
35086      */
35087        /**
35088      * @cfg {String} qtip Mouse over tip
35089      */
35090      
35091     /**
35092      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35093      */
35094     invalidClass : "x-form-invalid",
35095     /**
35096      * @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")
35097      */
35098     invalidText : "The value in this field is invalid",
35099     /**
35100      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35101      */
35102     focusClass : "x-form-focus",
35103     /**
35104      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35105       automatic validation (defaults to "keyup").
35106      */
35107     validationEvent : "keyup",
35108     /**
35109      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35110      */
35111     validateOnBlur : true,
35112     /**
35113      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35114      */
35115     validationDelay : 250,
35116     /**
35117      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35118      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35119      */
35120     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35121     /**
35122      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35123      */
35124     fieldClass : "x-form-field",
35125     /**
35126      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35127      *<pre>
35128 Value         Description
35129 -----------   ----------------------------------------------------------------------
35130 qtip          Display a quick tip when the user hovers over the field
35131 title         Display a default browser title attribute popup
35132 under         Add a block div beneath the field containing the error text
35133 side          Add an error icon to the right of the field with a popup on hover
35134 [element id]  Add the error text directly to the innerHTML of the specified element
35135 </pre>
35136      */
35137     msgTarget : 'qtip',
35138     /**
35139      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35140      */
35141     msgFx : 'normal',
35142
35143     /**
35144      * @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.
35145      */
35146     readOnly : false,
35147
35148     /**
35149      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35150      */
35151     disabled : false,
35152
35153     /**
35154      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35155      */
35156     inputType : undefined,
35157     
35158     /**
35159      * @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).
35160          */
35161         tabIndex : undefined,
35162         
35163     // private
35164     isFormField : true,
35165
35166     // private
35167     hasFocus : false,
35168     /**
35169      * @property {Roo.Element} fieldEl
35170      * Element Containing the rendered Field (with label etc.)
35171      */
35172     /**
35173      * @cfg {Mixed} value A value to initialize this field with.
35174      */
35175     value : undefined,
35176
35177     /**
35178      * @cfg {String} name The field's HTML name attribute.
35179      */
35180     /**
35181      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35182      */
35183
35184         // private ??
35185         initComponent : function(){
35186         Roo.form.Field.superclass.initComponent.call(this);
35187         this.addEvents({
35188             /**
35189              * @event focus
35190              * Fires when this field receives input focus.
35191              * @param {Roo.form.Field} this
35192              */
35193             focus : true,
35194             /**
35195              * @event blur
35196              * Fires when this field loses input focus.
35197              * @param {Roo.form.Field} this
35198              */
35199             blur : true,
35200             /**
35201              * @event specialkey
35202              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35203              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35204              * @param {Roo.form.Field} this
35205              * @param {Roo.EventObject} e The event object
35206              */
35207             specialkey : true,
35208             /**
35209              * @event change
35210              * Fires just before the field blurs if the field value has changed.
35211              * @param {Roo.form.Field} this
35212              * @param {Mixed} newValue The new value
35213              * @param {Mixed} oldValue The original value
35214              */
35215             change : true,
35216             /**
35217              * @event invalid
35218              * Fires after the field has been marked as invalid.
35219              * @param {Roo.form.Field} this
35220              * @param {String} msg The validation message
35221              */
35222             invalid : true,
35223             /**
35224              * @event valid
35225              * Fires after the field has been validated with no errors.
35226              * @param {Roo.form.Field} this
35227              */
35228             valid : true,
35229              /**
35230              * @event keyup
35231              * Fires after the key up
35232              * @param {Roo.form.Field} this
35233              * @param {Roo.EventObject}  e The event Object
35234              */
35235             keyup : true
35236         });
35237     },
35238
35239     /**
35240      * Returns the name attribute of the field if available
35241      * @return {String} name The field name
35242      */
35243     getName: function(){
35244          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35245     },
35246
35247     // private
35248     onRender : function(ct, position){
35249         Roo.form.Field.superclass.onRender.call(this, ct, position);
35250         if(!this.el){
35251             var cfg = this.getAutoCreate();
35252             if(!cfg.name){
35253                 cfg.name = this.name || this.id;
35254             }
35255             if(this.inputType){
35256                 cfg.type = this.inputType;
35257             }
35258             this.el = ct.createChild(cfg, position);
35259         }
35260         var type = this.el.dom.type;
35261         if(type){
35262             if(type == 'password'){
35263                 type = 'text';
35264             }
35265             this.el.addClass('x-form-'+type);
35266         }
35267         if(this.readOnly){
35268             this.el.dom.readOnly = true;
35269         }
35270         if(this.tabIndex !== undefined){
35271             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35272         }
35273
35274         this.el.addClass([this.fieldClass, this.cls]);
35275         this.initValue();
35276     },
35277
35278     /**
35279      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35280      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35281      * @return {Roo.form.Field} this
35282      */
35283     applyTo : function(target){
35284         this.allowDomMove = false;
35285         this.el = Roo.get(target);
35286         this.render(this.el.dom.parentNode);
35287         return this;
35288     },
35289
35290     // private
35291     initValue : function(){
35292         if(this.value !== undefined){
35293             this.setValue(this.value);
35294         }else if(this.el.dom.value.length > 0){
35295             this.setValue(this.el.dom.value);
35296         }
35297     },
35298
35299     /**
35300      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35301      */
35302     isDirty : function() {
35303         if(this.disabled) {
35304             return false;
35305         }
35306         return String(this.getValue()) !== String(this.originalValue);
35307     },
35308
35309     // private
35310     afterRender : function(){
35311         Roo.form.Field.superclass.afterRender.call(this);
35312         this.initEvents();
35313     },
35314
35315     // private
35316     fireKey : function(e){
35317         //Roo.log('field ' + e.getKey());
35318         if(e.isNavKeyPress()){
35319             this.fireEvent("specialkey", this, e);
35320         }
35321     },
35322
35323     /**
35324      * Resets the current field value to the originally loaded value and clears any validation messages
35325      */
35326     reset : function(){
35327         this.setValue(this.originalValue);
35328         this.clearInvalid();
35329     },
35330
35331     // private
35332     initEvents : function(){
35333         // safari killled keypress - so keydown is now used..
35334         this.el.on("keydown" , this.fireKey,  this);
35335         this.el.on("focus", this.onFocus,  this);
35336         this.el.on("blur", this.onBlur,  this);
35337         this.el.relayEvent('keyup', this);
35338
35339         // reference to original value for reset
35340         this.originalValue = this.getValue();
35341     },
35342
35343     // private
35344     onFocus : function(){
35345         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35346             this.el.addClass(this.focusClass);
35347         }
35348         if(!this.hasFocus){
35349             this.hasFocus = true;
35350             this.startValue = this.getValue();
35351             this.fireEvent("focus", this);
35352         }
35353     },
35354
35355     beforeBlur : Roo.emptyFn,
35356
35357     // private
35358     onBlur : function(){
35359         this.beforeBlur();
35360         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35361             this.el.removeClass(this.focusClass);
35362         }
35363         this.hasFocus = false;
35364         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35365             this.validate();
35366         }
35367         var v = this.getValue();
35368         if(String(v) !== String(this.startValue)){
35369             this.fireEvent('change', this, v, this.startValue);
35370         }
35371         this.fireEvent("blur", this);
35372     },
35373
35374     /**
35375      * Returns whether or not the field value is currently valid
35376      * @param {Boolean} preventMark True to disable marking the field invalid
35377      * @return {Boolean} True if the value is valid, else false
35378      */
35379     isValid : function(preventMark){
35380         if(this.disabled){
35381             return true;
35382         }
35383         var restore = this.preventMark;
35384         this.preventMark = preventMark === true;
35385         var v = this.validateValue(this.processValue(this.getRawValue()));
35386         this.preventMark = restore;
35387         return v;
35388     },
35389
35390     /**
35391      * Validates the field value
35392      * @return {Boolean} True if the value is valid, else false
35393      */
35394     validate : function(){
35395         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35396             this.clearInvalid();
35397             return true;
35398         }
35399         return false;
35400     },
35401
35402     processValue : function(value){
35403         return value;
35404     },
35405
35406     // private
35407     // Subclasses should provide the validation implementation by overriding this
35408     validateValue : function(value){
35409         return true;
35410     },
35411
35412     /**
35413      * Mark this field as invalid
35414      * @param {String} msg The validation message
35415      */
35416     markInvalid : function(msg){
35417         if(!this.rendered || this.preventMark){ // not rendered
35418             return;
35419         }
35420         this.el.addClass(this.invalidClass);
35421         msg = msg || this.invalidText;
35422         switch(this.msgTarget){
35423             case 'qtip':
35424                 this.el.dom.qtip = msg;
35425                 this.el.dom.qclass = 'x-form-invalid-tip';
35426                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35427                     Roo.QuickTips.enable();
35428                 }
35429                 break;
35430             case 'title':
35431                 this.el.dom.title = msg;
35432                 break;
35433             case 'under':
35434                 if(!this.errorEl){
35435                     var elp = this.el.findParent('.x-form-element', 5, true);
35436                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35437                     this.errorEl.setWidth(elp.getWidth(true)-20);
35438                 }
35439                 this.errorEl.update(msg);
35440                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35441                 break;
35442             case 'side':
35443                 if(!this.errorIcon){
35444                     var elp = this.el.findParent('.x-form-element', 5, true);
35445                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35446                 }
35447                 this.alignErrorIcon();
35448                 this.errorIcon.dom.qtip = msg;
35449                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35450                 this.errorIcon.show();
35451                 this.on('resize', this.alignErrorIcon, this);
35452                 break;
35453             default:
35454                 var t = Roo.getDom(this.msgTarget);
35455                 t.innerHTML = msg;
35456                 t.style.display = this.msgDisplay;
35457                 break;
35458         }
35459         this.fireEvent('invalid', this, msg);
35460     },
35461
35462     // private
35463     alignErrorIcon : function(){
35464         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35465     },
35466
35467     /**
35468      * Clear any invalid styles/messages for this field
35469      */
35470     clearInvalid : function(){
35471         if(!this.rendered || this.preventMark){ // not rendered
35472             return;
35473         }
35474         this.el.removeClass(this.invalidClass);
35475         switch(this.msgTarget){
35476             case 'qtip':
35477                 this.el.dom.qtip = '';
35478                 break;
35479             case 'title':
35480                 this.el.dom.title = '';
35481                 break;
35482             case 'under':
35483                 if(this.errorEl){
35484                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35485                 }
35486                 break;
35487             case 'side':
35488                 if(this.errorIcon){
35489                     this.errorIcon.dom.qtip = '';
35490                     this.errorIcon.hide();
35491                     this.un('resize', this.alignErrorIcon, this);
35492                 }
35493                 break;
35494             default:
35495                 var t = Roo.getDom(this.msgTarget);
35496                 t.innerHTML = '';
35497                 t.style.display = 'none';
35498                 break;
35499         }
35500         this.fireEvent('valid', this);
35501     },
35502
35503     /**
35504      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35505      * @return {Mixed} value The field value
35506      */
35507     getRawValue : function(){
35508         var v = this.el.getValue();
35509         if(v === this.emptyText){
35510             v = '';
35511         }
35512         return v;
35513     },
35514
35515     /**
35516      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35517      * @return {Mixed} value The field value
35518      */
35519     getValue : function(){
35520         var v = this.el.getValue();
35521         if(v === this.emptyText || v === undefined){
35522             v = '';
35523         }
35524         return v;
35525     },
35526
35527     /**
35528      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35529      * @param {Mixed} value The value to set
35530      */
35531     setRawValue : function(v){
35532         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35533     },
35534
35535     /**
35536      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35537      * @param {Mixed} value The value to set
35538      */
35539     setValue : function(v){
35540         this.value = v;
35541         if(this.rendered){
35542             this.el.dom.value = (v === null || v === undefined ? '' : v);
35543             this.validate();
35544         }
35545     },
35546
35547     adjustSize : function(w, h){
35548         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35549         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35550         return s;
35551     },
35552
35553     adjustWidth : function(tag, w){
35554         tag = tag.toLowerCase();
35555         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35556             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35557                 if(tag == 'input'){
35558                     return w + 2;
35559                 }
35560                 if(tag = 'textarea'){
35561                     return w-2;
35562                 }
35563             }else if(Roo.isOpera){
35564                 if(tag == 'input'){
35565                     return w + 2;
35566                 }
35567                 if(tag = 'textarea'){
35568                     return w-2;
35569                 }
35570             }
35571         }
35572         return w;
35573     }
35574 });
35575
35576
35577 // anything other than normal should be considered experimental
35578 Roo.form.Field.msgFx = {
35579     normal : {
35580         show: function(msgEl, f){
35581             msgEl.setDisplayed('block');
35582         },
35583
35584         hide : function(msgEl, f){
35585             msgEl.setDisplayed(false).update('');
35586         }
35587     },
35588
35589     slide : {
35590         show: function(msgEl, f){
35591             msgEl.slideIn('t', {stopFx:true});
35592         },
35593
35594         hide : function(msgEl, f){
35595             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35596         }
35597     },
35598
35599     slideRight : {
35600         show: function(msgEl, f){
35601             msgEl.fixDisplay();
35602             msgEl.alignTo(f.el, 'tl-tr');
35603             msgEl.slideIn('l', {stopFx:true});
35604         },
35605
35606         hide : function(msgEl, f){
35607             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35608         }
35609     }
35610 };/*
35611  * Based on:
35612  * Ext JS Library 1.1.1
35613  * Copyright(c) 2006-2007, Ext JS, LLC.
35614  *
35615  * Originally Released Under LGPL - original licence link has changed is not relivant.
35616  *
35617  * Fork - LGPL
35618  * <script type="text/javascript">
35619  */
35620  
35621
35622 /**
35623  * @class Roo.form.TextField
35624  * @extends Roo.form.Field
35625  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35626  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35627  * @constructor
35628  * Creates a new TextField
35629  * @param {Object} config Configuration options
35630  */
35631 Roo.form.TextField = function(config){
35632     Roo.form.TextField.superclass.constructor.call(this, config);
35633     this.addEvents({
35634         /**
35635          * @event autosize
35636          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35637          * according to the default logic, but this event provides a hook for the developer to apply additional
35638          * logic at runtime to resize the field if needed.
35639              * @param {Roo.form.Field} this This text field
35640              * @param {Number} width The new field width
35641              */
35642         autosize : true
35643     });
35644 };
35645
35646 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35647     /**
35648      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35649      */
35650     grow : false,
35651     /**
35652      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35653      */
35654     growMin : 30,
35655     /**
35656      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35657      */
35658     growMax : 800,
35659     /**
35660      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35661      */
35662     vtype : null,
35663     /**
35664      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35665      */
35666     maskRe : null,
35667     /**
35668      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35669      */
35670     disableKeyFilter : false,
35671     /**
35672      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35673      */
35674     allowBlank : true,
35675     /**
35676      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35677      */
35678     minLength : 0,
35679     /**
35680      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35681      */
35682     maxLength : Number.MAX_VALUE,
35683     /**
35684      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35685      */
35686     minLengthText : "The minimum length for this field is {0}",
35687     /**
35688      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35689      */
35690     maxLengthText : "The maximum length for this field is {0}",
35691     /**
35692      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35693      */
35694     selectOnFocus : false,
35695     /**
35696      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35697      */
35698     blankText : "This field is required",
35699     /**
35700      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35701      * If available, this function will be called only after the basic validators all return true, and will be passed the
35702      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35703      */
35704     validator : null,
35705     /**
35706      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35707      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35708      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35709      */
35710     regex : null,
35711     /**
35712      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35713      */
35714     regexText : "",
35715     /**
35716      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35717      */
35718     emptyText : null,
35719     /**
35720      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35721      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35722      */
35723     emptyClass : 'x-form-empty-field',
35724
35725     // private
35726     initEvents : function(){
35727         Roo.form.TextField.superclass.initEvents.call(this);
35728         if(this.validationEvent == 'keyup'){
35729             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35730             this.el.on('keyup', this.filterValidation, this);
35731         }
35732         else if(this.validationEvent !== false){
35733             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35734         }
35735         if(this.selectOnFocus || this.emptyText){
35736             this.on("focus", this.preFocus, this);
35737             if(this.emptyText){
35738                 this.on('blur', this.postBlur, this);
35739                 this.applyEmptyText();
35740             }
35741         }
35742         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35743             this.el.on("keypress", this.filterKeys, this);
35744         }
35745         if(this.grow){
35746             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35747             this.el.on("click", this.autoSize,  this);
35748         }
35749     },
35750
35751     processValue : function(value){
35752         if(this.stripCharsRe){
35753             var newValue = value.replace(this.stripCharsRe, '');
35754             if(newValue !== value){
35755                 this.setRawValue(newValue);
35756                 return newValue;
35757             }
35758         }
35759         return value;
35760     },
35761
35762     filterValidation : function(e){
35763         if(!e.isNavKeyPress()){
35764             this.validationTask.delay(this.validationDelay);
35765         }
35766     },
35767
35768     // private
35769     onKeyUp : function(e){
35770         if(!e.isNavKeyPress()){
35771             this.autoSize();
35772         }
35773     },
35774
35775     /**
35776      * Resets the current field value to the originally-loaded value and clears any validation messages.
35777      * Also adds emptyText and emptyClass if the original value was blank.
35778      */
35779     reset : function(){
35780         Roo.form.TextField.superclass.reset.call(this);
35781         this.applyEmptyText();
35782     },
35783
35784     applyEmptyText : function(){
35785         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35786             this.setRawValue(this.emptyText);
35787             this.el.addClass(this.emptyClass);
35788         }
35789     },
35790
35791     // private
35792     preFocus : function(){
35793         if(this.emptyText){
35794             if(this.el.dom.value == this.emptyText){
35795                 this.setRawValue('');
35796             }
35797             this.el.removeClass(this.emptyClass);
35798         }
35799         if(this.selectOnFocus){
35800             this.el.dom.select();
35801         }
35802     },
35803
35804     // private
35805     postBlur : function(){
35806         this.applyEmptyText();
35807     },
35808
35809     // private
35810     filterKeys : function(e){
35811         var k = e.getKey();
35812         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35813             return;
35814         }
35815         var c = e.getCharCode(), cc = String.fromCharCode(c);
35816         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35817             return;
35818         }
35819         if(!this.maskRe.test(cc)){
35820             e.stopEvent();
35821         }
35822     },
35823
35824     setValue : function(v){
35825         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35826             this.el.removeClass(this.emptyClass);
35827         }
35828         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35829         this.applyEmptyText();
35830         this.autoSize();
35831     },
35832
35833     /**
35834      * Validates a value according to the field's validation rules and marks the field as invalid
35835      * if the validation fails
35836      * @param {Mixed} value The value to validate
35837      * @return {Boolean} True if the value is valid, else false
35838      */
35839     validateValue : function(value){
35840         if(value.length < 1 || value === this.emptyText){ // if it's blank
35841              if(this.allowBlank){
35842                 this.clearInvalid();
35843                 return true;
35844              }else{
35845                 this.markInvalid(this.blankText);
35846                 return false;
35847              }
35848         }
35849         if(value.length < this.minLength){
35850             this.markInvalid(String.format(this.minLengthText, this.minLength));
35851             return false;
35852         }
35853         if(value.length > this.maxLength){
35854             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35855             return false;
35856         }
35857         if(this.vtype){
35858             var vt = Roo.form.VTypes;
35859             if(!vt[this.vtype](value, this)){
35860                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35861                 return false;
35862             }
35863         }
35864         if(typeof this.validator == "function"){
35865             var msg = this.validator(value);
35866             if(msg !== true){
35867                 this.markInvalid(msg);
35868                 return false;
35869             }
35870         }
35871         if(this.regex && !this.regex.test(value)){
35872             this.markInvalid(this.regexText);
35873             return false;
35874         }
35875         return true;
35876     },
35877
35878     /**
35879      * Selects text in this field
35880      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35881      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35882      */
35883     selectText : function(start, end){
35884         var v = this.getRawValue();
35885         if(v.length > 0){
35886             start = start === undefined ? 0 : start;
35887             end = end === undefined ? v.length : end;
35888             var d = this.el.dom;
35889             if(d.setSelectionRange){
35890                 d.setSelectionRange(start, end);
35891             }else if(d.createTextRange){
35892                 var range = d.createTextRange();
35893                 range.moveStart("character", start);
35894                 range.moveEnd("character", v.length-end);
35895                 range.select();
35896             }
35897         }
35898     },
35899
35900     /**
35901      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35902      * This only takes effect if grow = true, and fires the autosize event.
35903      */
35904     autoSize : function(){
35905         if(!this.grow || !this.rendered){
35906             return;
35907         }
35908         if(!this.metrics){
35909             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35910         }
35911         var el = this.el;
35912         var v = el.dom.value;
35913         var d = document.createElement('div');
35914         d.appendChild(document.createTextNode(v));
35915         v = d.innerHTML;
35916         d = null;
35917         v += "&#160;";
35918         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35919         this.el.setWidth(w);
35920         this.fireEvent("autosize", this, w);
35921     }
35922 });/*
35923  * Based on:
35924  * Ext JS Library 1.1.1
35925  * Copyright(c) 2006-2007, Ext JS, LLC.
35926  *
35927  * Originally Released Under LGPL - original licence link has changed is not relivant.
35928  *
35929  * Fork - LGPL
35930  * <script type="text/javascript">
35931  */
35932  
35933 /**
35934  * @class Roo.form.Hidden
35935  * @extends Roo.form.TextField
35936  * Simple Hidden element used on forms 
35937  * 
35938  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35939  * 
35940  * @constructor
35941  * Creates a new Hidden form element.
35942  * @param {Object} config Configuration options
35943  */
35944
35945
35946
35947 // easy hidden field...
35948 Roo.form.Hidden = function(config){
35949     Roo.form.Hidden.superclass.constructor.call(this, config);
35950 };
35951   
35952 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35953     fieldLabel:      '',
35954     inputType:      'hidden',
35955     width:          50,
35956     allowBlank:     true,
35957     labelSeparator: '',
35958     hidden:         true,
35959     itemCls :       'x-form-item-display-none'
35960
35961
35962 });
35963
35964
35965 /*
35966  * Based on:
35967  * Ext JS Library 1.1.1
35968  * Copyright(c) 2006-2007, Ext JS, LLC.
35969  *
35970  * Originally Released Under LGPL - original licence link has changed is not relivant.
35971  *
35972  * Fork - LGPL
35973  * <script type="text/javascript">
35974  */
35975  
35976 /**
35977  * @class Roo.form.TriggerField
35978  * @extends Roo.form.TextField
35979  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35980  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35981  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35982  * for which you can provide a custom implementation.  For example:
35983  * <pre><code>
35984 var trigger = new Roo.form.TriggerField();
35985 trigger.onTriggerClick = myTriggerFn;
35986 trigger.applyTo('my-field');
35987 </code></pre>
35988  *
35989  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35990  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35991  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35992  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35993  * @constructor
35994  * Create a new TriggerField.
35995  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35996  * to the base TextField)
35997  */
35998 Roo.form.TriggerField = function(config){
35999     this.mimicing = false;
36000     Roo.form.TriggerField.superclass.constructor.call(this, config);
36001 };
36002
36003 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36004     /**
36005      * @cfg {String} triggerClass A CSS class to apply to the trigger
36006      */
36007     /**
36008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36009      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36010      */
36011     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36012     /**
36013      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36014      */
36015     hideTrigger:false,
36016
36017     /** @cfg {Boolean} grow @hide */
36018     /** @cfg {Number} growMin @hide */
36019     /** @cfg {Number} growMax @hide */
36020
36021     /**
36022      * @hide 
36023      * @method
36024      */
36025     autoSize: Roo.emptyFn,
36026     // private
36027     monitorTab : true,
36028     // private
36029     deferHeight : true,
36030
36031     
36032     actionMode : 'wrap',
36033     // private
36034     onResize : function(w, h){
36035         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36036         if(typeof w == 'number'){
36037             var x = w - this.trigger.getWidth();
36038             this.el.setWidth(this.adjustWidth('input', x));
36039             this.trigger.setStyle('left', x+'px');
36040         }
36041     },
36042
36043     // private
36044     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36045
36046     // private
36047     getResizeEl : function(){
36048         return this.wrap;
36049     },
36050
36051     // private
36052     getPositionEl : function(){
36053         return this.wrap;
36054     },
36055
36056     // private
36057     alignErrorIcon : function(){
36058         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36059     },
36060
36061     // private
36062     onRender : function(ct, position){
36063         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36064         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36065         this.trigger = this.wrap.createChild(this.triggerConfig ||
36066                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36067         if(this.hideTrigger){
36068             this.trigger.setDisplayed(false);
36069         }
36070         this.initTrigger();
36071         if(!this.width){
36072             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36073         }
36074     },
36075
36076     // private
36077     initTrigger : function(){
36078         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36079         this.trigger.addClassOnOver('x-form-trigger-over');
36080         this.trigger.addClassOnClick('x-form-trigger-click');
36081     },
36082
36083     // private
36084     onDestroy : function(){
36085         if(this.trigger){
36086             this.trigger.removeAllListeners();
36087             this.trigger.remove();
36088         }
36089         if(this.wrap){
36090             this.wrap.remove();
36091         }
36092         Roo.form.TriggerField.superclass.onDestroy.call(this);
36093     },
36094
36095     // private
36096     onFocus : function(){
36097         Roo.form.TriggerField.superclass.onFocus.call(this);
36098         if(!this.mimicing){
36099             this.wrap.addClass('x-trigger-wrap-focus');
36100             this.mimicing = true;
36101             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36102             if(this.monitorTab){
36103                 this.el.on("keydown", this.checkTab, this);
36104             }
36105         }
36106     },
36107
36108     // private
36109     checkTab : function(e){
36110         if(e.getKey() == e.TAB){
36111             this.triggerBlur();
36112         }
36113     },
36114
36115     // private
36116     onBlur : function(){
36117         // do nothing
36118     },
36119
36120     // private
36121     mimicBlur : function(e, t){
36122         if(!this.wrap.contains(t) && this.validateBlur()){
36123             this.triggerBlur();
36124         }
36125     },
36126
36127     // private
36128     triggerBlur : function(){
36129         this.mimicing = false;
36130         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36131         if(this.monitorTab){
36132             this.el.un("keydown", this.checkTab, this);
36133         }
36134         this.wrap.removeClass('x-trigger-wrap-focus');
36135         Roo.form.TriggerField.superclass.onBlur.call(this);
36136     },
36137
36138     // private
36139     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36140     validateBlur : function(e, t){
36141         return true;
36142     },
36143
36144     // private
36145     onDisable : function(){
36146         Roo.form.TriggerField.superclass.onDisable.call(this);
36147         if(this.wrap){
36148             this.wrap.addClass('x-item-disabled');
36149         }
36150     },
36151
36152     // private
36153     onEnable : function(){
36154         Roo.form.TriggerField.superclass.onEnable.call(this);
36155         if(this.wrap){
36156             this.wrap.removeClass('x-item-disabled');
36157         }
36158     },
36159
36160     // private
36161     onShow : function(){
36162         var ae = this.getActionEl();
36163         
36164         if(ae){
36165             ae.dom.style.display = '';
36166             ae.dom.style.visibility = 'visible';
36167         }
36168     },
36169
36170     // private
36171     
36172     onHide : function(){
36173         var ae = this.getActionEl();
36174         ae.dom.style.display = 'none';
36175     },
36176
36177     /**
36178      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36179      * by an implementing function.
36180      * @method
36181      * @param {EventObject} e
36182      */
36183     onTriggerClick : Roo.emptyFn
36184 });
36185
36186 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36187 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36188 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36189 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36190     initComponent : function(){
36191         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36192
36193         this.triggerConfig = {
36194             tag:'span', cls:'x-form-twin-triggers', cn:[
36195             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36196             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36197         ]};
36198     },
36199
36200     getTrigger : function(index){
36201         return this.triggers[index];
36202     },
36203
36204     initTrigger : function(){
36205         var ts = this.trigger.select('.x-form-trigger', true);
36206         this.wrap.setStyle('overflow', 'hidden');
36207         var triggerField = this;
36208         ts.each(function(t, all, index){
36209             t.hide = function(){
36210                 var w = triggerField.wrap.getWidth();
36211                 this.dom.style.display = 'none';
36212                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36213             };
36214             t.show = function(){
36215                 var w = triggerField.wrap.getWidth();
36216                 this.dom.style.display = '';
36217                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36218             };
36219             var triggerIndex = 'Trigger'+(index+1);
36220
36221             if(this['hide'+triggerIndex]){
36222                 t.dom.style.display = 'none';
36223             }
36224             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36225             t.addClassOnOver('x-form-trigger-over');
36226             t.addClassOnClick('x-form-trigger-click');
36227         }, this);
36228         this.triggers = ts.elements;
36229     },
36230
36231     onTrigger1Click : Roo.emptyFn,
36232     onTrigger2Click : Roo.emptyFn
36233 });/*
36234  * Based on:
36235  * Ext JS Library 1.1.1
36236  * Copyright(c) 2006-2007, Ext JS, LLC.
36237  *
36238  * Originally Released Under LGPL - original licence link has changed is not relivant.
36239  *
36240  * Fork - LGPL
36241  * <script type="text/javascript">
36242  */
36243  
36244 /**
36245  * @class Roo.form.TextArea
36246  * @extends Roo.form.TextField
36247  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36248  * support for auto-sizing.
36249  * @constructor
36250  * Creates a new TextArea
36251  * @param {Object} config Configuration options
36252  */
36253 Roo.form.TextArea = function(config){
36254     Roo.form.TextArea.superclass.constructor.call(this, config);
36255     // these are provided exchanges for backwards compat
36256     // minHeight/maxHeight were replaced by growMin/growMax to be
36257     // compatible with TextField growing config values
36258     if(this.minHeight !== undefined){
36259         this.growMin = this.minHeight;
36260     }
36261     if(this.maxHeight !== undefined){
36262         this.growMax = this.maxHeight;
36263     }
36264 };
36265
36266 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36267     /**
36268      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36269      */
36270     growMin : 60,
36271     /**
36272      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36273      */
36274     growMax: 1000,
36275     /**
36276      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36277      * in the field (equivalent to setting overflow: hidden, defaults to false)
36278      */
36279     preventScrollbars: false,
36280     /**
36281      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36282      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36283      */
36284
36285     // private
36286     onRender : function(ct, position){
36287         if(!this.el){
36288             this.defaultAutoCreate = {
36289                 tag: "textarea",
36290                 style:"width:300px;height:60px;",
36291                 autocomplete: "off"
36292             };
36293         }
36294         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36295         if(this.grow){
36296             this.textSizeEl = Roo.DomHelper.append(document.body, {
36297                 tag: "pre", cls: "x-form-grow-sizer"
36298             });
36299             if(this.preventScrollbars){
36300                 this.el.setStyle("overflow", "hidden");
36301             }
36302             this.el.setHeight(this.growMin);
36303         }
36304     },
36305
36306     onDestroy : function(){
36307         if(this.textSizeEl){
36308             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36309         }
36310         Roo.form.TextArea.superclass.onDestroy.call(this);
36311     },
36312
36313     // private
36314     onKeyUp : function(e){
36315         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36316             this.autoSize();
36317         }
36318     },
36319
36320     /**
36321      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36322      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36323      */
36324     autoSize : function(){
36325         if(!this.grow || !this.textSizeEl){
36326             return;
36327         }
36328         var el = this.el;
36329         var v = el.dom.value;
36330         var ts = this.textSizeEl;
36331
36332         ts.innerHTML = '';
36333         ts.appendChild(document.createTextNode(v));
36334         v = ts.innerHTML;
36335
36336         Roo.fly(ts).setWidth(this.el.getWidth());
36337         if(v.length < 1){
36338             v = "&#160;&#160;";
36339         }else{
36340             if(Roo.isIE){
36341                 v = v.replace(/\n/g, '<p>&#160;</p>');
36342             }
36343             v += "&#160;\n&#160;";
36344         }
36345         ts.innerHTML = v;
36346         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36347         if(h != this.lastHeight){
36348             this.lastHeight = h;
36349             this.el.setHeight(h);
36350             this.fireEvent("autosize", this, h);
36351         }
36352     }
36353 });/*
36354  * Based on:
36355  * Ext JS Library 1.1.1
36356  * Copyright(c) 2006-2007, Ext JS, LLC.
36357  *
36358  * Originally Released Under LGPL - original licence link has changed is not relivant.
36359  *
36360  * Fork - LGPL
36361  * <script type="text/javascript">
36362  */
36363  
36364
36365 /**
36366  * @class Roo.form.NumberField
36367  * @extends Roo.form.TextField
36368  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36369  * @constructor
36370  * Creates a new NumberField
36371  * @param {Object} config Configuration options
36372  */
36373 Roo.form.NumberField = function(config){
36374     Roo.form.NumberField.superclass.constructor.call(this, config);
36375 };
36376
36377 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36378     /**
36379      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36380      */
36381     fieldClass: "x-form-field x-form-num-field",
36382     /**
36383      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36384      */
36385     allowDecimals : true,
36386     /**
36387      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36388      */
36389     decimalSeparator : ".",
36390     /**
36391      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36392      */
36393     decimalPrecision : 2,
36394     /**
36395      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36396      */
36397     allowNegative : true,
36398     /**
36399      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36400      */
36401     minValue : Number.NEGATIVE_INFINITY,
36402     /**
36403      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36404      */
36405     maxValue : Number.MAX_VALUE,
36406     /**
36407      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36408      */
36409     minText : "The minimum value for this field is {0}",
36410     /**
36411      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36412      */
36413     maxText : "The maximum value for this field is {0}",
36414     /**
36415      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36416      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36417      */
36418     nanText : "{0} is not a valid number",
36419
36420     // private
36421     initEvents : function(){
36422         Roo.form.NumberField.superclass.initEvents.call(this);
36423         var allowed = "0123456789";
36424         if(this.allowDecimals){
36425             allowed += this.decimalSeparator;
36426         }
36427         if(this.allowNegative){
36428             allowed += "-";
36429         }
36430         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36431         var keyPress = function(e){
36432             var k = e.getKey();
36433             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36434                 return;
36435             }
36436             var c = e.getCharCode();
36437             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36438                 e.stopEvent();
36439             }
36440         };
36441         this.el.on("keypress", keyPress, this);
36442     },
36443
36444     // private
36445     validateValue : function(value){
36446         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36447             return false;
36448         }
36449         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36450              return true;
36451         }
36452         var num = this.parseValue(value);
36453         if(isNaN(num)){
36454             this.markInvalid(String.format(this.nanText, value));
36455             return false;
36456         }
36457         if(num < this.minValue){
36458             this.markInvalid(String.format(this.minText, this.minValue));
36459             return false;
36460         }
36461         if(num > this.maxValue){
36462             this.markInvalid(String.format(this.maxText, this.maxValue));
36463             return false;
36464         }
36465         return true;
36466     },
36467
36468     getValue : function(){
36469         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36470     },
36471
36472     // private
36473     parseValue : function(value){
36474         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36475         return isNaN(value) ? '' : value;
36476     },
36477
36478     // private
36479     fixPrecision : function(value){
36480         var nan = isNaN(value);
36481         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36482             return nan ? '' : value;
36483         }
36484         return parseFloat(value).toFixed(this.decimalPrecision);
36485     },
36486
36487     setValue : function(v){
36488         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36489     },
36490
36491     // private
36492     decimalPrecisionFcn : function(v){
36493         return Math.floor(v);
36494     },
36495
36496     beforeBlur : function(){
36497         var v = this.parseValue(this.getRawValue());
36498         if(v){
36499             this.setValue(this.fixPrecision(v));
36500         }
36501     }
36502 });/*
36503  * Based on:
36504  * Ext JS Library 1.1.1
36505  * Copyright(c) 2006-2007, Ext JS, LLC.
36506  *
36507  * Originally Released Under LGPL - original licence link has changed is not relivant.
36508  *
36509  * Fork - LGPL
36510  * <script type="text/javascript">
36511  */
36512  
36513 /**
36514  * @class Roo.form.DateField
36515  * @extends Roo.form.TriggerField
36516  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36517 * @constructor
36518 * Create a new DateField
36519 * @param {Object} config
36520  */
36521 Roo.form.DateField = function(config){
36522     Roo.form.DateField.superclass.constructor.call(this, config);
36523     
36524       this.addEvents({
36525          
36526         /**
36527          * @event select
36528          * Fires when a date is selected
36529              * @param {Roo.form.DateField} combo This combo box
36530              * @param {Date} date The date selected
36531              */
36532         'select' : true
36533          
36534     });
36535     
36536     
36537     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36538     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36539     this.ddMatch = null;
36540     if(this.disabledDates){
36541         var dd = this.disabledDates;
36542         var re = "(?:";
36543         for(var i = 0; i < dd.length; i++){
36544             re += dd[i];
36545             if(i != dd.length-1) re += "|";
36546         }
36547         this.ddMatch = new RegExp(re + ")");
36548     }
36549 };
36550
36551 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36552     /**
36553      * @cfg {String} format
36554      * The default date format string which can be overriden for localization support.  The format must be
36555      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36556      */
36557     format : "m/d/y",
36558     /**
36559      * @cfg {String} altFormats
36560      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36561      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36562      */
36563     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36564     /**
36565      * @cfg {Array} disabledDays
36566      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36567      */
36568     disabledDays : null,
36569     /**
36570      * @cfg {String} disabledDaysText
36571      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36572      */
36573     disabledDaysText : "Disabled",
36574     /**
36575      * @cfg {Array} disabledDates
36576      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36577      * expression so they are very powerful. Some examples:
36578      * <ul>
36579      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36580      * <li>["03/08", "09/16"] would disable those days for every year</li>
36581      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36582      * <li>["03/../2006"] would disable every day in March 2006</li>
36583      * <li>["^03"] would disable every day in every March</li>
36584      * </ul>
36585      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36586      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36587      */
36588     disabledDates : null,
36589     /**
36590      * @cfg {String} disabledDatesText
36591      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36592      */
36593     disabledDatesText : "Disabled",
36594     /**
36595      * @cfg {Date/String} minValue
36596      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36597      * valid format (defaults to null).
36598      */
36599     minValue : null,
36600     /**
36601      * @cfg {Date/String} maxValue
36602      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36603      * valid format (defaults to null).
36604      */
36605     maxValue : null,
36606     /**
36607      * @cfg {String} minText
36608      * The error text to display when the date in the cell is before minValue (defaults to
36609      * 'The date in this field must be after {minValue}').
36610      */
36611     minText : "The date in this field must be equal to or after {0}",
36612     /**
36613      * @cfg {String} maxText
36614      * The error text to display when the date in the cell is after maxValue (defaults to
36615      * 'The date in this field must be before {maxValue}').
36616      */
36617     maxText : "The date in this field must be equal to or before {0}",
36618     /**
36619      * @cfg {String} invalidText
36620      * The error text to display when the date in the field is invalid (defaults to
36621      * '{value} is not a valid date - it must be in the format {format}').
36622      */
36623     invalidText : "{0} is not a valid date - it must be in the format {1}",
36624     /**
36625      * @cfg {String} triggerClass
36626      * An additional CSS class used to style the trigger button.  The trigger will always get the
36627      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36628      * which displays a calendar icon).
36629      */
36630     triggerClass : 'x-form-date-trigger',
36631     
36632
36633     /**
36634      * @cfg {bool} useIso
36635      * if enabled, then the date field will use a hidden field to store the 
36636      * real value as iso formated date. default (false)
36637      */ 
36638     useIso : false,
36639     /**
36640      * @cfg {String/Object} autoCreate
36641      * A DomHelper element spec, or true for a default element spec (defaults to
36642      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36643      */ 
36644     // private
36645     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36646     
36647     // private
36648     hiddenField: false,
36649     
36650     onRender : function(ct, position)
36651     {
36652         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36653         if (this.useIso) {
36654             this.el.dom.removeAttribute('name'); 
36655             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36656                     'before', true);
36657             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36658             // prevent input submission
36659             this.hiddenName = this.name;
36660         }
36661             
36662             
36663     },
36664     
36665     // private
36666     validateValue : function(value)
36667     {
36668         value = this.formatDate(value);
36669         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36670             return false;
36671         }
36672         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36673              return true;
36674         }
36675         var svalue = value;
36676         value = this.parseDate(value);
36677         if(!value){
36678             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36679             return false;
36680         }
36681         var time = value.getTime();
36682         if(this.minValue && time < this.minValue.getTime()){
36683             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36684             return false;
36685         }
36686         if(this.maxValue && time > this.maxValue.getTime()){
36687             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36688             return false;
36689         }
36690         if(this.disabledDays){
36691             var day = value.getDay();
36692             for(var i = 0; i < this.disabledDays.length; i++) {
36693                 if(day === this.disabledDays[i]){
36694                     this.markInvalid(this.disabledDaysText);
36695                     return false;
36696                 }
36697             }
36698         }
36699         var fvalue = this.formatDate(value);
36700         if(this.ddMatch && this.ddMatch.test(fvalue)){
36701             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36702             return false;
36703         }
36704         return true;
36705     },
36706
36707     // private
36708     // Provides logic to override the default TriggerField.validateBlur which just returns true
36709     validateBlur : function(){
36710         return !this.menu || !this.menu.isVisible();
36711     },
36712
36713     /**
36714      * Returns the current date value of the date field.
36715      * @return {Date} The date value
36716      */
36717     getValue : function(){
36718         
36719         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36720     },
36721
36722     /**
36723      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36724      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36725      * (the default format used is "m/d/y").
36726      * <br />Usage:
36727      * <pre><code>
36728 //All of these calls set the same date value (May 4, 2006)
36729
36730 //Pass a date object:
36731 var dt = new Date('5/4/06');
36732 dateField.setValue(dt);
36733
36734 //Pass a date string (default format):
36735 dateField.setValue('5/4/06');
36736
36737 //Pass a date string (custom format):
36738 dateField.format = 'Y-m-d';
36739 dateField.setValue('2006-5-4');
36740 </code></pre>
36741      * @param {String/Date} date The date or valid date string
36742      */
36743     setValue : function(date){
36744         if (this.hiddenField) {
36745             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36746         }
36747         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36748     },
36749
36750     // private
36751     parseDate : function(value){
36752         if(!value || value instanceof Date){
36753             return value;
36754         }
36755         var v = Date.parseDate(value, this.format);
36756         if(!v && this.altFormats){
36757             if(!this.altFormatsArray){
36758                 this.altFormatsArray = this.altFormats.split("|");
36759             }
36760             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36761                 v = Date.parseDate(value, this.altFormatsArray[i]);
36762             }
36763         }
36764         return v;
36765     },
36766
36767     // private
36768     formatDate : function(date, fmt){
36769         return (!date || !(date instanceof Date)) ?
36770                date : date.dateFormat(fmt || this.format);
36771     },
36772
36773     // private
36774     menuListeners : {
36775         select: function(m, d){
36776             this.setValue(d);
36777             this.fireEvent('select', this, d);
36778         },
36779         show : function(){ // retain focus styling
36780             this.onFocus();
36781         },
36782         hide : function(){
36783             this.focus.defer(10, this);
36784             var ml = this.menuListeners;
36785             this.menu.un("select", ml.select,  this);
36786             this.menu.un("show", ml.show,  this);
36787             this.menu.un("hide", ml.hide,  this);
36788         }
36789     },
36790
36791     // private
36792     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36793     onTriggerClick : function(){
36794         if(this.disabled){
36795             return;
36796         }
36797         if(this.menu == null){
36798             this.menu = new Roo.menu.DateMenu();
36799         }
36800         Roo.apply(this.menu.picker,  {
36801             showClear: this.allowBlank,
36802             minDate : this.minValue,
36803             maxDate : this.maxValue,
36804             disabledDatesRE : this.ddMatch,
36805             disabledDatesText : this.disabledDatesText,
36806             disabledDays : this.disabledDays,
36807             disabledDaysText : this.disabledDaysText,
36808             format : this.format,
36809             minText : String.format(this.minText, this.formatDate(this.minValue)),
36810             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36811         });
36812         this.menu.on(Roo.apply({}, this.menuListeners, {
36813             scope:this
36814         }));
36815         this.menu.picker.setValue(this.getValue() || new Date());
36816         this.menu.show(this.el, "tl-bl?");
36817     },
36818
36819     beforeBlur : function(){
36820         var v = this.parseDate(this.getRawValue());
36821         if(v){
36822             this.setValue(v);
36823         }
36824     }
36825
36826     /** @cfg {Boolean} grow @hide */
36827     /** @cfg {Number} growMin @hide */
36828     /** @cfg {Number} growMax @hide */
36829     /**
36830      * @hide
36831      * @method autoSize
36832      */
36833 });/*
36834  * Based on:
36835  * Ext JS Library 1.1.1
36836  * Copyright(c) 2006-2007, Ext JS, LLC.
36837  *
36838  * Originally Released Under LGPL - original licence link has changed is not relivant.
36839  *
36840  * Fork - LGPL
36841  * <script type="text/javascript">
36842  */
36843  
36844
36845 /**
36846  * @class Roo.form.ComboBox
36847  * @extends Roo.form.TriggerField
36848  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36849  * @constructor
36850  * Create a new ComboBox.
36851  * @param {Object} config Configuration options
36852  */
36853 Roo.form.ComboBox = function(config){
36854     Roo.form.ComboBox.superclass.constructor.call(this, config);
36855     this.addEvents({
36856         /**
36857          * @event expand
36858          * Fires when the dropdown list is expanded
36859              * @param {Roo.form.ComboBox} combo This combo box
36860              */
36861         'expand' : true,
36862         /**
36863          * @event collapse
36864          * Fires when the dropdown list is collapsed
36865              * @param {Roo.form.ComboBox} combo This combo box
36866              */
36867         'collapse' : true,
36868         /**
36869          * @event beforeselect
36870          * Fires before a list item is selected. Return false to cancel the selection.
36871              * @param {Roo.form.ComboBox} combo This combo box
36872              * @param {Roo.data.Record} record The data record returned from the underlying store
36873              * @param {Number} index The index of the selected item in the dropdown list
36874              */
36875         'beforeselect' : true,
36876         /**
36877          * @event select
36878          * Fires when a list item is selected
36879              * @param {Roo.form.ComboBox} combo This combo box
36880              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36881              * @param {Number} index The index of the selected item in the dropdown list
36882              */
36883         'select' : true,
36884         /**
36885          * @event beforequery
36886          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36887          * The event object passed has these properties:
36888              * @param {Roo.form.ComboBox} combo This combo box
36889              * @param {String} query The query
36890              * @param {Boolean} forceAll true to force "all" query
36891              * @param {Boolean} cancel true to cancel the query
36892              * @param {Object} e The query event object
36893              */
36894         'beforequery': true,
36895          /**
36896          * @event add
36897          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36898              * @param {Roo.form.ComboBox} combo This combo box
36899              */
36900         'add' : true,
36901         /**
36902          * @event edit
36903          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36904              * @param {Roo.form.ComboBox} combo This combo box
36905              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36906              */
36907         'edit' : true
36908         
36909         
36910     });
36911     if(this.transform){
36912         this.allowDomMove = false;
36913         var s = Roo.getDom(this.transform);
36914         if(!this.hiddenName){
36915             this.hiddenName = s.name;
36916         }
36917         if(!this.store){
36918             this.mode = 'local';
36919             var d = [], opts = s.options;
36920             for(var i = 0, len = opts.length;i < len; i++){
36921                 var o = opts[i];
36922                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36923                 if(o.selected) {
36924                     this.value = value;
36925                 }
36926                 d.push([value, o.text]);
36927             }
36928             this.store = new Roo.data.SimpleStore({
36929                 'id': 0,
36930                 fields: ['value', 'text'],
36931                 data : d
36932             });
36933             this.valueField = 'value';
36934             this.displayField = 'text';
36935         }
36936         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36937         if(!this.lazyRender){
36938             this.target = true;
36939             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36940             s.parentNode.removeChild(s); // remove it
36941             this.render(this.el.parentNode);
36942         }else{
36943             s.parentNode.removeChild(s); // remove it
36944         }
36945
36946     }
36947     if (this.store) {
36948         this.store = Roo.factory(this.store, Roo.data);
36949     }
36950     
36951     this.selectedIndex = -1;
36952     if(this.mode == 'local'){
36953         if(config.queryDelay === undefined){
36954             this.queryDelay = 10;
36955         }
36956         if(config.minChars === undefined){
36957             this.minChars = 0;
36958         }
36959     }
36960 };
36961
36962 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36963     /**
36964      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36965      */
36966     /**
36967      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36968      * rendering into an Roo.Editor, defaults to false)
36969      */
36970     /**
36971      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36972      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36973      */
36974     /**
36975      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36976      */
36977     /**
36978      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36979      * the dropdown list (defaults to undefined, with no header element)
36980      */
36981
36982      /**
36983      * @cfg {String/Roo.Template} tpl The template to use to render the output
36984      */
36985      
36986     // private
36987     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36988     /**
36989      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36990      */
36991     listWidth: undefined,
36992     /**
36993      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36994      * mode = 'remote' or 'text' if mode = 'local')
36995      */
36996     displayField: undefined,
36997     /**
36998      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36999      * mode = 'remote' or 'value' if mode = 'local'). 
37000      * Note: use of a valueField requires the user make a selection
37001      * in order for a value to be mapped.
37002      */
37003     valueField: undefined,
37004     /**
37005      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37006      * field's data value (defaults to the underlying DOM element's name)
37007      */
37008     hiddenName: undefined,
37009     /**
37010      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37011      */
37012     listClass: '',
37013     /**
37014      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37015      */
37016     selectedClass: 'x-combo-selected',
37017     /**
37018      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37019      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37020      * which displays a downward arrow icon).
37021      */
37022     triggerClass : 'x-form-arrow-trigger',
37023     /**
37024      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37025      */
37026     shadow:'sides',
37027     /**
37028      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37029      * anchor positions (defaults to 'tl-bl')
37030      */
37031     listAlign: 'tl-bl?',
37032     /**
37033      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37034      */
37035     maxHeight: 300,
37036     /**
37037      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37038      * query specified by the allQuery config option (defaults to 'query')
37039      */
37040     triggerAction: 'query',
37041     /**
37042      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37043      * (defaults to 4, does not apply if editable = false)
37044      */
37045     minChars : 4,
37046     /**
37047      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37048      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37049      */
37050     typeAhead: false,
37051     /**
37052      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37053      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37054      */
37055     queryDelay: 500,
37056     /**
37057      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37058      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37059      */
37060     pageSize: 0,
37061     /**
37062      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37063      * when editable = true (defaults to false)
37064      */
37065     selectOnFocus:false,
37066     /**
37067      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37068      */
37069     queryParam: 'query',
37070     /**
37071      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37072      * when mode = 'remote' (defaults to 'Loading...')
37073      */
37074     loadingText: 'Loading...',
37075     /**
37076      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37077      */
37078     resizable: false,
37079     /**
37080      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37081      */
37082     handleHeight : 8,
37083     /**
37084      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37085      * traditional select (defaults to true)
37086      */
37087     editable: true,
37088     /**
37089      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37090      */
37091     allQuery: '',
37092     /**
37093      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37094      */
37095     mode: 'remote',
37096     /**
37097      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37098      * listWidth has a higher value)
37099      */
37100     minListWidth : 70,
37101     /**
37102      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37103      * allow the user to set arbitrary text into the field (defaults to false)
37104      */
37105     forceSelection:false,
37106     /**
37107      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37108      * if typeAhead = true (defaults to 250)
37109      */
37110     typeAheadDelay : 250,
37111     /**
37112      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37113      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37114      */
37115     valueNotFoundText : undefined,
37116     /**
37117      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37118      */
37119     blockFocus : false,
37120     
37121     /**
37122      * @cfg {Boolean} disableClear Disable showing of clear button.
37123      */
37124     disableClear : false,
37125     /**
37126      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37127      */
37128     alwaysQuery : false,
37129     
37130     //private
37131     addicon : false,
37132     editicon: false,
37133     
37134     
37135     // private
37136     onRender : function(ct, position){
37137         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37138         if(this.hiddenName){
37139             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37140                     'before', true);
37141             this.hiddenField.value =
37142                 this.hiddenValue !== undefined ? this.hiddenValue :
37143                 this.value !== undefined ? this.value : '';
37144
37145             // prevent input submission
37146             this.el.dom.removeAttribute('name');
37147         }
37148         if(Roo.isGecko){
37149             this.el.dom.setAttribute('autocomplete', 'off');
37150         }
37151
37152         var cls = 'x-combo-list';
37153
37154         this.list = new Roo.Layer({
37155             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37156         });
37157
37158         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37159         this.list.setWidth(lw);
37160         this.list.swallowEvent('mousewheel');
37161         this.assetHeight = 0;
37162
37163         if(this.title){
37164             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37165             this.assetHeight += this.header.getHeight();
37166         }
37167
37168         this.innerList = this.list.createChild({cls:cls+'-inner'});
37169         this.innerList.on('mouseover', this.onViewOver, this);
37170         this.innerList.on('mousemove', this.onViewMove, this);
37171         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37172         
37173         if(this.allowBlank && !this.pageSize && !this.disableClear){
37174             this.footer = this.list.createChild({cls:cls+'-ft'});
37175             this.pageTb = new Roo.Toolbar(this.footer);
37176            
37177         }
37178         if(this.pageSize){
37179             this.footer = this.list.createChild({cls:cls+'-ft'});
37180             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37181                     {pageSize: this.pageSize});
37182             
37183         }
37184         
37185         if (this.pageTb && this.allowBlank && !this.disableClear) {
37186             var _this = this;
37187             this.pageTb.add(new Roo.Toolbar.Fill(), {
37188                 cls: 'x-btn-icon x-btn-clear',
37189                 text: '&#160;',
37190                 handler: function()
37191                 {
37192                     _this.collapse();
37193                     _this.clearValue();
37194                     _this.onSelect(false, -1);
37195                 }
37196             });
37197         }
37198         if (this.footer) {
37199             this.assetHeight += this.footer.getHeight();
37200         }
37201         
37202
37203         if(!this.tpl){
37204             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37205         }
37206
37207         this.view = new Roo.View(this.innerList, this.tpl, {
37208             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37209         });
37210
37211         this.view.on('click', this.onViewClick, this);
37212
37213         this.store.on('beforeload', this.onBeforeLoad, this);
37214         this.store.on('load', this.onLoad, this);
37215         this.store.on('loadexception', this.collapse, this);
37216
37217         if(this.resizable){
37218             this.resizer = new Roo.Resizable(this.list,  {
37219                pinned:true, handles:'se'
37220             });
37221             this.resizer.on('resize', function(r, w, h){
37222                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37223                 this.listWidth = w;
37224                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37225                 this.restrictHeight();
37226             }, this);
37227             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37228         }
37229         if(!this.editable){
37230             this.editable = true;
37231             this.setEditable(false);
37232         }  
37233         
37234         
37235         if (typeof(this.events.add.listeners) != 'undefined') {
37236             
37237             this.addicon = this.wrap.createChild(
37238                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37239        
37240             this.addicon.on('click', function(e) {
37241                 this.fireEvent('add', this);
37242             }, this);
37243         }
37244         if (typeof(this.events.edit.listeners) != 'undefined') {
37245             
37246             this.editicon = this.wrap.createChild(
37247                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37248             if (this.addicon) {
37249                 this.editicon.setStyle('margin-left', '40px');
37250             }
37251             this.editicon.on('click', function(e) {
37252                 
37253                 // we fire even  if inothing is selected..
37254                 this.fireEvent('edit', this, this.lastData );
37255                 
37256             }, this);
37257         }
37258         
37259         
37260         
37261     },
37262
37263     // private
37264     initEvents : function(){
37265         Roo.form.ComboBox.superclass.initEvents.call(this);
37266
37267         this.keyNav = new Roo.KeyNav(this.el, {
37268             "up" : function(e){
37269                 this.inKeyMode = true;
37270                 this.selectPrev();
37271             },
37272
37273             "down" : function(e){
37274                 if(!this.isExpanded()){
37275                     this.onTriggerClick();
37276                 }else{
37277                     this.inKeyMode = true;
37278                     this.selectNext();
37279                 }
37280             },
37281
37282             "enter" : function(e){
37283                 this.onViewClick();
37284                 //return true;
37285             },
37286
37287             "esc" : function(e){
37288                 this.collapse();
37289             },
37290
37291             "tab" : function(e){
37292                 this.onViewClick(false);
37293                 return true;
37294             },
37295
37296             scope : this,
37297
37298             doRelay : function(foo, bar, hname){
37299                 if(hname == 'down' || this.scope.isExpanded()){
37300                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37301                 }
37302                 return true;
37303             },
37304
37305             forceKeyDown: true
37306         });
37307         this.queryDelay = Math.max(this.queryDelay || 10,
37308                 this.mode == 'local' ? 10 : 250);
37309         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37310         if(this.typeAhead){
37311             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37312         }
37313         if(this.editable !== false){
37314             this.el.on("keyup", this.onKeyUp, this);
37315         }
37316         if(this.forceSelection){
37317             this.on('blur', this.doForce, this);
37318         }
37319     },
37320
37321     onDestroy : function(){
37322         if(this.view){
37323             this.view.setStore(null);
37324             this.view.el.removeAllListeners();
37325             this.view.el.remove();
37326             this.view.purgeListeners();
37327         }
37328         if(this.list){
37329             this.list.destroy();
37330         }
37331         if(this.store){
37332             this.store.un('beforeload', this.onBeforeLoad, this);
37333             this.store.un('load', this.onLoad, this);
37334             this.store.un('loadexception', this.collapse, this);
37335         }
37336         Roo.form.ComboBox.superclass.onDestroy.call(this);
37337     },
37338
37339     // private
37340     fireKey : function(e){
37341         if(e.isNavKeyPress() && !this.list.isVisible()){
37342             this.fireEvent("specialkey", this, e);
37343         }
37344     },
37345
37346     // private
37347     onResize: function(w, h){
37348         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37349         
37350         if(typeof w != 'number'){
37351             // we do not handle it!?!?
37352             return;
37353         }
37354         var tw = this.trigger.getWidth();
37355         tw += this.addicon ? this.addicon.getWidth() : 0;
37356         tw += this.editicon ? this.editicon.getWidth() : 0;
37357         var x = w - tw;
37358         this.el.setWidth( this.adjustWidth('input', x));
37359             
37360         this.trigger.setStyle('left', x+'px');
37361         
37362         if(this.list && this.listWidth === undefined){
37363             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37364             this.list.setWidth(lw);
37365             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37366         }
37367         
37368     
37369         
37370     },
37371
37372     /**
37373      * Allow or prevent the user from directly editing the field text.  If false is passed,
37374      * the user will only be able to select from the items defined in the dropdown list.  This method
37375      * is the runtime equivalent of setting the 'editable' config option at config time.
37376      * @param {Boolean} value True to allow the user to directly edit the field text
37377      */
37378     setEditable : function(value){
37379         if(value == this.editable){
37380             return;
37381         }
37382         this.editable = value;
37383         if(!value){
37384             this.el.dom.setAttribute('readOnly', true);
37385             this.el.on('mousedown', this.onTriggerClick,  this);
37386             this.el.addClass('x-combo-noedit');
37387         }else{
37388             this.el.dom.setAttribute('readOnly', false);
37389             this.el.un('mousedown', this.onTriggerClick,  this);
37390             this.el.removeClass('x-combo-noedit');
37391         }
37392     },
37393
37394     // private
37395     onBeforeLoad : function(){
37396         if(!this.hasFocus){
37397             return;
37398         }
37399         this.innerList.update(this.loadingText ?
37400                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37401         this.restrictHeight();
37402         this.selectedIndex = -1;
37403     },
37404
37405     // private
37406     onLoad : function(){
37407         if(!this.hasFocus){
37408             return;
37409         }
37410         if(this.store.getCount() > 0){
37411             this.expand();
37412             this.restrictHeight();
37413             if(this.lastQuery == this.allQuery){
37414                 if(this.editable){
37415                     this.el.dom.select();
37416                 }
37417                 if(!this.selectByValue(this.value, true)){
37418                     this.select(0, true);
37419                 }
37420             }else{
37421                 this.selectNext();
37422                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37423                     this.taTask.delay(this.typeAheadDelay);
37424                 }
37425             }
37426         }else{
37427             this.onEmptyResults();
37428         }
37429         //this.el.focus();
37430     },
37431
37432     // private
37433     onTypeAhead : function(){
37434         if(this.store.getCount() > 0){
37435             var r = this.store.getAt(0);
37436             var newValue = r.data[this.displayField];
37437             var len = newValue.length;
37438             var selStart = this.getRawValue().length;
37439             if(selStart != len){
37440                 this.setRawValue(newValue);
37441                 this.selectText(selStart, newValue.length);
37442             }
37443         }
37444     },
37445
37446     // private
37447     onSelect : function(record, index){
37448         if(this.fireEvent('beforeselect', this, record, index) !== false){
37449             this.setFromData(index > -1 ? record.data : false);
37450             this.collapse();
37451             this.fireEvent('select', this, record, index);
37452         }
37453     },
37454
37455     /**
37456      * Returns the currently selected field value or empty string if no value is set.
37457      * @return {String} value The selected value
37458      */
37459     getValue : function(){
37460         if(this.valueField){
37461             return typeof this.value != 'undefined' ? this.value : '';
37462         }else{
37463             return Roo.form.ComboBox.superclass.getValue.call(this);
37464         }
37465     },
37466
37467     /**
37468      * Clears any text/value currently set in the field
37469      */
37470     clearValue : function(){
37471         if(this.hiddenField){
37472             this.hiddenField.value = '';
37473         }
37474         this.value = '';
37475         this.setRawValue('');
37476         this.lastSelectionText = '';
37477         this.applyEmptyText();
37478     },
37479
37480     /**
37481      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37482      * will be displayed in the field.  If the value does not match the data value of an existing item,
37483      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37484      * Otherwise the field will be blank (although the value will still be set).
37485      * @param {String} value The value to match
37486      */
37487     setValue : function(v){
37488         var text = v;
37489         if(this.valueField){
37490             var r = this.findRecord(this.valueField, v);
37491             if(r){
37492                 text = r.data[this.displayField];
37493             }else if(this.valueNotFoundText !== undefined){
37494                 text = this.valueNotFoundText;
37495             }
37496         }
37497         this.lastSelectionText = text;
37498         if(this.hiddenField){
37499             this.hiddenField.value = v;
37500         }
37501         Roo.form.ComboBox.superclass.setValue.call(this, text);
37502         this.value = v;
37503     },
37504     /**
37505      * @property {Object} the last set data for the element
37506      */
37507     
37508     lastData : false,
37509     /**
37510      * Sets the value of the field based on a object which is related to the record format for the store.
37511      * @param {Object} value the value to set as. or false on reset?
37512      */
37513     setFromData : function(o){
37514         var dv = ''; // display value
37515         var vv = ''; // value value..
37516         this.lastData = o;
37517         if (this.displayField) {
37518             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37519         } else {
37520             // this is an error condition!!!
37521             console.log('no value field set for '+ this.name);
37522         }
37523         
37524         if(this.valueField){
37525             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37526         }
37527         if(this.hiddenField){
37528             this.hiddenField.value = vv;
37529             
37530             this.lastSelectionText = dv;
37531             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37532             this.value = vv;
37533             return;
37534         }
37535         // no hidden field.. - we store the value in 'value', but still display
37536         // display field!!!!
37537         this.lastSelectionText = dv;
37538         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37539         this.value = vv;
37540         
37541         
37542     },
37543     // private
37544     reset : function(){
37545         // overridden so that last data is reset..
37546         this.setValue(this.originalValue);
37547         this.clearInvalid();
37548         this.lastData = false;
37549     },
37550     // private
37551     findRecord : function(prop, value){
37552         var record;
37553         if(this.store.getCount() > 0){
37554             this.store.each(function(r){
37555                 if(r.data[prop] == value){
37556                     record = r;
37557                     return false;
37558                 }
37559             });
37560         }
37561         return record;
37562     },
37563
37564     // private
37565     onViewMove : function(e, t){
37566         this.inKeyMode = false;
37567     },
37568
37569     // private
37570     onViewOver : function(e, t){
37571         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37572             return;
37573         }
37574         var item = this.view.findItemFromChild(t);
37575         if(item){
37576             var index = this.view.indexOf(item);
37577             this.select(index, false);
37578         }
37579     },
37580
37581     // private
37582     onViewClick : function(doFocus){
37583         var index = this.view.getSelectedIndexes()[0];
37584         var r = this.store.getAt(index);
37585         if(r){
37586             this.onSelect(r, index);
37587         }
37588         if(doFocus !== false && !this.blockFocus){
37589             this.el.focus();
37590         }
37591     },
37592
37593     // private
37594     restrictHeight : function(){
37595         this.innerList.dom.style.height = '';
37596         var inner = this.innerList.dom;
37597         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37598         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37599         this.list.beginUpdate();
37600         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37601         this.list.alignTo(this.el, this.listAlign);
37602         this.list.endUpdate();
37603     },
37604
37605     // private
37606     onEmptyResults : function(){
37607         this.collapse();
37608     },
37609
37610     /**
37611      * Returns true if the dropdown list is expanded, else false.
37612      */
37613     isExpanded : function(){
37614         return this.list.isVisible();
37615     },
37616
37617     /**
37618      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37619      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37620      * @param {String} value The data value of the item to select
37621      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37622      * selected item if it is not currently in view (defaults to true)
37623      * @return {Boolean} True if the value matched an item in the list, else false
37624      */
37625     selectByValue : function(v, scrollIntoView){
37626         if(v !== undefined && v !== null){
37627             var r = this.findRecord(this.valueField || this.displayField, v);
37628             if(r){
37629                 this.select(this.store.indexOf(r), scrollIntoView);
37630                 return true;
37631             }
37632         }
37633         return false;
37634     },
37635
37636     /**
37637      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37638      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37639      * @param {Number} index The zero-based index of the list item to select
37640      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37641      * selected item if it is not currently in view (defaults to true)
37642      */
37643     select : function(index, scrollIntoView){
37644         this.selectedIndex = index;
37645         this.view.select(index);
37646         if(scrollIntoView !== false){
37647             var el = this.view.getNode(index);
37648             if(el){
37649                 this.innerList.scrollChildIntoView(el, false);
37650             }
37651         }
37652     },
37653
37654     // private
37655     selectNext : function(){
37656         var ct = this.store.getCount();
37657         if(ct > 0){
37658             if(this.selectedIndex == -1){
37659                 this.select(0);
37660             }else if(this.selectedIndex < ct-1){
37661                 this.select(this.selectedIndex+1);
37662             }
37663         }
37664     },
37665
37666     // private
37667     selectPrev : function(){
37668         var ct = this.store.getCount();
37669         if(ct > 0){
37670             if(this.selectedIndex == -1){
37671                 this.select(0);
37672             }else if(this.selectedIndex != 0){
37673                 this.select(this.selectedIndex-1);
37674             }
37675         }
37676     },
37677
37678     // private
37679     onKeyUp : function(e){
37680         if(this.editable !== false && !e.isSpecialKey()){
37681             this.lastKey = e.getKey();
37682             this.dqTask.delay(this.queryDelay);
37683         }
37684     },
37685
37686     // private
37687     validateBlur : function(){
37688         return !this.list || !this.list.isVisible();   
37689     },
37690
37691     // private
37692     initQuery : function(){
37693         this.doQuery(this.getRawValue());
37694     },
37695
37696     // private
37697     doForce : function(){
37698         if(this.el.dom.value.length > 0){
37699             this.el.dom.value =
37700                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37701             this.applyEmptyText();
37702         }
37703     },
37704
37705     /**
37706      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37707      * query allowing the query action to be canceled if needed.
37708      * @param {String} query The SQL query to execute
37709      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37710      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37711      * saved in the current store (defaults to false)
37712      */
37713     doQuery : function(q, forceAll){
37714         if(q === undefined || q === null){
37715             q = '';
37716         }
37717         var qe = {
37718             query: q,
37719             forceAll: forceAll,
37720             combo: this,
37721             cancel:false
37722         };
37723         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37724             return false;
37725         }
37726         q = qe.query;
37727         forceAll = qe.forceAll;
37728         if(forceAll === true || (q.length >= this.minChars)){
37729             if(this.lastQuery != q || this.alwaysQuery){
37730                 this.lastQuery = q;
37731                 if(this.mode == 'local'){
37732                     this.selectedIndex = -1;
37733                     if(forceAll){
37734                         this.store.clearFilter();
37735                     }else{
37736                         this.store.filter(this.displayField, q);
37737                     }
37738                     this.onLoad();
37739                 }else{
37740                     this.store.baseParams[this.queryParam] = q;
37741                     this.store.load({
37742                         params: this.getParams(q)
37743                     });
37744                     this.expand();
37745                 }
37746             }else{
37747                 this.selectedIndex = -1;
37748                 this.onLoad();   
37749             }
37750         }
37751     },
37752
37753     // private
37754     getParams : function(q){
37755         var p = {};
37756         //p[this.queryParam] = q;
37757         if(this.pageSize){
37758             p.start = 0;
37759             p.limit = this.pageSize;
37760         }
37761         return p;
37762     },
37763
37764     /**
37765      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37766      */
37767     collapse : function(){
37768         if(!this.isExpanded()){
37769             return;
37770         }
37771         this.list.hide();
37772         Roo.get(document).un('mousedown', this.collapseIf, this);
37773         Roo.get(document).un('mousewheel', this.collapseIf, this);
37774         if (!this.editable) {
37775             Roo.get(document).un('keydown', this.listKeyPress, this);
37776         }
37777         this.fireEvent('collapse', this);
37778     },
37779
37780     // private
37781     collapseIf : function(e){
37782         if(!e.within(this.wrap) && !e.within(this.list)){
37783             this.collapse();
37784         }
37785     },
37786
37787     /**
37788      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37789      */
37790     expand : function(){
37791         if(this.isExpanded() || !this.hasFocus){
37792             return;
37793         }
37794         this.list.alignTo(this.el, this.listAlign);
37795         this.list.show();
37796         Roo.get(document).on('mousedown', this.collapseIf, this);
37797         Roo.get(document).on('mousewheel', this.collapseIf, this);
37798         if (!this.editable) {
37799             Roo.get(document).on('keydown', this.listKeyPress, this);
37800         }
37801         
37802         this.fireEvent('expand', this);
37803     },
37804
37805     // private
37806     // Implements the default empty TriggerField.onTriggerClick function
37807     onTriggerClick : function(){
37808         if(this.disabled){
37809             return;
37810         }
37811         if(this.isExpanded()){
37812             this.collapse();
37813             if (!this.blockFocus) {
37814                 this.el.focus();
37815             }
37816             
37817         }else {
37818             this.hasFocus = true;
37819             if(this.triggerAction == 'all') {
37820                 this.doQuery(this.allQuery, true);
37821             } else {
37822                 this.doQuery(this.getRawValue());
37823             }
37824             if (!this.blockFocus) {
37825                 this.el.focus();
37826             }
37827         }
37828     },
37829     listKeyPress : function(e)
37830     {
37831         //Roo.log('listkeypress');
37832         // scroll to first matching element based on key pres..
37833         if (e.isSpecialKey()) {
37834             return false;
37835         }
37836         var k = String.fromCharCode(e.getKey()).toUpperCase();
37837         //Roo.log(k);
37838         var match  = false;
37839         var csel = this.view.getSelectedNodes();
37840         var cselitem = false;
37841         if (csel.length) {
37842             var ix = this.view.indexOf(csel[0]);
37843             cselitem  = this.store.getAt(ix);
37844             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37845                 cselitem = false;
37846             }
37847             
37848         }
37849         
37850         this.store.each(function(v) { 
37851             if (cselitem) {
37852                 // start at existing selection.
37853                 if (cselitem.id == v.id) {
37854                     cselitem = false;
37855                 }
37856                 return;
37857             }
37858                 
37859             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37860                 match = this.store.indexOf(v);
37861                 return false;
37862             }
37863         }, this);
37864         
37865         if (match === false) {
37866             return true; // no more action?
37867         }
37868         // scroll to?
37869         this.view.select(match);
37870         var sn = Roo.get(this.view.getSelectedNodes()[0])
37871         sn.scrollIntoView(sn.dom.parentNode, false);
37872     }
37873
37874     /** 
37875     * @cfg {Boolean} grow 
37876     * @hide 
37877     */
37878     /** 
37879     * @cfg {Number} growMin 
37880     * @hide 
37881     */
37882     /** 
37883     * @cfg {Number} growMax 
37884     * @hide 
37885     */
37886     /**
37887      * @hide
37888      * @method autoSize
37889      */
37890 });/*
37891  * Based on:
37892  * Ext JS Library 1.1.1
37893  * Copyright(c) 2006-2007, Ext JS, LLC.
37894  *
37895  * Originally Released Under LGPL - original licence link has changed is not relivant.
37896  *
37897  * Fork - LGPL
37898  * <script type="text/javascript">
37899  */
37900 /**
37901  * @class Roo.form.Checkbox
37902  * @extends Roo.form.Field
37903  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37904  * @constructor
37905  * Creates a new Checkbox
37906  * @param {Object} config Configuration options
37907  */
37908 Roo.form.Checkbox = function(config){
37909     Roo.form.Checkbox.superclass.constructor.call(this, config);
37910     this.addEvents({
37911         /**
37912          * @event check
37913          * Fires when the checkbox is checked or unchecked.
37914              * @param {Roo.form.Checkbox} this This checkbox
37915              * @param {Boolean} checked The new checked value
37916              */
37917         check : true
37918     });
37919 };
37920
37921 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37922     /**
37923      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37924      */
37925     focusClass : undefined,
37926     /**
37927      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37928      */
37929     fieldClass: "x-form-field",
37930     /**
37931      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37932      */
37933     checked: false,
37934     /**
37935      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37936      * {tag: "input", type: "checkbox", autocomplete: "off"})
37937      */
37938     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37939     /**
37940      * @cfg {String} boxLabel The text that appears beside the checkbox
37941      */
37942     boxLabel : "",
37943     /**
37944      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37945      */  
37946     inputValue : '1',
37947     /**
37948      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37949      */
37950      valueOff: '0', // value when not checked..
37951
37952     actionMode : 'viewEl', 
37953     //
37954     // private
37955     itemCls : 'x-menu-check-item x-form-item',
37956     groupClass : 'x-menu-group-item',
37957     inputType : 'hidden',
37958     
37959     
37960     inSetChecked: false, // check that we are not calling self...
37961     
37962     inputElement: false, // real input element?
37963     basedOn: false, // ????
37964     
37965     isFormField: true, // not sure where this is needed!!!!
37966
37967     onResize : function(){
37968         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37969         if(!this.boxLabel){
37970             this.el.alignTo(this.wrap, 'c-c');
37971         }
37972     },
37973
37974     initEvents : function(){
37975         Roo.form.Checkbox.superclass.initEvents.call(this);
37976         this.el.on("click", this.onClick,  this);
37977         this.el.on("change", this.onClick,  this);
37978     },
37979
37980
37981     getResizeEl : function(){
37982         return this.wrap;
37983     },
37984
37985     getPositionEl : function(){
37986         return this.wrap;
37987     },
37988
37989     // private
37990     onRender : function(ct, position){
37991         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37992         /*
37993         if(this.inputValue !== undefined){
37994             this.el.dom.value = this.inputValue;
37995         }
37996         */
37997         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37998         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37999         var viewEl = this.wrap.createChild({ 
38000             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38001         this.viewEl = viewEl;   
38002         this.wrap.on('click', this.onClick,  this); 
38003         
38004         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38005         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38006         
38007         
38008         
38009         if(this.boxLabel){
38010             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38011         //    viewEl.on('click', this.onClick,  this); 
38012         }
38013         //if(this.checked){
38014             this.setChecked(this.checked);
38015         //}else{
38016             //this.checked = this.el.dom;
38017         //}
38018
38019     },
38020
38021     // private
38022     initValue : Roo.emptyFn,
38023
38024     /**
38025      * Returns the checked state of the checkbox.
38026      * @return {Boolean} True if checked, else false
38027      */
38028     getValue : function(){
38029         if(this.el){
38030             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38031         }
38032         return this.valueOff;
38033         
38034     },
38035
38036         // private
38037     onClick : function(){ 
38038         this.setChecked(!this.checked);
38039
38040         //if(this.el.dom.checked != this.checked){
38041         //    this.setValue(this.el.dom.checked);
38042        // }
38043     },
38044
38045     /**
38046      * Sets the checked state of the checkbox.
38047      * On is always based on a string comparison between inputValue and the param.
38048      * @param {Boolean/String} value - the value to set 
38049      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38050      */
38051     setValue : function(v,suppressEvent){
38052         
38053         
38054         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38055         //if(this.el && this.el.dom){
38056         //    this.el.dom.checked = this.checked;
38057         //    this.el.dom.defaultChecked = this.checked;
38058         //}
38059         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38060         //this.fireEvent("check", this, this.checked);
38061     },
38062     // private..
38063     setChecked : function(state,suppressEvent)
38064     {
38065         if (this.inSetChecked) {
38066             this.checked = state;
38067             return;
38068         }
38069         
38070     
38071         if(this.wrap){
38072             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38073         }
38074         this.checked = state;
38075         if(suppressEvent !== true){
38076             this.fireEvent('check', this, state);
38077         }
38078         this.inSetChecked = true;
38079         this.el.dom.value = state ? this.inputValue : this.valueOff;
38080         this.inSetChecked = false;
38081         
38082     },
38083     // handle setting of hidden value by some other method!!?!?
38084     setFromHidden: function()
38085     {
38086         if(!this.el){
38087             return;
38088         }
38089         //console.log("SET FROM HIDDEN");
38090         //alert('setFrom hidden');
38091         this.setValue(this.el.dom.value);
38092     },
38093     
38094     onDestroy : function()
38095     {
38096         if(this.viewEl){
38097             Roo.get(this.viewEl).remove();
38098         }
38099          
38100         Roo.form.Checkbox.superclass.onDestroy.call(this);
38101     }
38102
38103 });/*
38104  * Based on:
38105  * Ext JS Library 1.1.1
38106  * Copyright(c) 2006-2007, Ext JS, LLC.
38107  *
38108  * Originally Released Under LGPL - original licence link has changed is not relivant.
38109  *
38110  * Fork - LGPL
38111  * <script type="text/javascript">
38112  */
38113  
38114 /**
38115  * @class Roo.form.Radio
38116  * @extends Roo.form.Checkbox
38117  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38118  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38119  * @constructor
38120  * Creates a new Radio
38121  * @param {Object} config Configuration options
38122  */
38123 Roo.form.Radio = function(){
38124     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38125 };
38126 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38127     inputType: 'radio',
38128
38129     /**
38130      * If this radio is part of a group, it will return the selected value
38131      * @return {String}
38132      */
38133     getGroupValue : function(){
38134         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38135     }
38136 });//<script type="text/javascript">
38137
38138 /*
38139  * Ext JS Library 1.1.1
38140  * Copyright(c) 2006-2007, Ext JS, LLC.
38141  * licensing@extjs.com
38142  * 
38143  * http://www.extjs.com/license
38144  */
38145  
38146  /*
38147   * 
38148   * Known bugs:
38149   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38150   * - IE ? - no idea how much works there.
38151   * 
38152   * 
38153   * 
38154   */
38155  
38156
38157 /**
38158  * @class Ext.form.HtmlEditor
38159  * @extends Ext.form.Field
38160  * Provides a lightweight HTML Editor component.
38161  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38162  * 
38163  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38164  * supported by this editor.</b><br/><br/>
38165  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38166  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38167  */
38168 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38169       /**
38170      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38171      */
38172     toolbars : false,
38173     /**
38174      * @cfg {String} createLinkText The default text for the create link prompt
38175      */
38176     createLinkText : 'Please enter the URL for the link:',
38177     /**
38178      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38179      */
38180     defaultLinkValue : 'http:/'+'/',
38181    
38182     
38183     // id of frame..
38184     frameId: false,
38185     
38186     // private properties
38187     validationEvent : false,
38188     deferHeight: true,
38189     initialized : false,
38190     activated : false,
38191     sourceEditMode : false,
38192     onFocus : Roo.emptyFn,
38193     iframePad:3,
38194     hideMode:'offsets',
38195     defaultAutoCreate : {
38196         tag: "textarea",
38197         style:"width:500px;height:300px;",
38198         autocomplete: "off"
38199     },
38200
38201     // private
38202     initComponent : function(){
38203         this.addEvents({
38204             /**
38205              * @event initialize
38206              * Fires when the editor is fully initialized (including the iframe)
38207              * @param {HtmlEditor} this
38208              */
38209             initialize: true,
38210             /**
38211              * @event activate
38212              * Fires when the editor is first receives the focus. Any insertion must wait
38213              * until after this event.
38214              * @param {HtmlEditor} this
38215              */
38216             activate: true,
38217              /**
38218              * @event beforesync
38219              * Fires before the textarea is updated with content from the editor iframe. Return false
38220              * to cancel the sync.
38221              * @param {HtmlEditor} this
38222              * @param {String} html
38223              */
38224             beforesync: true,
38225              /**
38226              * @event beforepush
38227              * Fires before the iframe editor is updated with content from the textarea. Return false
38228              * to cancel the push.
38229              * @param {HtmlEditor} this
38230              * @param {String} html
38231              */
38232             beforepush: true,
38233              /**
38234              * @event sync
38235              * Fires when the textarea is updated with content from the editor iframe.
38236              * @param {HtmlEditor} this
38237              * @param {String} html
38238              */
38239             sync: true,
38240              /**
38241              * @event push
38242              * Fires when the iframe editor is updated with content from the textarea.
38243              * @param {HtmlEditor} this
38244              * @param {String} html
38245              */
38246             push: true,
38247              /**
38248              * @event editmodechange
38249              * Fires when the editor switches edit modes
38250              * @param {HtmlEditor} this
38251              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38252              */
38253             editmodechange: true,
38254             /**
38255              * @event editorevent
38256              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38257              * @param {HtmlEditor} this
38258              */
38259             editorevent: true
38260         })
38261     },
38262
38263     /**
38264      * Protected method that will not generally be called directly. It
38265      * is called when the editor creates its toolbar. Override this method if you need to
38266      * add custom toolbar buttons.
38267      * @param {HtmlEditor} editor
38268      */
38269     createToolbar : function(editor){
38270         if (!editor.toolbars || !editor.toolbars.length) {
38271             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38272         }
38273         
38274         for (var i =0 ; i < editor.toolbars.length;i++) {
38275             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38276             editor.toolbars[i].init(editor);
38277         }
38278          
38279         
38280     },
38281
38282     /**
38283      * Protected method that will not generally be called directly. It
38284      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38285      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38286      */
38287     getDocMarkup : function(){
38288         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38289     },
38290
38291     // private
38292     onRender : function(ct, position){
38293         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38294         this.el.dom.style.border = '0 none';
38295         this.el.dom.setAttribute('tabIndex', -1);
38296         this.el.addClass('x-hidden');
38297         if(Roo.isIE){ // fix IE 1px bogus margin
38298             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38299         }
38300         this.wrap = this.el.wrap({
38301             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38302         });
38303
38304         this.frameId = Roo.id();
38305         this.createToolbar(this);
38306         
38307         
38308         
38309         
38310       
38311         
38312         var iframe = this.wrap.createChild({
38313             tag: 'iframe',
38314             id: this.frameId,
38315             name: this.frameId,
38316             frameBorder : 'no',
38317             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38318         });
38319         
38320        // console.log(iframe);
38321         //this.wrap.dom.appendChild(iframe);
38322
38323         this.iframe = iframe.dom;
38324
38325          this.assignDocWin();
38326         
38327         this.doc.designMode = 'on';
38328        
38329         this.doc.open();
38330         this.doc.write(this.getDocMarkup());
38331         this.doc.close();
38332
38333         
38334         var task = { // must defer to wait for browser to be ready
38335             run : function(){
38336                 //console.log("run task?" + this.doc.readyState);
38337                 this.assignDocWin();
38338                 if(this.doc.body || this.doc.readyState == 'complete'){
38339                     try {
38340                         this.doc.designMode="on";
38341                     } catch (e) {
38342                         return;
38343                     }
38344                     Roo.TaskMgr.stop(task);
38345                     this.initEditor.defer(10, this);
38346                 }
38347             },
38348             interval : 10,
38349             duration:10000,
38350             scope: this
38351         };
38352         Roo.TaskMgr.start(task);
38353
38354         if(!this.width){
38355             this.setSize(this.el.getSize());
38356         }
38357     },
38358
38359     // private
38360     onResize : function(w, h){
38361         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38362         if(this.el && this.iframe){
38363             if(typeof w == 'number'){
38364                 var aw = w - this.wrap.getFrameWidth('lr');
38365                 this.el.setWidth(this.adjustWidth('textarea', aw));
38366                 this.iframe.style.width = aw + 'px';
38367             }
38368             if(typeof h == 'number'){
38369                 var tbh = 0;
38370                 for (var i =0; i < this.toolbars.length;i++) {
38371                     // fixme - ask toolbars for heights?
38372                     tbh += this.toolbars[i].tb.el.getHeight();
38373                 }
38374                 
38375                 
38376                 
38377                 
38378                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38379                 this.el.setHeight(this.adjustWidth('textarea', ah));
38380                 this.iframe.style.height = ah + 'px';
38381                 if(this.doc){
38382                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38383                 }
38384             }
38385         }
38386     },
38387
38388     /**
38389      * Toggles the editor between standard and source edit mode.
38390      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38391      */
38392     toggleSourceEdit : function(sourceEditMode){
38393         
38394         this.sourceEditMode = sourceEditMode === true;
38395         
38396         if(this.sourceEditMode){
38397           
38398             this.syncValue();
38399             this.iframe.className = 'x-hidden';
38400             this.el.removeClass('x-hidden');
38401             this.el.dom.removeAttribute('tabIndex');
38402             this.el.focus();
38403         }else{
38404              
38405             this.pushValue();
38406             this.iframe.className = '';
38407             this.el.addClass('x-hidden');
38408             this.el.dom.setAttribute('tabIndex', -1);
38409             this.deferFocus();
38410         }
38411         this.setSize(this.wrap.getSize());
38412         this.fireEvent('editmodechange', this, this.sourceEditMode);
38413     },
38414
38415     // private used internally
38416     createLink : function(){
38417         var url = prompt(this.createLinkText, this.defaultLinkValue);
38418         if(url && url != 'http:/'+'/'){
38419             this.relayCmd('createlink', url);
38420         }
38421     },
38422
38423     // private (for BoxComponent)
38424     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38425
38426     // private (for BoxComponent)
38427     getResizeEl : function(){
38428         return this.wrap;
38429     },
38430
38431     // private (for BoxComponent)
38432     getPositionEl : function(){
38433         return this.wrap;
38434     },
38435
38436     // private
38437     initEvents : function(){
38438         this.originalValue = this.getValue();
38439     },
38440
38441     /**
38442      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38443      * @method
38444      */
38445     markInvalid : Roo.emptyFn,
38446     /**
38447      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38448      * @method
38449      */
38450     clearInvalid : Roo.emptyFn,
38451
38452     setValue : function(v){
38453         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38454         this.pushValue();
38455     },
38456
38457     /**
38458      * Protected method that will not generally be called directly. If you need/want
38459      * custom HTML cleanup, this is the method you should override.
38460      * @param {String} html The HTML to be cleaned
38461      * return {String} The cleaned HTML
38462      */
38463     cleanHtml : function(html){
38464         html = String(html);
38465         if(html.length > 5){
38466             if(Roo.isSafari){ // strip safari nonsense
38467                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38468             }
38469         }
38470         if(html == '&nbsp;'){
38471             html = '';
38472         }
38473         return html;
38474     },
38475
38476     /**
38477      * Protected method that will not generally be called directly. Syncs the contents
38478      * of the editor iframe with the textarea.
38479      */
38480     syncValue : function(){
38481         if(this.initialized){
38482             var bd = (this.doc.body || this.doc.documentElement);
38483             var html = bd.innerHTML;
38484             if(Roo.isSafari){
38485                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38486                 var m = bs.match(/text-align:(.*?);/i);
38487                 if(m && m[1]){
38488                     html = '<div style="'+m[0]+'">' + html + '</div>';
38489                 }
38490             }
38491             html = this.cleanHtml(html);
38492             if(this.fireEvent('beforesync', this, html) !== false){
38493                 this.el.dom.value = html;
38494                 this.fireEvent('sync', this, html);
38495             }
38496         }
38497     },
38498
38499     /**
38500      * Protected method that will not generally be called directly. Pushes the value of the textarea
38501      * into the iframe editor.
38502      */
38503     pushValue : function(){
38504         if(this.initialized){
38505             var v = this.el.dom.value;
38506             if(v.length < 1){
38507                 v = '&#160;';
38508             }
38509             if(this.fireEvent('beforepush', this, v) !== false){
38510                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38511                 this.fireEvent('push', this, v);
38512             }
38513         }
38514     },
38515
38516     // private
38517     deferFocus : function(){
38518         this.focus.defer(10, this);
38519     },
38520
38521     // doc'ed in Field
38522     focus : function(){
38523         if(this.win && !this.sourceEditMode){
38524             this.win.focus();
38525         }else{
38526             this.el.focus();
38527         }
38528     },
38529     
38530     assignDocWin: function()
38531     {
38532         var iframe = this.iframe;
38533         
38534          if(Roo.isIE){
38535             this.doc = iframe.contentWindow.document;
38536             this.win = iframe.contentWindow;
38537         } else {
38538             if (!Roo.get(this.frameId)) {
38539                 return;
38540             }
38541             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38542             this.win = Roo.get(this.frameId).dom.contentWindow;
38543         }
38544     },
38545     
38546     // private
38547     initEditor : function(){
38548         //console.log("INIT EDITOR");
38549         this.assignDocWin();
38550         
38551         
38552         
38553         this.doc.designMode="on";
38554         this.doc.open();
38555         this.doc.write(this.getDocMarkup());
38556         this.doc.close();
38557         
38558         var dbody = (this.doc.body || this.doc.documentElement);
38559         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38560         // this copies styles from the containing element into thsi one..
38561         // not sure why we need all of this..
38562         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38563         ss['background-attachment'] = 'fixed'; // w3c
38564         dbody.bgProperties = 'fixed'; // ie
38565         Roo.DomHelper.applyStyles(dbody, ss);
38566         Roo.EventManager.on(this.doc, {
38567             'mousedown': this.onEditorEvent,
38568             'dblclick': this.onEditorEvent,
38569             'click': this.onEditorEvent,
38570             'keyup': this.onEditorEvent,
38571             buffer:100,
38572             scope: this
38573         });
38574         if(Roo.isGecko){
38575             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38576         }
38577         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38578             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38579         }
38580         this.initialized = true;
38581
38582         this.fireEvent('initialize', this);
38583         this.pushValue();
38584     },
38585
38586     // private
38587     onDestroy : function(){
38588         
38589         
38590         
38591         if(this.rendered){
38592             
38593             for (var i =0; i < this.toolbars.length;i++) {
38594                 // fixme - ask toolbars for heights?
38595                 this.toolbars[i].onDestroy();
38596             }
38597             
38598             this.wrap.dom.innerHTML = '';
38599             this.wrap.remove();
38600         }
38601     },
38602
38603     // private
38604     onFirstFocus : function(){
38605         
38606         this.assignDocWin();
38607         
38608         
38609         this.activated = true;
38610         for (var i =0; i < this.toolbars.length;i++) {
38611             this.toolbars[i].onFirstFocus();
38612         }
38613        
38614         if(Roo.isGecko){ // prevent silly gecko errors
38615             this.win.focus();
38616             var s = this.win.getSelection();
38617             if(!s.focusNode || s.focusNode.nodeType != 3){
38618                 var r = s.getRangeAt(0);
38619                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38620                 r.collapse(true);
38621                 this.deferFocus();
38622             }
38623             try{
38624                 this.execCmd('useCSS', true);
38625                 this.execCmd('styleWithCSS', false);
38626             }catch(e){}
38627         }
38628         this.fireEvent('activate', this);
38629     },
38630
38631     // private
38632     adjustFont: function(btn){
38633         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38634         //if(Roo.isSafari){ // safari
38635         //    adjust *= 2;
38636        // }
38637         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38638         if(Roo.isSafari){ // safari
38639             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38640             v =  (v < 10) ? 10 : v;
38641             v =  (v > 48) ? 48 : v;
38642             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38643             
38644         }
38645         
38646         
38647         v = Math.max(1, v+adjust);
38648         
38649         this.execCmd('FontSize', v  );
38650     },
38651
38652     onEditorEvent : function(e){
38653         this.fireEvent('editorevent', this, e);
38654       //  this.updateToolbar();
38655         this.syncValue();
38656     },
38657
38658     insertTag : function(tg)
38659     {
38660         // could be a bit smarter... -> wrap the current selected tRoo..
38661         
38662         this.execCmd("formatblock",   tg);
38663         
38664     },
38665     
38666     insertText : function(txt)
38667     {
38668         
38669         
38670         range = this.createRange();
38671         range.deleteContents();
38672                //alert(Sender.getAttribute('label'));
38673                
38674         range.insertNode(this.doc.createTextNode(txt));
38675     } ,
38676     
38677     // private
38678     relayBtnCmd : function(btn){
38679         this.relayCmd(btn.cmd);
38680     },
38681
38682     /**
38683      * Executes a Midas editor command on the editor document and performs necessary focus and
38684      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38685      * @param {String} cmd The Midas command
38686      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38687      */
38688     relayCmd : function(cmd, value){
38689         this.win.focus();
38690         this.execCmd(cmd, value);
38691         this.fireEvent('editorevent', this);
38692         //this.updateToolbar();
38693         this.deferFocus();
38694     },
38695
38696     /**
38697      * Executes a Midas editor command directly on the editor document.
38698      * For visual commands, you should use {@link #relayCmd} instead.
38699      * <b>This should only be called after the editor is initialized.</b>
38700      * @param {String} cmd The Midas command
38701      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38702      */
38703     execCmd : function(cmd, value){
38704         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38705         this.syncValue();
38706     },
38707
38708    
38709     /**
38710      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38711      * to insert tRoo.
38712      * @param {String} text
38713      */
38714     insertAtCursor : function(text){
38715         if(!this.activated){
38716             return;
38717         }
38718         if(Roo.isIE){
38719             this.win.focus();
38720             var r = this.doc.selection.createRange();
38721             if(r){
38722                 r.collapse(true);
38723                 r.pasteHTML(text);
38724                 this.syncValue();
38725                 this.deferFocus();
38726             }
38727         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38728             this.win.focus();
38729             this.execCmd('InsertHTML', text);
38730             this.deferFocus();
38731         }
38732     },
38733  // private
38734     mozKeyPress : function(e){
38735         if(e.ctrlKey){
38736             var c = e.getCharCode(), cmd;
38737           
38738             if(c > 0){
38739                 c = String.fromCharCode(c).toLowerCase();
38740                 switch(c){
38741                     case 'b':
38742                         cmd = 'bold';
38743                     break;
38744                     case 'i':
38745                         cmd = 'italic';
38746                     break;
38747                     case 'u':
38748                         cmd = 'underline';
38749                     case 'v':
38750                         this.cleanUpPaste.defer(100, this);
38751                         return;
38752                     break;
38753                 }
38754                 if(cmd){
38755                     this.win.focus();
38756                     this.execCmd(cmd);
38757                     this.deferFocus();
38758                     e.preventDefault();
38759                 }
38760                 
38761             }
38762         }
38763     },
38764
38765     // private
38766     fixKeys : function(){ // load time branching for fastest keydown performance
38767         if(Roo.isIE){
38768             return function(e){
38769                 var k = e.getKey(), r;
38770                 if(k == e.TAB){
38771                     e.stopEvent();
38772                     r = this.doc.selection.createRange();
38773                     if(r){
38774                         r.collapse(true);
38775                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38776                         this.deferFocus();
38777                     }
38778                     return;
38779                 }
38780                 
38781                 if(k == e.ENTER){
38782                     r = this.doc.selection.createRange();
38783                     if(r){
38784                         var target = r.parentElement();
38785                         if(!target || target.tagName.toLowerCase() != 'li'){
38786                             e.stopEvent();
38787                             r.pasteHTML('<br />');
38788                             r.collapse(false);
38789                             r.select();
38790                         }
38791                     }
38792                 }
38793                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38794                     this.cleanUpPaste.defer(100, this);
38795                     return;
38796                 }
38797                 
38798                 
38799             };
38800         }else if(Roo.isOpera){
38801             return function(e){
38802                 var k = e.getKey();
38803                 if(k == e.TAB){
38804                     e.stopEvent();
38805                     this.win.focus();
38806                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38807                     this.deferFocus();
38808                 }
38809                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38810                     this.cleanUpPaste.defer(100, this);
38811                     return;
38812                 }
38813                 
38814             };
38815         }else if(Roo.isSafari){
38816             return function(e){
38817                 var k = e.getKey();
38818                 
38819                 if(k == e.TAB){
38820                     e.stopEvent();
38821                     this.execCmd('InsertText','\t');
38822                     this.deferFocus();
38823                     return;
38824                 }
38825                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38826                     this.cleanUpPaste.defer(100, this);
38827                     return;
38828                 }
38829                 
38830              };
38831         }
38832     }(),
38833     
38834     getAllAncestors: function()
38835     {
38836         var p = this.getSelectedNode();
38837         var a = [];
38838         if (!p) {
38839             a.push(p); // push blank onto stack..
38840             p = this.getParentElement();
38841         }
38842         
38843         
38844         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38845             a.push(p);
38846             p = p.parentNode;
38847         }
38848         a.push(this.doc.body);
38849         return a;
38850     },
38851     lastSel : false,
38852     lastSelNode : false,
38853     
38854     
38855     getSelection : function() 
38856     {
38857         this.assignDocWin();
38858         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38859     },
38860     
38861     getSelectedNode: function() 
38862     {
38863         // this may only work on Gecko!!!
38864         
38865         // should we cache this!!!!
38866         
38867         
38868         
38869          
38870         var range = this.createRange(this.getSelection());
38871         
38872         if (Roo.isIE) {
38873             var parent = range.parentElement();
38874             while (true) {
38875                 var testRange = range.duplicate();
38876                 testRange.moveToElementText(parent);
38877                 if (testRange.inRange(range)) {
38878                     break;
38879                 }
38880                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38881                     break;
38882                 }
38883                 parent = parent.parentElement;
38884             }
38885             return parent;
38886         }
38887         
38888         
38889         var ar = range.endContainer.childNodes;
38890         if (!ar.length) {
38891             ar = range.commonAncestorContainer.childNodes;
38892             //alert(ar.length);
38893         }
38894         var nodes = [];
38895         var other_nodes = [];
38896         var has_other_nodes = false;
38897         for (var i=0;i<ar.length;i++) {
38898             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38899                 continue;
38900             }
38901             // fullly contained node.
38902             
38903             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38904                 nodes.push(ar[i]);
38905                 continue;
38906             }
38907             
38908             // probably selected..
38909             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38910                 other_nodes.push(ar[i]);
38911                 continue;
38912             }
38913             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38914                 continue;
38915             }
38916             
38917             
38918             has_other_nodes = true;
38919         }
38920         if (!nodes.length && other_nodes.length) {
38921             nodes= other_nodes;
38922         }
38923         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38924             return false;
38925         }
38926         
38927         return nodes[0];
38928     },
38929     createRange: function(sel)
38930     {
38931         // this has strange effects when using with 
38932         // top toolbar - not sure if it's a great idea.
38933         //this.editor.contentWindow.focus();
38934         if (typeof sel != "undefined") {
38935             try {
38936                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38937             } catch(e) {
38938                 return this.doc.createRange();
38939             }
38940         } else {
38941             return this.doc.createRange();
38942         }
38943     },
38944     getParentElement: function()
38945     {
38946         
38947         this.assignDocWin();
38948         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38949         
38950         var range = this.createRange(sel);
38951          
38952         try {
38953             var p = range.commonAncestorContainer;
38954             while (p.nodeType == 3) { // text node
38955                 p = p.parentNode;
38956             }
38957             return p;
38958         } catch (e) {
38959             return null;
38960         }
38961     
38962     },
38963     
38964     
38965     
38966     // BC Hacks - cause I cant work out what i was trying to do..
38967     rangeIntersectsNode : function(range, node)
38968     {
38969         var nodeRange = node.ownerDocument.createRange();
38970         try {
38971             nodeRange.selectNode(node);
38972         }
38973         catch (e) {
38974             nodeRange.selectNodeContents(node);
38975         }
38976
38977         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38978                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38979     },
38980     rangeCompareNode : function(range, node) {
38981         var nodeRange = node.ownerDocument.createRange();
38982         try {
38983             nodeRange.selectNode(node);
38984         } catch (e) {
38985             nodeRange.selectNodeContents(node);
38986         }
38987         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38988         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38989
38990         if (nodeIsBefore && !nodeIsAfter)
38991             return 0;
38992         if (!nodeIsBefore && nodeIsAfter)
38993             return 1;
38994         if (nodeIsBefore && nodeIsAfter)
38995             return 2;
38996
38997         return 3;
38998     },
38999
39000     // private? - in a new class?
39001     cleanUpPaste :  function()
39002     {
39003         // cleans up the whole document..
39004       //  console.log('cleanuppaste');
39005         this.cleanUpChildren(this.doc.body)
39006         
39007         
39008     },
39009     cleanUpChildren : function (n)
39010     {
39011         if (!n.childNodes.length) {
39012             return;
39013         }
39014         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39015            this.cleanUpChild(n.childNodes[i]);
39016         }
39017     },
39018     
39019     
39020         
39021     
39022     cleanUpChild : function (node)
39023     {
39024         //console.log(node);
39025         if (node.nodeName == "#text") {
39026             // clean up silly Windows -- stuff?
39027             return; 
39028         }
39029         if (node.nodeName == "#comment") {
39030             node.parentNode.removeChild(node);
39031             // clean up silly Windows -- stuff?
39032             return; 
39033         }
39034         
39035         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39036             // remove node.
39037             node.parentNode.removeChild(node);
39038             return;
39039             
39040         }
39041         if (!node.attributes || !node.attributes.length) {
39042             this.cleanUpChildren(node);
39043             return;
39044         }
39045         
39046         function cleanAttr(n,v)
39047         {
39048             
39049             if (v.match(/^\./) || v.match(/^\//)) {
39050                 return;
39051             }
39052             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39053                 return;
39054             }
39055             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39056             node.removeAttribute(n);
39057             
39058         }
39059         
39060         function cleanStyle(n,v)
39061         {
39062             if (v.match(/expression/)) { //XSS?? should we even bother..
39063                 node.removeAttribute(n);
39064                 return;
39065             }
39066             
39067             
39068             var parts = v.split(/;/);
39069             Roo.each(parts, function(p) {
39070                 p = p.replace(/\s+/g,'');
39071                 if (!p.length) {
39072                     return;
39073                 }
39074                 var l = p.split(':').shift().replace(/\s+/g,'');
39075                 
39076                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39077                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39078                     node.removeAttribute(n);
39079                     return false;
39080                 }
39081             });
39082             
39083             
39084         }
39085         
39086         
39087         for (var i = node.attributes.length-1; i > -1 ; i--) {
39088             var a = node.attributes[i];
39089             //console.log(a);
39090             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39091                 node.removeAttribute(a.name);
39092                 return;
39093             }
39094             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39095                 cleanAttr(a.name,a.value); // fixme..
39096                 return;
39097             }
39098             if (a.name == 'style') {
39099                 cleanStyle(a.name,a.value);
39100             }
39101             /// clean up MS crap..
39102             if (a.name == 'class') {
39103                 if (a.value.match(/^Mso/)) {
39104                     node.className = '';
39105                 }
39106             }
39107             
39108             // style cleanup!?
39109             // class cleanup?
39110             
39111         }
39112         
39113         
39114         this.cleanUpChildren(node);
39115         
39116         
39117     }
39118     
39119     
39120     // hide stuff that is not compatible
39121     /**
39122      * @event blur
39123      * @hide
39124      */
39125     /**
39126      * @event change
39127      * @hide
39128      */
39129     /**
39130      * @event focus
39131      * @hide
39132      */
39133     /**
39134      * @event specialkey
39135      * @hide
39136      */
39137     /**
39138      * @cfg {String} fieldClass @hide
39139      */
39140     /**
39141      * @cfg {String} focusClass @hide
39142      */
39143     /**
39144      * @cfg {String} autoCreate @hide
39145      */
39146     /**
39147      * @cfg {String} inputType @hide
39148      */
39149     /**
39150      * @cfg {String} invalidClass @hide
39151      */
39152     /**
39153      * @cfg {String} invalidText @hide
39154      */
39155     /**
39156      * @cfg {String} msgFx @hide
39157      */
39158     /**
39159      * @cfg {String} validateOnBlur @hide
39160      */
39161 });
39162
39163 Roo.form.HtmlEditor.white = [
39164         'area', 'br', 'img', 'input', 'hr', 'wbr',
39165         
39166        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39167        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39168        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39169        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39170        'table',   'ul',         'xmp', 
39171        
39172        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39173       'thead',   'tr', 
39174      
39175       'dir', 'menu', 'ol', 'ul', 'dl',
39176        
39177       'embed',  'object'
39178 ];
39179
39180
39181 Roo.form.HtmlEditor.black = [
39182     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39183         'applet', // 
39184         'base',   'basefont', 'bgsound', 'blink',  'body', 
39185         'frame',  'frameset', 'head',    'html',   'ilayer', 
39186         'iframe', 'layer',  'link',     'meta',    'object',   
39187         'script', 'style' ,'title',  'xml' // clean later..
39188 ];
39189 Roo.form.HtmlEditor.clean = [
39190     'script', 'style', 'title', 'xml'
39191 ];
39192
39193 // attributes..
39194
39195 Roo.form.HtmlEditor.ablack = [
39196     'on'
39197 ];
39198     
39199 Roo.form.HtmlEditor.aclean = [ 
39200     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39201 ];
39202
39203 // protocols..
39204 Roo.form.HtmlEditor.pwhite= [
39205         'http',  'https',  'mailto'
39206 ];
39207
39208 Roo.form.HtmlEditor.cwhite= [
39209         'text-align',
39210         'font-size'
39211 ];
39212
39213 // <script type="text/javascript">
39214 /*
39215  * Based on
39216  * Ext JS Library 1.1.1
39217  * Copyright(c) 2006-2007, Ext JS, LLC.
39218  *  
39219  
39220  */
39221
39222 /**
39223  * @class Roo.form.HtmlEditorToolbar1
39224  * Basic Toolbar
39225  * 
39226  * Usage:
39227  *
39228  new Roo.form.HtmlEditor({
39229     ....
39230     toolbars : [
39231         new Roo.form.HtmlEditorToolbar1({
39232             disable : { fonts: 1 , format: 1, ..., ... , ...],
39233             btns : [ .... ]
39234         })
39235     }
39236      
39237  * 
39238  * @cfg {Object} disable List of elements to disable..
39239  * @cfg {Array} btns List of additional buttons.
39240  * 
39241  * 
39242  * NEEDS Extra CSS? 
39243  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39244  */
39245  
39246 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39247 {
39248     
39249     Roo.apply(this, config);
39250     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39251     // dont call parent... till later.
39252 }
39253
39254 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39255     
39256     tb: false,
39257     
39258     rendered: false,
39259     
39260     editor : false,
39261     /**
39262      * @cfg {Object} disable  List of toolbar elements to disable
39263          
39264      */
39265     disable : false,
39266       /**
39267      * @cfg {Array} fontFamilies An array of available font families
39268      */
39269     fontFamilies : [
39270         'Arial',
39271         'Courier New',
39272         'Tahoma',
39273         'Times New Roman',
39274         'Verdana'
39275     ],
39276     
39277     specialChars : [
39278            "&#169;",
39279           "&#174;",     
39280           "&#8482;",    
39281           "&#163;" ,    
39282          // "&#8212;",    
39283           "&#8230;",    
39284           "&#247;" ,    
39285         //  "&#225;" ,     ?? a acute?
39286            "&#8364;"    , //Euro
39287        //   "&#8220;"    ,
39288         //  "&#8221;"    ,
39289         //  "&#8226;"    ,
39290           "&#176;"  //   , // degrees
39291
39292          // "&#233;"     , // e ecute
39293          // "&#250;"     , // u ecute?
39294     ],
39295     inputElements : [ 
39296             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39297             "input:submit", "input:button", "select", "textarea", "label" ],
39298     formats : [
39299         ["p"] ,  
39300         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39301         ["pre"],[ "code"], 
39302         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39303     ],
39304      /**
39305      * @cfg {String} defaultFont default font to use.
39306      */
39307     defaultFont: 'tahoma',
39308    
39309     fontSelect : false,
39310     
39311     
39312     formatCombo : false,
39313     
39314     init : function(editor)
39315     {
39316         this.editor = editor;
39317         
39318         
39319         var fid = editor.frameId;
39320         var etb = this;
39321         function btn(id, toggle, handler){
39322             var xid = fid + '-'+ id ;
39323             return {
39324                 id : xid,
39325                 cmd : id,
39326                 cls : 'x-btn-icon x-edit-'+id,
39327                 enableToggle:toggle !== false,
39328                 scope: editor, // was editor...
39329                 handler:handler||editor.relayBtnCmd,
39330                 clickEvent:'mousedown',
39331                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39332                 tabIndex:-1
39333             };
39334         }
39335         
39336         
39337         
39338         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39339         this.tb = tb;
39340          // stop form submits
39341         tb.el.on('click', function(e){
39342             e.preventDefault(); // what does this do?
39343         });
39344
39345         if(!this.disable.font && !Roo.isSafari){
39346             /* why no safari for fonts
39347             editor.fontSelect = tb.el.createChild({
39348                 tag:'select',
39349                 tabIndex: -1,
39350                 cls:'x-font-select',
39351                 html: editor.createFontOptions()
39352             });
39353             editor.fontSelect.on('change', function(){
39354                 var font = editor.fontSelect.dom.value;
39355                 editor.relayCmd('fontname', font);
39356                 editor.deferFocus();
39357             }, editor);
39358             tb.add(
39359                 editor.fontSelect.dom,
39360                 '-'
39361             );
39362             */
39363         };
39364         if(!this.disable.formats){
39365             this.formatCombo = new Roo.form.ComboBox({
39366                 store: new Roo.data.SimpleStore({
39367                     id : 'tag',
39368                     fields: ['tag'],
39369                     data : this.formats // from states.js
39370                 }),
39371                 blockFocus : true,
39372                 //autoCreate : {tag: "div",  size: "20"},
39373                 displayField:'tag',
39374                 typeAhead: false,
39375                 mode: 'local',
39376                 editable : false,
39377                 triggerAction: 'all',
39378                 emptyText:'Add tag',
39379                 selectOnFocus:true,
39380                 width:135,
39381                 listeners : {
39382                     'select': function(c, r, i) {
39383                         editor.insertTag(r.get('tag'));
39384                         editor.focus();
39385                     }
39386                 }
39387
39388             });
39389             tb.addField(this.formatCombo);
39390             
39391         }
39392         
39393         if(!this.disable.format){
39394             tb.add(
39395                 btn('bold'),
39396                 btn('italic'),
39397                 btn('underline')
39398             );
39399         };
39400         if(!this.disable.fontSize){
39401             tb.add(
39402                 '-',
39403                 
39404                 
39405                 btn('increasefontsize', false, editor.adjustFont),
39406                 btn('decreasefontsize', false, editor.adjustFont)
39407             );
39408         };
39409         
39410         
39411         if(this.disable.colors){
39412             tb.add(
39413                 '-', {
39414                     id:editor.frameId +'-forecolor',
39415                     cls:'x-btn-icon x-edit-forecolor',
39416                     clickEvent:'mousedown',
39417                     tooltip: this.buttonTips['forecolor'] || undefined,
39418                     tabIndex:-1,
39419                     menu : new Roo.menu.ColorMenu({
39420                         allowReselect: true,
39421                         focus: Roo.emptyFn,
39422                         value:'000000',
39423                         plain:true,
39424                         selectHandler: function(cp, color){
39425                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39426                             editor.deferFocus();
39427                         },
39428                         scope: editor,
39429                         clickEvent:'mousedown'
39430                     })
39431                 }, {
39432                     id:editor.frameId +'backcolor',
39433                     cls:'x-btn-icon x-edit-backcolor',
39434                     clickEvent:'mousedown',
39435                     tooltip: this.buttonTips['backcolor'] || undefined,
39436                     tabIndex:-1,
39437                     menu : new Roo.menu.ColorMenu({
39438                         focus: Roo.emptyFn,
39439                         value:'FFFFFF',
39440                         plain:true,
39441                         allowReselect: true,
39442                         selectHandler: function(cp, color){
39443                             if(Roo.isGecko){
39444                                 editor.execCmd('useCSS', false);
39445                                 editor.execCmd('hilitecolor', color);
39446                                 editor.execCmd('useCSS', true);
39447                                 editor.deferFocus();
39448                             }else{
39449                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39450                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39451                                 editor.deferFocus();
39452                             }
39453                         },
39454                         scope:editor,
39455                         clickEvent:'mousedown'
39456                     })
39457                 }
39458             );
39459         };
39460         // now add all the items...
39461         
39462
39463         if(!this.disable.alignments){
39464             tb.add(
39465                 '-',
39466                 btn('justifyleft'),
39467                 btn('justifycenter'),
39468                 btn('justifyright')
39469             );
39470         };
39471
39472         //if(!Roo.isSafari){
39473             if(!this.disable.links){
39474                 tb.add(
39475                     '-',
39476                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39477                 );
39478             };
39479
39480             if(!this.disable.lists){
39481                 tb.add(
39482                     '-',
39483                     btn('insertorderedlist'),
39484                     btn('insertunorderedlist')
39485                 );
39486             }
39487             if(!this.disable.sourceEdit){
39488                 tb.add(
39489                     '-',
39490                     btn('sourceedit', true, function(btn){
39491                         this.toggleSourceEdit(btn.pressed);
39492                     })
39493                 );
39494             }
39495         //}
39496         
39497         var smenu = { };
39498         // special menu.. - needs to be tidied up..
39499         if (!this.disable.special) {
39500             smenu = {
39501                 text: "&#169;",
39502                 cls: 'x-edit-none',
39503                 menu : {
39504                     items : []
39505                    }
39506             };
39507             for (var i =0; i < this.specialChars.length; i++) {
39508                 smenu.menu.items.push({
39509                     
39510                     html: this.specialChars[i],
39511                     handler: function(a,b) {
39512                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39513                         
39514                     },
39515                     tabIndex:-1
39516                 });
39517             }
39518             
39519             
39520             tb.add(smenu);
39521             
39522             
39523         }
39524         if (this.btns) {
39525             for(var i =0; i< this.btns.length;i++) {
39526                 var b = this.btns[i];
39527                 b.cls =  'x-edit-none';
39528                 b.scope = editor;
39529                 tb.add(b);
39530             }
39531         
39532         }
39533         
39534         
39535         
39536         // disable everything...
39537         
39538         this.tb.items.each(function(item){
39539            if(item.id != editor.frameId+ '-sourceedit'){
39540                 item.disable();
39541             }
39542         });
39543         this.rendered = true;
39544         
39545         // the all the btns;
39546         editor.on('editorevent', this.updateToolbar, this);
39547         // other toolbars need to implement this..
39548         //editor.on('editmodechange', this.updateToolbar, this);
39549     },
39550     
39551     
39552     
39553     /**
39554      * Protected method that will not generally be called directly. It triggers
39555      * a toolbar update by reading the markup state of the current selection in the editor.
39556      */
39557     updateToolbar: function(){
39558
39559         if(!this.editor.activated){
39560             this.editor.onFirstFocus();
39561             return;
39562         }
39563
39564         var btns = this.tb.items.map, 
39565             doc = this.editor.doc,
39566             frameId = this.editor.frameId;
39567
39568         if(!this.disable.font && !Roo.isSafari){
39569             /*
39570             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39571             if(name != this.fontSelect.dom.value){
39572                 this.fontSelect.dom.value = name;
39573             }
39574             */
39575         }
39576         if(!this.disable.format){
39577             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39578             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39579             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39580         }
39581         if(!this.disable.alignments){
39582             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39583             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39584             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39585         }
39586         if(!Roo.isSafari && !this.disable.lists){
39587             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39588             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39589         }
39590         
39591         var ans = this.editor.getAllAncestors();
39592         if (this.formatCombo) {
39593             
39594             
39595             var store = this.formatCombo.store;
39596             this.formatCombo.setValue("");
39597             for (var i =0; i < ans.length;i++) {
39598                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39599                     // select it..
39600                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39601                     break;
39602                 }
39603             }
39604         }
39605         
39606         
39607         
39608         // hides menus... - so this cant be on a menu...
39609         Roo.menu.MenuMgr.hideAll();
39610
39611         //this.editorsyncValue();
39612     },
39613    
39614     
39615     createFontOptions : function(){
39616         var buf = [], fs = this.fontFamilies, ff, lc;
39617         for(var i = 0, len = fs.length; i< len; i++){
39618             ff = fs[i];
39619             lc = ff.toLowerCase();
39620             buf.push(
39621                 '<option value="',lc,'" style="font-family:',ff,';"',
39622                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39623                     ff,
39624                 '</option>'
39625             );
39626         }
39627         return buf.join('');
39628     },
39629     
39630     toggleSourceEdit : function(sourceEditMode){
39631         if(sourceEditMode === undefined){
39632             sourceEditMode = !this.sourceEditMode;
39633         }
39634         this.sourceEditMode = sourceEditMode === true;
39635         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39636         // just toggle the button?
39637         if(btn.pressed !== this.editor.sourceEditMode){
39638             btn.toggle(this.editor.sourceEditMode);
39639             return;
39640         }
39641         
39642         if(this.sourceEditMode){
39643             this.tb.items.each(function(item){
39644                 if(item.cmd != 'sourceedit'){
39645                     item.disable();
39646                 }
39647             });
39648           
39649         }else{
39650             if(this.initialized){
39651                 this.tb.items.each(function(item){
39652                     item.enable();
39653                 });
39654             }
39655             
39656         }
39657         // tell the editor that it's been pressed..
39658         this.editor.toggleSourceEdit(sourceEditMode);
39659        
39660     },
39661      /**
39662      * Object collection of toolbar tooltips for the buttons in the editor. The key
39663      * is the command id associated with that button and the value is a valid QuickTips object.
39664      * For example:
39665 <pre><code>
39666 {
39667     bold : {
39668         title: 'Bold (Ctrl+B)',
39669         text: 'Make the selected text bold.',
39670         cls: 'x-html-editor-tip'
39671     },
39672     italic : {
39673         title: 'Italic (Ctrl+I)',
39674         text: 'Make the selected text italic.',
39675         cls: 'x-html-editor-tip'
39676     },
39677     ...
39678 </code></pre>
39679     * @type Object
39680      */
39681     buttonTips : {
39682         bold : {
39683             title: 'Bold (Ctrl+B)',
39684             text: 'Make the selected text bold.',
39685             cls: 'x-html-editor-tip'
39686         },
39687         italic : {
39688             title: 'Italic (Ctrl+I)',
39689             text: 'Make the selected text italic.',
39690             cls: 'x-html-editor-tip'
39691         },
39692         underline : {
39693             title: 'Underline (Ctrl+U)',
39694             text: 'Underline the selected text.',
39695             cls: 'x-html-editor-tip'
39696         },
39697         increasefontsize : {
39698             title: 'Grow Text',
39699             text: 'Increase the font size.',
39700             cls: 'x-html-editor-tip'
39701         },
39702         decreasefontsize : {
39703             title: 'Shrink Text',
39704             text: 'Decrease the font size.',
39705             cls: 'x-html-editor-tip'
39706         },
39707         backcolor : {
39708             title: 'Text Highlight Color',
39709             text: 'Change the background color of the selected text.',
39710             cls: 'x-html-editor-tip'
39711         },
39712         forecolor : {
39713             title: 'Font Color',
39714             text: 'Change the color of the selected text.',
39715             cls: 'x-html-editor-tip'
39716         },
39717         justifyleft : {
39718             title: 'Align Text Left',
39719             text: 'Align text to the left.',
39720             cls: 'x-html-editor-tip'
39721         },
39722         justifycenter : {
39723             title: 'Center Text',
39724             text: 'Center text in the editor.',
39725             cls: 'x-html-editor-tip'
39726         },
39727         justifyright : {
39728             title: 'Align Text Right',
39729             text: 'Align text to the right.',
39730             cls: 'x-html-editor-tip'
39731         },
39732         insertunorderedlist : {
39733             title: 'Bullet List',
39734             text: 'Start a bulleted list.',
39735             cls: 'x-html-editor-tip'
39736         },
39737         insertorderedlist : {
39738             title: 'Numbered List',
39739             text: 'Start a numbered list.',
39740             cls: 'x-html-editor-tip'
39741         },
39742         createlink : {
39743             title: 'Hyperlink',
39744             text: 'Make the selected text a hyperlink.',
39745             cls: 'x-html-editor-tip'
39746         },
39747         sourceedit : {
39748             title: 'Source Edit',
39749             text: 'Switch to source editing mode.',
39750             cls: 'x-html-editor-tip'
39751         }
39752     },
39753     // private
39754     onDestroy : function(){
39755         if(this.rendered){
39756             
39757             this.tb.items.each(function(item){
39758                 if(item.menu){
39759                     item.menu.removeAll();
39760                     if(item.menu.el){
39761                         item.menu.el.destroy();
39762                     }
39763                 }
39764                 item.destroy();
39765             });
39766              
39767         }
39768     },
39769     onFirstFocus: function() {
39770         this.tb.items.each(function(item){
39771            item.enable();
39772         });
39773     }
39774 });
39775
39776
39777
39778
39779 // <script type="text/javascript">
39780 /*
39781  * Based on
39782  * Ext JS Library 1.1.1
39783  * Copyright(c) 2006-2007, Ext JS, LLC.
39784  *  
39785  
39786  */
39787
39788  
39789 /**
39790  * @class Roo.form.HtmlEditor.ToolbarContext
39791  * Context Toolbar
39792  * 
39793  * Usage:
39794  *
39795  new Roo.form.HtmlEditor({
39796     ....
39797     toolbars : [
39798         new Roo.form.HtmlEditor.ToolbarStandard(),
39799         new Roo.form.HtmlEditor.ToolbarContext()
39800         })
39801     }
39802      
39803  * 
39804  * @config : {Object} disable List of elements to disable.. (not done yet.)
39805  * 
39806  * 
39807  */
39808
39809 Roo.form.HtmlEditor.ToolbarContext = function(config)
39810 {
39811     
39812     Roo.apply(this, config);
39813     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39814     // dont call parent... till later.
39815 }
39816 Roo.form.HtmlEditor.ToolbarContext.types = {
39817     'IMG' : {
39818         width : {
39819             title: "Width",
39820             width: 40
39821         },
39822         height:  {
39823             title: "Height",
39824             width: 40
39825         },
39826         align: {
39827             title: "Align",
39828             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39829             width : 80
39830             
39831         },
39832         border: {
39833             title: "Border",
39834             width: 40
39835         },
39836         alt: {
39837             title: "Alt",
39838             width: 120
39839         },
39840         src : {
39841             title: "Src",
39842             width: 220
39843         }
39844         
39845     },
39846     'A' : {
39847         name : {
39848             title: "Name",
39849             width: 50
39850         },
39851         href:  {
39852             title: "Href",
39853             width: 220
39854         } // border?
39855         
39856     },
39857     'TABLE' : {
39858         rows : {
39859             title: "Rows",
39860             width: 20
39861         },
39862         cols : {
39863             title: "Cols",
39864             width: 20
39865         },
39866         width : {
39867             title: "Width",
39868             width: 40
39869         },
39870         height : {
39871             title: "Height",
39872             width: 40
39873         },
39874         border : {
39875             title: "Border",
39876             width: 20
39877         }
39878     },
39879     'TD' : {
39880         width : {
39881             title: "Width",
39882             width: 40
39883         },
39884         height : {
39885             title: "Height",
39886             width: 40
39887         },   
39888         align: {
39889             title: "Align",
39890             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39891             width: 40
39892         },
39893         valign: {
39894             title: "Valign",
39895             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39896             width: 40
39897         },
39898         colspan: {
39899             title: "Colspan",
39900             width: 20
39901             
39902         }
39903     },
39904     'INPUT' : {
39905         name : {
39906             title: "name",
39907             width: 120
39908         },
39909         value : {
39910             title: "Value",
39911             width: 120
39912         },
39913         width : {
39914             title: "Width",
39915             width: 40
39916         }
39917     },
39918     'LABEL' : {
39919         'for' : {
39920             title: "For",
39921             width: 120
39922         }
39923     },
39924     'TEXTAREA' : {
39925           name : {
39926             title: "name",
39927             width: 120
39928         },
39929         rows : {
39930             title: "Rows",
39931             width: 20
39932         },
39933         cols : {
39934             title: "Cols",
39935             width: 20
39936         }
39937     },
39938     'SELECT' : {
39939         name : {
39940             title: "name",
39941             width: 120
39942         },
39943         selectoptions : {
39944             title: "Options",
39945             width: 200
39946         }
39947     },
39948     'BODY' : {
39949         title : {
39950             title: "title",
39951             width: 120,
39952             disabled : true
39953         }
39954     }
39955 };
39956
39957
39958
39959 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39960     
39961     tb: false,
39962     
39963     rendered: false,
39964     
39965     editor : false,
39966     /**
39967      * @cfg {Object} disable  List of toolbar elements to disable
39968          
39969      */
39970     disable : false,
39971     
39972     
39973     
39974     toolbars : false,
39975     
39976     init : function(editor)
39977     {
39978         this.editor = editor;
39979         
39980         
39981         var fid = editor.frameId;
39982         var etb = this;
39983         function btn(id, toggle, handler){
39984             var xid = fid + '-'+ id ;
39985             return {
39986                 id : xid,
39987                 cmd : id,
39988                 cls : 'x-btn-icon x-edit-'+id,
39989                 enableToggle:toggle !== false,
39990                 scope: editor, // was editor...
39991                 handler:handler||editor.relayBtnCmd,
39992                 clickEvent:'mousedown',
39993                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39994                 tabIndex:-1
39995             };
39996         }
39997         // create a new element.
39998         var wdiv = editor.wrap.createChild({
39999                 tag: 'div'
40000             }, editor.wrap.dom.firstChild.nextSibling, true);
40001         
40002         // can we do this more than once??
40003         
40004          // stop form submits
40005       
40006  
40007         // disable everything...
40008         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40009         this.toolbars = {};
40010            
40011         for (var i in  ty) {
40012           
40013             this.toolbars[i] = this.buildToolbar(ty[i],i);
40014         }
40015         this.tb = this.toolbars.BODY;
40016         this.tb.el.show();
40017         
40018          
40019         this.rendered = true;
40020         
40021         // the all the btns;
40022         editor.on('editorevent', this.updateToolbar, this);
40023         // other toolbars need to implement this..
40024         //editor.on('editmodechange', this.updateToolbar, this);
40025     },
40026     
40027     
40028     
40029     /**
40030      * Protected method that will not generally be called directly. It triggers
40031      * a toolbar update by reading the markup state of the current selection in the editor.
40032      */
40033     updateToolbar: function(){
40034
40035         if(!this.editor.activated){
40036             this.editor.onFirstFocus();
40037             return;
40038         }
40039
40040         
40041         var ans = this.editor.getAllAncestors();
40042         
40043         // pick
40044         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40045         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40046         sel = sel ? sel : this.editor.doc.body;
40047         sel = sel.tagName.length ? sel : this.editor.doc.body;
40048         var tn = sel.tagName.toUpperCase();
40049         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40050         tn = sel.tagName.toUpperCase();
40051         if (this.tb.name  == tn) {
40052             return; // no change
40053         }
40054         this.tb.el.hide();
40055         ///console.log("show: " + tn);
40056         this.tb =  this.toolbars[tn];
40057         this.tb.el.show();
40058         this.tb.fields.each(function(e) {
40059             e.setValue(sel.getAttribute(e.name));
40060         });
40061         this.tb.selectedNode = sel;
40062         
40063         
40064         Roo.menu.MenuMgr.hideAll();
40065
40066         //this.editorsyncValue();
40067     },
40068    
40069        
40070     // private
40071     onDestroy : function(){
40072         if(this.rendered){
40073             
40074             this.tb.items.each(function(item){
40075                 if(item.menu){
40076                     item.menu.removeAll();
40077                     if(item.menu.el){
40078                         item.menu.el.destroy();
40079                     }
40080                 }
40081                 item.destroy();
40082             });
40083              
40084         }
40085     },
40086     onFirstFocus: function() {
40087         // need to do this for all the toolbars..
40088         this.tb.items.each(function(item){
40089            item.enable();
40090         });
40091     },
40092     buildToolbar: function(tlist, nm)
40093     {
40094         var editor = this.editor;
40095          // create a new element.
40096         var wdiv = editor.wrap.createChild({
40097                 tag: 'div'
40098             }, editor.wrap.dom.firstChild.nextSibling, true);
40099         
40100        
40101         var tb = new Roo.Toolbar(wdiv);
40102         tb.add(nm+ ":&nbsp;");
40103         for (var i in tlist) {
40104             var item = tlist[i];
40105             tb.add(item.title + ":&nbsp;");
40106             if (item.opts) {
40107                 // fixme
40108                 
40109               
40110                 tb.addField( new Roo.form.ComboBox({
40111                     store: new Roo.data.SimpleStore({
40112                         id : 'val',
40113                         fields: ['val'],
40114                         data : item.opts // from states.js
40115                     }),
40116                     name : i,
40117                     displayField:'val',
40118                     typeAhead: false,
40119                     mode: 'local',
40120                     editable : false,
40121                     triggerAction: 'all',
40122                     emptyText:'Select',
40123                     selectOnFocus:true,
40124                     width: item.width ? item.width  : 130,
40125                     listeners : {
40126                         'select': function(c, r, i) {
40127                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40128                         }
40129                     }
40130
40131                 }));
40132                 continue;
40133                     
40134                 
40135                 
40136                 
40137                 
40138                 tb.addField( new Roo.form.TextField({
40139                     name: i,
40140                     width: 100,
40141                     //allowBlank:false,
40142                     value: ''
40143                 }));
40144                 continue;
40145             }
40146             tb.addField( new Roo.form.TextField({
40147                 name: i,
40148                 width: item.width,
40149                 //allowBlank:true,
40150                 value: '',
40151                 listeners: {
40152                     'change' : function(f, nv, ov) {
40153                         tb.selectedNode.setAttribute(f.name, nv);
40154                     }
40155                 }
40156             }));
40157              
40158         }
40159         tb.el.on('click', function(e){
40160             e.preventDefault(); // what does this do?
40161         });
40162         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40163         tb.el.hide();
40164         tb.name = nm;
40165         // dont need to disable them... as they will get hidden
40166         return tb;
40167          
40168         
40169     }
40170     
40171     
40172     
40173     
40174 });
40175
40176
40177
40178
40179
40180 /*
40181  * Based on:
40182  * Ext JS Library 1.1.1
40183  * Copyright(c) 2006-2007, Ext JS, LLC.
40184  *
40185  * Originally Released Under LGPL - original licence link has changed is not relivant.
40186  *
40187  * Fork - LGPL
40188  * <script type="text/javascript">
40189  */
40190  
40191 /**
40192  * @class Roo.form.BasicForm
40193  * @extends Roo.util.Observable
40194  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40195  * @constructor
40196  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40197  * @param {Object} config Configuration options
40198  */
40199 Roo.form.BasicForm = function(el, config){
40200     this.allItems = [];
40201     this.childForms = [];
40202     Roo.apply(this, config);
40203     /*
40204      * The Roo.form.Field items in this form.
40205      * @type MixedCollection
40206      */
40207      
40208      
40209     this.items = new Roo.util.MixedCollection(false, function(o){
40210         return o.id || (o.id = Roo.id());
40211     });
40212     this.addEvents({
40213         /**
40214          * @event beforeaction
40215          * Fires before any action is performed. Return false to cancel the action.
40216          * @param {Form} this
40217          * @param {Action} action The action to be performed
40218          */
40219         beforeaction: true,
40220         /**
40221          * @event actionfailed
40222          * Fires when an action fails.
40223          * @param {Form} this
40224          * @param {Action} action The action that failed
40225          */
40226         actionfailed : true,
40227         /**
40228          * @event actioncomplete
40229          * Fires when an action is completed.
40230          * @param {Form} this
40231          * @param {Action} action The action that completed
40232          */
40233         actioncomplete : true
40234     });
40235     if(el){
40236         this.initEl(el);
40237     }
40238     Roo.form.BasicForm.superclass.constructor.call(this);
40239 };
40240
40241 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40242     /**
40243      * @cfg {String} method
40244      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40245      */
40246     /**
40247      * @cfg {DataReader} reader
40248      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40249      * This is optional as there is built-in support for processing JSON.
40250      */
40251     /**
40252      * @cfg {DataReader} errorReader
40253      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40254      * This is completely optional as there is built-in support for processing JSON.
40255      */
40256     /**
40257      * @cfg {String} url
40258      * The URL to use for form actions if one isn't supplied in the action options.
40259      */
40260     /**
40261      * @cfg {Boolean} fileUpload
40262      * Set to true if this form is a file upload.
40263      */
40264     /**
40265      * @cfg {Object} baseParams
40266      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40267      */
40268     /**
40269      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40270      */
40271     timeout: 30,
40272
40273     // private
40274     activeAction : null,
40275
40276     /**
40277      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40278      * or setValues() data instead of when the form was first created.
40279      */
40280     trackResetOnLoad : false,
40281     
40282     
40283     /**
40284      * childForms - used for multi-tab forms
40285      * @type {Array}
40286      */
40287     childForms : false,
40288     
40289     /**
40290      * allItems - full list of fields.
40291      * @type {Array}
40292      */
40293     allItems : false,
40294     
40295     /**
40296      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40297      * element by passing it or its id or mask the form itself by passing in true.
40298      * @type Mixed
40299      */
40300     waitMsgTarget : undefined,
40301
40302     // private
40303     initEl : function(el){
40304         this.el = Roo.get(el);
40305         this.id = this.el.id || Roo.id();
40306         this.el.on('submit', this.onSubmit, this);
40307         this.el.addClass('x-form');
40308     },
40309
40310     // private
40311     onSubmit : function(e){
40312         e.stopEvent();
40313     },
40314
40315     /**
40316      * Returns true if client-side validation on the form is successful.
40317      * @return Boolean
40318      */
40319     isValid : function(){
40320         var valid = true;
40321         this.items.each(function(f){
40322            if(!f.validate()){
40323                valid = false;
40324            }
40325         });
40326         return valid;
40327     },
40328
40329     /**
40330      * Returns true if any fields in this form have changed since their original load.
40331      * @return Boolean
40332      */
40333     isDirty : function(){
40334         var dirty = false;
40335         this.items.each(function(f){
40336            if(f.isDirty()){
40337                dirty = true;
40338                return false;
40339            }
40340         });
40341         return dirty;
40342     },
40343
40344     /**
40345      * Performs a predefined action (submit or load) or custom actions you define on this form.
40346      * @param {String} actionName The name of the action type
40347      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40348      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40349      * accept other config options):
40350      * <pre>
40351 Property          Type             Description
40352 ----------------  ---------------  ----------------------------------------------------------------------------------
40353 url               String           The url for the action (defaults to the form's url)
40354 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40355 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40356 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40357                                    validate the form on the client (defaults to false)
40358      * </pre>
40359      * @return {BasicForm} this
40360      */
40361     doAction : function(action, options){
40362         if(typeof action == 'string'){
40363             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40364         }
40365         if(this.fireEvent('beforeaction', this, action) !== false){
40366             this.beforeAction(action);
40367             action.run.defer(100, action);
40368         }
40369         return this;
40370     },
40371
40372     /**
40373      * Shortcut to do a submit action.
40374      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40375      * @return {BasicForm} this
40376      */
40377     submit : function(options){
40378         this.doAction('submit', options);
40379         return this;
40380     },
40381
40382     /**
40383      * Shortcut to do a load action.
40384      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40385      * @return {BasicForm} this
40386      */
40387     load : function(options){
40388         this.doAction('load', options);
40389         return this;
40390     },
40391
40392     /**
40393      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40394      * @param {Record} record The record to edit
40395      * @return {BasicForm} this
40396      */
40397     updateRecord : function(record){
40398         record.beginEdit();
40399         var fs = record.fields;
40400         fs.each(function(f){
40401             var field = this.findField(f.name);
40402             if(field){
40403                 record.set(f.name, field.getValue());
40404             }
40405         }, this);
40406         record.endEdit();
40407         return this;
40408     },
40409
40410     /**
40411      * Loads an Roo.data.Record into this form.
40412      * @param {Record} record The record to load
40413      * @return {BasicForm} this
40414      */
40415     loadRecord : function(record){
40416         this.setValues(record.data);
40417         return this;
40418     },
40419
40420     // private
40421     beforeAction : function(action){
40422         var o = action.options;
40423         if(o.waitMsg){
40424             if(this.waitMsgTarget === true){
40425                 this.el.mask(o.waitMsg, 'x-mask-loading');
40426             }else if(this.waitMsgTarget){
40427                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40428                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40429             }else{
40430                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40431             }
40432         }
40433     },
40434
40435     // private
40436     afterAction : function(action, success){
40437         this.activeAction = null;
40438         var o = action.options;
40439         if(o.waitMsg){
40440             if(this.waitMsgTarget === true){
40441                 this.el.unmask();
40442             }else if(this.waitMsgTarget){
40443                 this.waitMsgTarget.unmask();
40444             }else{
40445                 Roo.MessageBox.updateProgress(1);
40446                 Roo.MessageBox.hide();
40447             }
40448         }
40449         if(success){
40450             if(o.reset){
40451                 this.reset();
40452             }
40453             Roo.callback(o.success, o.scope, [this, action]);
40454             this.fireEvent('actioncomplete', this, action);
40455         }else{
40456             Roo.callback(o.failure, o.scope, [this, action]);
40457             this.fireEvent('actionfailed', this, action);
40458         }
40459     },
40460
40461     /**
40462      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40463      * @param {String} id The value to search for
40464      * @return Field
40465      */
40466     findField : function(id){
40467         var field = this.items.get(id);
40468         if(!field){
40469             this.items.each(function(f){
40470                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40471                     field = f;
40472                     return false;
40473                 }
40474             });
40475         }
40476         return field || null;
40477     },
40478
40479     /**
40480      * Add a secondary form to this one, 
40481      * Used to provide tabbed forms. One form is primary, with hidden values 
40482      * which mirror the elements from the other forms.
40483      * 
40484      * @param {Roo.form.Form} form to add.
40485      * 
40486      */
40487     addForm : function(form)
40488     {
40489        
40490         if (this.childForms.indexOf(form) > -1) {
40491             // already added..
40492             return;
40493         }
40494         this.childForms.push(form);
40495         var n = '';
40496         Roo.each(form.allItems, function (fe) {
40497             
40498             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40499             if (this.findField(n)) { // already added..
40500                 return;
40501             }
40502             var add = new Roo.form.Hidden({
40503                 name : n
40504             });
40505             add.render(this.el);
40506             
40507             this.add( add );
40508         }, this);
40509         
40510     },
40511     /**
40512      * Mark fields in this form invalid in bulk.
40513      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40514      * @return {BasicForm} this
40515      */
40516     markInvalid : function(errors){
40517         if(errors instanceof Array){
40518             for(var i = 0, len = errors.length; i < len; i++){
40519                 var fieldError = errors[i];
40520                 var f = this.findField(fieldError.id);
40521                 if(f){
40522                     f.markInvalid(fieldError.msg);
40523                 }
40524             }
40525         }else{
40526             var field, id;
40527             for(id in errors){
40528                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40529                     field.markInvalid(errors[id]);
40530                 }
40531             }
40532         }
40533         Roo.each(this.childForms || [], function (f) {
40534             f.markInvalid(errors);
40535         });
40536         
40537         return this;
40538     },
40539
40540     /**
40541      * Set values for fields in this form in bulk.
40542      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40543      * @return {BasicForm} this
40544      */
40545     setValues : function(values){
40546         if(values instanceof Array){ // array of objects
40547             for(var i = 0, len = values.length; i < len; i++){
40548                 var v = values[i];
40549                 var f = this.findField(v.id);
40550                 if(f){
40551                     f.setValue(v.value);
40552                     if(this.trackResetOnLoad){
40553                         f.originalValue = f.getValue();
40554                     }
40555                 }
40556             }
40557         }else{ // object hash
40558             var field, id;
40559             for(id in values){
40560                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40561                     
40562                     if (field.setFromData && 
40563                         field.valueField && 
40564                         field.displayField &&
40565                         // combos' with local stores can 
40566                         // be queried via setValue()
40567                         // to set their value..
40568                         (field.store && !field.store.isLocal)
40569                         ) {
40570                         // it's a combo
40571                         var sd = { };
40572                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40573                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40574                         field.setFromData(sd);
40575                         
40576                     } else {
40577                         field.setValue(values[id]);
40578                     }
40579                     
40580                     
40581                     if(this.trackResetOnLoad){
40582                         field.originalValue = field.getValue();
40583                     }
40584                 }
40585             }
40586         }
40587          
40588         Roo.each(this.childForms || [], function (f) {
40589             f.setValues(values);
40590         });
40591                 
40592         return this;
40593     },
40594
40595     /**
40596      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40597      * they are returned as an array.
40598      * @param {Boolean} asString
40599      * @return {Object}
40600      */
40601     getValues : function(asString){
40602         if (this.childForms) {
40603             // copy values from the child forms
40604             Roo.each(this.childForms, function (f) {
40605                 this.setValues(f.getValues());
40606             }, this);
40607         }
40608         
40609         
40610         
40611         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40612         if(asString === true){
40613             return fs;
40614         }
40615         return Roo.urlDecode(fs);
40616     },
40617     
40618     /**
40619      * Returns the fields in this form as an object with key/value pairs. 
40620      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40621      * @return {Object}
40622      */
40623     getFieldValues : function()
40624     {
40625         if (this.childForms) {
40626             // copy values from the child forms
40627             Roo.each(this.childForms, function (f) {
40628                 this.setValues(f.getValues());
40629             }, this);
40630         }
40631         
40632         var ret = {};
40633         this.items.each(function(f){
40634             if (!f.getName()) {
40635                 return;
40636             }
40637             var v = f.getValue();
40638             if ((typeof(v) == 'object') && f.getRawValue) {
40639                 v = f.getRawValue() ; // dates..
40640             }
40641             ret[f.getName()] = v;
40642         });
40643         
40644         return ret;
40645     },
40646
40647     /**
40648      * Clears all invalid messages in this form.
40649      * @return {BasicForm} this
40650      */
40651     clearInvalid : function(){
40652         this.items.each(function(f){
40653            f.clearInvalid();
40654         });
40655         
40656         Roo.each(this.childForms || [], function (f) {
40657             f.clearInvalid();
40658         });
40659         
40660         
40661         return this;
40662     },
40663
40664     /**
40665      * Resets this form.
40666      * @return {BasicForm} this
40667      */
40668     reset : function(){
40669         this.items.each(function(f){
40670             f.reset();
40671         });
40672         
40673         Roo.each(this.childForms || [], function (f) {
40674             f.reset();
40675         });
40676        
40677         
40678         return this;
40679     },
40680
40681     /**
40682      * Add Roo.form components to this form.
40683      * @param {Field} field1
40684      * @param {Field} field2 (optional)
40685      * @param {Field} etc (optional)
40686      * @return {BasicForm} this
40687      */
40688     add : function(){
40689         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40690         return this;
40691     },
40692
40693
40694     /**
40695      * Removes a field from the items collection (does NOT remove its markup).
40696      * @param {Field} field
40697      * @return {BasicForm} this
40698      */
40699     remove : function(field){
40700         this.items.remove(field);
40701         return this;
40702     },
40703
40704     /**
40705      * Looks at the fields in this form, checks them for an id attribute,
40706      * and calls applyTo on the existing dom element with that id.
40707      * @return {BasicForm} this
40708      */
40709     render : function(){
40710         this.items.each(function(f){
40711             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40712                 f.applyTo(f.id);
40713             }
40714         });
40715         return this;
40716     },
40717
40718     /**
40719      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40720      * @param {Object} values
40721      * @return {BasicForm} this
40722      */
40723     applyToFields : function(o){
40724         this.items.each(function(f){
40725            Roo.apply(f, o);
40726         });
40727         return this;
40728     },
40729
40730     /**
40731      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40732      * @param {Object} values
40733      * @return {BasicForm} this
40734      */
40735     applyIfToFields : function(o){
40736         this.items.each(function(f){
40737            Roo.applyIf(f, o);
40738         });
40739         return this;
40740     }
40741 });
40742
40743 // back compat
40744 Roo.BasicForm = Roo.form.BasicForm;/*
40745  * Based on:
40746  * Ext JS Library 1.1.1
40747  * Copyright(c) 2006-2007, Ext JS, LLC.
40748  *
40749  * Originally Released Under LGPL - original licence link has changed is not relivant.
40750  *
40751  * Fork - LGPL
40752  * <script type="text/javascript">
40753  */
40754
40755 /**
40756  * @class Roo.form.Form
40757  * @extends Roo.form.BasicForm
40758  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40759  * @constructor
40760  * @param {Object} config Configuration options
40761  */
40762 Roo.form.Form = function(config){
40763     var xitems =  [];
40764     if (config.items) {
40765         xitems = config.items;
40766         delete config.items;
40767     }
40768    
40769     
40770     Roo.form.Form.superclass.constructor.call(this, null, config);
40771     this.url = this.url || this.action;
40772     if(!this.root){
40773         this.root = new Roo.form.Layout(Roo.applyIf({
40774             id: Roo.id()
40775         }, config));
40776     }
40777     this.active = this.root;
40778     /**
40779      * Array of all the buttons that have been added to this form via {@link addButton}
40780      * @type Array
40781      */
40782     this.buttons = [];
40783     this.allItems = [];
40784     this.addEvents({
40785         /**
40786          * @event clientvalidation
40787          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40788          * @param {Form} this
40789          * @param {Boolean} valid true if the form has passed client-side validation
40790          */
40791         clientvalidation: true,
40792         /**
40793          * @event rendered
40794          * Fires when the form is rendered
40795          * @param {Roo.form.Form} form
40796          */
40797         rendered : true
40798     });
40799     
40800     if (this.progressUrl) {
40801             // push a hidden field onto the list of fields..
40802             this.addxtype( {
40803                     xns: Roo.form, 
40804                     xtype : 'Hidden', 
40805                     name : 'UPLOAD_IDENTIFIER' 
40806             });
40807         }
40808         
40809     
40810     Roo.each(xitems, this.addxtype, this);
40811     
40812     
40813     
40814 };
40815
40816 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40817     /**
40818      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40819      */
40820     /**
40821      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40822      */
40823     /**
40824      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40825      */
40826     buttonAlign:'center',
40827
40828     /**
40829      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40830      */
40831     minButtonWidth:75,
40832
40833     /**
40834      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40835      * This property cascades to child containers if not set.
40836      */
40837     labelAlign:'left',
40838
40839     /**
40840      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40841      * fires a looping event with that state. This is required to bind buttons to the valid
40842      * state using the config value formBind:true on the button.
40843      */
40844     monitorValid : false,
40845
40846     /**
40847      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40848      */
40849     monitorPoll : 200,
40850     
40851     /**
40852      * @cfg {String} progressUrl - Url to return progress data 
40853      */
40854     
40855     progressUrl : false,
40856   
40857     /**
40858      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40859      * fields are added and the column is closed. If no fields are passed the column remains open
40860      * until end() is called.
40861      * @param {Object} config The config to pass to the column
40862      * @param {Field} field1 (optional)
40863      * @param {Field} field2 (optional)
40864      * @param {Field} etc (optional)
40865      * @return Column The column container object
40866      */
40867     column : function(c){
40868         var col = new Roo.form.Column(c);
40869         this.start(col);
40870         if(arguments.length > 1){ // duplicate code required because of Opera
40871             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40872             this.end();
40873         }
40874         return col;
40875     },
40876
40877     /**
40878      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40879      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40880      * until end() is called.
40881      * @param {Object} config The config to pass to the fieldset
40882      * @param {Field} field1 (optional)
40883      * @param {Field} field2 (optional)
40884      * @param {Field} etc (optional)
40885      * @return FieldSet The fieldset container object
40886      */
40887     fieldset : function(c){
40888         var fs = new Roo.form.FieldSet(c);
40889         this.start(fs);
40890         if(arguments.length > 1){ // duplicate code required because of Opera
40891             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40892             this.end();
40893         }
40894         return fs;
40895     },
40896
40897     /**
40898      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40899      * fields are added and the container is closed. If no fields are passed the container remains open
40900      * until end() is called.
40901      * @param {Object} config The config to pass to the Layout
40902      * @param {Field} field1 (optional)
40903      * @param {Field} field2 (optional)
40904      * @param {Field} etc (optional)
40905      * @return Layout The container object
40906      */
40907     container : function(c){
40908         var l = new Roo.form.Layout(c);
40909         this.start(l);
40910         if(arguments.length > 1){ // duplicate code required because of Opera
40911             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40912             this.end();
40913         }
40914         return l;
40915     },
40916
40917     /**
40918      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40919      * @param {Object} container A Roo.form.Layout or subclass of Layout
40920      * @return {Form} this
40921      */
40922     start : function(c){
40923         // cascade label info
40924         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40925         this.active.stack.push(c);
40926         c.ownerCt = this.active;
40927         this.active = c;
40928         return this;
40929     },
40930
40931     /**
40932      * Closes the current open container
40933      * @return {Form} this
40934      */
40935     end : function(){
40936         if(this.active == this.root){
40937             return this;
40938         }
40939         this.active = this.active.ownerCt;
40940         return this;
40941     },
40942
40943     /**
40944      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40945      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40946      * as the label of the field.
40947      * @param {Field} field1
40948      * @param {Field} field2 (optional)
40949      * @param {Field} etc. (optional)
40950      * @return {Form} this
40951      */
40952     add : function(){
40953         this.active.stack.push.apply(this.active.stack, arguments);
40954         this.allItems.push.apply(this.allItems,arguments);
40955         var r = [];
40956         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40957             if(a[i].isFormField){
40958                 r.push(a[i]);
40959             }
40960         }
40961         if(r.length > 0){
40962             Roo.form.Form.superclass.add.apply(this, r);
40963         }
40964         return this;
40965     },
40966     
40967
40968     
40969     
40970     
40971      /**
40972      * Find any element that has been added to a form, using it's ID or name
40973      * This can include framesets, columns etc. along with regular fields..
40974      * @param {String} id - id or name to find.
40975      
40976      * @return {Element} e - or false if nothing found.
40977      */
40978     findbyId : function(id)
40979     {
40980         var ret = false;
40981         if (!id) {
40982             return ret;
40983         }
40984         Ext.each(this.allItems, function(f){
40985             if (f.id == id || f.name == id ){
40986                 ret = f;
40987                 return false;
40988             }
40989         });
40990         return ret;
40991     },
40992
40993     
40994     
40995     /**
40996      * Render this form into the passed container. This should only be called once!
40997      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40998      * @return {Form} this
40999      */
41000     render : function(ct)
41001     {
41002         
41003         
41004         
41005         ct = Roo.get(ct);
41006         var o = this.autoCreate || {
41007             tag: 'form',
41008             method : this.method || 'POST',
41009             id : this.id || Roo.id()
41010         };
41011         this.initEl(ct.createChild(o));
41012
41013         this.root.render(this.el);
41014         
41015        
41016              
41017         this.items.each(function(f){
41018             f.render('x-form-el-'+f.id);
41019         });
41020
41021         if(this.buttons.length > 0){
41022             // tables are required to maintain order and for correct IE layout
41023             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41024                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41025                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41026             }}, null, true);
41027             var tr = tb.getElementsByTagName('tr')[0];
41028             for(var i = 0, len = this.buttons.length; i < len; i++) {
41029                 var b = this.buttons[i];
41030                 var td = document.createElement('td');
41031                 td.className = 'x-form-btn-td';
41032                 b.render(tr.appendChild(td));
41033             }
41034         }
41035         if(this.monitorValid){ // initialize after render
41036             this.startMonitoring();
41037         }
41038         this.fireEvent('rendered', this);
41039         return this;
41040     },
41041
41042     /**
41043      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41044      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41045      * object or a valid Roo.DomHelper element config
41046      * @param {Function} handler The function called when the button is clicked
41047      * @param {Object} scope (optional) The scope of the handler function
41048      * @return {Roo.Button}
41049      */
41050     addButton : function(config, handler, scope){
41051         var bc = {
41052             handler: handler,
41053             scope: scope,
41054             minWidth: this.minButtonWidth,
41055             hideParent:true
41056         };
41057         if(typeof config == "string"){
41058             bc.text = config;
41059         }else{
41060             Roo.apply(bc, config);
41061         }
41062         var btn = new Roo.Button(null, bc);
41063         this.buttons.push(btn);
41064         return btn;
41065     },
41066
41067      /**
41068      * Adds a series of form elements (using the xtype property as the factory method.
41069      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41070      * @param {Object} config 
41071      */
41072     
41073     addxtype : function()
41074     {
41075         var ar = Array.prototype.slice.call(arguments, 0);
41076         var ret = false;
41077         for(var i = 0; i < ar.length; i++) {
41078             if (!ar[i]) {
41079                 continue; // skip -- if this happends something invalid got sent, we 
41080                 // should ignore it, as basically that interface element will not show up
41081                 // and that should be pretty obvious!!
41082             }
41083             
41084             if (Roo.form[ar[i].xtype]) {
41085                 ar[i].form = this;
41086                 var fe = Roo.factory(ar[i], Roo.form);
41087                 if (!ret) {
41088                     ret = fe;
41089                 }
41090                 fe.form = this;
41091                 if (fe.store) {
41092                     fe.store.form = this;
41093                 }
41094                 if (fe.isLayout) {  
41095                          
41096                     this.start(fe);
41097                     this.allItems.push(fe);
41098                     if (fe.items && fe.addxtype) {
41099                         fe.addxtype.apply(fe, fe.items);
41100                         delete fe.items;
41101                     }
41102                      this.end();
41103                     continue;
41104                 }
41105                 
41106                 
41107                  
41108                 this.add(fe);
41109               //  console.log('adding ' + ar[i].xtype);
41110             }
41111             if (ar[i].xtype == 'Button') {  
41112                 //console.log('adding button');
41113                 //console.log(ar[i]);
41114                 this.addButton(ar[i]);
41115                 this.allItems.push(fe);
41116                 continue;
41117             }
41118             
41119             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41120                 alert('end is not supported on xtype any more, use items');
41121             //    this.end();
41122             //    //console.log('adding end');
41123             }
41124             
41125         }
41126         return ret;
41127     },
41128     
41129     /**
41130      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41131      * option "monitorValid"
41132      */
41133     startMonitoring : function(){
41134         if(!this.bound){
41135             this.bound = true;
41136             Roo.TaskMgr.start({
41137                 run : this.bindHandler,
41138                 interval : this.monitorPoll || 200,
41139                 scope: this
41140             });
41141         }
41142     },
41143
41144     /**
41145      * Stops monitoring of the valid state of this form
41146      */
41147     stopMonitoring : function(){
41148         this.bound = false;
41149     },
41150
41151     // private
41152     bindHandler : function(){
41153         if(!this.bound){
41154             return false; // stops binding
41155         }
41156         var valid = true;
41157         this.items.each(function(f){
41158             if(!f.isValid(true)){
41159                 valid = false;
41160                 return false;
41161             }
41162         });
41163         for(var i = 0, len = this.buttons.length; i < len; i++){
41164             var btn = this.buttons[i];
41165             if(btn.formBind === true && btn.disabled === valid){
41166                 btn.setDisabled(!valid);
41167             }
41168         }
41169         this.fireEvent('clientvalidation', this, valid);
41170     }
41171     
41172     
41173     
41174     
41175     
41176     
41177     
41178     
41179 });
41180
41181
41182 // back compat
41183 Roo.Form = Roo.form.Form;
41184 /*
41185  * Based on:
41186  * Ext JS Library 1.1.1
41187  * Copyright(c) 2006-2007, Ext JS, LLC.
41188  *
41189  * Originally Released Under LGPL - original licence link has changed is not relivant.
41190  *
41191  * Fork - LGPL
41192  * <script type="text/javascript">
41193  */
41194  
41195  /**
41196  * @class Roo.form.Action
41197  * Internal Class used to handle form actions
41198  * @constructor
41199  * @param {Roo.form.BasicForm} el The form element or its id
41200  * @param {Object} config Configuration options
41201  */
41202  
41203  
41204 // define the action interface
41205 Roo.form.Action = function(form, options){
41206     this.form = form;
41207     this.options = options || {};
41208 };
41209 /**
41210  * Client Validation Failed
41211  * @const 
41212  */
41213 Roo.form.Action.CLIENT_INVALID = 'client';
41214 /**
41215  * Server Validation Failed
41216  * @const 
41217  */
41218  Roo.form.Action.SERVER_INVALID = 'server';
41219  /**
41220  * Connect to Server Failed
41221  * @const 
41222  */
41223 Roo.form.Action.CONNECT_FAILURE = 'connect';
41224 /**
41225  * Reading Data from Server Failed
41226  * @const 
41227  */
41228 Roo.form.Action.LOAD_FAILURE = 'load';
41229
41230 Roo.form.Action.prototype = {
41231     type : 'default',
41232     failureType : undefined,
41233     response : undefined,
41234     result : undefined,
41235
41236     // interface method
41237     run : function(options){
41238
41239     },
41240
41241     // interface method
41242     success : function(response){
41243
41244     },
41245
41246     // interface method
41247     handleResponse : function(response){
41248
41249     },
41250
41251     // default connection failure
41252     failure : function(response){
41253         this.response = response;
41254         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41255         this.form.afterAction(this, false);
41256     },
41257
41258     processResponse : function(response){
41259         this.response = response;
41260         if(!response.responseText){
41261             return true;
41262         }
41263         this.result = this.handleResponse(response);
41264         return this.result;
41265     },
41266
41267     // utility functions used internally
41268     getUrl : function(appendParams){
41269         var url = this.options.url || this.form.url || this.form.el.dom.action;
41270         if(appendParams){
41271             var p = this.getParams();
41272             if(p){
41273                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41274             }
41275         }
41276         return url;
41277     },
41278
41279     getMethod : function(){
41280         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41281     },
41282
41283     getParams : function(){
41284         var bp = this.form.baseParams;
41285         var p = this.options.params;
41286         if(p){
41287             if(typeof p == "object"){
41288                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41289             }else if(typeof p == 'string' && bp){
41290                 p += '&' + Roo.urlEncode(bp);
41291             }
41292         }else if(bp){
41293             p = Roo.urlEncode(bp);
41294         }
41295         return p;
41296     },
41297
41298     createCallback : function(){
41299         return {
41300             success: this.success,
41301             failure: this.failure,
41302             scope: this,
41303             timeout: (this.form.timeout*1000),
41304             upload: this.form.fileUpload ? this.success : undefined
41305         };
41306     }
41307 };
41308
41309 Roo.form.Action.Submit = function(form, options){
41310     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41311 };
41312
41313 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41314     type : 'submit',
41315
41316     haveProgress : false,
41317     uploadComplete : false,
41318     
41319     // uploadProgress indicator.
41320     uploadProgress : function()
41321     {
41322         if (!this.form.progressUrl) {
41323             return;
41324         }
41325         
41326         if (!this.haveProgress) {
41327             Roo.MessageBox.progress("Uploading", "Uploading");
41328         }
41329         if (this.uploadComplete) {
41330            Roo.MessageBox.hide();
41331            return;
41332         }
41333         
41334         this.haveProgress = true;
41335    
41336         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41337         
41338         var c = new Roo.data.Connection();
41339         c.request({
41340             url : this.form.progressUrl,
41341             params: {
41342                 id : uid
41343             },
41344             method: 'GET',
41345             success : function(req){
41346                //console.log(data);
41347                 var rdata = false;
41348                 var edata;
41349                 try  {
41350                    rdata = Roo.decode(req.responseText)
41351                 } catch (e) {
41352                     Roo.log("Invalid data from server..");
41353                     Roo.log(edata);
41354                     return;
41355                 }
41356                 if (!rdata || !rdata.success) {
41357                     Roo.log(rdata);
41358                     return;
41359                 }
41360                 var data = rdata.data;
41361                 
41362                 if (this.uploadComplete) {
41363                    Roo.MessageBox.hide();
41364                    return;
41365                 }
41366                    
41367                 if (data){
41368                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41369                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41370                     );
41371                 }
41372                 this.uploadProgress.defer(2000,this);
41373             },
41374        
41375             failure: function(data) {
41376                 Roo.log('progress url failed ');
41377                 Roo.log(data);
41378             },
41379             scope : this
41380         });
41381            
41382     },
41383     
41384     
41385     run : function()
41386     {
41387         // run get Values on the form, so it syncs any secondary forms.
41388         this.form.getValues();
41389         
41390         var o = this.options;
41391         var method = this.getMethod();
41392         var isPost = method == 'POST';
41393         if(o.clientValidation === false || this.form.isValid()){
41394             
41395             if (this.form.progressUrl) {
41396                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41397                     (new Date() * 1) + '' + Math.random());
41398                     
41399             } 
41400             
41401             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41402                 form:this.form.el.dom,
41403                 url:this.getUrl(!isPost),
41404                 method: method,
41405                 params:isPost ? this.getParams() : null,
41406                 isUpload: this.form.fileUpload
41407             }));
41408             
41409             this.uploadProgress();
41410
41411         }else if (o.clientValidation !== false){ // client validation failed
41412             this.failureType = Roo.form.Action.CLIENT_INVALID;
41413             this.form.afterAction(this, false);
41414         }
41415     },
41416
41417     success : function(response)
41418     {
41419         this.uploadComplete= true;
41420         if (this.haveProgress) {
41421             Roo.MessageBox.hide();
41422         }
41423         
41424         var result = this.processResponse(response);
41425         if(result === true || result.success){
41426             this.form.afterAction(this, true);
41427             return;
41428         }
41429         if(result.errors){
41430             this.form.markInvalid(result.errors);
41431             this.failureType = Roo.form.Action.SERVER_INVALID;
41432         }
41433         this.form.afterAction(this, false);
41434     },
41435     failure : function(response)
41436     {
41437         this.uploadComplete= true;
41438         if (this.haveProgress) {
41439             Roo.MessageBox.hide();
41440         }
41441         
41442         this.response = response;
41443         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41444         this.form.afterAction(this, false);
41445     },
41446     
41447     handleResponse : function(response){
41448         if(this.form.errorReader){
41449             var rs = this.form.errorReader.read(response);
41450             var errors = [];
41451             if(rs.records){
41452                 for(var i = 0, len = rs.records.length; i < len; i++) {
41453                     var r = rs.records[i];
41454                     errors[i] = r.data;
41455                 }
41456             }
41457             if(errors.length < 1){
41458                 errors = null;
41459             }
41460             return {
41461                 success : rs.success,
41462                 errors : errors
41463             };
41464         }
41465         var ret = false;
41466         try {
41467             ret = Roo.decode(response.responseText);
41468         } catch (e) {
41469             ret = {
41470                 success: false,
41471                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41472                 errors : []
41473             };
41474         }
41475         return ret;
41476         
41477     }
41478 });
41479
41480
41481 Roo.form.Action.Load = function(form, options){
41482     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41483     this.reader = this.form.reader;
41484 };
41485
41486 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41487     type : 'load',
41488
41489     run : function(){
41490         Roo.Ajax.request(Roo.apply(
41491                 this.createCallback(), {
41492                     method:this.getMethod(),
41493                     url:this.getUrl(false),
41494                     params:this.getParams()
41495         }));
41496     },
41497
41498     success : function(response){
41499         var result = this.processResponse(response);
41500         if(result === true || !result.success || !result.data){
41501             this.failureType = Roo.form.Action.LOAD_FAILURE;
41502             this.form.afterAction(this, false);
41503             return;
41504         }
41505         this.form.clearInvalid();
41506         this.form.setValues(result.data);
41507         this.form.afterAction(this, true);
41508     },
41509
41510     handleResponse : function(response){
41511         if(this.form.reader){
41512             var rs = this.form.reader.read(response);
41513             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41514             return {
41515                 success : rs.success,
41516                 data : data
41517             };
41518         }
41519         return Roo.decode(response.responseText);
41520     }
41521 });
41522
41523 Roo.form.Action.ACTION_TYPES = {
41524     'load' : Roo.form.Action.Load,
41525     'submit' : Roo.form.Action.Submit
41526 };/*
41527  * Based on:
41528  * Ext JS Library 1.1.1
41529  * Copyright(c) 2006-2007, Ext JS, LLC.
41530  *
41531  * Originally Released Under LGPL - original licence link has changed is not relivant.
41532  *
41533  * Fork - LGPL
41534  * <script type="text/javascript">
41535  */
41536  
41537 /**
41538  * @class Roo.form.Layout
41539  * @extends Roo.Component
41540  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41541  * @constructor
41542  * @param {Object} config Configuration options
41543  */
41544 Roo.form.Layout = function(config){
41545     var xitems = [];
41546     if (config.items) {
41547         xitems = config.items;
41548         delete config.items;
41549     }
41550     Roo.form.Layout.superclass.constructor.call(this, config);
41551     this.stack = [];
41552     Roo.each(xitems, this.addxtype, this);
41553      
41554 };
41555
41556 Roo.extend(Roo.form.Layout, Roo.Component, {
41557     /**
41558      * @cfg {String/Object} autoCreate
41559      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41560      */
41561     /**
41562      * @cfg {String/Object/Function} style
41563      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41564      * a function which returns such a specification.
41565      */
41566     /**
41567      * @cfg {String} labelAlign
41568      * Valid values are "left," "top" and "right" (defaults to "left")
41569      */
41570     /**
41571      * @cfg {Number} labelWidth
41572      * Fixed width in pixels of all field labels (defaults to undefined)
41573      */
41574     /**
41575      * @cfg {Boolean} clear
41576      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41577      */
41578     clear : true,
41579     /**
41580      * @cfg {String} labelSeparator
41581      * The separator to use after field labels (defaults to ':')
41582      */
41583     labelSeparator : ':',
41584     /**
41585      * @cfg {Boolean} hideLabels
41586      * True to suppress the display of field labels in this layout (defaults to false)
41587      */
41588     hideLabels : false,
41589
41590     // private
41591     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41592     
41593     isLayout : true,
41594     
41595     // private
41596     onRender : function(ct, position){
41597         if(this.el){ // from markup
41598             this.el = Roo.get(this.el);
41599         }else {  // generate
41600             var cfg = this.getAutoCreate();
41601             this.el = ct.createChild(cfg, position);
41602         }
41603         if(this.style){
41604             this.el.applyStyles(this.style);
41605         }
41606         if(this.labelAlign){
41607             this.el.addClass('x-form-label-'+this.labelAlign);
41608         }
41609         if(this.hideLabels){
41610             this.labelStyle = "display:none";
41611             this.elementStyle = "padding-left:0;";
41612         }else{
41613             if(typeof this.labelWidth == 'number'){
41614                 this.labelStyle = "width:"+this.labelWidth+"px;";
41615                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41616             }
41617             if(this.labelAlign == 'top'){
41618                 this.labelStyle = "width:auto;";
41619                 this.elementStyle = "padding-left:0;";
41620             }
41621         }
41622         var stack = this.stack;
41623         var slen = stack.length;
41624         if(slen > 0){
41625             if(!this.fieldTpl){
41626                 var t = new Roo.Template(
41627                     '<div class="x-form-item {5}">',
41628                         '<label for="{0}" style="{2}">{1}{4}</label>',
41629                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41630                         '</div>',
41631                     '</div><div class="x-form-clear-left"></div>'
41632                 );
41633                 t.disableFormats = true;
41634                 t.compile();
41635                 Roo.form.Layout.prototype.fieldTpl = t;
41636             }
41637             for(var i = 0; i < slen; i++) {
41638                 if(stack[i].isFormField){
41639                     this.renderField(stack[i]);
41640                 }else{
41641                     this.renderComponent(stack[i]);
41642                 }
41643             }
41644         }
41645         if(this.clear){
41646             this.el.createChild({cls:'x-form-clear'});
41647         }
41648     },
41649
41650     // private
41651     renderField : function(f){
41652         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41653                f.id, //0
41654                f.fieldLabel, //1
41655                f.labelStyle||this.labelStyle||'', //2
41656                this.elementStyle||'', //3
41657                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41658                f.itemCls||this.itemCls||''  //5
41659        ], true).getPrevSibling());
41660     },
41661
41662     // private
41663     renderComponent : function(c){
41664         c.render(c.isLayout ? this.el : this.el.createChild());    
41665     },
41666     /**
41667      * Adds a object form elements (using the xtype property as the factory method.)
41668      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41669      * @param {Object} config 
41670      */
41671     addxtype : function(o)
41672     {
41673         // create the lement.
41674         o.form = this.form;
41675         var fe = Roo.factory(o, Roo.form);
41676         this.form.allItems.push(fe);
41677         this.stack.push(fe);
41678         
41679         if (fe.isFormField) {
41680             this.form.items.add(fe);
41681         }
41682          
41683         return fe;
41684     }
41685 });
41686
41687 /**
41688  * @class Roo.form.Column
41689  * @extends Roo.form.Layout
41690  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41691  * @constructor
41692  * @param {Object} config Configuration options
41693  */
41694 Roo.form.Column = function(config){
41695     Roo.form.Column.superclass.constructor.call(this, config);
41696 };
41697
41698 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41699     /**
41700      * @cfg {Number/String} width
41701      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41702      */
41703     /**
41704      * @cfg {String/Object} autoCreate
41705      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41706      */
41707
41708     // private
41709     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41710
41711     // private
41712     onRender : function(ct, position){
41713         Roo.form.Column.superclass.onRender.call(this, ct, position);
41714         if(this.width){
41715             this.el.setWidth(this.width);
41716         }
41717     }
41718 });
41719
41720
41721 /**
41722  * @class Roo.form.Row
41723  * @extends Roo.form.Layout
41724  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41725  * @constructor
41726  * @param {Object} config Configuration options
41727  */
41728
41729  
41730 Roo.form.Row = function(config){
41731     Roo.form.Row.superclass.constructor.call(this, config);
41732 };
41733  
41734 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41735       /**
41736      * @cfg {Number/String} width
41737      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41738      */
41739     /**
41740      * @cfg {Number/String} height
41741      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41742      */
41743     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41744     
41745     padWidth : 20,
41746     // private
41747     onRender : function(ct, position){
41748         //console.log('row render');
41749         if(!this.rowTpl){
41750             var t = new Roo.Template(
41751                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41752                     '<label for="{0}" style="{2}">{1}{4}</label>',
41753                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41754                     '</div>',
41755                 '</div>'
41756             );
41757             t.disableFormats = true;
41758             t.compile();
41759             Roo.form.Layout.prototype.rowTpl = t;
41760         }
41761         this.fieldTpl = this.rowTpl;
41762         
41763         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41764         var labelWidth = 100;
41765         
41766         if ((this.labelAlign != 'top')) {
41767             if (typeof this.labelWidth == 'number') {
41768                 labelWidth = this.labelWidth
41769             }
41770             this.padWidth =  20 + labelWidth;
41771             
41772         }
41773         
41774         Roo.form.Column.superclass.onRender.call(this, ct, position);
41775         if(this.width){
41776             this.el.setWidth(this.width);
41777         }
41778         if(this.height){
41779             this.el.setHeight(this.height);
41780         }
41781     },
41782     
41783     // private
41784     renderField : function(f){
41785         f.fieldEl = this.fieldTpl.append(this.el, [
41786                f.id, f.fieldLabel,
41787                f.labelStyle||this.labelStyle||'',
41788                this.elementStyle||'',
41789                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41790                f.itemCls||this.itemCls||'',
41791                f.width ? f.width + this.padWidth : 160 + this.padWidth
41792        ],true);
41793     }
41794 });
41795  
41796
41797 /**
41798  * @class Roo.form.FieldSet
41799  * @extends Roo.form.Layout
41800  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41801  * @constructor
41802  * @param {Object} config Configuration options
41803  */
41804 Roo.form.FieldSet = function(config){
41805     Roo.form.FieldSet.superclass.constructor.call(this, config);
41806 };
41807
41808 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41809     /**
41810      * @cfg {String} legend
41811      * The text to display as the legend for the FieldSet (defaults to '')
41812      */
41813     /**
41814      * @cfg {String/Object} autoCreate
41815      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41816      */
41817
41818     // private
41819     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41820
41821     // private
41822     onRender : function(ct, position){
41823         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41824         if(this.legend){
41825             this.setLegend(this.legend);
41826         }
41827     },
41828
41829     // private
41830     setLegend : function(text){
41831         if(this.rendered){
41832             this.el.child('legend').update(text);
41833         }
41834     }
41835 });/*
41836  * Based on:
41837  * Ext JS Library 1.1.1
41838  * Copyright(c) 2006-2007, Ext JS, LLC.
41839  *
41840  * Originally Released Under LGPL - original licence link has changed is not relivant.
41841  *
41842  * Fork - LGPL
41843  * <script type="text/javascript">
41844  */
41845 /**
41846  * @class Roo.form.VTypes
41847  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41848  * @singleton
41849  */
41850 Roo.form.VTypes = function(){
41851     // closure these in so they are only created once.
41852     var alpha = /^[a-zA-Z_]+$/;
41853     var alphanum = /^[a-zA-Z0-9_]+$/;
41854     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41855     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41856
41857     // All these messages and functions are configurable
41858     return {
41859         /**
41860          * The function used to validate email addresses
41861          * @param {String} value The email address
41862          */
41863         'email' : function(v){
41864             return email.test(v);
41865         },
41866         /**
41867          * The error text to display when the email validation function returns false
41868          * @type String
41869          */
41870         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41871         /**
41872          * The keystroke filter mask to be applied on email input
41873          * @type RegExp
41874          */
41875         'emailMask' : /[a-z0-9_\.\-@]/i,
41876
41877         /**
41878          * The function used to validate URLs
41879          * @param {String} value The URL
41880          */
41881         'url' : function(v){
41882             return url.test(v);
41883         },
41884         /**
41885          * The error text to display when the url validation function returns false
41886          * @type String
41887          */
41888         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41889         
41890         /**
41891          * The function used to validate alpha values
41892          * @param {String} value The value
41893          */
41894         'alpha' : function(v){
41895             return alpha.test(v);
41896         },
41897         /**
41898          * The error text to display when the alpha validation function returns false
41899          * @type String
41900          */
41901         'alphaText' : 'This field should only contain letters and _',
41902         /**
41903          * The keystroke filter mask to be applied on alpha input
41904          * @type RegExp
41905          */
41906         'alphaMask' : /[a-z_]/i,
41907
41908         /**
41909          * The function used to validate alphanumeric values
41910          * @param {String} value The value
41911          */
41912         'alphanum' : function(v){
41913             return alphanum.test(v);
41914         },
41915         /**
41916          * The error text to display when the alphanumeric validation function returns false
41917          * @type String
41918          */
41919         'alphanumText' : 'This field should only contain letters, numbers and _',
41920         /**
41921          * The keystroke filter mask to be applied on alphanumeric input
41922          * @type RegExp
41923          */
41924         'alphanumMask' : /[a-z0-9_]/i
41925     };
41926 }();//<script type="text/javascript">
41927
41928 /**
41929  * @class Roo.form.FCKeditor
41930  * @extends Roo.form.TextArea
41931  * Wrapper around the FCKEditor http://www.fckeditor.net
41932  * @constructor
41933  * Creates a new FCKeditor
41934  * @param {Object} config Configuration options
41935  */
41936 Roo.form.FCKeditor = function(config){
41937     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41938     this.addEvents({
41939          /**
41940          * @event editorinit
41941          * Fired when the editor is initialized - you can add extra handlers here..
41942          * @param {FCKeditor} this
41943          * @param {Object} the FCK object.
41944          */
41945         editorinit : true
41946     });
41947     
41948     
41949 };
41950 Roo.form.FCKeditor.editors = { };
41951 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41952 {
41953     //defaultAutoCreate : {
41954     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41955     //},
41956     // private
41957     /**
41958      * @cfg {Object} fck options - see fck manual for details.
41959      */
41960     fckconfig : false,
41961     
41962     /**
41963      * @cfg {Object} fck toolbar set (Basic or Default)
41964      */
41965     toolbarSet : 'Basic',
41966     /**
41967      * @cfg {Object} fck BasePath
41968      */ 
41969     basePath : '/fckeditor/',
41970     
41971     
41972     frame : false,
41973     
41974     value : '',
41975     
41976    
41977     onRender : function(ct, position)
41978     {
41979         if(!this.el){
41980             this.defaultAutoCreate = {
41981                 tag: "textarea",
41982                 style:"width:300px;height:60px;",
41983                 autocomplete: "off"
41984             };
41985         }
41986         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41987         /*
41988         if(this.grow){
41989             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41990             if(this.preventScrollbars){
41991                 this.el.setStyle("overflow", "hidden");
41992             }
41993             this.el.setHeight(this.growMin);
41994         }
41995         */
41996         //console.log('onrender' + this.getId() );
41997         Roo.form.FCKeditor.editors[this.getId()] = this;
41998          
41999
42000         this.replaceTextarea() ;
42001         
42002     },
42003     
42004     getEditor : function() {
42005         return this.fckEditor;
42006     },
42007     /**
42008      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42009      * @param {Mixed} value The value to set
42010      */
42011     
42012     
42013     setValue : function(value)
42014     {
42015         //console.log('setValue: ' + value);
42016         
42017         if(typeof(value) == 'undefined') { // not sure why this is happending...
42018             return;
42019         }
42020         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42021         
42022         //if(!this.el || !this.getEditor()) {
42023         //    this.value = value;
42024             //this.setValue.defer(100,this,[value]);    
42025         //    return;
42026         //} 
42027         
42028         if(!this.getEditor()) {
42029             return;
42030         }
42031         
42032         this.getEditor().SetData(value);
42033         
42034         //
42035
42036     },
42037
42038     /**
42039      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42040      * @return {Mixed} value The field value
42041      */
42042     getValue : function()
42043     {
42044         
42045         if (this.frame && this.frame.dom.style.display == 'none') {
42046             return Roo.form.FCKeditor.superclass.getValue.call(this);
42047         }
42048         
42049         if(!this.el || !this.getEditor()) {
42050            
42051            // this.getValue.defer(100,this); 
42052             return this.value;
42053         }
42054        
42055         
42056         var value=this.getEditor().GetData();
42057         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42058         return Roo.form.FCKeditor.superclass.getValue.call(this);
42059         
42060
42061     },
42062
42063     /**
42064      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42065      * @return {Mixed} value The field value
42066      */
42067     getRawValue : function()
42068     {
42069         if (this.frame && this.frame.dom.style.display == 'none') {
42070             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42071         }
42072         
42073         if(!this.el || !this.getEditor()) {
42074             //this.getRawValue.defer(100,this); 
42075             return this.value;
42076             return;
42077         }
42078         
42079         
42080         
42081         var value=this.getEditor().GetData();
42082         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42083         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42084          
42085     },
42086     
42087     setSize : function(w,h) {
42088         
42089         
42090         
42091         //if (this.frame && this.frame.dom.style.display == 'none') {
42092         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42093         //    return;
42094         //}
42095         //if(!this.el || !this.getEditor()) {
42096         //    this.setSize.defer(100,this, [w,h]); 
42097         //    return;
42098         //}
42099         
42100         
42101         
42102         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42103         
42104         this.frame.dom.setAttribute('width', w);
42105         this.frame.dom.setAttribute('height', h);
42106         this.frame.setSize(w,h);
42107         
42108     },
42109     
42110     toggleSourceEdit : function(value) {
42111         
42112       
42113          
42114         this.el.dom.style.display = value ? '' : 'none';
42115         this.frame.dom.style.display = value ?  'none' : '';
42116         
42117     },
42118     
42119     
42120     focus: function(tag)
42121     {
42122         if (this.frame.dom.style.display == 'none') {
42123             return Roo.form.FCKeditor.superclass.focus.call(this);
42124         }
42125         if(!this.el || !this.getEditor()) {
42126             this.focus.defer(100,this, [tag]); 
42127             return;
42128         }
42129         
42130         
42131         
42132         
42133         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42134         this.getEditor().Focus();
42135         if (tgs.length) {
42136             if (!this.getEditor().Selection.GetSelection()) {
42137                 this.focus.defer(100,this, [tag]); 
42138                 return;
42139             }
42140             
42141             
42142             var r = this.getEditor().EditorDocument.createRange();
42143             r.setStart(tgs[0],0);
42144             r.setEnd(tgs[0],0);
42145             this.getEditor().Selection.GetSelection().removeAllRanges();
42146             this.getEditor().Selection.GetSelection().addRange(r);
42147             this.getEditor().Focus();
42148         }
42149         
42150     },
42151     
42152     
42153     
42154     replaceTextarea : function()
42155     {
42156         if ( document.getElementById( this.getId() + '___Frame' ) )
42157             return ;
42158         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42159         //{
42160             // We must check the elements firstly using the Id and then the name.
42161         var oTextarea = document.getElementById( this.getId() );
42162         
42163         var colElementsByName = document.getElementsByName( this.getId() ) ;
42164          
42165         oTextarea.style.display = 'none' ;
42166
42167         if ( oTextarea.tabIndex ) {            
42168             this.TabIndex = oTextarea.tabIndex ;
42169         }
42170         
42171         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42172         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42173         this.frame = Roo.get(this.getId() + '___Frame')
42174     },
42175     
42176     _getConfigHtml : function()
42177     {
42178         var sConfig = '' ;
42179
42180         for ( var o in this.fckconfig ) {
42181             sConfig += sConfig.length > 0  ? '&amp;' : '';
42182             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42183         }
42184
42185         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42186     },
42187     
42188     
42189     _getIFrameHtml : function()
42190     {
42191         var sFile = 'fckeditor.html' ;
42192         /* no idea what this is about..
42193         try
42194         {
42195             if ( (/fcksource=true/i).test( window.top.location.search ) )
42196                 sFile = 'fckeditor.original.html' ;
42197         }
42198         catch (e) { 
42199         */
42200
42201         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42202         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42203         
42204         
42205         var html = '<iframe id="' + this.getId() +
42206             '___Frame" src="' + sLink +
42207             '" width="' + this.width +
42208             '" height="' + this.height + '"' +
42209             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42210             ' frameborder="0" scrolling="no"></iframe>' ;
42211
42212         return html ;
42213     },
42214     
42215     _insertHtmlBefore : function( html, element )
42216     {
42217         if ( element.insertAdjacentHTML )       {
42218             // IE
42219             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42220         } else { // Gecko
42221             var oRange = document.createRange() ;
42222             oRange.setStartBefore( element ) ;
42223             var oFragment = oRange.createContextualFragment( html );
42224             element.parentNode.insertBefore( oFragment, element ) ;
42225         }
42226     }
42227     
42228     
42229   
42230     
42231     
42232     
42233     
42234
42235 });
42236
42237 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42238
42239 function FCKeditor_OnComplete(editorInstance){
42240     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42241     f.fckEditor = editorInstance;
42242     //console.log("loaded");
42243     f.fireEvent('editorinit', f, editorInstance);
42244
42245   
42246
42247  
42248
42249
42250
42251
42252
42253
42254
42255
42256
42257
42258
42259
42260
42261
42262
42263 //<script type="text/javascript">
42264 /**
42265  * @class Roo.form.GridField
42266  * @extends Roo.form.Field
42267  * Embed a grid (or editable grid into a form)
42268  * STATUS ALPHA
42269  * 
42270  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42271  * it needs 
42272  * xgrid.store = Roo.data.Store
42273  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42274  * xgrid.store.reader = Roo.data.JsonReader 
42275  * 
42276  * 
42277  * @constructor
42278  * Creates a new GridField
42279  * @param {Object} config Configuration options
42280  */
42281 Roo.form.GridField = function(config){
42282     Roo.form.GridField.superclass.constructor.call(this, config);
42283      
42284 };
42285
42286 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42287     /**
42288      * @cfg {Number} width  - used to restrict width of grid..
42289      */
42290     width : 100,
42291     /**
42292      * @cfg {Number} height - used to restrict height of grid..
42293      */
42294     height : 50,
42295      /**
42296      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42297          * 
42298          *}
42299      */
42300     xgrid : false, 
42301     /**
42302      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42303      * {tag: "input", type: "checkbox", autocomplete: "off"})
42304      */
42305    // defaultAutoCreate : { tag: 'div' },
42306     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42307     /**
42308      * @cfg {String} addTitle Text to include for adding a title.
42309      */
42310     addTitle : false,
42311     //
42312     onResize : function(){
42313         Roo.form.Field.superclass.onResize.apply(this, arguments);
42314     },
42315
42316     initEvents : function(){
42317         // Roo.form.Checkbox.superclass.initEvents.call(this);
42318         // has no events...
42319        
42320     },
42321
42322
42323     getResizeEl : function(){
42324         return this.wrap;
42325     },
42326
42327     getPositionEl : function(){
42328         return this.wrap;
42329     },
42330
42331     // private
42332     onRender : function(ct, position){
42333         
42334         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42335         var style = this.style;
42336         delete this.style;
42337         
42338         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42339         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42340         this.viewEl = this.wrap.createChild({ tag: 'div' });
42341         if (style) {
42342             this.viewEl.applyStyles(style);
42343         }
42344         if (this.width) {
42345             this.viewEl.setWidth(this.width);
42346         }
42347         if (this.height) {
42348             this.viewEl.setHeight(this.height);
42349         }
42350         //if(this.inputValue !== undefined){
42351         //this.setValue(this.value);
42352         
42353         
42354         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42355         
42356         
42357         this.grid.render();
42358         this.grid.getDataSource().on('remove', this.refreshValue, this);
42359         this.grid.getDataSource().on('update', this.refreshValue, this);
42360         this.grid.on('afteredit', this.refreshValue, this);
42361  
42362     },
42363      
42364     
42365     /**
42366      * Sets the value of the item. 
42367      * @param {String} either an object  or a string..
42368      */
42369     setValue : function(v){
42370         //this.value = v;
42371         v = v || []; // empty set..
42372         // this does not seem smart - it really only affects memoryproxy grids..
42373         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42374             var ds = this.grid.getDataSource();
42375             // assumes a json reader..
42376             var data = {}
42377             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42378             ds.loadData( data);
42379         }
42380         Roo.form.GridField.superclass.setValue.call(this, v);
42381         this.refreshValue();
42382         // should load data in the grid really....
42383     },
42384     
42385     // private
42386     refreshValue: function() {
42387          var val = [];
42388         this.grid.getDataSource().each(function(r) {
42389             val.push(r.data);
42390         });
42391         this.el.dom.value = Roo.encode(val);
42392     }
42393     
42394      
42395     
42396     
42397 });/*
42398  * Based on:
42399  * Ext JS Library 1.1.1
42400  * Copyright(c) 2006-2007, Ext JS, LLC.
42401  *
42402  * Originally Released Under LGPL - original licence link has changed is not relivant.
42403  *
42404  * Fork - LGPL
42405  * <script type="text/javascript">
42406  */
42407 /**
42408  * @class Roo.form.DisplayField
42409  * @extends Roo.form.Field
42410  * A generic Field to display non-editable data.
42411  * @constructor
42412  * Creates a new Display Field item.
42413  * @param {Object} config Configuration options
42414  */
42415 Roo.form.DisplayField = function(config){
42416     Roo.form.DisplayField.superclass.constructor.call(this, config);
42417     
42418 };
42419
42420 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42421     inputType:      'hidden',
42422     allowBlank:     true,
42423     readOnly:         true,
42424     
42425  
42426     /**
42427      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42428      */
42429     focusClass : undefined,
42430     /**
42431      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42432      */
42433     fieldClass: 'x-form-field',
42434     
42435      /**
42436      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42437      */
42438     valueRenderer: undefined,
42439     
42440     width: 100,
42441     /**
42442      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42443      * {tag: "input", type: "checkbox", autocomplete: "off"})
42444      */
42445      
42446  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42447
42448     onResize : function(){
42449         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42450         
42451     },
42452
42453     initEvents : function(){
42454         // Roo.form.Checkbox.superclass.initEvents.call(this);
42455         // has no events...
42456        
42457     },
42458
42459
42460     getResizeEl : function(){
42461         return this.wrap;
42462     },
42463
42464     getPositionEl : function(){
42465         return this.wrap;
42466     },
42467
42468     // private
42469     onRender : function(ct, position){
42470         
42471         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42472         //if(this.inputValue !== undefined){
42473         this.wrap = this.el.wrap();
42474         
42475         this.viewEl = this.wrap.createChild({ tag: 'div'});
42476         
42477         if (this.bodyStyle) {
42478             this.viewEl.applyStyles(this.bodyStyle);
42479         }
42480         //this.viewEl.setStyle('padding', '2px');
42481         
42482         this.setValue(this.value);
42483         
42484     },
42485 /*
42486     // private
42487     initValue : Roo.emptyFn,
42488
42489   */
42490
42491         // private
42492     onClick : function(){
42493         
42494     },
42495
42496     /**
42497      * Sets the checked state of the checkbox.
42498      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42499      */
42500     setValue : function(v){
42501         this.value = v;
42502         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42503         // this might be called before we have a dom element..
42504         if (!this.viewEl) {
42505             return;
42506         }
42507         this.viewEl.dom.innerHTML = html;
42508         Roo.form.DisplayField.superclass.setValue.call(this, v);
42509
42510     }
42511 });//<script type="text/javasscript">
42512  
42513
42514 /**
42515  * @class Roo.DDView
42516  * A DnD enabled version of Roo.View.
42517  * @param {Element/String} container The Element in which to create the View.
42518  * @param {String} tpl The template string used to create the markup for each element of the View
42519  * @param {Object} config The configuration properties. These include all the config options of
42520  * {@link Roo.View} plus some specific to this class.<br>
42521  * <p>
42522  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42523  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42524  * <p>
42525  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42526 .x-view-drag-insert-above {
42527         border-top:1px dotted #3366cc;
42528 }
42529 .x-view-drag-insert-below {
42530         border-bottom:1px dotted #3366cc;
42531 }
42532 </code></pre>
42533  * 
42534  */
42535  
42536 Roo.DDView = function(container, tpl, config) {
42537     Roo.DDView.superclass.constructor.apply(this, arguments);
42538     this.getEl().setStyle("outline", "0px none");
42539     this.getEl().unselectable();
42540     if (this.dragGroup) {
42541                 this.setDraggable(this.dragGroup.split(","));
42542     }
42543     if (this.dropGroup) {
42544                 this.setDroppable(this.dropGroup.split(","));
42545     }
42546     if (this.deletable) {
42547         this.setDeletable();
42548     }
42549     this.isDirtyFlag = false;
42550         this.addEvents({
42551                 "drop" : true
42552         });
42553 };
42554
42555 Roo.extend(Roo.DDView, Roo.View, {
42556 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42557 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42558 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42559 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42560
42561         isFormField: true,
42562
42563         reset: Roo.emptyFn,
42564         
42565         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42566
42567         validate: function() {
42568                 return true;
42569         },
42570         
42571         destroy: function() {
42572                 this.purgeListeners();
42573                 this.getEl.removeAllListeners();
42574                 this.getEl().remove();
42575                 if (this.dragZone) {
42576                         if (this.dragZone.destroy) {
42577                                 this.dragZone.destroy();
42578                         }
42579                 }
42580                 if (this.dropZone) {
42581                         if (this.dropZone.destroy) {
42582                                 this.dropZone.destroy();
42583                         }
42584                 }
42585         },
42586
42587 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42588         getName: function() {
42589                 return this.name;
42590         },
42591
42592 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42593         setValue: function(v) {
42594                 if (!this.store) {
42595                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42596                 }
42597                 var data = {};
42598                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42599                 this.store.proxy = new Roo.data.MemoryProxy(data);
42600                 this.store.load();
42601         },
42602
42603 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42604         getValue: function() {
42605                 var result = '(';
42606                 this.store.each(function(rec) {
42607                         result += rec.id + ',';
42608                 });
42609                 return result.substr(0, result.length - 1) + ')';
42610         },
42611         
42612         getIds: function() {
42613                 var i = 0, result = new Array(this.store.getCount());
42614                 this.store.each(function(rec) {
42615                         result[i++] = rec.id;
42616                 });
42617                 return result;
42618         },
42619         
42620         isDirty: function() {
42621                 return this.isDirtyFlag;
42622         },
42623
42624 /**
42625  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42626  *      whole Element becomes the target, and this causes the drop gesture to append.
42627  */
42628     getTargetFromEvent : function(e) {
42629                 var target = e.getTarget();
42630                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42631                 target = target.parentNode;
42632                 }
42633                 if (!target) {
42634                         target = this.el.dom.lastChild || this.el.dom;
42635                 }
42636                 return target;
42637     },
42638
42639 /**
42640  *      Create the drag data which consists of an object which has the property "ddel" as
42641  *      the drag proxy element. 
42642  */
42643     getDragData : function(e) {
42644         var target = this.findItemFromChild(e.getTarget());
42645                 if(target) {
42646                         this.handleSelection(e);
42647                         var selNodes = this.getSelectedNodes();
42648             var dragData = {
42649                 source: this,
42650                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42651                 nodes: selNodes,
42652                 records: []
42653                         };
42654                         var selectedIndices = this.getSelectedIndexes();
42655                         for (var i = 0; i < selectedIndices.length; i++) {
42656                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42657                         }
42658                         if (selNodes.length == 1) {
42659                                 dragData.ddel = target.cloneNode(true); // the div element
42660                         } else {
42661                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42662                                 div.className = 'multi-proxy';
42663                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42664                                         div.appendChild(selNodes[i].cloneNode(true));
42665                                 }
42666                                 dragData.ddel = div;
42667                         }
42668             //console.log(dragData)
42669             //console.log(dragData.ddel.innerHTML)
42670                         return dragData;
42671                 }
42672         //console.log('nodragData')
42673                 return false;
42674     },
42675     
42676 /**     Specify to which ddGroup items in this DDView may be dragged. */
42677     setDraggable: function(ddGroup) {
42678         if (ddGroup instanceof Array) {
42679                 Roo.each(ddGroup, this.setDraggable, this);
42680                 return;
42681         }
42682         if (this.dragZone) {
42683                 this.dragZone.addToGroup(ddGroup);
42684         } else {
42685                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42686                                 containerScroll: true,
42687                                 ddGroup: ddGroup 
42688
42689                         });
42690 //                      Draggability implies selection. DragZone's mousedown selects the element.
42691                         if (!this.multiSelect) { this.singleSelect = true; }
42692
42693 //                      Wire the DragZone's handlers up to methods in *this*
42694                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42695                 }
42696     },
42697
42698 /**     Specify from which ddGroup this DDView accepts drops. */
42699     setDroppable: function(ddGroup) {
42700         if (ddGroup instanceof Array) {
42701                 Roo.each(ddGroup, this.setDroppable, this);
42702                 return;
42703         }
42704         if (this.dropZone) {
42705                 this.dropZone.addToGroup(ddGroup);
42706         } else {
42707                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42708                                 containerScroll: true,
42709                                 ddGroup: ddGroup
42710                         });
42711
42712 //                      Wire the DropZone's handlers up to methods in *this*
42713                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42714                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42715                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42716                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42717                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42718                 }
42719     },
42720
42721 /**     Decide whether to drop above or below a View node. */
42722     getDropPoint : function(e, n, dd){
42723         if (n == this.el.dom) { return "above"; }
42724                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42725                 var c = t + (b - t) / 2;
42726                 var y = Roo.lib.Event.getPageY(e);
42727                 if(y <= c) {
42728                         return "above";
42729                 }else{
42730                         return "below";
42731                 }
42732     },
42733
42734     onNodeEnter : function(n, dd, e, data){
42735                 return false;
42736     },
42737     
42738     onNodeOver : function(n, dd, e, data){
42739                 var pt = this.getDropPoint(e, n, dd);
42740                 // set the insert point style on the target node
42741                 var dragElClass = this.dropNotAllowed;
42742                 if (pt) {
42743                         var targetElClass;
42744                         if (pt == "above"){
42745                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42746                                 targetElClass = "x-view-drag-insert-above";
42747                         } else {
42748                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42749                                 targetElClass = "x-view-drag-insert-below";
42750                         }
42751                         if (this.lastInsertClass != targetElClass){
42752                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42753                                 this.lastInsertClass = targetElClass;
42754                         }
42755                 }
42756                 return dragElClass;
42757         },
42758
42759     onNodeOut : function(n, dd, e, data){
42760                 this.removeDropIndicators(n);
42761     },
42762
42763     onNodeDrop : function(n, dd, e, data){
42764         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42765                 return false;
42766         }
42767         var pt = this.getDropPoint(e, n, dd);
42768                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42769                 if (pt == "below") { insertAt++; }
42770                 for (var i = 0; i < data.records.length; i++) {
42771                         var r = data.records[i];
42772                         var dup = this.store.getById(r.id);
42773                         if (dup && (dd != this.dragZone)) {
42774                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42775                         } else {
42776                                 if (data.copy) {
42777                                         this.store.insert(insertAt++, r.copy());
42778                                 } else {
42779                                         data.source.isDirtyFlag = true;
42780                                         r.store.remove(r);
42781                                         this.store.insert(insertAt++, r);
42782                                 }
42783                                 this.isDirtyFlag = true;
42784                         }
42785                 }
42786                 this.dragZone.cachedTarget = null;
42787                 return true;
42788     },
42789
42790     removeDropIndicators : function(n){
42791                 if(n){
42792                         Roo.fly(n).removeClass([
42793                                 "x-view-drag-insert-above",
42794                                 "x-view-drag-insert-below"]);
42795                         this.lastInsertClass = "_noclass";
42796                 }
42797     },
42798
42799 /**
42800  *      Utility method. Add a delete option to the DDView's context menu.
42801  *      @param {String} imageUrl The URL of the "delete" icon image.
42802  */
42803         setDeletable: function(imageUrl) {
42804                 if (!this.singleSelect && !this.multiSelect) {
42805                         this.singleSelect = true;
42806                 }
42807                 var c = this.getContextMenu();
42808                 this.contextMenu.on("itemclick", function(item) {
42809                         switch (item.id) {
42810                                 case "delete":
42811                                         this.remove(this.getSelectedIndexes());
42812                                         break;
42813                         }
42814                 }, this);
42815                 this.contextMenu.add({
42816                         icon: imageUrl,
42817                         id: "delete",
42818                         text: 'Delete'
42819                 });
42820         },
42821         
42822 /**     Return the context menu for this DDView. */
42823         getContextMenu: function() {
42824                 if (!this.contextMenu) {
42825 //                      Create the View's context menu
42826                         this.contextMenu = new Roo.menu.Menu({
42827                                 id: this.id + "-contextmenu"
42828                         });
42829                         this.el.on("contextmenu", this.showContextMenu, this);
42830                 }
42831                 return this.contextMenu;
42832         },
42833         
42834         disableContextMenu: function() {
42835                 if (this.contextMenu) {
42836                         this.el.un("contextmenu", this.showContextMenu, this);
42837                 }
42838         },
42839
42840         showContextMenu: function(e, item) {
42841         item = this.findItemFromChild(e.getTarget());
42842                 if (item) {
42843                         e.stopEvent();
42844                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42845                         this.contextMenu.showAt(e.getXY());
42846             }
42847     },
42848
42849 /**
42850  *      Remove {@link Roo.data.Record}s at the specified indices.
42851  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42852  */
42853     remove: function(selectedIndices) {
42854                 selectedIndices = [].concat(selectedIndices);
42855                 for (var i = 0; i < selectedIndices.length; i++) {
42856                         var rec = this.store.getAt(selectedIndices[i]);
42857                         this.store.remove(rec);
42858                 }
42859     },
42860
42861 /**
42862  *      Double click fires the event, but also, if this is draggable, and there is only one other
42863  *      related DropZone, it transfers the selected node.
42864  */
42865     onDblClick : function(e){
42866         var item = this.findItemFromChild(e.getTarget());
42867         if(item){
42868             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42869                 return false;
42870             }
42871             if (this.dragGroup) {
42872                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42873                     while (targets.indexOf(this.dropZone) > -1) {
42874                             targets.remove(this.dropZone);
42875                                 }
42876                     if (targets.length == 1) {
42877                                         this.dragZone.cachedTarget = null;
42878                         var el = Roo.get(targets[0].getEl());
42879                         var box = el.getBox(true);
42880                         targets[0].onNodeDrop(el.dom, {
42881                                 target: el.dom,
42882                                 xy: [box.x, box.y + box.height - 1]
42883                         }, null, this.getDragData(e));
42884                     }
42885                 }
42886         }
42887     },
42888     
42889     handleSelection: function(e) {
42890                 this.dragZone.cachedTarget = null;
42891         var item = this.findItemFromChild(e.getTarget());
42892         if (!item) {
42893                 this.clearSelections(true);
42894                 return;
42895         }
42896                 if (item && (this.multiSelect || this.singleSelect)){
42897                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42898                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42899                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42900                                 this.unselect(item);
42901                         } else {
42902                                 this.select(item, this.multiSelect && e.ctrlKey);
42903                                 this.lastSelection = item;
42904                         }
42905                 }
42906     },
42907
42908     onItemClick : function(item, index, e){
42909                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42910                         return false;
42911                 }
42912                 return true;
42913     },
42914
42915     unselect : function(nodeInfo, suppressEvent){
42916                 var node = this.getNode(nodeInfo);
42917                 if(node && this.isSelected(node)){
42918                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42919                                 Roo.fly(node).removeClass(this.selectedClass);
42920                                 this.selections.remove(node);
42921                                 if(!suppressEvent){
42922                                         this.fireEvent("selectionchange", this, this.selections);
42923                                 }
42924                         }
42925                 }
42926     }
42927 });
42928 /*
42929  * Based on:
42930  * Ext JS Library 1.1.1
42931  * Copyright(c) 2006-2007, Ext JS, LLC.
42932  *
42933  * Originally Released Under LGPL - original licence link has changed is not relivant.
42934  *
42935  * Fork - LGPL
42936  * <script type="text/javascript">
42937  */
42938  
42939 /**
42940  * @class Roo.LayoutManager
42941  * @extends Roo.util.Observable
42942  * Base class for layout managers.
42943  */
42944 Roo.LayoutManager = function(container, config){
42945     Roo.LayoutManager.superclass.constructor.call(this);
42946     this.el = Roo.get(container);
42947     // ie scrollbar fix
42948     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42949         document.body.scroll = "no";
42950     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42951         this.el.position('relative');
42952     }
42953     this.id = this.el.id;
42954     this.el.addClass("x-layout-container");
42955     /** false to disable window resize monitoring @type Boolean */
42956     this.monitorWindowResize = true;
42957     this.regions = {};
42958     this.addEvents({
42959         /**
42960          * @event layout
42961          * Fires when a layout is performed. 
42962          * @param {Roo.LayoutManager} this
42963          */
42964         "layout" : true,
42965         /**
42966          * @event regionresized
42967          * Fires when the user resizes a region. 
42968          * @param {Roo.LayoutRegion} region The resized region
42969          * @param {Number} newSize The new size (width for east/west, height for north/south)
42970          */
42971         "regionresized" : true,
42972         /**
42973          * @event regioncollapsed
42974          * Fires when a region is collapsed. 
42975          * @param {Roo.LayoutRegion} region The collapsed region
42976          */
42977         "regioncollapsed" : true,
42978         /**
42979          * @event regionexpanded
42980          * Fires when a region is expanded.  
42981          * @param {Roo.LayoutRegion} region The expanded region
42982          */
42983         "regionexpanded" : true
42984     });
42985     this.updating = false;
42986     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42987 };
42988
42989 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42990     /**
42991      * Returns true if this layout is currently being updated
42992      * @return {Boolean}
42993      */
42994     isUpdating : function(){
42995         return this.updating; 
42996     },
42997     
42998     /**
42999      * Suspend the LayoutManager from doing auto-layouts while
43000      * making multiple add or remove calls
43001      */
43002     beginUpdate : function(){
43003         this.updating = true;    
43004     },
43005     
43006     /**
43007      * Restore auto-layouts and optionally disable the manager from performing a layout
43008      * @param {Boolean} noLayout true to disable a layout update 
43009      */
43010     endUpdate : function(noLayout){
43011         this.updating = false;
43012         if(!noLayout){
43013             this.layout();
43014         }    
43015     },
43016     
43017     layout: function(){
43018         
43019     },
43020     
43021     onRegionResized : function(region, newSize){
43022         this.fireEvent("regionresized", region, newSize);
43023         this.layout();
43024     },
43025     
43026     onRegionCollapsed : function(region){
43027         this.fireEvent("regioncollapsed", region);
43028     },
43029     
43030     onRegionExpanded : function(region){
43031         this.fireEvent("regionexpanded", region);
43032     },
43033         
43034     /**
43035      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43036      * performs box-model adjustments.
43037      * @return {Object} The size as an object {width: (the width), height: (the height)}
43038      */
43039     getViewSize : function(){
43040         var size;
43041         if(this.el.dom != document.body){
43042             size = this.el.getSize();
43043         }else{
43044             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43045         }
43046         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43047         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43048         return size;
43049     },
43050     
43051     /**
43052      * Returns the Element this layout is bound to.
43053      * @return {Roo.Element}
43054      */
43055     getEl : function(){
43056         return this.el;
43057     },
43058     
43059     /**
43060      * Returns the specified region.
43061      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43062      * @return {Roo.LayoutRegion}
43063      */
43064     getRegion : function(target){
43065         return this.regions[target.toLowerCase()];
43066     },
43067     
43068     onWindowResize : function(){
43069         if(this.monitorWindowResize){
43070             this.layout();
43071         }
43072     }
43073 });/*
43074  * Based on:
43075  * Ext JS Library 1.1.1
43076  * Copyright(c) 2006-2007, Ext JS, LLC.
43077  *
43078  * Originally Released Under LGPL - original licence link has changed is not relivant.
43079  *
43080  * Fork - LGPL
43081  * <script type="text/javascript">
43082  */
43083 /**
43084  * @class Roo.BorderLayout
43085  * @extends Roo.LayoutManager
43086  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43087  * please see: <br><br>
43088  * <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>
43089  * <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>
43090  * Example:
43091  <pre><code>
43092  var layout = new Roo.BorderLayout(document.body, {
43093     north: {
43094         initialSize: 25,
43095         titlebar: false
43096     },
43097     west: {
43098         split:true,
43099         initialSize: 200,
43100         minSize: 175,
43101         maxSize: 400,
43102         titlebar: true,
43103         collapsible: true
43104     },
43105     east: {
43106         split:true,
43107         initialSize: 202,
43108         minSize: 175,
43109         maxSize: 400,
43110         titlebar: true,
43111         collapsible: true
43112     },
43113     south: {
43114         split:true,
43115         initialSize: 100,
43116         minSize: 100,
43117         maxSize: 200,
43118         titlebar: true,
43119         collapsible: true
43120     },
43121     center: {
43122         titlebar: true,
43123         autoScroll:true,
43124         resizeTabs: true,
43125         minTabWidth: 50,
43126         preferredTabWidth: 150
43127     }
43128 });
43129
43130 // shorthand
43131 var CP = Roo.ContentPanel;
43132
43133 layout.beginUpdate();
43134 layout.add("north", new CP("north", "North"));
43135 layout.add("south", new CP("south", {title: "South", closable: true}));
43136 layout.add("west", new CP("west", {title: "West"}));
43137 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43138 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43139 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43140 layout.getRegion("center").showPanel("center1");
43141 layout.endUpdate();
43142 </code></pre>
43143
43144 <b>The container the layout is rendered into can be either the body element or any other element.
43145 If it is not the body element, the container needs to either be an absolute positioned element,
43146 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43147 the container size if it is not the body element.</b>
43148
43149 * @constructor
43150 * Create a new BorderLayout
43151 * @param {String/HTMLElement/Element} container The container this layout is bound to
43152 * @param {Object} config Configuration options
43153  */
43154 Roo.BorderLayout = function(container, config){
43155     config = config || {};
43156     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43157     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43158     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43159         var target = this.factory.validRegions[i];
43160         if(config[target]){
43161             this.addRegion(target, config[target]);
43162         }
43163     }
43164 };
43165
43166 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43167     /**
43168      * Creates and adds a new region if it doesn't already exist.
43169      * @param {String} target The target region key (north, south, east, west or center).
43170      * @param {Object} config The regions config object
43171      * @return {BorderLayoutRegion} The new region
43172      */
43173     addRegion : function(target, config){
43174         if(!this.regions[target]){
43175             var r = this.factory.create(target, this, config);
43176             this.bindRegion(target, r);
43177         }
43178         return this.regions[target];
43179     },
43180
43181     // private (kinda)
43182     bindRegion : function(name, r){
43183         this.regions[name] = r;
43184         r.on("visibilitychange", this.layout, this);
43185         r.on("paneladded", this.layout, this);
43186         r.on("panelremoved", this.layout, this);
43187         r.on("invalidated", this.layout, this);
43188         r.on("resized", this.onRegionResized, this);
43189         r.on("collapsed", this.onRegionCollapsed, this);
43190         r.on("expanded", this.onRegionExpanded, this);
43191     },
43192
43193     /**
43194      * Performs a layout update.
43195      */
43196     layout : function(){
43197         if(this.updating) return;
43198         var size = this.getViewSize();
43199         var w = size.width;
43200         var h = size.height;
43201         var centerW = w;
43202         var centerH = h;
43203         var centerY = 0;
43204         var centerX = 0;
43205         //var x = 0, y = 0;
43206
43207         var rs = this.regions;
43208         var north = rs["north"];
43209         var south = rs["south"]; 
43210         var west = rs["west"];
43211         var east = rs["east"];
43212         var center = rs["center"];
43213         //if(this.hideOnLayout){ // not supported anymore
43214             //c.el.setStyle("display", "none");
43215         //}
43216         if(north && north.isVisible()){
43217             var b = north.getBox();
43218             var m = north.getMargins();
43219             b.width = w - (m.left+m.right);
43220             b.x = m.left;
43221             b.y = m.top;
43222             centerY = b.height + b.y + m.bottom;
43223             centerH -= centerY;
43224             north.updateBox(this.safeBox(b));
43225         }
43226         if(south && south.isVisible()){
43227             var b = south.getBox();
43228             var m = south.getMargins();
43229             b.width = w - (m.left+m.right);
43230             b.x = m.left;
43231             var totalHeight = (b.height + m.top + m.bottom);
43232             b.y = h - totalHeight + m.top;
43233             centerH -= totalHeight;
43234             south.updateBox(this.safeBox(b));
43235         }
43236         if(west && west.isVisible()){
43237             var b = west.getBox();
43238             var m = west.getMargins();
43239             b.height = centerH - (m.top+m.bottom);
43240             b.x = m.left;
43241             b.y = centerY + m.top;
43242             var totalWidth = (b.width + m.left + m.right);
43243             centerX += totalWidth;
43244             centerW -= totalWidth;
43245             west.updateBox(this.safeBox(b));
43246         }
43247         if(east && east.isVisible()){
43248             var b = east.getBox();
43249             var m = east.getMargins();
43250             b.height = centerH - (m.top+m.bottom);
43251             var totalWidth = (b.width + m.left + m.right);
43252             b.x = w - totalWidth + m.left;
43253             b.y = centerY + m.top;
43254             centerW -= totalWidth;
43255             east.updateBox(this.safeBox(b));
43256         }
43257         if(center){
43258             var m = center.getMargins();
43259             var centerBox = {
43260                 x: centerX + m.left,
43261                 y: centerY + m.top,
43262                 width: centerW - (m.left+m.right),
43263                 height: centerH - (m.top+m.bottom)
43264             };
43265             //if(this.hideOnLayout){
43266                 //center.el.setStyle("display", "block");
43267             //}
43268             center.updateBox(this.safeBox(centerBox));
43269         }
43270         this.el.repaint();
43271         this.fireEvent("layout", this);
43272     },
43273
43274     // private
43275     safeBox : function(box){
43276         box.width = Math.max(0, box.width);
43277         box.height = Math.max(0, box.height);
43278         return box;
43279     },
43280
43281     /**
43282      * Adds a ContentPanel (or subclass) to this layout.
43283      * @param {String} target The target region key (north, south, east, west or center).
43284      * @param {Roo.ContentPanel} panel The panel to add
43285      * @return {Roo.ContentPanel} The added panel
43286      */
43287     add : function(target, panel){
43288          
43289         target = target.toLowerCase();
43290         return this.regions[target].add(panel);
43291     },
43292
43293     /**
43294      * Remove a ContentPanel (or subclass) to this layout.
43295      * @param {String} target The target region key (north, south, east, west or center).
43296      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43297      * @return {Roo.ContentPanel} The removed panel
43298      */
43299     remove : function(target, panel){
43300         target = target.toLowerCase();
43301         return this.regions[target].remove(panel);
43302     },
43303
43304     /**
43305      * Searches all regions for a panel with the specified id
43306      * @param {String} panelId
43307      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43308      */
43309     findPanel : function(panelId){
43310         var rs = this.regions;
43311         for(var target in rs){
43312             if(typeof rs[target] != "function"){
43313                 var p = rs[target].getPanel(panelId);
43314                 if(p){
43315                     return p;
43316                 }
43317             }
43318         }
43319         return null;
43320     },
43321
43322     /**
43323      * Searches all regions for a panel with the specified id and activates (shows) it.
43324      * @param {String/ContentPanel} panelId The panels id or the panel itself
43325      * @return {Roo.ContentPanel} The shown panel or null
43326      */
43327     showPanel : function(panelId) {
43328       var rs = this.regions;
43329       for(var target in rs){
43330          var r = rs[target];
43331          if(typeof r != "function"){
43332             if(r.hasPanel(panelId)){
43333                return r.showPanel(panelId);
43334             }
43335          }
43336       }
43337       return null;
43338    },
43339
43340    /**
43341      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43342      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43343      */
43344     restoreState : function(provider){
43345         if(!provider){
43346             provider = Roo.state.Manager;
43347         }
43348         var sm = new Roo.LayoutStateManager();
43349         sm.init(this, provider);
43350     },
43351
43352     /**
43353      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43354      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43355      * a valid ContentPanel config object.  Example:
43356      * <pre><code>
43357 // Create the main layout
43358 var layout = new Roo.BorderLayout('main-ct', {
43359     west: {
43360         split:true,
43361         minSize: 175,
43362         titlebar: true
43363     },
43364     center: {
43365         title:'Components'
43366     }
43367 }, 'main-ct');
43368
43369 // Create and add multiple ContentPanels at once via configs
43370 layout.batchAdd({
43371    west: {
43372        id: 'source-files',
43373        autoCreate:true,
43374        title:'Ext Source Files',
43375        autoScroll:true,
43376        fitToFrame:true
43377    },
43378    center : {
43379        el: cview,
43380        autoScroll:true,
43381        fitToFrame:true,
43382        toolbar: tb,
43383        resizeEl:'cbody'
43384    }
43385 });
43386 </code></pre>
43387      * @param {Object} regions An object containing ContentPanel configs by region name
43388      */
43389     batchAdd : function(regions){
43390         this.beginUpdate();
43391         for(var rname in regions){
43392             var lr = this.regions[rname];
43393             if(lr){
43394                 this.addTypedPanels(lr, regions[rname]);
43395             }
43396         }
43397         this.endUpdate();
43398     },
43399
43400     // private
43401     addTypedPanels : function(lr, ps){
43402         if(typeof ps == 'string'){
43403             lr.add(new Roo.ContentPanel(ps));
43404         }
43405         else if(ps instanceof Array){
43406             for(var i =0, len = ps.length; i < len; i++){
43407                 this.addTypedPanels(lr, ps[i]);
43408             }
43409         }
43410         else if(!ps.events){ // raw config?
43411             var el = ps.el;
43412             delete ps.el; // prevent conflict
43413             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43414         }
43415         else {  // panel object assumed!
43416             lr.add(ps);
43417         }
43418     },
43419     /**
43420      * Adds a xtype elements to the layout.
43421      * <pre><code>
43422
43423 layout.addxtype({
43424        xtype : 'ContentPanel',
43425        region: 'west',
43426        items: [ .... ]
43427    }
43428 );
43429
43430 layout.addxtype({
43431         xtype : 'NestedLayoutPanel',
43432         region: 'west',
43433         layout: {
43434            center: { },
43435            west: { }   
43436         },
43437         items : [ ... list of content panels or nested layout panels.. ]
43438    }
43439 );
43440 </code></pre>
43441      * @param {Object} cfg Xtype definition of item to add.
43442      */
43443     addxtype : function(cfg)
43444     {
43445         // basically accepts a pannel...
43446         // can accept a layout region..!?!?
43447        // console.log('BorderLayout add ' + cfg.xtype)
43448         
43449         if (!cfg.xtype.match(/Panel$/)) {
43450             return false;
43451         }
43452         var ret = false;
43453         var region = cfg.region;
43454         delete cfg.region;
43455         
43456           
43457         var xitems = [];
43458         if (cfg.items) {
43459             xitems = cfg.items;
43460             delete cfg.items;
43461         }
43462         
43463         
43464         switch(cfg.xtype) 
43465         {
43466             case 'ContentPanel':  // ContentPanel (el, cfg)
43467             case 'ScrollPanel':  // ContentPanel (el, cfg)
43468                 if(cfg.autoCreate) {
43469                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43470                 } else {
43471                     var el = this.el.createChild();
43472                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43473                 }
43474                 
43475                 this.add(region, ret);
43476                 break;
43477             
43478             
43479             case 'TreePanel': // our new panel!
43480                 cfg.el = this.el.createChild();
43481                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43482                 this.add(region, ret);
43483                 break;
43484             
43485             case 'NestedLayoutPanel': 
43486                 // create a new Layout (which is  a Border Layout...
43487                 var el = this.el.createChild();
43488                 var clayout = cfg.layout;
43489                 delete cfg.layout;
43490                 clayout.items   = clayout.items  || [];
43491                 // replace this exitems with the clayout ones..
43492                 xitems = clayout.items;
43493                  
43494                 
43495                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43496                     cfg.background = false;
43497                 }
43498                 var layout = new Roo.BorderLayout(el, clayout);
43499                 
43500                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43501                 //console.log('adding nested layout panel '  + cfg.toSource());
43502                 this.add(region, ret);
43503                 
43504                 break;
43505                 
43506             case 'GridPanel': 
43507             
43508                 // needs grid and region
43509                 
43510                 //var el = this.getRegion(region).el.createChild();
43511                 var el = this.el.createChild();
43512                 // create the grid first...
43513                 
43514                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43515                 delete cfg.grid;
43516                 if (region == 'center' && this.active ) {
43517                     cfg.background = false;
43518                 }
43519                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43520                 
43521                 this.add(region, ret);
43522                 if (cfg.background) {
43523                     ret.on('activate', function(gp) {
43524                         if (!gp.grid.rendered) {
43525                             gp.grid.render();
43526                         }
43527                     });
43528                 } else {
43529                     grid.render();
43530                 }
43531                 break;
43532            
43533                
43534                 
43535                 
43536             default: 
43537                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43538                 return;
43539              // GridPanel (grid, cfg)
43540             
43541         }
43542         this.beginUpdate();
43543         // add children..
43544         Roo.each(xitems, function(i)  {
43545             ret.addxtype(i);
43546         });
43547         this.endUpdate();
43548         return ret;
43549         
43550     }
43551 });
43552
43553 /**
43554  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43555  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43556  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43557  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43558  * <pre><code>
43559 // shorthand
43560 var CP = Roo.ContentPanel;
43561
43562 var layout = Roo.BorderLayout.create({
43563     north: {
43564         initialSize: 25,
43565         titlebar: false,
43566         panels: [new CP("north", "North")]
43567     },
43568     west: {
43569         split:true,
43570         initialSize: 200,
43571         minSize: 175,
43572         maxSize: 400,
43573         titlebar: true,
43574         collapsible: true,
43575         panels: [new CP("west", {title: "West"})]
43576     },
43577     east: {
43578         split:true,
43579         initialSize: 202,
43580         minSize: 175,
43581         maxSize: 400,
43582         titlebar: true,
43583         collapsible: true,
43584         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43585     },
43586     south: {
43587         split:true,
43588         initialSize: 100,
43589         minSize: 100,
43590         maxSize: 200,
43591         titlebar: true,
43592         collapsible: true,
43593         panels: [new CP("south", {title: "South", closable: true})]
43594     },
43595     center: {
43596         titlebar: true,
43597         autoScroll:true,
43598         resizeTabs: true,
43599         minTabWidth: 50,
43600         preferredTabWidth: 150,
43601         panels: [
43602             new CP("center1", {title: "Close Me", closable: true}),
43603             new CP("center2", {title: "Center Panel", closable: false})
43604         ]
43605     }
43606 }, document.body);
43607
43608 layout.getRegion("center").showPanel("center1");
43609 </code></pre>
43610  * @param config
43611  * @param targetEl
43612  */
43613 Roo.BorderLayout.create = function(config, targetEl){
43614     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43615     layout.beginUpdate();
43616     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43617     for(var j = 0, jlen = regions.length; j < jlen; j++){
43618         var lr = regions[j];
43619         if(layout.regions[lr] && config[lr].panels){
43620             var r = layout.regions[lr];
43621             var ps = config[lr].panels;
43622             layout.addTypedPanels(r, ps);
43623         }
43624     }
43625     layout.endUpdate();
43626     return layout;
43627 };
43628
43629 // private
43630 Roo.BorderLayout.RegionFactory = {
43631     // private
43632     validRegions : ["north","south","east","west","center"],
43633
43634     // private
43635     create : function(target, mgr, config){
43636         target = target.toLowerCase();
43637         if(config.lightweight || config.basic){
43638             return new Roo.BasicLayoutRegion(mgr, config, target);
43639         }
43640         switch(target){
43641             case "north":
43642                 return new Roo.NorthLayoutRegion(mgr, config);
43643             case "south":
43644                 return new Roo.SouthLayoutRegion(mgr, config);
43645             case "east":
43646                 return new Roo.EastLayoutRegion(mgr, config);
43647             case "west":
43648                 return new Roo.WestLayoutRegion(mgr, config);
43649             case "center":
43650                 return new Roo.CenterLayoutRegion(mgr, config);
43651         }
43652         throw 'Layout region "'+target+'" not supported.';
43653     }
43654 };/*
43655  * Based on:
43656  * Ext JS Library 1.1.1
43657  * Copyright(c) 2006-2007, Ext JS, LLC.
43658  *
43659  * Originally Released Under LGPL - original licence link has changed is not relivant.
43660  *
43661  * Fork - LGPL
43662  * <script type="text/javascript">
43663  */
43664  
43665 /**
43666  * @class Roo.BasicLayoutRegion
43667  * @extends Roo.util.Observable
43668  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43669  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43670  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43671  */
43672 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43673     this.mgr = mgr;
43674     this.position  = pos;
43675     this.events = {
43676         /**
43677          * @scope Roo.BasicLayoutRegion
43678          */
43679         
43680         /**
43681          * @event beforeremove
43682          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43683          * @param {Roo.LayoutRegion} this
43684          * @param {Roo.ContentPanel} panel The panel
43685          * @param {Object} e The cancel event object
43686          */
43687         "beforeremove" : true,
43688         /**
43689          * @event invalidated
43690          * Fires when the layout for this region is changed.
43691          * @param {Roo.LayoutRegion} this
43692          */
43693         "invalidated" : true,
43694         /**
43695          * @event visibilitychange
43696          * Fires when this region is shown or hidden 
43697          * @param {Roo.LayoutRegion} this
43698          * @param {Boolean} visibility true or false
43699          */
43700         "visibilitychange" : true,
43701         /**
43702          * @event paneladded
43703          * Fires when a panel is added. 
43704          * @param {Roo.LayoutRegion} this
43705          * @param {Roo.ContentPanel} panel The panel
43706          */
43707         "paneladded" : true,
43708         /**
43709          * @event panelremoved
43710          * Fires when a panel is removed. 
43711          * @param {Roo.LayoutRegion} this
43712          * @param {Roo.ContentPanel} panel The panel
43713          */
43714         "panelremoved" : true,
43715         /**
43716          * @event collapsed
43717          * Fires when this region is collapsed.
43718          * @param {Roo.LayoutRegion} this
43719          */
43720         "collapsed" : true,
43721         /**
43722          * @event expanded
43723          * Fires when this region is expanded.
43724          * @param {Roo.LayoutRegion} this
43725          */
43726         "expanded" : true,
43727         /**
43728          * @event slideshow
43729          * Fires when this region is slid into view.
43730          * @param {Roo.LayoutRegion} this
43731          */
43732         "slideshow" : true,
43733         /**
43734          * @event slidehide
43735          * Fires when this region slides out of view. 
43736          * @param {Roo.LayoutRegion} this
43737          */
43738         "slidehide" : true,
43739         /**
43740          * @event panelactivated
43741          * Fires when a panel is activated. 
43742          * @param {Roo.LayoutRegion} this
43743          * @param {Roo.ContentPanel} panel The activated panel
43744          */
43745         "panelactivated" : true,
43746         /**
43747          * @event resized
43748          * Fires when the user resizes this region. 
43749          * @param {Roo.LayoutRegion} this
43750          * @param {Number} newSize The new size (width for east/west, height for north/south)
43751          */
43752         "resized" : true
43753     };
43754     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43755     this.panels = new Roo.util.MixedCollection();
43756     this.panels.getKey = this.getPanelId.createDelegate(this);
43757     this.box = null;
43758     this.activePanel = null;
43759     // ensure listeners are added...
43760     
43761     if (config.listeners || config.events) {
43762         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43763             listeners : config.listeners || {},
43764             events : config.events || {}
43765         });
43766     }
43767     
43768     if(skipConfig !== true){
43769         this.applyConfig(config);
43770     }
43771 };
43772
43773 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43774     getPanelId : function(p){
43775         return p.getId();
43776     },
43777     
43778     applyConfig : function(config){
43779         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43780         this.config = config;
43781         
43782     },
43783     
43784     /**
43785      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43786      * the width, for horizontal (north, south) the height.
43787      * @param {Number} newSize The new width or height
43788      */
43789     resizeTo : function(newSize){
43790         var el = this.el ? this.el :
43791                  (this.activePanel ? this.activePanel.getEl() : null);
43792         if(el){
43793             switch(this.position){
43794                 case "east":
43795                 case "west":
43796                     el.setWidth(newSize);
43797                     this.fireEvent("resized", this, newSize);
43798                 break;
43799                 case "north":
43800                 case "south":
43801                     el.setHeight(newSize);
43802                     this.fireEvent("resized", this, newSize);
43803                 break;                
43804             }
43805         }
43806     },
43807     
43808     getBox : function(){
43809         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43810     },
43811     
43812     getMargins : function(){
43813         return this.margins;
43814     },
43815     
43816     updateBox : function(box){
43817         this.box = box;
43818         var el = this.activePanel.getEl();
43819         el.dom.style.left = box.x + "px";
43820         el.dom.style.top = box.y + "px";
43821         this.activePanel.setSize(box.width, box.height);
43822     },
43823     
43824     /**
43825      * Returns the container element for this region.
43826      * @return {Roo.Element}
43827      */
43828     getEl : function(){
43829         return this.activePanel;
43830     },
43831     
43832     /**
43833      * Returns true if this region is currently visible.
43834      * @return {Boolean}
43835      */
43836     isVisible : function(){
43837         return this.activePanel ? true : false;
43838     },
43839     
43840     setActivePanel : function(panel){
43841         panel = this.getPanel(panel);
43842         if(this.activePanel && this.activePanel != panel){
43843             this.activePanel.setActiveState(false);
43844             this.activePanel.getEl().setLeftTop(-10000,-10000);
43845         }
43846         this.activePanel = panel;
43847         panel.setActiveState(true);
43848         if(this.box){
43849             panel.setSize(this.box.width, this.box.height);
43850         }
43851         this.fireEvent("panelactivated", this, panel);
43852         this.fireEvent("invalidated");
43853     },
43854     
43855     /**
43856      * Show the specified panel.
43857      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43858      * @return {Roo.ContentPanel} The shown panel or null
43859      */
43860     showPanel : function(panel){
43861         if(panel = this.getPanel(panel)){
43862             this.setActivePanel(panel);
43863         }
43864         return panel;
43865     },
43866     
43867     /**
43868      * Get the active panel for this region.
43869      * @return {Roo.ContentPanel} The active panel or null
43870      */
43871     getActivePanel : function(){
43872         return this.activePanel;
43873     },
43874     
43875     /**
43876      * Add the passed ContentPanel(s)
43877      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43878      * @return {Roo.ContentPanel} The panel added (if only one was added)
43879      */
43880     add : function(panel){
43881         if(arguments.length > 1){
43882             for(var i = 0, len = arguments.length; i < len; i++) {
43883                 this.add(arguments[i]);
43884             }
43885             return null;
43886         }
43887         if(this.hasPanel(panel)){
43888             this.showPanel(panel);
43889             return panel;
43890         }
43891         var el = panel.getEl();
43892         if(el.dom.parentNode != this.mgr.el.dom){
43893             this.mgr.el.dom.appendChild(el.dom);
43894         }
43895         if(panel.setRegion){
43896             panel.setRegion(this);
43897         }
43898         this.panels.add(panel);
43899         el.setStyle("position", "absolute");
43900         if(!panel.background){
43901             this.setActivePanel(panel);
43902             if(this.config.initialSize && this.panels.getCount()==1){
43903                 this.resizeTo(this.config.initialSize);
43904             }
43905         }
43906         this.fireEvent("paneladded", this, panel);
43907         return panel;
43908     },
43909     
43910     /**
43911      * Returns true if the panel is in this region.
43912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43913      * @return {Boolean}
43914      */
43915     hasPanel : function(panel){
43916         if(typeof panel == "object"){ // must be panel obj
43917             panel = panel.getId();
43918         }
43919         return this.getPanel(panel) ? true : false;
43920     },
43921     
43922     /**
43923      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43924      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43925      * @param {Boolean} preservePanel Overrides the config preservePanel option
43926      * @return {Roo.ContentPanel} The panel that was removed
43927      */
43928     remove : function(panel, preservePanel){
43929         panel = this.getPanel(panel);
43930         if(!panel){
43931             return null;
43932         }
43933         var e = {};
43934         this.fireEvent("beforeremove", this, panel, e);
43935         if(e.cancel === true){
43936             return null;
43937         }
43938         var panelId = panel.getId();
43939         this.panels.removeKey(panelId);
43940         return panel;
43941     },
43942     
43943     /**
43944      * Returns the panel specified or null if it's not in this region.
43945      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43946      * @return {Roo.ContentPanel}
43947      */
43948     getPanel : function(id){
43949         if(typeof id == "object"){ // must be panel obj
43950             return id;
43951         }
43952         return this.panels.get(id);
43953     },
43954     
43955     /**
43956      * Returns this regions position (north/south/east/west/center).
43957      * @return {String} 
43958      */
43959     getPosition: function(){
43960         return this.position;    
43961     }
43962 });/*
43963  * Based on:
43964  * Ext JS Library 1.1.1
43965  * Copyright(c) 2006-2007, Ext JS, LLC.
43966  *
43967  * Originally Released Under LGPL - original licence link has changed is not relivant.
43968  *
43969  * Fork - LGPL
43970  * <script type="text/javascript">
43971  */
43972  
43973 /**
43974  * @class Roo.LayoutRegion
43975  * @extends Roo.BasicLayoutRegion
43976  * This class represents a region in a layout manager.
43977  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43978  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43979  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43980  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43981  * @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})
43982  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43983  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43984  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43985  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43986  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43987  * @cfg {String} title The title for the region (overrides panel titles)
43988  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43989  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43990  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43991  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43992  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43993  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43994  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43995  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43996  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43997  * @cfg {Boolean} showPin True to show a pin button
43998 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43999 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44000 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44001 * @cfg {Number} width  For East/West panels
44002 * @cfg {Number} height For North/South panels
44003 * @cfg {Boolean} split To show the splitter
44004  */
44005 Roo.LayoutRegion = function(mgr, config, pos){
44006     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44007     var dh = Roo.DomHelper;
44008     /** This region's container element 
44009     * @type Roo.Element */
44010     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44011     /** This region's title element 
44012     * @type Roo.Element */
44013
44014     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44015         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44016         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44017     ]}, true);
44018     this.titleEl.enableDisplayMode();
44019     /** This region's title text element 
44020     * @type HTMLElement */
44021     this.titleTextEl = this.titleEl.dom.firstChild;
44022     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44023     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44024     this.closeBtn.enableDisplayMode();
44025     this.closeBtn.on("click", this.closeClicked, this);
44026     this.closeBtn.hide();
44027
44028     this.createBody(config);
44029     this.visible = true;
44030     this.collapsed = false;
44031
44032     if(config.hideWhenEmpty){
44033         this.hide();
44034         this.on("paneladded", this.validateVisibility, this);
44035         this.on("panelremoved", this.validateVisibility, this);
44036     }
44037     this.applyConfig(config);
44038 };
44039
44040 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44041
44042     createBody : function(){
44043         /** This region's body element 
44044         * @type Roo.Element */
44045         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44046     },
44047
44048     applyConfig : function(c){
44049         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44050             var dh = Roo.DomHelper;
44051             if(c.titlebar !== false){
44052                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44053                 this.collapseBtn.on("click", this.collapse, this);
44054                 this.collapseBtn.enableDisplayMode();
44055
44056                 if(c.showPin === true || this.showPin){
44057                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44058                     this.stickBtn.enableDisplayMode();
44059                     this.stickBtn.on("click", this.expand, this);
44060                     this.stickBtn.hide();
44061                 }
44062             }
44063             /** This region's collapsed element
44064             * @type Roo.Element */
44065             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44066                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44067             ]}, true);
44068             if(c.floatable !== false){
44069                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44070                this.collapsedEl.on("click", this.collapseClick, this);
44071             }
44072
44073             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44074                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44075                    id: "message", unselectable: "on", style:{"float":"left"}});
44076                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44077              }
44078             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44079             this.expandBtn.on("click", this.expand, this);
44080         }
44081         if(this.collapseBtn){
44082             this.collapseBtn.setVisible(c.collapsible == true);
44083         }
44084         this.cmargins = c.cmargins || this.cmargins ||
44085                          (this.position == "west" || this.position == "east" ?
44086                              {top: 0, left: 2, right:2, bottom: 0} :
44087                              {top: 2, left: 0, right:0, bottom: 2});
44088         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44089         this.bottomTabs = c.tabPosition != "top";
44090         this.autoScroll = c.autoScroll || false;
44091         if(this.autoScroll){
44092             this.bodyEl.setStyle("overflow", "auto");
44093         }else{
44094             this.bodyEl.setStyle("overflow", "hidden");
44095         }
44096         //if(c.titlebar !== false){
44097             if((!c.titlebar && !c.title) || c.titlebar === false){
44098                 this.titleEl.hide();
44099             }else{
44100                 this.titleEl.show();
44101                 if(c.title){
44102                     this.titleTextEl.innerHTML = c.title;
44103                 }
44104             }
44105         //}
44106         this.duration = c.duration || .30;
44107         this.slideDuration = c.slideDuration || .45;
44108         this.config = c;
44109         if(c.collapsed){
44110             this.collapse(true);
44111         }
44112         if(c.hidden){
44113             this.hide();
44114         }
44115     },
44116     /**
44117      * Returns true if this region is currently visible.
44118      * @return {Boolean}
44119      */
44120     isVisible : function(){
44121         return this.visible;
44122     },
44123
44124     /**
44125      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44126      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44127      */
44128     setCollapsedTitle : function(title){
44129         title = title || "&#160;";
44130         if(this.collapsedTitleTextEl){
44131             this.collapsedTitleTextEl.innerHTML = title;
44132         }
44133     },
44134
44135     getBox : function(){
44136         var b;
44137         if(!this.collapsed){
44138             b = this.el.getBox(false, true);
44139         }else{
44140             b = this.collapsedEl.getBox(false, true);
44141         }
44142         return b;
44143     },
44144
44145     getMargins : function(){
44146         return this.collapsed ? this.cmargins : this.margins;
44147     },
44148
44149     highlight : function(){
44150         this.el.addClass("x-layout-panel-dragover");
44151     },
44152
44153     unhighlight : function(){
44154         this.el.removeClass("x-layout-panel-dragover");
44155     },
44156
44157     updateBox : function(box){
44158         this.box = box;
44159         if(!this.collapsed){
44160             this.el.dom.style.left = box.x + "px";
44161             this.el.dom.style.top = box.y + "px";
44162             this.updateBody(box.width, box.height);
44163         }else{
44164             this.collapsedEl.dom.style.left = box.x + "px";
44165             this.collapsedEl.dom.style.top = box.y + "px";
44166             this.collapsedEl.setSize(box.width, box.height);
44167         }
44168         if(this.tabs){
44169             this.tabs.autoSizeTabs();
44170         }
44171     },
44172
44173     updateBody : function(w, h){
44174         if(w !== null){
44175             this.el.setWidth(w);
44176             w -= this.el.getBorderWidth("rl");
44177             if(this.config.adjustments){
44178                 w += this.config.adjustments[0];
44179             }
44180         }
44181         if(h !== null){
44182             this.el.setHeight(h);
44183             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44184             h -= this.el.getBorderWidth("tb");
44185             if(this.config.adjustments){
44186                 h += this.config.adjustments[1];
44187             }
44188             this.bodyEl.setHeight(h);
44189             if(this.tabs){
44190                 h = this.tabs.syncHeight(h);
44191             }
44192         }
44193         if(this.panelSize){
44194             w = w !== null ? w : this.panelSize.width;
44195             h = h !== null ? h : this.panelSize.height;
44196         }
44197         if(this.activePanel){
44198             var el = this.activePanel.getEl();
44199             w = w !== null ? w : el.getWidth();
44200             h = h !== null ? h : el.getHeight();
44201             this.panelSize = {width: w, height: h};
44202             this.activePanel.setSize(w, h);
44203         }
44204         if(Roo.isIE && this.tabs){
44205             this.tabs.el.repaint();
44206         }
44207     },
44208
44209     /**
44210      * Returns the container element for this region.
44211      * @return {Roo.Element}
44212      */
44213     getEl : function(){
44214         return this.el;
44215     },
44216
44217     /**
44218      * Hides this region.
44219      */
44220     hide : function(){
44221         if(!this.collapsed){
44222             this.el.dom.style.left = "-2000px";
44223             this.el.hide();
44224         }else{
44225             this.collapsedEl.dom.style.left = "-2000px";
44226             this.collapsedEl.hide();
44227         }
44228         this.visible = false;
44229         this.fireEvent("visibilitychange", this, false);
44230     },
44231
44232     /**
44233      * Shows this region if it was previously hidden.
44234      */
44235     show : function(){
44236         if(!this.collapsed){
44237             this.el.show();
44238         }else{
44239             this.collapsedEl.show();
44240         }
44241         this.visible = true;
44242         this.fireEvent("visibilitychange", this, true);
44243     },
44244
44245     closeClicked : function(){
44246         if(this.activePanel){
44247             this.remove(this.activePanel);
44248         }
44249     },
44250
44251     collapseClick : function(e){
44252         if(this.isSlid){
44253            e.stopPropagation();
44254            this.slideIn();
44255         }else{
44256            e.stopPropagation();
44257            this.slideOut();
44258         }
44259     },
44260
44261     /**
44262      * Collapses this region.
44263      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44264      */
44265     collapse : function(skipAnim){
44266         if(this.collapsed) return;
44267         this.collapsed = true;
44268         if(this.split){
44269             this.split.el.hide();
44270         }
44271         if(this.config.animate && skipAnim !== true){
44272             this.fireEvent("invalidated", this);
44273             this.animateCollapse();
44274         }else{
44275             this.el.setLocation(-20000,-20000);
44276             this.el.hide();
44277             this.collapsedEl.show();
44278             this.fireEvent("collapsed", this);
44279             this.fireEvent("invalidated", this);
44280         }
44281     },
44282
44283     animateCollapse : function(){
44284         // overridden
44285     },
44286
44287     /**
44288      * Expands this region if it was previously collapsed.
44289      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44290      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44291      */
44292     expand : function(e, skipAnim){
44293         if(e) e.stopPropagation();
44294         if(!this.collapsed || this.el.hasActiveFx()) return;
44295         if(this.isSlid){
44296             this.afterSlideIn();
44297             skipAnim = true;
44298         }
44299         this.collapsed = false;
44300         if(this.config.animate && skipAnim !== true){
44301             this.animateExpand();
44302         }else{
44303             this.el.show();
44304             if(this.split){
44305                 this.split.el.show();
44306             }
44307             this.collapsedEl.setLocation(-2000,-2000);
44308             this.collapsedEl.hide();
44309             this.fireEvent("invalidated", this);
44310             this.fireEvent("expanded", this);
44311         }
44312     },
44313
44314     animateExpand : function(){
44315         // overridden
44316     },
44317
44318     initTabs : function(){
44319         this.bodyEl.setStyle("overflow", "hidden");
44320         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44321             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44322             disableTooltips: this.config.disableTabTips
44323         });
44324         if(this.config.hideTabs){
44325             ts.stripWrap.setDisplayed(false);
44326         }
44327         this.tabs = ts;
44328         ts.resizeTabs = this.config.resizeTabs === true;
44329         ts.minTabWidth = this.config.minTabWidth || 40;
44330         ts.maxTabWidth = this.config.maxTabWidth || 250;
44331         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44332         ts.monitorResize = false;
44333         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44334         ts.bodyEl.addClass('x-layout-tabs-body');
44335         this.panels.each(this.initPanelAsTab, this);
44336     },
44337
44338     initPanelAsTab : function(panel){
44339         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44340                     this.config.closeOnTab && panel.isClosable());
44341         if(panel.tabTip !== undefined){
44342             ti.setTooltip(panel.tabTip);
44343         }
44344         ti.on("activate", function(){
44345               this.setActivePanel(panel);
44346         }, this);
44347         if(this.config.closeOnTab){
44348             ti.on("beforeclose", function(t, e){
44349                 e.cancel = true;
44350                 this.remove(panel);
44351             }, this);
44352         }
44353         return ti;
44354     },
44355
44356     updatePanelTitle : function(panel, title){
44357         if(this.activePanel == panel){
44358             this.updateTitle(title);
44359         }
44360         if(this.tabs){
44361             var ti = this.tabs.getTab(panel.getEl().id);
44362             ti.setText(title);
44363             if(panel.tabTip !== undefined){
44364                 ti.setTooltip(panel.tabTip);
44365             }
44366         }
44367     },
44368
44369     updateTitle : function(title){
44370         if(this.titleTextEl && !this.config.title){
44371             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44372         }
44373     },
44374
44375     setActivePanel : function(panel){
44376         panel = this.getPanel(panel);
44377         if(this.activePanel && this.activePanel != panel){
44378             this.activePanel.setActiveState(false);
44379         }
44380         this.activePanel = panel;
44381         panel.setActiveState(true);
44382         if(this.panelSize){
44383             panel.setSize(this.panelSize.width, this.panelSize.height);
44384         }
44385         if(this.closeBtn){
44386             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44387         }
44388         this.updateTitle(panel.getTitle());
44389         if(this.tabs){
44390             this.fireEvent("invalidated", this);
44391         }
44392         this.fireEvent("panelactivated", this, panel);
44393     },
44394
44395     /**
44396      * Shows the specified panel.
44397      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44398      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44399      */
44400     showPanel : function(panel){
44401         if(panel = this.getPanel(panel)){
44402             if(this.tabs){
44403                 var tab = this.tabs.getTab(panel.getEl().id);
44404                 if(tab.isHidden()){
44405                     this.tabs.unhideTab(tab.id);
44406                 }
44407                 tab.activate();
44408             }else{
44409                 this.setActivePanel(panel);
44410             }
44411         }
44412         return panel;
44413     },
44414
44415     /**
44416      * Get the active panel for this region.
44417      * @return {Roo.ContentPanel} The active panel or null
44418      */
44419     getActivePanel : function(){
44420         return this.activePanel;
44421     },
44422
44423     validateVisibility : function(){
44424         if(this.panels.getCount() < 1){
44425             this.updateTitle("&#160;");
44426             this.closeBtn.hide();
44427             this.hide();
44428         }else{
44429             if(!this.isVisible()){
44430                 this.show();
44431             }
44432         }
44433     },
44434
44435     /**
44436      * Adds the passed ContentPanel(s) to this region.
44437      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44438      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44439      */
44440     add : function(panel){
44441         if(arguments.length > 1){
44442             for(var i = 0, len = arguments.length; i < len; i++) {
44443                 this.add(arguments[i]);
44444             }
44445             return null;
44446         }
44447         if(this.hasPanel(panel)){
44448             this.showPanel(panel);
44449             return panel;
44450         }
44451         panel.setRegion(this);
44452         this.panels.add(panel);
44453         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44454             this.bodyEl.dom.appendChild(panel.getEl().dom);
44455             if(panel.background !== true){
44456                 this.setActivePanel(panel);
44457             }
44458             this.fireEvent("paneladded", this, panel);
44459             return panel;
44460         }
44461         if(!this.tabs){
44462             this.initTabs();
44463         }else{
44464             this.initPanelAsTab(panel);
44465         }
44466         if(panel.background !== true){
44467             this.tabs.activate(panel.getEl().id);
44468         }
44469         this.fireEvent("paneladded", this, panel);
44470         return panel;
44471     },
44472
44473     /**
44474      * Hides the tab for the specified panel.
44475      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44476      */
44477     hidePanel : function(panel){
44478         if(this.tabs && (panel = this.getPanel(panel))){
44479             this.tabs.hideTab(panel.getEl().id);
44480         }
44481     },
44482
44483     /**
44484      * Unhides the tab for a previously hidden panel.
44485      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44486      */
44487     unhidePanel : function(panel){
44488         if(this.tabs && (panel = this.getPanel(panel))){
44489             this.tabs.unhideTab(panel.getEl().id);
44490         }
44491     },
44492
44493     clearPanels : function(){
44494         while(this.panels.getCount() > 0){
44495              this.remove(this.panels.first());
44496         }
44497     },
44498
44499     /**
44500      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44501      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44502      * @param {Boolean} preservePanel Overrides the config preservePanel option
44503      * @return {Roo.ContentPanel} The panel that was removed
44504      */
44505     remove : function(panel, preservePanel){
44506         panel = this.getPanel(panel);
44507         if(!panel){
44508             return null;
44509         }
44510         var e = {};
44511         this.fireEvent("beforeremove", this, panel, e);
44512         if(e.cancel === true){
44513             return null;
44514         }
44515         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44516         var panelId = panel.getId();
44517         this.panels.removeKey(panelId);
44518         if(preservePanel){
44519             document.body.appendChild(panel.getEl().dom);
44520         }
44521         if(this.tabs){
44522             this.tabs.removeTab(panel.getEl().id);
44523         }else if (!preservePanel){
44524             this.bodyEl.dom.removeChild(panel.getEl().dom);
44525         }
44526         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44527             var p = this.panels.first();
44528             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44529             tempEl.appendChild(p.getEl().dom);
44530             this.bodyEl.update("");
44531             this.bodyEl.dom.appendChild(p.getEl().dom);
44532             tempEl = null;
44533             this.updateTitle(p.getTitle());
44534             this.tabs = null;
44535             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44536             this.setActivePanel(p);
44537         }
44538         panel.setRegion(null);
44539         if(this.activePanel == panel){
44540             this.activePanel = null;
44541         }
44542         if(this.config.autoDestroy !== false && preservePanel !== true){
44543             try{panel.destroy();}catch(e){}
44544         }
44545         this.fireEvent("panelremoved", this, panel);
44546         return panel;
44547     },
44548
44549     /**
44550      * Returns the TabPanel component used by this region
44551      * @return {Roo.TabPanel}
44552      */
44553     getTabs : function(){
44554         return this.tabs;
44555     },
44556
44557     createTool : function(parentEl, className){
44558         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44559             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44560         btn.addClassOnOver("x-layout-tools-button-over");
44561         return btn;
44562     }
44563 });/*
44564  * Based on:
44565  * Ext JS Library 1.1.1
44566  * Copyright(c) 2006-2007, Ext JS, LLC.
44567  *
44568  * Originally Released Under LGPL - original licence link has changed is not relivant.
44569  *
44570  * Fork - LGPL
44571  * <script type="text/javascript">
44572  */
44573  
44574
44575
44576 /**
44577  * @class Roo.SplitLayoutRegion
44578  * @extends Roo.LayoutRegion
44579  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44580  */
44581 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44582     this.cursor = cursor;
44583     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44584 };
44585
44586 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44587     splitTip : "Drag to resize.",
44588     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44589     useSplitTips : false,
44590
44591     applyConfig : function(config){
44592         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44593         if(config.split){
44594             if(!this.split){
44595                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44596                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44597                 /** The SplitBar for this region 
44598                 * @type Roo.SplitBar */
44599                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44600                 this.split.on("moved", this.onSplitMove, this);
44601                 this.split.useShim = config.useShim === true;
44602                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44603                 if(this.useSplitTips){
44604                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44605                 }
44606                 if(config.collapsible){
44607                     this.split.el.on("dblclick", this.collapse,  this);
44608                 }
44609             }
44610             if(typeof config.minSize != "undefined"){
44611                 this.split.minSize = config.minSize;
44612             }
44613             if(typeof config.maxSize != "undefined"){
44614                 this.split.maxSize = config.maxSize;
44615             }
44616             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44617                 this.hideSplitter();
44618             }
44619         }
44620     },
44621
44622     getHMaxSize : function(){
44623          var cmax = this.config.maxSize || 10000;
44624          var center = this.mgr.getRegion("center");
44625          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44626     },
44627
44628     getVMaxSize : function(){
44629          var cmax = this.config.maxSize || 10000;
44630          var center = this.mgr.getRegion("center");
44631          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44632     },
44633
44634     onSplitMove : function(split, newSize){
44635         this.fireEvent("resized", this, newSize);
44636     },
44637     
44638     /** 
44639      * Returns the {@link Roo.SplitBar} for this region.
44640      * @return {Roo.SplitBar}
44641      */
44642     getSplitBar : function(){
44643         return this.split;
44644     },
44645     
44646     hide : function(){
44647         this.hideSplitter();
44648         Roo.SplitLayoutRegion.superclass.hide.call(this);
44649     },
44650
44651     hideSplitter : function(){
44652         if(this.split){
44653             this.split.el.setLocation(-2000,-2000);
44654             this.split.el.hide();
44655         }
44656     },
44657
44658     show : function(){
44659         if(this.split){
44660             this.split.el.show();
44661         }
44662         Roo.SplitLayoutRegion.superclass.show.call(this);
44663     },
44664     
44665     beforeSlide: function(){
44666         if(Roo.isGecko){// firefox overflow auto bug workaround
44667             this.bodyEl.clip();
44668             if(this.tabs) this.tabs.bodyEl.clip();
44669             if(this.activePanel){
44670                 this.activePanel.getEl().clip();
44671                 
44672                 if(this.activePanel.beforeSlide){
44673                     this.activePanel.beforeSlide();
44674                 }
44675             }
44676         }
44677     },
44678     
44679     afterSlide : function(){
44680         if(Roo.isGecko){// firefox overflow auto bug workaround
44681             this.bodyEl.unclip();
44682             if(this.tabs) this.tabs.bodyEl.unclip();
44683             if(this.activePanel){
44684                 this.activePanel.getEl().unclip();
44685                 if(this.activePanel.afterSlide){
44686                     this.activePanel.afterSlide();
44687                 }
44688             }
44689         }
44690     },
44691
44692     initAutoHide : function(){
44693         if(this.autoHide !== false){
44694             if(!this.autoHideHd){
44695                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44696                 this.autoHideHd = {
44697                     "mouseout": function(e){
44698                         if(!e.within(this.el, true)){
44699                             st.delay(500);
44700                         }
44701                     },
44702                     "mouseover" : function(e){
44703                         st.cancel();
44704                     },
44705                     scope : this
44706                 };
44707             }
44708             this.el.on(this.autoHideHd);
44709         }
44710     },
44711
44712     clearAutoHide : function(){
44713         if(this.autoHide !== false){
44714             this.el.un("mouseout", this.autoHideHd.mouseout);
44715             this.el.un("mouseover", this.autoHideHd.mouseover);
44716         }
44717     },
44718
44719     clearMonitor : function(){
44720         Roo.get(document).un("click", this.slideInIf, this);
44721     },
44722
44723     // these names are backwards but not changed for compat
44724     slideOut : function(){
44725         if(this.isSlid || this.el.hasActiveFx()){
44726             return;
44727         }
44728         this.isSlid = true;
44729         if(this.collapseBtn){
44730             this.collapseBtn.hide();
44731         }
44732         this.closeBtnState = this.closeBtn.getStyle('display');
44733         this.closeBtn.hide();
44734         if(this.stickBtn){
44735             this.stickBtn.show();
44736         }
44737         this.el.show();
44738         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44739         this.beforeSlide();
44740         this.el.setStyle("z-index", 10001);
44741         this.el.slideIn(this.getSlideAnchor(), {
44742             callback: function(){
44743                 this.afterSlide();
44744                 this.initAutoHide();
44745                 Roo.get(document).on("click", this.slideInIf, this);
44746                 this.fireEvent("slideshow", this);
44747             },
44748             scope: this,
44749             block: true
44750         });
44751     },
44752
44753     afterSlideIn : function(){
44754         this.clearAutoHide();
44755         this.isSlid = false;
44756         this.clearMonitor();
44757         this.el.setStyle("z-index", "");
44758         if(this.collapseBtn){
44759             this.collapseBtn.show();
44760         }
44761         this.closeBtn.setStyle('display', this.closeBtnState);
44762         if(this.stickBtn){
44763             this.stickBtn.hide();
44764         }
44765         this.fireEvent("slidehide", this);
44766     },
44767
44768     slideIn : function(cb){
44769         if(!this.isSlid || this.el.hasActiveFx()){
44770             Roo.callback(cb);
44771             return;
44772         }
44773         this.isSlid = false;
44774         this.beforeSlide();
44775         this.el.slideOut(this.getSlideAnchor(), {
44776             callback: function(){
44777                 this.el.setLeftTop(-10000, -10000);
44778                 this.afterSlide();
44779                 this.afterSlideIn();
44780                 Roo.callback(cb);
44781             },
44782             scope: this,
44783             block: true
44784         });
44785     },
44786     
44787     slideInIf : function(e){
44788         if(!e.within(this.el)){
44789             this.slideIn();
44790         }
44791     },
44792
44793     animateCollapse : function(){
44794         this.beforeSlide();
44795         this.el.setStyle("z-index", 20000);
44796         var anchor = this.getSlideAnchor();
44797         this.el.slideOut(anchor, {
44798             callback : function(){
44799                 this.el.setStyle("z-index", "");
44800                 this.collapsedEl.slideIn(anchor, {duration:.3});
44801                 this.afterSlide();
44802                 this.el.setLocation(-10000,-10000);
44803                 this.el.hide();
44804                 this.fireEvent("collapsed", this);
44805             },
44806             scope: this,
44807             block: true
44808         });
44809     },
44810
44811     animateExpand : function(){
44812         this.beforeSlide();
44813         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44814         this.el.setStyle("z-index", 20000);
44815         this.collapsedEl.hide({
44816             duration:.1
44817         });
44818         this.el.slideIn(this.getSlideAnchor(), {
44819             callback : function(){
44820                 this.el.setStyle("z-index", "");
44821                 this.afterSlide();
44822                 if(this.split){
44823                     this.split.el.show();
44824                 }
44825                 this.fireEvent("invalidated", this);
44826                 this.fireEvent("expanded", this);
44827             },
44828             scope: this,
44829             block: true
44830         });
44831     },
44832
44833     anchors : {
44834         "west" : "left",
44835         "east" : "right",
44836         "north" : "top",
44837         "south" : "bottom"
44838     },
44839
44840     sanchors : {
44841         "west" : "l",
44842         "east" : "r",
44843         "north" : "t",
44844         "south" : "b"
44845     },
44846
44847     canchors : {
44848         "west" : "tl-tr",
44849         "east" : "tr-tl",
44850         "north" : "tl-bl",
44851         "south" : "bl-tl"
44852     },
44853
44854     getAnchor : function(){
44855         return this.anchors[this.position];
44856     },
44857
44858     getCollapseAnchor : function(){
44859         return this.canchors[this.position];
44860     },
44861
44862     getSlideAnchor : function(){
44863         return this.sanchors[this.position];
44864     },
44865
44866     getAlignAdj : function(){
44867         var cm = this.cmargins;
44868         switch(this.position){
44869             case "west":
44870                 return [0, 0];
44871             break;
44872             case "east":
44873                 return [0, 0];
44874             break;
44875             case "north":
44876                 return [0, 0];
44877             break;
44878             case "south":
44879                 return [0, 0];
44880             break;
44881         }
44882     },
44883
44884     getExpandAdj : function(){
44885         var c = this.collapsedEl, cm = this.cmargins;
44886         switch(this.position){
44887             case "west":
44888                 return [-(cm.right+c.getWidth()+cm.left), 0];
44889             break;
44890             case "east":
44891                 return [cm.right+c.getWidth()+cm.left, 0];
44892             break;
44893             case "north":
44894                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44895             break;
44896             case "south":
44897                 return [0, cm.top+cm.bottom+c.getHeight()];
44898             break;
44899         }
44900     }
44901 });/*
44902  * Based on:
44903  * Ext JS Library 1.1.1
44904  * Copyright(c) 2006-2007, Ext JS, LLC.
44905  *
44906  * Originally Released Under LGPL - original licence link has changed is not relivant.
44907  *
44908  * Fork - LGPL
44909  * <script type="text/javascript">
44910  */
44911 /*
44912  * These classes are private internal classes
44913  */
44914 Roo.CenterLayoutRegion = function(mgr, config){
44915     Roo.LayoutRegion.call(this, mgr, config, "center");
44916     this.visible = true;
44917     this.minWidth = config.minWidth || 20;
44918     this.minHeight = config.minHeight || 20;
44919 };
44920
44921 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44922     hide : function(){
44923         // center panel can't be hidden
44924     },
44925     
44926     show : function(){
44927         // center panel can't be hidden
44928     },
44929     
44930     getMinWidth: function(){
44931         return this.minWidth;
44932     },
44933     
44934     getMinHeight: function(){
44935         return this.minHeight;
44936     }
44937 });
44938
44939
44940 Roo.NorthLayoutRegion = function(mgr, config){
44941     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44942     if(this.split){
44943         this.split.placement = Roo.SplitBar.TOP;
44944         this.split.orientation = Roo.SplitBar.VERTICAL;
44945         this.split.el.addClass("x-layout-split-v");
44946     }
44947     var size = config.initialSize || config.height;
44948     if(typeof size != "undefined"){
44949         this.el.setHeight(size);
44950     }
44951 };
44952 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44953     orientation: Roo.SplitBar.VERTICAL,
44954     getBox : function(){
44955         if(this.collapsed){
44956             return this.collapsedEl.getBox();
44957         }
44958         var box = this.el.getBox();
44959         if(this.split){
44960             box.height += this.split.el.getHeight();
44961         }
44962         return box;
44963     },
44964     
44965     updateBox : function(box){
44966         if(this.split && !this.collapsed){
44967             box.height -= this.split.el.getHeight();
44968             this.split.el.setLeft(box.x);
44969             this.split.el.setTop(box.y+box.height);
44970             this.split.el.setWidth(box.width);
44971         }
44972         if(this.collapsed){
44973             this.updateBody(box.width, null);
44974         }
44975         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44976     }
44977 });
44978
44979 Roo.SouthLayoutRegion = function(mgr, config){
44980     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44981     if(this.split){
44982         this.split.placement = Roo.SplitBar.BOTTOM;
44983         this.split.orientation = Roo.SplitBar.VERTICAL;
44984         this.split.el.addClass("x-layout-split-v");
44985     }
44986     var size = config.initialSize || config.height;
44987     if(typeof size != "undefined"){
44988         this.el.setHeight(size);
44989     }
44990 };
44991 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44992     orientation: Roo.SplitBar.VERTICAL,
44993     getBox : function(){
44994         if(this.collapsed){
44995             return this.collapsedEl.getBox();
44996         }
44997         var box = this.el.getBox();
44998         if(this.split){
44999             var sh = this.split.el.getHeight();
45000             box.height += sh;
45001             box.y -= sh;
45002         }
45003         return box;
45004     },
45005     
45006     updateBox : function(box){
45007         if(this.split && !this.collapsed){
45008             var sh = this.split.el.getHeight();
45009             box.height -= sh;
45010             box.y += sh;
45011             this.split.el.setLeft(box.x);
45012             this.split.el.setTop(box.y-sh);
45013             this.split.el.setWidth(box.width);
45014         }
45015         if(this.collapsed){
45016             this.updateBody(box.width, null);
45017         }
45018         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45019     }
45020 });
45021
45022 Roo.EastLayoutRegion = function(mgr, config){
45023     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45024     if(this.split){
45025         this.split.placement = Roo.SplitBar.RIGHT;
45026         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45027         this.split.el.addClass("x-layout-split-h");
45028     }
45029     var size = config.initialSize || config.width;
45030     if(typeof size != "undefined"){
45031         this.el.setWidth(size);
45032     }
45033 };
45034 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45035     orientation: Roo.SplitBar.HORIZONTAL,
45036     getBox : function(){
45037         if(this.collapsed){
45038             return this.collapsedEl.getBox();
45039         }
45040         var box = this.el.getBox();
45041         if(this.split){
45042             var sw = this.split.el.getWidth();
45043             box.width += sw;
45044             box.x -= sw;
45045         }
45046         return box;
45047     },
45048
45049     updateBox : function(box){
45050         if(this.split && !this.collapsed){
45051             var sw = this.split.el.getWidth();
45052             box.width -= sw;
45053             this.split.el.setLeft(box.x);
45054             this.split.el.setTop(box.y);
45055             this.split.el.setHeight(box.height);
45056             box.x += sw;
45057         }
45058         if(this.collapsed){
45059             this.updateBody(null, box.height);
45060         }
45061         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45062     }
45063 });
45064
45065 Roo.WestLayoutRegion = function(mgr, config){
45066     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45067     if(this.split){
45068         this.split.placement = Roo.SplitBar.LEFT;
45069         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45070         this.split.el.addClass("x-layout-split-h");
45071     }
45072     var size = config.initialSize || config.width;
45073     if(typeof size != "undefined"){
45074         this.el.setWidth(size);
45075     }
45076 };
45077 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45078     orientation: Roo.SplitBar.HORIZONTAL,
45079     getBox : function(){
45080         if(this.collapsed){
45081             return this.collapsedEl.getBox();
45082         }
45083         var box = this.el.getBox();
45084         if(this.split){
45085             box.width += this.split.el.getWidth();
45086         }
45087         return box;
45088     },
45089     
45090     updateBox : function(box){
45091         if(this.split && !this.collapsed){
45092             var sw = this.split.el.getWidth();
45093             box.width -= sw;
45094             this.split.el.setLeft(box.x+box.width);
45095             this.split.el.setTop(box.y);
45096             this.split.el.setHeight(box.height);
45097         }
45098         if(this.collapsed){
45099             this.updateBody(null, box.height);
45100         }
45101         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45102     }
45103 });
45104 /*
45105  * Based on:
45106  * Ext JS Library 1.1.1
45107  * Copyright(c) 2006-2007, Ext JS, LLC.
45108  *
45109  * Originally Released Under LGPL - original licence link has changed is not relivant.
45110  *
45111  * Fork - LGPL
45112  * <script type="text/javascript">
45113  */
45114  
45115  
45116 /*
45117  * Private internal class for reading and applying state
45118  */
45119 Roo.LayoutStateManager = function(layout){
45120      // default empty state
45121      this.state = {
45122         north: {},
45123         south: {},
45124         east: {},
45125         west: {}       
45126     };
45127 };
45128
45129 Roo.LayoutStateManager.prototype = {
45130     init : function(layout, provider){
45131         this.provider = provider;
45132         var state = provider.get(layout.id+"-layout-state");
45133         if(state){
45134             var wasUpdating = layout.isUpdating();
45135             if(!wasUpdating){
45136                 layout.beginUpdate();
45137             }
45138             for(var key in state){
45139                 if(typeof state[key] != "function"){
45140                     var rstate = state[key];
45141                     var r = layout.getRegion(key);
45142                     if(r && rstate){
45143                         if(rstate.size){
45144                             r.resizeTo(rstate.size);
45145                         }
45146                         if(rstate.collapsed == true){
45147                             r.collapse(true);
45148                         }else{
45149                             r.expand(null, true);
45150                         }
45151                     }
45152                 }
45153             }
45154             if(!wasUpdating){
45155                 layout.endUpdate();
45156             }
45157             this.state = state; 
45158         }
45159         this.layout = layout;
45160         layout.on("regionresized", this.onRegionResized, this);
45161         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45162         layout.on("regionexpanded", this.onRegionExpanded, this);
45163     },
45164     
45165     storeState : function(){
45166         this.provider.set(this.layout.id+"-layout-state", this.state);
45167     },
45168     
45169     onRegionResized : function(region, newSize){
45170         this.state[region.getPosition()].size = newSize;
45171         this.storeState();
45172     },
45173     
45174     onRegionCollapsed : function(region){
45175         this.state[region.getPosition()].collapsed = true;
45176         this.storeState();
45177     },
45178     
45179     onRegionExpanded : function(region){
45180         this.state[region.getPosition()].collapsed = false;
45181         this.storeState();
45182     }
45183 };/*
45184  * Based on:
45185  * Ext JS Library 1.1.1
45186  * Copyright(c) 2006-2007, Ext JS, LLC.
45187  *
45188  * Originally Released Under LGPL - original licence link has changed is not relivant.
45189  *
45190  * Fork - LGPL
45191  * <script type="text/javascript">
45192  */
45193 /**
45194  * @class Roo.ContentPanel
45195  * @extends Roo.util.Observable
45196  * A basic ContentPanel element.
45197  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45198  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45199  * @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
45200  * @cfg {Boolean} closable True if the panel can be closed/removed
45201  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45202  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45203  * @cfg {Toolbar} toolbar A toolbar for this panel
45204  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45205  * @cfg {String} title The title for this panel
45206  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45207  * @cfg {String} url Calls {@link #setUrl} with this value
45208  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45209  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45210  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45211  * @constructor
45212  * Create a new ContentPanel.
45213  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45214  * @param {String/Object} config A string to set only the title or a config object
45215  * @param {String} content (optional) Set the HTML content for this panel
45216  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45217  */
45218 Roo.ContentPanel = function(el, config, content){
45219     
45220      
45221     /*
45222     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45223         config = el;
45224         el = Roo.id();
45225     }
45226     if (config && config.parentLayout) { 
45227         el = config.parentLayout.el.createChild(); 
45228     }
45229     */
45230     if(el.autoCreate){ // xtype is available if this is called from factory
45231         config = el;
45232         el = Roo.id();
45233     }
45234     this.el = Roo.get(el);
45235     if(!this.el && config && config.autoCreate){
45236         if(typeof config.autoCreate == "object"){
45237             if(!config.autoCreate.id){
45238                 config.autoCreate.id = config.id||el;
45239             }
45240             this.el = Roo.DomHelper.append(document.body,
45241                         config.autoCreate, true);
45242         }else{
45243             this.el = Roo.DomHelper.append(document.body,
45244                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45245         }
45246     }
45247     this.closable = false;
45248     this.loaded = false;
45249     this.active = false;
45250     if(typeof config == "string"){
45251         this.title = config;
45252     }else{
45253         Roo.apply(this, config);
45254     }
45255     
45256     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45257         this.wrapEl = this.el.wrap();    
45258         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45259         
45260     }
45261     
45262     
45263     
45264     if(this.resizeEl){
45265         this.resizeEl = Roo.get(this.resizeEl, true);
45266     }else{
45267         this.resizeEl = this.el;
45268     }
45269     this.addEvents({
45270         /**
45271          * @event activate
45272          * Fires when this panel is activated. 
45273          * @param {Roo.ContentPanel} this
45274          */
45275         "activate" : true,
45276         /**
45277          * @event deactivate
45278          * Fires when this panel is activated. 
45279          * @param {Roo.ContentPanel} this
45280          */
45281         "deactivate" : true,
45282
45283         /**
45284          * @event resize
45285          * Fires when this panel is resized if fitToFrame is true.
45286          * @param {Roo.ContentPanel} this
45287          * @param {Number} width The width after any component adjustments
45288          * @param {Number} height The height after any component adjustments
45289          */
45290         "resize" : true
45291     });
45292     if(this.autoScroll){
45293         this.resizeEl.setStyle("overflow", "auto");
45294     }
45295     content = content || this.content;
45296     if(content){
45297         this.setContent(content);
45298     }
45299     if(config && config.url){
45300         this.setUrl(this.url, this.params, this.loadOnce);
45301     }
45302     
45303     
45304     
45305     Roo.ContentPanel.superclass.constructor.call(this);
45306 };
45307
45308 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45309     tabTip:'',
45310     setRegion : function(region){
45311         this.region = region;
45312         if(region){
45313            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45314         }else{
45315            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45316         } 
45317     },
45318     
45319     /**
45320      * Returns the toolbar for this Panel if one was configured. 
45321      * @return {Roo.Toolbar} 
45322      */
45323     getToolbar : function(){
45324         return this.toolbar;
45325     },
45326     
45327     setActiveState : function(active){
45328         this.active = active;
45329         if(!active){
45330             this.fireEvent("deactivate", this);
45331         }else{
45332             this.fireEvent("activate", this);
45333         }
45334     },
45335     /**
45336      * Updates this panel's element
45337      * @param {String} content The new content
45338      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45339     */
45340     setContent : function(content, loadScripts){
45341         this.el.update(content, loadScripts);
45342     },
45343
45344     ignoreResize : function(w, h){
45345         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45346             return true;
45347         }else{
45348             this.lastSize = {width: w, height: h};
45349             return false;
45350         }
45351     },
45352     /**
45353      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45354      * @return {Roo.UpdateManager} The UpdateManager
45355      */
45356     getUpdateManager : function(){
45357         return this.el.getUpdateManager();
45358     },
45359      /**
45360      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45361      * @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:
45362 <pre><code>
45363 panel.load({
45364     url: "your-url.php",
45365     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45366     callback: yourFunction,
45367     scope: yourObject, //(optional scope)
45368     discardUrl: false,
45369     nocache: false,
45370     text: "Loading...",
45371     timeout: 30,
45372     scripts: false
45373 });
45374 </code></pre>
45375      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45376      * 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.
45377      * @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}
45378      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45379      * @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.
45380      * @return {Roo.ContentPanel} this
45381      */
45382     load : function(){
45383         var um = this.el.getUpdateManager();
45384         um.update.apply(um, arguments);
45385         return this;
45386     },
45387
45388
45389     /**
45390      * 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.
45391      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45392      * @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)
45393      * @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)
45394      * @return {Roo.UpdateManager} The UpdateManager
45395      */
45396     setUrl : function(url, params, loadOnce){
45397         if(this.refreshDelegate){
45398             this.removeListener("activate", this.refreshDelegate);
45399         }
45400         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45401         this.on("activate", this.refreshDelegate);
45402         return this.el.getUpdateManager();
45403     },
45404     
45405     _handleRefresh : function(url, params, loadOnce){
45406         if(!loadOnce || !this.loaded){
45407             var updater = this.el.getUpdateManager();
45408             updater.update(url, params, this._setLoaded.createDelegate(this));
45409         }
45410     },
45411     
45412     _setLoaded : function(){
45413         this.loaded = true;
45414     }, 
45415     
45416     /**
45417      * Returns this panel's id
45418      * @return {String} 
45419      */
45420     getId : function(){
45421         return this.el.id;
45422     },
45423     
45424     /** 
45425      * Returns this panel's element - used by regiosn to add.
45426      * @return {Roo.Element} 
45427      */
45428     getEl : function(){
45429         return this.wrapEl || this.el;
45430     },
45431     
45432     adjustForComponents : function(width, height){
45433         if(this.resizeEl != this.el){
45434             width -= this.el.getFrameWidth('lr');
45435             height -= this.el.getFrameWidth('tb');
45436         }
45437         if(this.toolbar){
45438             var te = this.toolbar.getEl();
45439             height -= te.getHeight();
45440             te.setWidth(width);
45441         }
45442         if(this.adjustments){
45443             width += this.adjustments[0];
45444             height += this.adjustments[1];
45445         }
45446         return {"width": width, "height": height};
45447     },
45448     
45449     setSize : function(width, height){
45450         if(this.fitToFrame && !this.ignoreResize(width, height)){
45451             if(this.fitContainer && this.resizeEl != this.el){
45452                 this.el.setSize(width, height);
45453             }
45454             var size = this.adjustForComponents(width, height);
45455             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45456             this.fireEvent('resize', this, size.width, size.height);
45457         }
45458     },
45459     
45460     /**
45461      * Returns this panel's title
45462      * @return {String} 
45463      */
45464     getTitle : function(){
45465         return this.title;
45466     },
45467     
45468     /**
45469      * Set this panel's title
45470      * @param {String} title
45471      */
45472     setTitle : function(title){
45473         this.title = title;
45474         if(this.region){
45475             this.region.updatePanelTitle(this, title);
45476         }
45477     },
45478     
45479     /**
45480      * Returns true is this panel was configured to be closable
45481      * @return {Boolean} 
45482      */
45483     isClosable : function(){
45484         return this.closable;
45485     },
45486     
45487     beforeSlide : function(){
45488         this.el.clip();
45489         this.resizeEl.clip();
45490     },
45491     
45492     afterSlide : function(){
45493         this.el.unclip();
45494         this.resizeEl.unclip();
45495     },
45496     
45497     /**
45498      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45499      *   Will fail silently if the {@link #setUrl} method has not been called.
45500      *   This does not activate the panel, just updates its content.
45501      */
45502     refresh : function(){
45503         if(this.refreshDelegate){
45504            this.loaded = false;
45505            this.refreshDelegate();
45506         }
45507     },
45508     
45509     /**
45510      * Destroys this panel
45511      */
45512     destroy : function(){
45513         this.el.removeAllListeners();
45514         var tempEl = document.createElement("span");
45515         tempEl.appendChild(this.el.dom);
45516         tempEl.innerHTML = "";
45517         this.el.remove();
45518         this.el = null;
45519     },
45520     
45521       /**
45522      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45523      * <pre><code>
45524
45525 layout.addxtype({
45526        xtype : 'Form',
45527        items: [ .... ]
45528    }
45529 );
45530
45531 </code></pre>
45532      * @param {Object} cfg Xtype definition of item to add.
45533      */
45534     
45535     addxtype : function(cfg) {
45536         // add form..
45537         if (cfg.xtype.match(/^Form$/)) {
45538             var el = this.el.createChild();
45539
45540             this.form = new  Roo.form.Form(cfg);
45541             
45542             
45543             if ( this.form.allItems.length) this.form.render(el.dom);
45544             return this.form;
45545         }
45546         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45547             // views..
45548             cfg.el = this.el.appendChild(document.createElement("div"));
45549             // factory?
45550             var ret = new Roo[cfg.xtype](cfg);
45551             ret.render(false, ''); // render blank..
45552             return ret;
45553             
45554         }
45555         return false;
45556         
45557     }
45558 });
45559
45560 /**
45561  * @class Roo.GridPanel
45562  * @extends Roo.ContentPanel
45563  * @constructor
45564  * Create a new GridPanel.
45565  * @param {Roo.grid.Grid} grid The grid for this panel
45566  * @param {String/Object} config A string to set only the panel's title, or a config object
45567  */
45568 Roo.GridPanel = function(grid, config){
45569     
45570   
45571     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45572         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45573         
45574     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45575     
45576     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45577     
45578     if(this.toolbar){
45579         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45580     }
45581     // xtype created footer. - not sure if will work as we normally have to render first..
45582     if (this.footer && !this.footer.el && this.footer.xtype) {
45583         
45584         this.footer.container = this.grid.getView().getFooterPanel(true);
45585         this.footer.dataSource = this.grid.dataSource;
45586         this.footer = Roo.factory(this.footer, Roo);
45587         
45588     }
45589     
45590     grid.monitorWindowResize = false; // turn off autosizing
45591     grid.autoHeight = false;
45592     grid.autoWidth = false;
45593     this.grid = grid;
45594     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45595 };
45596
45597 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45598     getId : function(){
45599         return this.grid.id;
45600     },
45601     
45602     /**
45603      * Returns the grid for this panel
45604      * @return {Roo.grid.Grid} 
45605      */
45606     getGrid : function(){
45607         return this.grid;    
45608     },
45609     
45610     setSize : function(width, height){
45611         if(!this.ignoreResize(width, height)){
45612             var grid = this.grid;
45613             var size = this.adjustForComponents(width, height);
45614             grid.getGridEl().setSize(size.width, size.height);
45615             grid.autoSize();
45616         }
45617     },
45618     
45619     beforeSlide : function(){
45620         this.grid.getView().scroller.clip();
45621     },
45622     
45623     afterSlide : function(){
45624         this.grid.getView().scroller.unclip();
45625     },
45626     
45627     destroy : function(){
45628         this.grid.destroy();
45629         delete this.grid;
45630         Roo.GridPanel.superclass.destroy.call(this); 
45631     }
45632 });
45633
45634
45635 /**
45636  * @class Roo.NestedLayoutPanel
45637  * @extends Roo.ContentPanel
45638  * @constructor
45639  * Create a new NestedLayoutPanel.
45640  * 
45641  * 
45642  * @param {Roo.BorderLayout} layout The layout for this panel
45643  * @param {String/Object} config A string to set only the title or a config object
45644  */
45645 Roo.NestedLayoutPanel = function(layout, config)
45646 {
45647     // construct with only one argument..
45648     /* FIXME - implement nicer consturctors
45649     if (layout.layout) {
45650         config = layout;
45651         layout = config.layout;
45652         delete config.layout;
45653     }
45654     if (layout.xtype && !layout.getEl) {
45655         // then layout needs constructing..
45656         layout = Roo.factory(layout, Roo);
45657     }
45658     */
45659     
45660     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45661     
45662     layout.monitorWindowResize = false; // turn off autosizing
45663     this.layout = layout;
45664     this.layout.getEl().addClass("x-layout-nested-layout");
45665     
45666     
45667     
45668 };
45669
45670 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45671
45672     setSize : function(width, height){
45673         if(!this.ignoreResize(width, height)){
45674             var size = this.adjustForComponents(width, height);
45675             var el = this.layout.getEl();
45676             el.setSize(size.width, size.height);
45677             var touch = el.dom.offsetWidth;
45678             this.layout.layout();
45679             // ie requires a double layout on the first pass
45680             if(Roo.isIE && !this.initialized){
45681                 this.initialized = true;
45682                 this.layout.layout();
45683             }
45684         }
45685     },
45686     
45687     // activate all subpanels if not currently active..
45688     
45689     setActiveState : function(active){
45690         this.active = active;
45691         if(!active){
45692             this.fireEvent("deactivate", this);
45693             return;
45694         }
45695         
45696         this.fireEvent("activate", this);
45697         // not sure if this should happen before or after..
45698         if (!this.layout) {
45699             return; // should not happen..
45700         }
45701         var reg = false;
45702         for (var r in this.layout.regions) {
45703             reg = this.layout.getRegion(r);
45704             if (reg.getActivePanel()) {
45705                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45706                 reg.setActivePanel(reg.getActivePanel());
45707                 continue;
45708             }
45709             if (!reg.panels.length) {
45710                 continue;
45711             }
45712             reg.showPanel(reg.getPanel(0));
45713         }
45714         
45715         
45716         
45717         
45718     },
45719     
45720     /**
45721      * Returns the nested BorderLayout for this panel
45722      * @return {Roo.BorderLayout} 
45723      */
45724     getLayout : function(){
45725         return this.layout;
45726     },
45727     
45728      /**
45729      * Adds a xtype elements to the layout of the nested panel
45730      * <pre><code>
45731
45732 panel.addxtype({
45733        xtype : 'ContentPanel',
45734        region: 'west',
45735        items: [ .... ]
45736    }
45737 );
45738
45739 panel.addxtype({
45740         xtype : 'NestedLayoutPanel',
45741         region: 'west',
45742         layout: {
45743            center: { },
45744            west: { }   
45745         },
45746         items : [ ... list of content panels or nested layout panels.. ]
45747    }
45748 );
45749 </code></pre>
45750      * @param {Object} cfg Xtype definition of item to add.
45751      */
45752     addxtype : function(cfg) {
45753         return this.layout.addxtype(cfg);
45754     
45755     }
45756 });
45757
45758 Roo.ScrollPanel = function(el, config, content){
45759     config = config || {};
45760     config.fitToFrame = true;
45761     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45762     
45763     this.el.dom.style.overflow = "hidden";
45764     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45765     this.el.removeClass("x-layout-inactive-content");
45766     this.el.on("mousewheel", this.onWheel, this);
45767
45768     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45769     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45770     up.unselectable(); down.unselectable();
45771     up.on("click", this.scrollUp, this);
45772     down.on("click", this.scrollDown, this);
45773     up.addClassOnOver("x-scroller-btn-over");
45774     down.addClassOnOver("x-scroller-btn-over");
45775     up.addClassOnClick("x-scroller-btn-click");
45776     down.addClassOnClick("x-scroller-btn-click");
45777     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45778
45779     this.resizeEl = this.el;
45780     this.el = wrap; this.up = up; this.down = down;
45781 };
45782
45783 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45784     increment : 100,
45785     wheelIncrement : 5,
45786     scrollUp : function(){
45787         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45788     },
45789
45790     scrollDown : function(){
45791         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45792     },
45793
45794     afterScroll : function(){
45795         var el = this.resizeEl;
45796         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45797         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45798         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45799     },
45800
45801     setSize : function(){
45802         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45803         this.afterScroll();
45804     },
45805
45806     onWheel : function(e){
45807         var d = e.getWheelDelta();
45808         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45809         this.afterScroll();
45810         e.stopEvent();
45811     },
45812
45813     setContent : function(content, loadScripts){
45814         this.resizeEl.update(content, loadScripts);
45815     }
45816
45817 });
45818
45819
45820
45821
45822
45823
45824
45825
45826
45827 /**
45828  * @class Roo.TreePanel
45829  * @extends Roo.ContentPanel
45830  * @constructor
45831  * Create a new TreePanel. - defaults to fit/scoll contents.
45832  * @param {String/Object} config A string to set only the panel's title, or a config object
45833  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45834  */
45835 Roo.TreePanel = function(config){
45836     var el = config.el;
45837     var tree = config.tree;
45838     delete config.tree; 
45839     delete config.el; // hopefull!
45840     
45841     // wrapper for IE7 strict & safari scroll issue
45842     
45843     var treeEl = el.createChild();
45844     config.resizeEl = treeEl;
45845     
45846     
45847     
45848     Roo.TreePanel.superclass.constructor.call(this, el, config);
45849  
45850  
45851     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45852     //console.log(tree);
45853     this.on('activate', function()
45854     {
45855         if (this.tree.rendered) {
45856             return;
45857         }
45858         //console.log('render tree');
45859         this.tree.render();
45860     });
45861     
45862     this.on('resize',  function (cp, w, h) {
45863             this.tree.innerCt.setWidth(w);
45864             this.tree.innerCt.setHeight(h);
45865             this.tree.innerCt.setStyle('overflow-y', 'auto');
45866     });
45867
45868         
45869     
45870 };
45871
45872 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45873     fitToFrame : true,
45874     autoScroll : true
45875 });
45876
45877
45878
45879
45880
45881
45882
45883
45884
45885
45886
45887 /*
45888  * Based on:
45889  * Ext JS Library 1.1.1
45890  * Copyright(c) 2006-2007, Ext JS, LLC.
45891  *
45892  * Originally Released Under LGPL - original licence link has changed is not relivant.
45893  *
45894  * Fork - LGPL
45895  * <script type="text/javascript">
45896  */
45897  
45898
45899 /**
45900  * @class Roo.ReaderLayout
45901  * @extends Roo.BorderLayout
45902  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45903  * center region containing two nested regions (a top one for a list view and one for item preview below),
45904  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45905  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45906  * expedites the setup of the overall layout and regions for this common application style.
45907  * Example:
45908  <pre><code>
45909 var reader = new Roo.ReaderLayout();
45910 var CP = Roo.ContentPanel;  // shortcut for adding
45911
45912 reader.beginUpdate();
45913 reader.add("north", new CP("north", "North"));
45914 reader.add("west", new CP("west", {title: "West"}));
45915 reader.add("east", new CP("east", {title: "East"}));
45916
45917 reader.regions.listView.add(new CP("listView", "List"));
45918 reader.regions.preview.add(new CP("preview", "Preview"));
45919 reader.endUpdate();
45920 </code></pre>
45921 * @constructor
45922 * Create a new ReaderLayout
45923 * @param {Object} config Configuration options
45924 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45925 * document.body if omitted)
45926 */
45927 Roo.ReaderLayout = function(config, renderTo){
45928     var c = config || {size:{}};
45929     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45930         north: c.north !== false ? Roo.apply({
45931             split:false,
45932             initialSize: 32,
45933             titlebar: false
45934         }, c.north) : false,
45935         west: c.west !== false ? Roo.apply({
45936             split:true,
45937             initialSize: 200,
45938             minSize: 175,
45939             maxSize: 400,
45940             titlebar: true,
45941             collapsible: true,
45942             animate: true,
45943             margins:{left:5,right:0,bottom:5,top:5},
45944             cmargins:{left:5,right:5,bottom:5,top:5}
45945         }, c.west) : false,
45946         east: c.east !== false ? Roo.apply({
45947             split:true,
45948             initialSize: 200,
45949             minSize: 175,
45950             maxSize: 400,
45951             titlebar: true,
45952             collapsible: true,
45953             animate: true,
45954             margins:{left:0,right:5,bottom:5,top:5},
45955             cmargins:{left:5,right:5,bottom:5,top:5}
45956         }, c.east) : false,
45957         center: Roo.apply({
45958             tabPosition: 'top',
45959             autoScroll:false,
45960             closeOnTab: true,
45961             titlebar:false,
45962             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45963         }, c.center)
45964     });
45965
45966     this.el.addClass('x-reader');
45967
45968     this.beginUpdate();
45969
45970     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45971         south: c.preview !== false ? Roo.apply({
45972             split:true,
45973             initialSize: 200,
45974             minSize: 100,
45975             autoScroll:true,
45976             collapsible:true,
45977             titlebar: true,
45978             cmargins:{top:5,left:0, right:0, bottom:0}
45979         }, c.preview) : false,
45980         center: Roo.apply({
45981             autoScroll:false,
45982             titlebar:false,
45983             minHeight:200
45984         }, c.listView)
45985     });
45986     this.add('center', new Roo.NestedLayoutPanel(inner,
45987             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45988
45989     this.endUpdate();
45990
45991     this.regions.preview = inner.getRegion('south');
45992     this.regions.listView = inner.getRegion('center');
45993 };
45994
45995 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45996  * Based on:
45997  * Ext JS Library 1.1.1
45998  * Copyright(c) 2006-2007, Ext JS, LLC.
45999  *
46000  * Originally Released Under LGPL - original licence link has changed is not relivant.
46001  *
46002  * Fork - LGPL
46003  * <script type="text/javascript">
46004  */
46005  
46006 /**
46007  * @class Roo.grid.Grid
46008  * @extends Roo.util.Observable
46009  * This class represents the primary interface of a component based grid control.
46010  * <br><br>Usage:<pre><code>
46011  var grid = new Roo.grid.Grid("my-container-id", {
46012      ds: myDataStore,
46013      cm: myColModel,
46014      selModel: mySelectionModel,
46015      autoSizeColumns: true,
46016      monitorWindowResize: false,
46017      trackMouseOver: true
46018  });
46019  // set any options
46020  grid.render();
46021  * </code></pre>
46022  * <b>Common Problems:</b><br/>
46023  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46024  * element will correct this<br/>
46025  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46026  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46027  * are unpredictable.<br/>
46028  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46029  * grid to calculate dimensions/offsets.<br/>
46030   * @constructor
46031  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46032  * The container MUST have some type of size defined for the grid to fill. The container will be
46033  * automatically set to position relative if it isn't already.
46034  * @param {Object} config A config object that sets properties on this grid.
46035  */
46036 Roo.grid.Grid = function(container, config){
46037         // initialize the container
46038         this.container = Roo.get(container);
46039         this.container.update("");
46040         this.container.setStyle("overflow", "hidden");
46041     this.container.addClass('x-grid-container');
46042
46043     this.id = this.container.id;
46044
46045     Roo.apply(this, config);
46046     // check and correct shorthanded configs
46047     if(this.ds){
46048         this.dataSource = this.ds;
46049         delete this.ds;
46050     }
46051     if(this.cm){
46052         this.colModel = this.cm;
46053         delete this.cm;
46054     }
46055     if(this.sm){
46056         this.selModel = this.sm;
46057         delete this.sm;
46058     }
46059
46060     if (this.selModel) {
46061         this.selModel = Roo.factory(this.selModel, Roo.grid);
46062         this.sm = this.selModel;
46063         this.sm.xmodule = this.xmodule || false;
46064     }
46065     if (typeof(this.colModel.config) == 'undefined') {
46066         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46067         this.cm = this.colModel;
46068         this.cm.xmodule = this.xmodule || false;
46069     }
46070     if (this.dataSource) {
46071         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46072         this.ds = this.dataSource;
46073         this.ds.xmodule = this.xmodule || false;
46074         
46075     }
46076     
46077     
46078     
46079     if(this.width){
46080         this.container.setWidth(this.width);
46081     }
46082
46083     if(this.height){
46084         this.container.setHeight(this.height);
46085     }
46086     /** @private */
46087         this.addEvents({
46088             // raw events
46089             /**
46090              * @event click
46091              * The raw click event for the entire grid.
46092              * @param {Roo.EventObject} e
46093              */
46094             "click" : true,
46095             /**
46096              * @event dblclick
46097              * The raw dblclick event for the entire grid.
46098              * @param {Roo.EventObject} e
46099              */
46100             "dblclick" : true,
46101             /**
46102              * @event contextmenu
46103              * The raw contextmenu event for the entire grid.
46104              * @param {Roo.EventObject} e
46105              */
46106             "contextmenu" : true,
46107             /**
46108              * @event mousedown
46109              * The raw mousedown event for the entire grid.
46110              * @param {Roo.EventObject} e
46111              */
46112             "mousedown" : true,
46113             /**
46114              * @event mouseup
46115              * The raw mouseup event for the entire grid.
46116              * @param {Roo.EventObject} e
46117              */
46118             "mouseup" : true,
46119             /**
46120              * @event mouseover
46121              * The raw mouseover event for the entire grid.
46122              * @param {Roo.EventObject} e
46123              */
46124             "mouseover" : true,
46125             /**
46126              * @event mouseout
46127              * The raw mouseout event for the entire grid.
46128              * @param {Roo.EventObject} e
46129              */
46130             "mouseout" : true,
46131             /**
46132              * @event keypress
46133              * The raw keypress event for the entire grid.
46134              * @param {Roo.EventObject} e
46135              */
46136             "keypress" : true,
46137             /**
46138              * @event keydown
46139              * The raw keydown event for the entire grid.
46140              * @param {Roo.EventObject} e
46141              */
46142             "keydown" : true,
46143
46144             // custom events
46145
46146             /**
46147              * @event cellclick
46148              * Fires when a cell is clicked
46149              * @param {Grid} this
46150              * @param {Number} rowIndex
46151              * @param {Number} columnIndex
46152              * @param {Roo.EventObject} e
46153              */
46154             "cellclick" : true,
46155             /**
46156              * @event celldblclick
46157              * Fires when a cell is double clicked
46158              * @param {Grid} this
46159              * @param {Number} rowIndex
46160              * @param {Number} columnIndex
46161              * @param {Roo.EventObject} e
46162              */
46163             "celldblclick" : true,
46164             /**
46165              * @event rowclick
46166              * Fires when a row is clicked
46167              * @param {Grid} this
46168              * @param {Number} rowIndex
46169              * @param {Roo.EventObject} e
46170              */
46171             "rowclick" : true,
46172             /**
46173              * @event rowdblclick
46174              * Fires when a row is double clicked
46175              * @param {Grid} this
46176              * @param {Number} rowIndex
46177              * @param {Roo.EventObject} e
46178              */
46179             "rowdblclick" : true,
46180             /**
46181              * @event headerclick
46182              * Fires when a header is clicked
46183              * @param {Grid} this
46184              * @param {Number} columnIndex
46185              * @param {Roo.EventObject} e
46186              */
46187             "headerclick" : true,
46188             /**
46189              * @event headerdblclick
46190              * Fires when a header cell is double clicked
46191              * @param {Grid} this
46192              * @param {Number} columnIndex
46193              * @param {Roo.EventObject} e
46194              */
46195             "headerdblclick" : true,
46196             /**
46197              * @event rowcontextmenu
46198              * Fires when a row is right clicked
46199              * @param {Grid} this
46200              * @param {Number} rowIndex
46201              * @param {Roo.EventObject} e
46202              */
46203             "rowcontextmenu" : true,
46204             /**
46205          * @event cellcontextmenu
46206          * Fires when a cell is right clicked
46207          * @param {Grid} this
46208          * @param {Number} rowIndex
46209          * @param {Number} cellIndex
46210          * @param {Roo.EventObject} e
46211          */
46212          "cellcontextmenu" : true,
46213             /**
46214              * @event headercontextmenu
46215              * Fires when a header is right clicked
46216              * @param {Grid} this
46217              * @param {Number} columnIndex
46218              * @param {Roo.EventObject} e
46219              */
46220             "headercontextmenu" : true,
46221             /**
46222              * @event bodyscroll
46223              * Fires when the body element is scrolled
46224              * @param {Number} scrollLeft
46225              * @param {Number} scrollTop
46226              */
46227             "bodyscroll" : true,
46228             /**
46229              * @event columnresize
46230              * Fires when the user resizes a column
46231              * @param {Number} columnIndex
46232              * @param {Number} newSize
46233              */
46234             "columnresize" : true,
46235             /**
46236              * @event columnmove
46237              * Fires when the user moves a column
46238              * @param {Number} oldIndex
46239              * @param {Number} newIndex
46240              */
46241             "columnmove" : true,
46242             /**
46243              * @event startdrag
46244              * Fires when row(s) start being dragged
46245              * @param {Grid} this
46246              * @param {Roo.GridDD} dd The drag drop object
46247              * @param {event} e The raw browser event
46248              */
46249             "startdrag" : true,
46250             /**
46251              * @event enddrag
46252              * Fires when a drag operation is complete
46253              * @param {Grid} this
46254              * @param {Roo.GridDD} dd The drag drop object
46255              * @param {event} e The raw browser event
46256              */
46257             "enddrag" : true,
46258             /**
46259              * @event dragdrop
46260              * Fires when dragged row(s) are dropped on a valid DD target
46261              * @param {Grid} this
46262              * @param {Roo.GridDD} dd The drag drop object
46263              * @param {String} targetId The target drag drop object
46264              * @param {event} e The raw browser event
46265              */
46266             "dragdrop" : true,
46267             /**
46268              * @event dragover
46269              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46270              * @param {Grid} this
46271              * @param {Roo.GridDD} dd The drag drop object
46272              * @param {String} targetId The target drag drop object
46273              * @param {event} e The raw browser event
46274              */
46275             "dragover" : true,
46276             /**
46277              * @event dragenter
46278              *  Fires when the dragged row(s) first cross another DD target while being dragged
46279              * @param {Grid} this
46280              * @param {Roo.GridDD} dd The drag drop object
46281              * @param {String} targetId The target drag drop object
46282              * @param {event} e The raw browser event
46283              */
46284             "dragenter" : true,
46285             /**
46286              * @event dragout
46287              * Fires when the dragged row(s) leave another DD target while being dragged
46288              * @param {Grid} this
46289              * @param {Roo.GridDD} dd The drag drop object
46290              * @param {String} targetId The target drag drop object
46291              * @param {event} e The raw browser event
46292              */
46293             "dragout" : true,
46294         /**
46295          * @event render
46296          * Fires when the grid is rendered
46297          * @param {Grid} grid
46298          */
46299         render : true
46300     });
46301
46302     Roo.grid.Grid.superclass.constructor.call(this);
46303 };
46304 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46305     
46306     /**
46307      * @cfg {String} ddGroup - drag drop group.
46308          */
46309     
46310     /**
46311      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46312          */
46313         minColumnWidth : 25,
46314
46315     /**
46316          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46317          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46318          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46319          */
46320         autoSizeColumns : false,
46321
46322         /**
46323          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46324          */
46325         autoSizeHeaders : true,
46326
46327         /**
46328          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46329          */
46330         monitorWindowResize : true,
46331
46332         /**
46333          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46334          * rows measured to get a columns size. Default is 0 (all rows).
46335          */
46336         maxRowsToMeasure : 0,
46337
46338         /**
46339          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46340          */
46341         trackMouseOver : true,
46342
46343     /**
46344          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46345          */
46346     
46347         /**
46348          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46349          */
46350         enableDragDrop : false,
46351
46352         /**
46353          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46354          */
46355         enableColumnMove : true,
46356
46357         /**
46358          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46359          */
46360         enableColumnHide : true,
46361
46362         /**
46363          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46364          */
46365         enableRowHeightSync : false,
46366
46367         /**
46368          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46369          */
46370         stripeRows : true,
46371
46372         /**
46373          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46374          */
46375         autoHeight : false,
46376
46377     /**
46378      * @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.
46379      */
46380     autoExpandColumn : false,
46381
46382     /**
46383     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46384     * Default is 50.
46385     */
46386     autoExpandMin : 50,
46387
46388     /**
46389     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46390     */
46391     autoExpandMax : 1000,
46392
46393     /**
46394          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46395          */
46396         view : null,
46397
46398         /**
46399      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46400          */
46401         loadMask : false,
46402
46403     // private
46404     rendered : false,
46405
46406     /**
46407     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46408     * of a fixed width. Default is false.
46409     */
46410     /**
46411     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46412     */
46413     /**
46414      * Called once after all setup has been completed and the grid is ready to be rendered.
46415      * @return {Roo.grid.Grid} this
46416      */
46417     render : function(){
46418         var c = this.container;
46419         // try to detect autoHeight/width mode
46420         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46421             this.autoHeight = true;
46422         }
46423         var view = this.getView();
46424         view.init(this);
46425
46426         c.on("click", this.onClick, this);
46427         c.on("dblclick", this.onDblClick, this);
46428         c.on("contextmenu", this.onContextMenu, this);
46429         c.on("keydown", this.onKeyDown, this);
46430
46431         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46432
46433         this.getSelectionModel().init(this);
46434
46435         view.render();
46436
46437         if(this.loadMask){
46438             this.loadMask = new Roo.LoadMask(this.container,
46439                     Roo.apply({store:this.dataSource}, this.loadMask));
46440         }
46441         
46442         
46443         if (this.toolbar && this.toolbar.xtype) {
46444             this.toolbar.container = this.getView().getHeaderPanel(true);
46445             this.toolbar = new Ext.Toolbar(this.toolbar);
46446         }
46447         if (this.footer && this.footer.xtype) {
46448             this.footer.dataSource = this.getDataSource();
46449             this.footer.container = this.getView().getFooterPanel(true);
46450             this.footer = Roo.factory(this.footer, Roo);
46451         }
46452         this.rendered = true;
46453         this.fireEvent('render', this);
46454         return this;
46455     },
46456
46457         /**
46458          * Reconfigures the grid to use a different Store and Column Model.
46459          * The View will be bound to the new objects and refreshed.
46460          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46461          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46462          */
46463     reconfigure : function(dataSource, colModel){
46464         if(this.loadMask){
46465             this.loadMask.destroy();
46466             this.loadMask = new Roo.LoadMask(this.container,
46467                     Roo.apply({store:dataSource}, this.loadMask));
46468         }
46469         this.view.bind(dataSource, colModel);
46470         this.dataSource = dataSource;
46471         this.colModel = colModel;
46472         this.view.refresh(true);
46473     },
46474
46475     // private
46476     onKeyDown : function(e){
46477         this.fireEvent("keydown", e);
46478     },
46479
46480     /**
46481      * Destroy this grid.
46482      * @param {Boolean} removeEl True to remove the element
46483      */
46484     destroy : function(removeEl, keepListeners){
46485         if(this.loadMask){
46486             this.loadMask.destroy();
46487         }
46488         var c = this.container;
46489         c.removeAllListeners();
46490         this.view.destroy();
46491         this.colModel.purgeListeners();
46492         if(!keepListeners){
46493             this.purgeListeners();
46494         }
46495         c.update("");
46496         if(removeEl === true){
46497             c.remove();
46498         }
46499     },
46500
46501     // private
46502     processEvent : function(name, e){
46503         this.fireEvent(name, e);
46504         var t = e.getTarget();
46505         var v = this.view;
46506         var header = v.findHeaderIndex(t);
46507         if(header !== false){
46508             this.fireEvent("header" + name, this, header, e);
46509         }else{
46510             var row = v.findRowIndex(t);
46511             var cell = v.findCellIndex(t);
46512             if(row !== false){
46513                 this.fireEvent("row" + name, this, row, e);
46514                 if(cell !== false){
46515                     this.fireEvent("cell" + name, this, row, cell, e);
46516                 }
46517             }
46518         }
46519     },
46520
46521     // private
46522     onClick : function(e){
46523         this.processEvent("click", e);
46524     },
46525
46526     // private
46527     onContextMenu : function(e, t){
46528         this.processEvent("contextmenu", e);
46529     },
46530
46531     // private
46532     onDblClick : function(e){
46533         this.processEvent("dblclick", e);
46534     },
46535
46536     // private
46537     walkCells : function(row, col, step, fn, scope){
46538         var cm = this.colModel, clen = cm.getColumnCount();
46539         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46540         if(step < 0){
46541             if(col < 0){
46542                 row--;
46543                 first = false;
46544             }
46545             while(row >= 0){
46546                 if(!first){
46547                     col = clen-1;
46548                 }
46549                 first = false;
46550                 while(col >= 0){
46551                     if(fn.call(scope || this, row, col, cm) === true){
46552                         return [row, col];
46553                     }
46554                     col--;
46555                 }
46556                 row--;
46557             }
46558         } else {
46559             if(col >= clen){
46560                 row++;
46561                 first = false;
46562             }
46563             while(row < rlen){
46564                 if(!first){
46565                     col = 0;
46566                 }
46567                 first = false;
46568                 while(col < clen){
46569                     if(fn.call(scope || this, row, col, cm) === true){
46570                         return [row, col];
46571                     }
46572                     col++;
46573                 }
46574                 row++;
46575             }
46576         }
46577         return null;
46578     },
46579
46580     // private
46581     getSelections : function(){
46582         return this.selModel.getSelections();
46583     },
46584
46585     /**
46586      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46587      * but if manual update is required this method will initiate it.
46588      */
46589     autoSize : function(){
46590         if(this.rendered){
46591             this.view.layout();
46592             if(this.view.adjustForScroll){
46593                 this.view.adjustForScroll();
46594             }
46595         }
46596     },
46597
46598     /**
46599      * Returns the grid's underlying element.
46600      * @return {Element} The element
46601      */
46602     getGridEl : function(){
46603         return this.container;
46604     },
46605
46606     // private for compatibility, overridden by editor grid
46607     stopEditing : function(){},
46608
46609     /**
46610      * Returns the grid's SelectionModel.
46611      * @return {SelectionModel}
46612      */
46613     getSelectionModel : function(){
46614         if(!this.selModel){
46615             this.selModel = new Roo.grid.RowSelectionModel();
46616         }
46617         return this.selModel;
46618     },
46619
46620     /**
46621      * Returns the grid's DataSource.
46622      * @return {DataSource}
46623      */
46624     getDataSource : function(){
46625         return this.dataSource;
46626     },
46627
46628     /**
46629      * Returns the grid's ColumnModel.
46630      * @return {ColumnModel}
46631      */
46632     getColumnModel : function(){
46633         return this.colModel;
46634     },
46635
46636     /**
46637      * Returns the grid's GridView object.
46638      * @return {GridView}
46639      */
46640     getView : function(){
46641         if(!this.view){
46642             this.view = new Roo.grid.GridView(this.viewConfig);
46643         }
46644         return this.view;
46645     },
46646     /**
46647      * Called to get grid's drag proxy text, by default returns this.ddText.
46648      * @return {String}
46649      */
46650     getDragDropText : function(){
46651         var count = this.selModel.getCount();
46652         return String.format(this.ddText, count, count == 1 ? '' : 's');
46653     }
46654 });
46655 /**
46656  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46657  * %0 is replaced with the number of selected rows.
46658  * @type String
46659  */
46660 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46661  * Based on:
46662  * Ext JS Library 1.1.1
46663  * Copyright(c) 2006-2007, Ext JS, LLC.
46664  *
46665  * Originally Released Under LGPL - original licence link has changed is not relivant.
46666  *
46667  * Fork - LGPL
46668  * <script type="text/javascript">
46669  */
46670  
46671 Roo.grid.AbstractGridView = function(){
46672         this.grid = null;
46673         
46674         this.events = {
46675             "beforerowremoved" : true,
46676             "beforerowsinserted" : true,
46677             "beforerefresh" : true,
46678             "rowremoved" : true,
46679             "rowsinserted" : true,
46680             "rowupdated" : true,
46681             "refresh" : true
46682         };
46683     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46684 };
46685
46686 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46687     rowClass : "x-grid-row",
46688     cellClass : "x-grid-cell",
46689     tdClass : "x-grid-td",
46690     hdClass : "x-grid-hd",
46691     splitClass : "x-grid-hd-split",
46692     
46693         init: function(grid){
46694         this.grid = grid;
46695                 var cid = this.grid.getGridEl().id;
46696         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46697         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46698         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46699         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46700         },
46701         
46702         getColumnRenderers : function(){
46703         var renderers = [];
46704         var cm = this.grid.colModel;
46705         var colCount = cm.getColumnCount();
46706         for(var i = 0; i < colCount; i++){
46707             renderers[i] = cm.getRenderer(i);
46708         }
46709         return renderers;
46710     },
46711     
46712     getColumnIds : function(){
46713         var ids = [];
46714         var cm = this.grid.colModel;
46715         var colCount = cm.getColumnCount();
46716         for(var i = 0; i < colCount; i++){
46717             ids[i] = cm.getColumnId(i);
46718         }
46719         return ids;
46720     },
46721     
46722     getDataIndexes : function(){
46723         if(!this.indexMap){
46724             this.indexMap = this.buildIndexMap();
46725         }
46726         return this.indexMap.colToData;
46727     },
46728     
46729     getColumnIndexByDataIndex : function(dataIndex){
46730         if(!this.indexMap){
46731             this.indexMap = this.buildIndexMap();
46732         }
46733         return this.indexMap.dataToCol[dataIndex];
46734     },
46735     
46736     /**
46737      * Set a css style for a column dynamically. 
46738      * @param {Number} colIndex The index of the column
46739      * @param {String} name The css property name
46740      * @param {String} value The css value
46741      */
46742     setCSSStyle : function(colIndex, name, value){
46743         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46744         Roo.util.CSS.updateRule(selector, name, value);
46745     },
46746     
46747     generateRules : function(cm){
46748         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46749         Roo.util.CSS.removeStyleSheet(rulesId);
46750         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46751             var cid = cm.getColumnId(i);
46752             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46753                          this.tdSelector, cid, " {\n}\n",
46754                          this.hdSelector, cid, " {\n}\n",
46755                          this.splitSelector, cid, " {\n}\n");
46756         }
46757         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46758     }
46759 });/*
46760  * Based on:
46761  * Ext JS Library 1.1.1
46762  * Copyright(c) 2006-2007, Ext JS, LLC.
46763  *
46764  * Originally Released Under LGPL - original licence link has changed is not relivant.
46765  *
46766  * Fork - LGPL
46767  * <script type="text/javascript">
46768  */
46769
46770 // private
46771 // This is a support class used internally by the Grid components
46772 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46773     this.grid = grid;
46774     this.view = grid.getView();
46775     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46776     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46777     if(hd2){
46778         this.setHandleElId(Roo.id(hd));
46779         this.setOuterHandleElId(Roo.id(hd2));
46780     }
46781     this.scroll = false;
46782 };
46783 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46784     maxDragWidth: 120,
46785     getDragData : function(e){
46786         var t = Roo.lib.Event.getTarget(e);
46787         var h = this.view.findHeaderCell(t);
46788         if(h){
46789             return {ddel: h.firstChild, header:h};
46790         }
46791         return false;
46792     },
46793
46794     onInitDrag : function(e){
46795         this.view.headersDisabled = true;
46796         var clone = this.dragData.ddel.cloneNode(true);
46797         clone.id = Roo.id();
46798         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46799         this.proxy.update(clone);
46800         return true;
46801     },
46802
46803     afterValidDrop : function(){
46804         var v = this.view;
46805         setTimeout(function(){
46806             v.headersDisabled = false;
46807         }, 50);
46808     },
46809
46810     afterInvalidDrop : function(){
46811         var v = this.view;
46812         setTimeout(function(){
46813             v.headersDisabled = false;
46814         }, 50);
46815     }
46816 });
46817 /*
46818  * Based on:
46819  * Ext JS Library 1.1.1
46820  * Copyright(c) 2006-2007, Ext JS, LLC.
46821  *
46822  * Originally Released Under LGPL - original licence link has changed is not relivant.
46823  *
46824  * Fork - LGPL
46825  * <script type="text/javascript">
46826  */
46827 // private
46828 // This is a support class used internally by the Grid components
46829 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46830     this.grid = grid;
46831     this.view = grid.getView();
46832     // split the proxies so they don't interfere with mouse events
46833     this.proxyTop = Roo.DomHelper.append(document.body, {
46834         cls:"col-move-top", html:"&#160;"
46835     }, true);
46836     this.proxyBottom = Roo.DomHelper.append(document.body, {
46837         cls:"col-move-bottom", html:"&#160;"
46838     }, true);
46839     this.proxyTop.hide = this.proxyBottom.hide = function(){
46840         this.setLeftTop(-100,-100);
46841         this.setStyle("visibility", "hidden");
46842     };
46843     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46844     // temporarily disabled
46845     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46846     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46847 };
46848 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46849     proxyOffsets : [-4, -9],
46850     fly: Roo.Element.fly,
46851
46852     getTargetFromEvent : function(e){
46853         var t = Roo.lib.Event.getTarget(e);
46854         var cindex = this.view.findCellIndex(t);
46855         if(cindex !== false){
46856             return this.view.getHeaderCell(cindex);
46857         }
46858     },
46859
46860     nextVisible : function(h){
46861         var v = this.view, cm = this.grid.colModel;
46862         h = h.nextSibling;
46863         while(h){
46864             if(!cm.isHidden(v.getCellIndex(h))){
46865                 return h;
46866             }
46867             h = h.nextSibling;
46868         }
46869         return null;
46870     },
46871
46872     prevVisible : function(h){
46873         var v = this.view, cm = this.grid.colModel;
46874         h = h.prevSibling;
46875         while(h){
46876             if(!cm.isHidden(v.getCellIndex(h))){
46877                 return h;
46878             }
46879             h = h.prevSibling;
46880         }
46881         return null;
46882     },
46883
46884     positionIndicator : function(h, n, e){
46885         var x = Roo.lib.Event.getPageX(e);
46886         var r = Roo.lib.Dom.getRegion(n.firstChild);
46887         var px, pt, py = r.top + this.proxyOffsets[1];
46888         if((r.right - x) <= (r.right-r.left)/2){
46889             px = r.right+this.view.borderWidth;
46890             pt = "after";
46891         }else{
46892             px = r.left;
46893             pt = "before";
46894         }
46895         var oldIndex = this.view.getCellIndex(h);
46896         var newIndex = this.view.getCellIndex(n);
46897
46898         if(this.grid.colModel.isFixed(newIndex)){
46899             return false;
46900         }
46901
46902         var locked = this.grid.colModel.isLocked(newIndex);
46903
46904         if(pt == "after"){
46905             newIndex++;
46906         }
46907         if(oldIndex < newIndex){
46908             newIndex--;
46909         }
46910         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46911             return false;
46912         }
46913         px +=  this.proxyOffsets[0];
46914         this.proxyTop.setLeftTop(px, py);
46915         this.proxyTop.show();
46916         if(!this.bottomOffset){
46917             this.bottomOffset = this.view.mainHd.getHeight();
46918         }
46919         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46920         this.proxyBottom.show();
46921         return pt;
46922     },
46923
46924     onNodeEnter : function(n, dd, e, data){
46925         if(data.header != n){
46926             this.positionIndicator(data.header, n, e);
46927         }
46928     },
46929
46930     onNodeOver : function(n, dd, e, data){
46931         var result = false;
46932         if(data.header != n){
46933             result = this.positionIndicator(data.header, n, e);
46934         }
46935         if(!result){
46936             this.proxyTop.hide();
46937             this.proxyBottom.hide();
46938         }
46939         return result ? this.dropAllowed : this.dropNotAllowed;
46940     },
46941
46942     onNodeOut : function(n, dd, e, data){
46943         this.proxyTop.hide();
46944         this.proxyBottom.hide();
46945     },
46946
46947     onNodeDrop : function(n, dd, e, data){
46948         var h = data.header;
46949         if(h != n){
46950             var cm = this.grid.colModel;
46951             var x = Roo.lib.Event.getPageX(e);
46952             var r = Roo.lib.Dom.getRegion(n.firstChild);
46953             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46954             var oldIndex = this.view.getCellIndex(h);
46955             var newIndex = this.view.getCellIndex(n);
46956             var locked = cm.isLocked(newIndex);
46957             if(pt == "after"){
46958                 newIndex++;
46959             }
46960             if(oldIndex < newIndex){
46961                 newIndex--;
46962             }
46963             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46964                 return false;
46965             }
46966             cm.setLocked(oldIndex, locked, true);
46967             cm.moveColumn(oldIndex, newIndex);
46968             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46969             return true;
46970         }
46971         return false;
46972     }
46973 });
46974 /*
46975  * Based on:
46976  * Ext JS Library 1.1.1
46977  * Copyright(c) 2006-2007, Ext JS, LLC.
46978  *
46979  * Originally Released Under LGPL - original licence link has changed is not relivant.
46980  *
46981  * Fork - LGPL
46982  * <script type="text/javascript">
46983  */
46984   
46985 /**
46986  * @class Roo.grid.GridView
46987  * @extends Roo.util.Observable
46988  *
46989  * @constructor
46990  * @param {Object} config
46991  */
46992 Roo.grid.GridView = function(config){
46993     Roo.grid.GridView.superclass.constructor.call(this);
46994     this.el = null;
46995
46996     Roo.apply(this, config);
46997 };
46998
46999 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47000
47001     /**
47002      * Override this function to apply custom css classes to rows during rendering
47003      * @param {Record} record The record
47004      * @param {Number} index
47005      * @method getRowClass
47006      */
47007     rowClass : "x-grid-row",
47008
47009     cellClass : "x-grid-col",
47010
47011     tdClass : "x-grid-td",
47012
47013     hdClass : "x-grid-hd",
47014
47015     splitClass : "x-grid-split",
47016
47017     sortClasses : ["sort-asc", "sort-desc"],
47018
47019     enableMoveAnim : false,
47020
47021     hlColor: "C3DAF9",
47022
47023     dh : Roo.DomHelper,
47024
47025     fly : Roo.Element.fly,
47026
47027     css : Roo.util.CSS,
47028
47029     borderWidth: 1,
47030
47031     splitOffset: 3,
47032
47033     scrollIncrement : 22,
47034
47035     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47036
47037     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47038
47039     bind : function(ds, cm){
47040         if(this.ds){
47041             this.ds.un("load", this.onLoad, this);
47042             this.ds.un("datachanged", this.onDataChange, this);
47043             this.ds.un("add", this.onAdd, this);
47044             this.ds.un("remove", this.onRemove, this);
47045             this.ds.un("update", this.onUpdate, this);
47046             this.ds.un("clear", this.onClear, this);
47047         }
47048         if(ds){
47049             ds.on("load", this.onLoad, this);
47050             ds.on("datachanged", this.onDataChange, this);
47051             ds.on("add", this.onAdd, this);
47052             ds.on("remove", this.onRemove, this);
47053             ds.on("update", this.onUpdate, this);
47054             ds.on("clear", this.onClear, this);
47055         }
47056         this.ds = ds;
47057
47058         if(this.cm){
47059             this.cm.un("widthchange", this.onColWidthChange, this);
47060             this.cm.un("headerchange", this.onHeaderChange, this);
47061             this.cm.un("hiddenchange", this.onHiddenChange, this);
47062             this.cm.un("columnmoved", this.onColumnMove, this);
47063             this.cm.un("columnlockchange", this.onColumnLock, this);
47064         }
47065         if(cm){
47066             this.generateRules(cm);
47067             cm.on("widthchange", this.onColWidthChange, this);
47068             cm.on("headerchange", this.onHeaderChange, this);
47069             cm.on("hiddenchange", this.onHiddenChange, this);
47070             cm.on("columnmoved", this.onColumnMove, this);
47071             cm.on("columnlockchange", this.onColumnLock, this);
47072         }
47073         this.cm = cm;
47074     },
47075
47076     init: function(grid){
47077                 Roo.grid.GridView.superclass.init.call(this, grid);
47078
47079                 this.bind(grid.dataSource, grid.colModel);
47080
47081             grid.on("headerclick", this.handleHeaderClick, this);
47082
47083         if(grid.trackMouseOver){
47084             grid.on("mouseover", this.onRowOver, this);
47085                 grid.on("mouseout", this.onRowOut, this);
47086             }
47087             grid.cancelTextSelection = function(){};
47088                 this.gridId = grid.id;
47089
47090                 var tpls = this.templates || {};
47091
47092                 if(!tpls.master){
47093                     tpls.master = new Roo.Template(
47094                        '<div class="x-grid" hidefocus="true">',
47095                           '<div class="x-grid-topbar"></div>',
47096                           '<div class="x-grid-scroller"><div></div></div>',
47097                           '<div class="x-grid-locked">',
47098                               '<div class="x-grid-header">{lockedHeader}</div>',
47099                               '<div class="x-grid-body">{lockedBody}</div>',
47100                           "</div>",
47101                           '<div class="x-grid-viewport">',
47102                               '<div class="x-grid-header">{header}</div>',
47103                               '<div class="x-grid-body">{body}</div>',
47104                           "</div>",
47105                           '<div class="x-grid-bottombar"></div>',
47106                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47107                           '<div class="x-grid-resize-proxy">&#160;</div>',
47108                        "</div>"
47109                     );
47110                     tpls.master.disableformats = true;
47111                 }
47112
47113                 if(!tpls.header){
47114                     tpls.header = new Roo.Template(
47115                        '<table border="0" cellspacing="0" cellpadding="0">',
47116                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47117                        "</table>{splits}"
47118                     );
47119                     tpls.header.disableformats = true;
47120                 }
47121                 tpls.header.compile();
47122
47123                 if(!tpls.hcell){
47124                     tpls.hcell = new Roo.Template(
47125                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47126                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47127                         "</div></td>"
47128                      );
47129                      tpls.hcell.disableFormats = true;
47130                 }
47131                 tpls.hcell.compile();
47132
47133                 if(!tpls.hsplit){
47134                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47135                     tpls.hsplit.disableFormats = true;
47136                 }
47137                 tpls.hsplit.compile();
47138
47139                 if(!tpls.body){
47140                     tpls.body = new Roo.Template(
47141                        '<table border="0" cellspacing="0" cellpadding="0">',
47142                        "<tbody>{rows}</tbody>",
47143                        "</table>"
47144                     );
47145                     tpls.body.disableFormats = true;
47146                 }
47147                 tpls.body.compile();
47148
47149                 if(!tpls.row){
47150                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47151                     tpls.row.disableFormats = true;
47152                 }
47153                 tpls.row.compile();
47154
47155                 if(!tpls.cell){
47156                     tpls.cell = new Roo.Template(
47157                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47158                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47159                         "</td>"
47160                     );
47161             tpls.cell.disableFormats = true;
47162         }
47163                 tpls.cell.compile();
47164
47165                 this.templates = tpls;
47166         },
47167
47168         // remap these for backwards compat
47169     onColWidthChange : function(){
47170         this.updateColumns.apply(this, arguments);
47171     },
47172     onHeaderChange : function(){
47173         this.updateHeaders.apply(this, arguments);
47174     }, 
47175     onHiddenChange : function(){
47176         this.handleHiddenChange.apply(this, arguments);
47177     },
47178     onColumnMove : function(){
47179         this.handleColumnMove.apply(this, arguments);
47180     },
47181     onColumnLock : function(){
47182         this.handleLockChange.apply(this, arguments);
47183     },
47184
47185     onDataChange : function(){
47186         this.refresh();
47187         this.updateHeaderSortState();
47188     },
47189
47190         onClear : function(){
47191         this.refresh();
47192     },
47193
47194         onUpdate : function(ds, record){
47195         this.refreshRow(record);
47196     },
47197
47198     refreshRow : function(record){
47199         var ds = this.ds, index;
47200         if(typeof record == 'number'){
47201             index = record;
47202             record = ds.getAt(index);
47203         }else{
47204             index = ds.indexOf(record);
47205         }
47206         this.insertRows(ds, index, index, true);
47207         this.onRemove(ds, record, index+1, true);
47208         this.syncRowHeights(index, index);
47209         this.layout();
47210         this.fireEvent("rowupdated", this, index, record);
47211     },
47212
47213     onAdd : function(ds, records, index){
47214         this.insertRows(ds, index, index + (records.length-1));
47215     },
47216
47217     onRemove : function(ds, record, index, isUpdate){
47218         if(isUpdate !== true){
47219             this.fireEvent("beforerowremoved", this, index, record);
47220         }
47221         var bt = this.getBodyTable(), lt = this.getLockedTable();
47222         if(bt.rows[index]){
47223             bt.firstChild.removeChild(bt.rows[index]);
47224         }
47225         if(lt.rows[index]){
47226             lt.firstChild.removeChild(lt.rows[index]);
47227         }
47228         if(isUpdate !== true){
47229             this.stripeRows(index);
47230             this.syncRowHeights(index, index);
47231             this.layout();
47232             this.fireEvent("rowremoved", this, index, record);
47233         }
47234     },
47235
47236     onLoad : function(){
47237         this.scrollToTop();
47238     },
47239
47240     /**
47241      * Scrolls the grid to the top
47242      */
47243     scrollToTop : function(){
47244         if(this.scroller){
47245             this.scroller.dom.scrollTop = 0;
47246             this.syncScroll();
47247         }
47248     },
47249
47250     /**
47251      * Gets a panel in the header of the grid that can be used for toolbars etc.
47252      * After modifying the contents of this panel a call to grid.autoSize() may be
47253      * required to register any changes in size.
47254      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47255      * @return Roo.Element
47256      */
47257     getHeaderPanel : function(doShow){
47258         if(doShow){
47259             this.headerPanel.show();
47260         }
47261         return this.headerPanel;
47262         },
47263
47264         /**
47265      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47266      * After modifying the contents of this panel a call to grid.autoSize() may be
47267      * required to register any changes in size.
47268      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47269      * @return Roo.Element
47270      */
47271     getFooterPanel : function(doShow){
47272         if(doShow){
47273             this.footerPanel.show();
47274         }
47275         return this.footerPanel;
47276         },
47277
47278         initElements : function(){
47279             var E = Roo.Element;
47280             var el = this.grid.getGridEl().dom.firstChild;
47281             var cs = el.childNodes;
47282
47283             this.el = new E(el);
47284             this.headerPanel = new E(el.firstChild);
47285             this.headerPanel.enableDisplayMode("block");
47286
47287         this.scroller = new E(cs[1]);
47288             this.scrollSizer = new E(this.scroller.dom.firstChild);
47289
47290             this.lockedWrap = new E(cs[2]);
47291             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47292             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47293
47294             this.mainWrap = new E(cs[3]);
47295             this.mainHd = new E(this.mainWrap.dom.firstChild);
47296             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47297
47298             this.footerPanel = new E(cs[4]);
47299             this.footerPanel.enableDisplayMode("block");
47300
47301         this.focusEl = new E(cs[5]);
47302         this.focusEl.swallowEvent("click", true);
47303         this.resizeProxy = new E(cs[6]);
47304
47305             this.headerSelector = String.format(
47306                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47307                this.lockedHd.id, this.mainHd.id
47308             );
47309
47310             this.splitterSelector = String.format(
47311                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47312                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47313             );
47314     },
47315     idToCssName : function(s)
47316     {
47317         return s.replace(/[^a-z0-9]+/ig, '-');
47318     },
47319
47320         getHeaderCell : function(index){
47321             return Roo.DomQuery.select(this.headerSelector)[index];
47322         },
47323
47324         getHeaderCellMeasure : function(index){
47325             return this.getHeaderCell(index).firstChild;
47326         },
47327
47328         getHeaderCellText : function(index){
47329             return this.getHeaderCell(index).firstChild.firstChild;
47330         },
47331
47332         getLockedTable : function(){
47333             return this.lockedBody.dom.firstChild;
47334         },
47335
47336         getBodyTable : function(){
47337             return this.mainBody.dom.firstChild;
47338         },
47339
47340         getLockedRow : function(index){
47341             return this.getLockedTable().rows[index];
47342         },
47343
47344         getRow : function(index){
47345             return this.getBodyTable().rows[index];
47346         },
47347
47348         getRowComposite : function(index){
47349             if(!this.rowEl){
47350                 this.rowEl = new Roo.CompositeElementLite();
47351             }
47352         var els = [], lrow, mrow;
47353         if(lrow = this.getLockedRow(index)){
47354             els.push(lrow);
47355         }
47356         if(mrow = this.getRow(index)){
47357             els.push(mrow);
47358         }
47359         this.rowEl.elements = els;
47360             return this.rowEl;
47361         },
47362
47363         getCell : function(rowIndex, colIndex){
47364             var locked = this.cm.getLockedCount();
47365             var source;
47366             if(colIndex < locked){
47367                 source = this.lockedBody.dom.firstChild;
47368             }else{
47369                 source = this.mainBody.dom.firstChild;
47370                 colIndex -= locked;
47371             }
47372         return source.rows[rowIndex].childNodes[colIndex];
47373         },
47374
47375         getCellText : function(rowIndex, colIndex){
47376             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47377         },
47378
47379         getCellBox : function(cell){
47380             var b = this.fly(cell).getBox();
47381         if(Roo.isOpera){ // opera fails to report the Y
47382             b.y = cell.offsetTop + this.mainBody.getY();
47383         }
47384         return b;
47385     },
47386
47387     getCellIndex : function(cell){
47388         var id = String(cell.className).match(this.cellRE);
47389         if(id){
47390             return parseInt(id[1], 10);
47391         }
47392         return 0;
47393     },
47394
47395     findHeaderIndex : function(n){
47396         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47397         return r ? this.getCellIndex(r) : false;
47398     },
47399
47400     findHeaderCell : function(n){
47401         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47402         return r ? r : false;
47403     },
47404
47405     findRowIndex : function(n){
47406         if(!n){
47407             return false;
47408         }
47409         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47410         return r ? r.rowIndex : false;
47411     },
47412
47413     findCellIndex : function(node){
47414         var stop = this.el.dom;
47415         while(node && node != stop){
47416             if(this.findRE.test(node.className)){
47417                 return this.getCellIndex(node);
47418             }
47419             node = node.parentNode;
47420         }
47421         return false;
47422     },
47423
47424     getColumnId : function(index){
47425             return this.cm.getColumnId(index);
47426         },
47427
47428         getSplitters : function(){
47429             if(this.splitterSelector){
47430                return Roo.DomQuery.select(this.splitterSelector);
47431             }else{
47432                 return null;
47433             }
47434         },
47435
47436         getSplitter : function(index){
47437             return this.getSplitters()[index];
47438         },
47439
47440     onRowOver : function(e, t){
47441         var row;
47442         if((row = this.findRowIndex(t)) !== false){
47443             this.getRowComposite(row).addClass("x-grid-row-over");
47444         }
47445     },
47446
47447     onRowOut : function(e, t){
47448         var row;
47449         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47450             this.getRowComposite(row).removeClass("x-grid-row-over");
47451         }
47452     },
47453
47454     renderHeaders : function(){
47455             var cm = this.cm;
47456         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47457         var cb = [], lb = [], sb = [], lsb = [], p = {};
47458         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47459             p.cellId = "x-grid-hd-0-" + i;
47460             p.splitId = "x-grid-csplit-0-" + i;
47461             p.id = cm.getColumnId(i);
47462             p.title = cm.getColumnTooltip(i) || "";
47463             p.value = cm.getColumnHeader(i) || "";
47464             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47465             if(!cm.isLocked(i)){
47466                 cb[cb.length] = ct.apply(p);
47467                 sb[sb.length] = st.apply(p);
47468             }else{
47469                 lb[lb.length] = ct.apply(p);
47470                 lsb[lsb.length] = st.apply(p);
47471             }
47472         }
47473         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47474                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47475         },
47476
47477         updateHeaders : function(){
47478         var html = this.renderHeaders();
47479         this.lockedHd.update(html[0]);
47480         this.mainHd.update(html[1]);
47481     },
47482
47483     /**
47484      * Focuses the specified row.
47485      * @param {Number} row The row index
47486      */
47487     focusRow : function(row){
47488         var x = this.scroller.dom.scrollLeft;
47489         this.focusCell(row, 0, false);
47490         this.scroller.dom.scrollLeft = x;
47491     },
47492
47493     /**
47494      * Focuses the specified cell.
47495      * @param {Number} row The row index
47496      * @param {Number} col The column index
47497      * @param {Boolean} hscroll false to disable horizontal scrolling
47498      */
47499     focusCell : function(row, col, hscroll){
47500         var el = this.ensureVisible(row, col, hscroll);
47501         this.focusEl.alignTo(el, "tl-tl");
47502         if(Roo.isGecko){
47503             this.focusEl.focus();
47504         }else{
47505             this.focusEl.focus.defer(1, this.focusEl);
47506         }
47507     },
47508
47509     /**
47510      * Scrolls the specified cell into view
47511      * @param {Number} row The row index
47512      * @param {Number} col The column index
47513      * @param {Boolean} hscroll false to disable horizontal scrolling
47514      */
47515     ensureVisible : function(row, col, hscroll){
47516         if(typeof row != "number"){
47517             row = row.rowIndex;
47518         }
47519         if(row < 0 && row >= this.ds.getCount()){
47520             return;
47521         }
47522         col = (col !== undefined ? col : 0);
47523         var cm = this.grid.colModel;
47524         while(cm.isHidden(col)){
47525             col++;
47526         }
47527
47528         var el = this.getCell(row, col);
47529         if(!el){
47530             return;
47531         }
47532         var c = this.scroller.dom;
47533
47534         var ctop = parseInt(el.offsetTop, 10);
47535         var cleft = parseInt(el.offsetLeft, 10);
47536         var cbot = ctop + el.offsetHeight;
47537         var cright = cleft + el.offsetWidth;
47538
47539         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47540         var stop = parseInt(c.scrollTop, 10);
47541         var sleft = parseInt(c.scrollLeft, 10);
47542         var sbot = stop + ch;
47543         var sright = sleft + c.clientWidth;
47544
47545         if(ctop < stop){
47546                 c.scrollTop = ctop;
47547         }else if(cbot > sbot){
47548             c.scrollTop = cbot-ch;
47549         }
47550
47551         if(hscroll !== false){
47552             if(cleft < sleft){
47553                 c.scrollLeft = cleft;
47554             }else if(cright > sright){
47555                 c.scrollLeft = cright-c.clientWidth;
47556             }
47557         }
47558         return el;
47559     },
47560
47561     updateColumns : function(){
47562         this.grid.stopEditing();
47563         var cm = this.grid.colModel, colIds = this.getColumnIds();
47564         //var totalWidth = cm.getTotalWidth();
47565         var pos = 0;
47566         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47567             //if(cm.isHidden(i)) continue;
47568             var w = cm.getColumnWidth(i);
47569             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47570             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47571         }
47572         this.updateSplitters();
47573     },
47574
47575     generateRules : function(cm){
47576         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47577         Roo.util.CSS.removeStyleSheet(rulesId);
47578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47579             var cid = cm.getColumnId(i);
47580             var align = '';
47581             if(cm.config[i].align){
47582                 align = 'text-align:'+cm.config[i].align+';';
47583             }
47584             var hidden = '';
47585             if(cm.isHidden(i)){
47586                 hidden = 'display:none;';
47587             }
47588             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47589             ruleBuf.push(
47590                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47591                     this.hdSelector, cid, " {\n", align, width, "}\n",
47592                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47593                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47594         }
47595         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47596     },
47597
47598     updateSplitters : function(){
47599         var cm = this.cm, s = this.getSplitters();
47600         if(s){ // splitters not created yet
47601             var pos = 0, locked = true;
47602             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47603                 if(cm.isHidden(i)) continue;
47604                 var w = cm.getColumnWidth(i);
47605                 if(!cm.isLocked(i) && locked){
47606                     pos = 0;
47607                     locked = false;
47608                 }
47609                 pos += w;
47610                 s[i].style.left = (pos-this.splitOffset) + "px";
47611             }
47612         }
47613     },
47614
47615     handleHiddenChange : function(colModel, colIndex, hidden){
47616         if(hidden){
47617             this.hideColumn(colIndex);
47618         }else{
47619             this.unhideColumn(colIndex);
47620         }
47621     },
47622
47623     hideColumn : function(colIndex){
47624         var cid = this.getColumnId(colIndex);
47625         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47626         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47627         if(Roo.isSafari){
47628             this.updateHeaders();
47629         }
47630         this.updateSplitters();
47631         this.layout();
47632     },
47633
47634     unhideColumn : function(colIndex){
47635         var cid = this.getColumnId(colIndex);
47636         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47637         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47638
47639         if(Roo.isSafari){
47640             this.updateHeaders();
47641         }
47642         this.updateSplitters();
47643         this.layout();
47644     },
47645
47646     insertRows : function(dm, firstRow, lastRow, isUpdate){
47647         if(firstRow == 0 && lastRow == dm.getCount()-1){
47648             this.refresh();
47649         }else{
47650             if(!isUpdate){
47651                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47652             }
47653             var s = this.getScrollState();
47654             var markup = this.renderRows(firstRow, lastRow);
47655             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47656             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47657             this.restoreScroll(s);
47658             if(!isUpdate){
47659                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47660                 this.syncRowHeights(firstRow, lastRow);
47661                 this.stripeRows(firstRow);
47662                 this.layout();
47663             }
47664         }
47665     },
47666
47667     bufferRows : function(markup, target, index){
47668         var before = null, trows = target.rows, tbody = target.tBodies[0];
47669         if(index < trows.length){
47670             before = trows[index];
47671         }
47672         var b = document.createElement("div");
47673         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47674         var rows = b.firstChild.rows;
47675         for(var i = 0, len = rows.length; i < len; i++){
47676             if(before){
47677                 tbody.insertBefore(rows[0], before);
47678             }else{
47679                 tbody.appendChild(rows[0]);
47680             }
47681         }
47682         b.innerHTML = "";
47683         b = null;
47684     },
47685
47686     deleteRows : function(dm, firstRow, lastRow){
47687         if(dm.getRowCount()<1){
47688             this.fireEvent("beforerefresh", this);
47689             this.mainBody.update("");
47690             this.lockedBody.update("");
47691             this.fireEvent("refresh", this);
47692         }else{
47693             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47694             var bt = this.getBodyTable();
47695             var tbody = bt.firstChild;
47696             var rows = bt.rows;
47697             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47698                 tbody.removeChild(rows[firstRow]);
47699             }
47700             this.stripeRows(firstRow);
47701             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47702         }
47703     },
47704
47705     updateRows : function(dataSource, firstRow, lastRow){
47706         var s = this.getScrollState();
47707         this.refresh();
47708         this.restoreScroll(s);
47709     },
47710
47711     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47712         if(!noRefresh){
47713            this.refresh();
47714         }
47715         this.updateHeaderSortState();
47716     },
47717
47718     getScrollState : function(){
47719         var sb = this.scroller.dom;
47720         return {left: sb.scrollLeft, top: sb.scrollTop};
47721     },
47722
47723     stripeRows : function(startRow){
47724         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47725             return;
47726         }
47727         startRow = startRow || 0;
47728         var rows = this.getBodyTable().rows;
47729         var lrows = this.getLockedTable().rows;
47730         var cls = ' x-grid-row-alt ';
47731         for(var i = startRow, len = rows.length; i < len; i++){
47732             var row = rows[i], lrow = lrows[i];
47733             var isAlt = ((i+1) % 2 == 0);
47734             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47735             if(isAlt == hasAlt){
47736                 continue;
47737             }
47738             if(isAlt){
47739                 row.className += " x-grid-row-alt";
47740             }else{
47741                 row.className = row.className.replace("x-grid-row-alt", "");
47742             }
47743             if(lrow){
47744                 lrow.className = row.className;
47745             }
47746         }
47747     },
47748
47749     restoreScroll : function(state){
47750         var sb = this.scroller.dom;
47751         sb.scrollLeft = state.left;
47752         sb.scrollTop = state.top;
47753         this.syncScroll();
47754     },
47755
47756     syncScroll : function(){
47757         var sb = this.scroller.dom;
47758         var sh = this.mainHd.dom;
47759         var bs = this.mainBody.dom;
47760         var lv = this.lockedBody.dom;
47761         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47762         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47763     },
47764
47765     handleScroll : function(e){
47766         this.syncScroll();
47767         var sb = this.scroller.dom;
47768         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47769         e.stopEvent();
47770     },
47771
47772     handleWheel : function(e){
47773         var d = e.getWheelDelta();
47774         this.scroller.dom.scrollTop -= d*22;
47775         // set this here to prevent jumpy scrolling on large tables
47776         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47777         e.stopEvent();
47778     },
47779
47780     renderRows : function(startRow, endRow){
47781         // pull in all the crap needed to render rows
47782         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47783         var colCount = cm.getColumnCount();
47784
47785         if(ds.getCount() < 1){
47786             return ["", ""];
47787         }
47788
47789         // build a map for all the columns
47790         var cs = [];
47791         for(var i = 0; i < colCount; i++){
47792             var name = cm.getDataIndex(i);
47793             cs[i] = {
47794                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47795                 renderer : cm.getRenderer(i),
47796                 id : cm.getColumnId(i),
47797                 locked : cm.isLocked(i)
47798             };
47799         }
47800
47801         startRow = startRow || 0;
47802         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47803
47804         // records to render
47805         var rs = ds.getRange(startRow, endRow);
47806
47807         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47808     },
47809
47810     // As much as I hate to duplicate code, this was branched because FireFox really hates
47811     // [].join("") on strings. The performance difference was substantial enough to
47812     // branch this function
47813     doRender : Roo.isGecko ?
47814             function(cs, rs, ds, startRow, colCount, stripe){
47815                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47816                 // buffers
47817                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47818                 for(var j = 0, len = rs.length; j < len; j++){
47819                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47820                     for(var i = 0; i < colCount; i++){
47821                         c = cs[i];
47822                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47823                         p.id = c.id;
47824                         p.css = p.attr = "";
47825                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47826                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47827                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47828                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47829                         }
47830                         var markup = ct.apply(p);
47831                         if(!c.locked){
47832                             cb+= markup;
47833                         }else{
47834                             lcb+= markup;
47835                         }
47836                     }
47837                     var alt = [];
47838                     if(stripe && ((rowIndex+1) % 2 == 0)){
47839                         alt[0] = "x-grid-row-alt";
47840                     }
47841                     if(r.dirty){
47842                         alt[1] = " x-grid-dirty-row";
47843                     }
47844                     rp.cells = lcb;
47845                     if(this.getRowClass){
47846                         alt[2] = this.getRowClass(r, rowIndex);
47847                     }
47848                     rp.alt = alt.join(" ");
47849                     lbuf+= rt.apply(rp);
47850                     rp.cells = cb;
47851                     buf+=  rt.apply(rp);
47852                 }
47853                 return [lbuf, buf];
47854             } :
47855             function(cs, rs, ds, startRow, colCount, stripe){
47856                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47857                 // buffers
47858                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47859                 for(var j = 0, len = rs.length; j < len; j++){
47860                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47861                     for(var i = 0; i < colCount; i++){
47862                         c = cs[i];
47863                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47864                         p.id = c.id;
47865                         p.css = p.attr = "";
47866                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47867                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47868                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47869                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47870                         }
47871                         var markup = ct.apply(p);
47872                         if(!c.locked){
47873                             cb[cb.length] = markup;
47874                         }else{
47875                             lcb[lcb.length] = markup;
47876                         }
47877                     }
47878                     var alt = [];
47879                     if(stripe && ((rowIndex+1) % 2 == 0)){
47880                         alt[0] = "x-grid-row-alt";
47881                     }
47882                     if(r.dirty){
47883                         alt[1] = " x-grid-dirty-row";
47884                     }
47885                     rp.cells = lcb;
47886                     if(this.getRowClass){
47887                         alt[2] = this.getRowClass(r, rowIndex);
47888                     }
47889                     rp.alt = alt.join(" ");
47890                     rp.cells = lcb.join("");
47891                     lbuf[lbuf.length] = rt.apply(rp);
47892                     rp.cells = cb.join("");
47893                     buf[buf.length] =  rt.apply(rp);
47894                 }
47895                 return [lbuf.join(""), buf.join("")];
47896             },
47897
47898     renderBody : function(){
47899         var markup = this.renderRows();
47900         var bt = this.templates.body;
47901         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47902     },
47903
47904     /**
47905      * Refreshes the grid
47906      * @param {Boolean} headersToo
47907      */
47908     refresh : function(headersToo){
47909         this.fireEvent("beforerefresh", this);
47910         this.grid.stopEditing();
47911         var result = this.renderBody();
47912         this.lockedBody.update(result[0]);
47913         this.mainBody.update(result[1]);
47914         if(headersToo === true){
47915             this.updateHeaders();
47916             this.updateColumns();
47917             this.updateSplitters();
47918             this.updateHeaderSortState();
47919         }
47920         this.syncRowHeights();
47921         this.layout();
47922         this.fireEvent("refresh", this);
47923     },
47924
47925     handleColumnMove : function(cm, oldIndex, newIndex){
47926         this.indexMap = null;
47927         var s = this.getScrollState();
47928         this.refresh(true);
47929         this.restoreScroll(s);
47930         this.afterMove(newIndex);
47931     },
47932
47933     afterMove : function(colIndex){
47934         if(this.enableMoveAnim && Roo.enableFx){
47935             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47936         }
47937     },
47938
47939     updateCell : function(dm, rowIndex, dataIndex){
47940         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47941         if(typeof colIndex == "undefined"){ // not present in grid
47942             return;
47943         }
47944         var cm = this.grid.colModel;
47945         var cell = this.getCell(rowIndex, colIndex);
47946         var cellText = this.getCellText(rowIndex, colIndex);
47947
47948         var p = {
47949             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47950             id : cm.getColumnId(colIndex),
47951             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47952         };
47953         var renderer = cm.getRenderer(colIndex);
47954         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47955         if(typeof val == "undefined" || val === "") val = "&#160;";
47956         cellText.innerHTML = val;
47957         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47958         this.syncRowHeights(rowIndex, rowIndex);
47959     },
47960
47961     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47962         var maxWidth = 0;
47963         if(this.grid.autoSizeHeaders){
47964             var h = this.getHeaderCellMeasure(colIndex);
47965             maxWidth = Math.max(maxWidth, h.scrollWidth);
47966         }
47967         var tb, index;
47968         if(this.cm.isLocked(colIndex)){
47969             tb = this.getLockedTable();
47970             index = colIndex;
47971         }else{
47972             tb = this.getBodyTable();
47973             index = colIndex - this.cm.getLockedCount();
47974         }
47975         if(tb && tb.rows){
47976             var rows = tb.rows;
47977             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47978             for(var i = 0; i < stopIndex; i++){
47979                 var cell = rows[i].childNodes[index].firstChild;
47980                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47981             }
47982         }
47983         return maxWidth + /*margin for error in IE*/ 5;
47984     },
47985     /**
47986      * Autofit a column to its content.
47987      * @param {Number} colIndex
47988      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47989      */
47990      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47991          if(this.cm.isHidden(colIndex)){
47992              return; // can't calc a hidden column
47993          }
47994         if(forceMinSize){
47995             var cid = this.cm.getColumnId(colIndex);
47996             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47997            if(this.grid.autoSizeHeaders){
47998                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47999            }
48000         }
48001         var newWidth = this.calcColumnWidth(colIndex);
48002         this.cm.setColumnWidth(colIndex,
48003             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48004         if(!suppressEvent){
48005             this.grid.fireEvent("columnresize", colIndex, newWidth);
48006         }
48007     },
48008
48009     /**
48010      * Autofits all columns to their content and then expands to fit any extra space in the grid
48011      */
48012      autoSizeColumns : function(){
48013         var cm = this.grid.colModel;
48014         var colCount = cm.getColumnCount();
48015         for(var i = 0; i < colCount; i++){
48016             this.autoSizeColumn(i, true, true);
48017         }
48018         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48019             this.fitColumns();
48020         }else{
48021             this.updateColumns();
48022             this.layout();
48023         }
48024     },
48025
48026     /**
48027      * Autofits all columns to the grid's width proportionate with their current size
48028      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48029      */
48030     fitColumns : function(reserveScrollSpace){
48031         var cm = this.grid.colModel;
48032         var colCount = cm.getColumnCount();
48033         var cols = [];
48034         var width = 0;
48035         var i, w;
48036         for (i = 0; i < colCount; i++){
48037             if(!cm.isHidden(i) && !cm.isFixed(i)){
48038                 w = cm.getColumnWidth(i);
48039                 cols.push(i);
48040                 cols.push(w);
48041                 width += w;
48042             }
48043         }
48044         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48045         if(reserveScrollSpace){
48046             avail -= 17;
48047         }
48048         var frac = (avail - cm.getTotalWidth())/width;
48049         while (cols.length){
48050             w = cols.pop();
48051             i = cols.pop();
48052             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48053         }
48054         this.updateColumns();
48055         this.layout();
48056     },
48057
48058     onRowSelect : function(rowIndex){
48059         var row = this.getRowComposite(rowIndex);
48060         row.addClass("x-grid-row-selected");
48061     },
48062
48063     onRowDeselect : function(rowIndex){
48064         var row = this.getRowComposite(rowIndex);
48065         row.removeClass("x-grid-row-selected");
48066     },
48067
48068     onCellSelect : function(row, col){
48069         var cell = this.getCell(row, col);
48070         if(cell){
48071             Roo.fly(cell).addClass("x-grid-cell-selected");
48072         }
48073     },
48074
48075     onCellDeselect : function(row, col){
48076         var cell = this.getCell(row, col);
48077         if(cell){
48078             Roo.fly(cell).removeClass("x-grid-cell-selected");
48079         }
48080     },
48081
48082     updateHeaderSortState : function(){
48083         var state = this.ds.getSortState();
48084         if(!state){
48085             return;
48086         }
48087         this.sortState = state;
48088         var sortColumn = this.cm.findColumnIndex(state.field);
48089         if(sortColumn != -1){
48090             var sortDir = state.direction;
48091             var sc = this.sortClasses;
48092             var hds = this.el.select(this.headerSelector).removeClass(sc);
48093             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48094         }
48095     },
48096
48097     handleHeaderClick : function(g, index){
48098         if(this.headersDisabled){
48099             return;
48100         }
48101         var dm = g.dataSource, cm = g.colModel;
48102             if(!cm.isSortable(index)){
48103             return;
48104         }
48105             g.stopEditing();
48106         dm.sort(cm.getDataIndex(index));
48107     },
48108
48109
48110     destroy : function(){
48111         if(this.colMenu){
48112             this.colMenu.removeAll();
48113             Roo.menu.MenuMgr.unregister(this.colMenu);
48114             this.colMenu.getEl().remove();
48115             delete this.colMenu;
48116         }
48117         if(this.hmenu){
48118             this.hmenu.removeAll();
48119             Roo.menu.MenuMgr.unregister(this.hmenu);
48120             this.hmenu.getEl().remove();
48121             delete this.hmenu;
48122         }
48123         if(this.grid.enableColumnMove){
48124             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48125             if(dds){
48126                 for(var dd in dds){
48127                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48128                         var elid = dds[dd].dragElId;
48129                         dds[dd].unreg();
48130                         Roo.get(elid).remove();
48131                     } else if(dds[dd].config.isTarget){
48132                         dds[dd].proxyTop.remove();
48133                         dds[dd].proxyBottom.remove();
48134                         dds[dd].unreg();
48135                     }
48136                     if(Roo.dd.DDM.locationCache[dd]){
48137                         delete Roo.dd.DDM.locationCache[dd];
48138                     }
48139                 }
48140                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48141             }
48142         }
48143         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48144         this.bind(null, null);
48145         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48146     },
48147
48148     handleLockChange : function(){
48149         this.refresh(true);
48150     },
48151
48152     onDenyColumnLock : function(){
48153
48154     },
48155
48156     onDenyColumnHide : function(){
48157
48158     },
48159
48160     handleHdMenuClick : function(item){
48161         var index = this.hdCtxIndex;
48162         var cm = this.cm, ds = this.ds;
48163         switch(item.id){
48164             case "asc":
48165                 ds.sort(cm.getDataIndex(index), "ASC");
48166                 break;
48167             case "desc":
48168                 ds.sort(cm.getDataIndex(index), "DESC");
48169                 break;
48170             case "lock":
48171                 var lc = cm.getLockedCount();
48172                 if(cm.getColumnCount(true) <= lc+1){
48173                     this.onDenyColumnLock();
48174                     return;
48175                 }
48176                 if(lc != index){
48177                     cm.setLocked(index, true, true);
48178                     cm.moveColumn(index, lc);
48179                     this.grid.fireEvent("columnmove", index, lc);
48180                 }else{
48181                     cm.setLocked(index, true);
48182                 }
48183             break;
48184             case "unlock":
48185                 var lc = cm.getLockedCount();
48186                 if((lc-1) != index){
48187                     cm.setLocked(index, false, true);
48188                     cm.moveColumn(index, lc-1);
48189                     this.grid.fireEvent("columnmove", index, lc-1);
48190                 }else{
48191                     cm.setLocked(index, false);
48192                 }
48193             break;
48194             default:
48195                 index = cm.getIndexById(item.id.substr(4));
48196                 if(index != -1){
48197                     if(item.checked && cm.getColumnCount(true) <= 1){
48198                         this.onDenyColumnHide();
48199                         return false;
48200                     }
48201                     cm.setHidden(index, item.checked);
48202                 }
48203         }
48204         return true;
48205     },
48206
48207     beforeColMenuShow : function(){
48208         var cm = this.cm,  colCount = cm.getColumnCount();
48209         this.colMenu.removeAll();
48210         for(var i = 0; i < colCount; i++){
48211             this.colMenu.add(new Roo.menu.CheckItem({
48212                 id: "col-"+cm.getColumnId(i),
48213                 text: cm.getColumnHeader(i),
48214                 checked: !cm.isHidden(i),
48215                 hideOnClick:false
48216             }));
48217         }
48218     },
48219
48220     handleHdCtx : function(g, index, e){
48221         e.stopEvent();
48222         var hd = this.getHeaderCell(index);
48223         this.hdCtxIndex = index;
48224         var ms = this.hmenu.items, cm = this.cm;
48225         ms.get("asc").setDisabled(!cm.isSortable(index));
48226         ms.get("desc").setDisabled(!cm.isSortable(index));
48227         if(this.grid.enableColLock !== false){
48228             ms.get("lock").setDisabled(cm.isLocked(index));
48229             ms.get("unlock").setDisabled(!cm.isLocked(index));
48230         }
48231         this.hmenu.show(hd, "tl-bl");
48232     },
48233
48234     handleHdOver : function(e){
48235         var hd = this.findHeaderCell(e.getTarget());
48236         if(hd && !this.headersDisabled){
48237             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48238                this.fly(hd).addClass("x-grid-hd-over");
48239             }
48240         }
48241     },
48242
48243     handleHdOut : function(e){
48244         var hd = this.findHeaderCell(e.getTarget());
48245         if(hd){
48246             this.fly(hd).removeClass("x-grid-hd-over");
48247         }
48248     },
48249
48250     handleSplitDblClick : function(e, t){
48251         var i = this.getCellIndex(t);
48252         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48253             this.autoSizeColumn(i, true);
48254             this.layout();
48255         }
48256     },
48257
48258     render : function(){
48259
48260         var cm = this.cm;
48261         var colCount = cm.getColumnCount();
48262
48263         if(this.grid.monitorWindowResize === true){
48264             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48265         }
48266         var header = this.renderHeaders();
48267         var body = this.templates.body.apply({rows:""});
48268         var html = this.templates.master.apply({
48269             lockedBody: body,
48270             body: body,
48271             lockedHeader: header[0],
48272             header: header[1]
48273         });
48274
48275         //this.updateColumns();
48276
48277         this.grid.getGridEl().dom.innerHTML = html;
48278
48279         this.initElements();
48280         
48281         // a kludge to fix the random scolling effect in webkit
48282         this.el.on("scroll", function() {
48283             this.el.dom.scrollTop=0; // hopefully not recursive..
48284         },this);
48285
48286         this.scroller.on("scroll", this.handleScroll, this);
48287         this.lockedBody.on("mousewheel", this.handleWheel, this);
48288         this.mainBody.on("mousewheel", this.handleWheel, this);
48289
48290         this.mainHd.on("mouseover", this.handleHdOver, this);
48291         this.mainHd.on("mouseout", this.handleHdOut, this);
48292         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48293                 {delegate: "."+this.splitClass});
48294
48295         this.lockedHd.on("mouseover", this.handleHdOver, this);
48296         this.lockedHd.on("mouseout", this.handleHdOut, this);
48297         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48298                 {delegate: "."+this.splitClass});
48299
48300         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48301             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48302         }
48303
48304         this.updateSplitters();
48305
48306         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48307             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48308             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48309         }
48310
48311         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48312             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48313             this.hmenu.add(
48314                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48315                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48316             );
48317             if(this.grid.enableColLock !== false){
48318                 this.hmenu.add('-',
48319                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48320                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48321                 );
48322             }
48323             if(this.grid.enableColumnHide !== false){
48324
48325                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48326                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48327                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48328
48329                 this.hmenu.add('-',
48330                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48331                 );
48332             }
48333             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48334
48335             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48336         }
48337
48338         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48339             this.dd = new Roo.grid.GridDragZone(this.grid, {
48340                 ddGroup : this.grid.ddGroup || 'GridDD'
48341             });
48342         }
48343
48344         /*
48345         for(var i = 0; i < colCount; i++){
48346             if(cm.isHidden(i)){
48347                 this.hideColumn(i);
48348             }
48349             if(cm.config[i].align){
48350                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48351                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48352             }
48353         }*/
48354         
48355         this.updateHeaderSortState();
48356
48357         this.beforeInitialResize();
48358         this.layout(true);
48359
48360         // two part rendering gives faster view to the user
48361         this.renderPhase2.defer(1, this);
48362     },
48363
48364     renderPhase2 : function(){
48365         // render the rows now
48366         this.refresh();
48367         if(this.grid.autoSizeColumns){
48368             this.autoSizeColumns();
48369         }
48370     },
48371
48372     beforeInitialResize : function(){
48373
48374     },
48375
48376     onColumnSplitterMoved : function(i, w){
48377         this.userResized = true;
48378         var cm = this.grid.colModel;
48379         cm.setColumnWidth(i, w, true);
48380         var cid = cm.getColumnId(i);
48381         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48382         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48383         this.updateSplitters();
48384         this.layout();
48385         this.grid.fireEvent("columnresize", i, w);
48386     },
48387
48388     syncRowHeights : function(startIndex, endIndex){
48389         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48390             startIndex = startIndex || 0;
48391             var mrows = this.getBodyTable().rows;
48392             var lrows = this.getLockedTable().rows;
48393             var len = mrows.length-1;
48394             endIndex = Math.min(endIndex || len, len);
48395             for(var i = startIndex; i <= endIndex; i++){
48396                 var m = mrows[i], l = lrows[i];
48397                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48398                 m.style.height = l.style.height = h + "px";
48399             }
48400         }
48401     },
48402
48403     layout : function(initialRender, is2ndPass){
48404         var g = this.grid;
48405         var auto = g.autoHeight;
48406         var scrollOffset = 16;
48407         var c = g.getGridEl(), cm = this.cm,
48408                 expandCol = g.autoExpandColumn,
48409                 gv = this;
48410         //c.beginMeasure();
48411
48412         if(!c.dom.offsetWidth){ // display:none?
48413             if(initialRender){
48414                 this.lockedWrap.show();
48415                 this.mainWrap.show();
48416             }
48417             return;
48418         }
48419
48420         var hasLock = this.cm.isLocked(0);
48421
48422         var tbh = this.headerPanel.getHeight();
48423         var bbh = this.footerPanel.getHeight();
48424
48425         if(auto){
48426             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48427             var newHeight = ch + c.getBorderWidth("tb");
48428             if(g.maxHeight){
48429                 newHeight = Math.min(g.maxHeight, newHeight);
48430             }
48431             c.setHeight(newHeight);
48432         }
48433
48434         if(g.autoWidth){
48435             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48436         }
48437
48438         var s = this.scroller;
48439
48440         var csize = c.getSize(true);
48441
48442         this.el.setSize(csize.width, csize.height);
48443
48444         this.headerPanel.setWidth(csize.width);
48445         this.footerPanel.setWidth(csize.width);
48446
48447         var hdHeight = this.mainHd.getHeight();
48448         var vw = csize.width;
48449         var vh = csize.height - (tbh + bbh);
48450
48451         s.setSize(vw, vh);
48452
48453         var bt = this.getBodyTable();
48454         var ltWidth = hasLock ?
48455                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48456
48457         var scrollHeight = bt.offsetHeight;
48458         var scrollWidth = ltWidth + bt.offsetWidth;
48459         var vscroll = false, hscroll = false;
48460
48461         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48462
48463         var lw = this.lockedWrap, mw = this.mainWrap;
48464         var lb = this.lockedBody, mb = this.mainBody;
48465
48466         setTimeout(function(){
48467             var t = s.dom.offsetTop;
48468             var w = s.dom.clientWidth,
48469                 h = s.dom.clientHeight;
48470
48471             lw.setTop(t);
48472             lw.setSize(ltWidth, h);
48473
48474             mw.setLeftTop(ltWidth, t);
48475             mw.setSize(w-ltWidth, h);
48476
48477             lb.setHeight(h-hdHeight);
48478             mb.setHeight(h-hdHeight);
48479
48480             if(is2ndPass !== true && !gv.userResized && expandCol){
48481                 // high speed resize without full column calculation
48482                 
48483                 var ci = cm.getIndexById(expandCol);
48484                 if (ci < 0) {
48485                     ci = cm.findColumnIndex(expandCol);
48486                 }
48487                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48488                 var expandId = cm.getColumnId(ci);
48489                 var  tw = cm.getTotalWidth(false);
48490                 var currentWidth = cm.getColumnWidth(ci);
48491                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48492                 if(currentWidth != cw){
48493                     cm.setColumnWidth(ci, cw, true);
48494                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48495                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48496                     gv.updateSplitters();
48497                     gv.layout(false, true);
48498                 }
48499             }
48500
48501             if(initialRender){
48502                 lw.show();
48503                 mw.show();
48504             }
48505             //c.endMeasure();
48506         }, 10);
48507     },
48508
48509     onWindowResize : function(){
48510         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48511             return;
48512         }
48513         this.layout();
48514     },
48515
48516     appendFooter : function(parentEl){
48517         return null;
48518     },
48519
48520     sortAscText : "Sort Ascending",
48521     sortDescText : "Sort Descending",
48522     lockText : "Lock Column",
48523     unlockText : "Unlock Column",
48524     columnsText : "Columns"
48525 });
48526
48527
48528 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48529     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48530     this.proxy.el.addClass('x-grid3-col-dd');
48531 };
48532
48533 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48534     handleMouseDown : function(e){
48535
48536     },
48537
48538     callHandleMouseDown : function(e){
48539         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48540     }
48541 });
48542 /*
48543  * Based on:
48544  * Ext JS Library 1.1.1
48545  * Copyright(c) 2006-2007, Ext JS, LLC.
48546  *
48547  * Originally Released Under LGPL - original licence link has changed is not relivant.
48548  *
48549  * Fork - LGPL
48550  * <script type="text/javascript">
48551  */
48552  
48553 // private
48554 // This is a support class used internally by the Grid components
48555 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48556     this.grid = grid;
48557     this.view = grid.getView();
48558     this.proxy = this.view.resizeProxy;
48559     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48560         "gridSplitters" + this.grid.getGridEl().id, {
48561         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48562     });
48563     this.setHandleElId(Roo.id(hd));
48564     this.setOuterHandleElId(Roo.id(hd2));
48565     this.scroll = false;
48566 };
48567 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48568     fly: Roo.Element.fly,
48569
48570     b4StartDrag : function(x, y){
48571         this.view.headersDisabled = true;
48572         this.proxy.setHeight(this.view.mainWrap.getHeight());
48573         var w = this.cm.getColumnWidth(this.cellIndex);
48574         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48575         this.resetConstraints();
48576         this.setXConstraint(minw, 1000);
48577         this.setYConstraint(0, 0);
48578         this.minX = x - minw;
48579         this.maxX = x + 1000;
48580         this.startPos = x;
48581         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48582     },
48583
48584
48585     handleMouseDown : function(e){
48586         ev = Roo.EventObject.setEvent(e);
48587         var t = this.fly(ev.getTarget());
48588         if(t.hasClass("x-grid-split")){
48589             this.cellIndex = this.view.getCellIndex(t.dom);
48590             this.split = t.dom;
48591             this.cm = this.grid.colModel;
48592             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48593                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48594             }
48595         }
48596     },
48597
48598     endDrag : function(e){
48599         this.view.headersDisabled = false;
48600         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48601         var diff = endX - this.startPos;
48602         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48603     },
48604
48605     autoOffset : function(){
48606         this.setDelta(0,0);
48607     }
48608 });/*
48609  * Based on:
48610  * Ext JS Library 1.1.1
48611  * Copyright(c) 2006-2007, Ext JS, LLC.
48612  *
48613  * Originally Released Under LGPL - original licence link has changed is not relivant.
48614  *
48615  * Fork - LGPL
48616  * <script type="text/javascript">
48617  */
48618  
48619 // private
48620 // This is a support class used internally by the Grid components
48621 Roo.grid.GridDragZone = function(grid, config){
48622     this.view = grid.getView();
48623     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48624     if(this.view.lockedBody){
48625         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48626         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48627     }
48628     this.scroll = false;
48629     this.grid = grid;
48630     this.ddel = document.createElement('div');
48631     this.ddel.className = 'x-grid-dd-wrap';
48632 };
48633
48634 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48635     ddGroup : "GridDD",
48636
48637     getDragData : function(e){
48638         var t = Roo.lib.Event.getTarget(e);
48639         var rowIndex = this.view.findRowIndex(t);
48640         if(rowIndex !== false){
48641             var sm = this.grid.selModel;
48642             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48643               //  sm.mouseDown(e, t);
48644             //}
48645             if (e.hasModifier()){
48646                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48647             }
48648             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48649         }
48650         return false;
48651     },
48652
48653     onInitDrag : function(e){
48654         var data = this.dragData;
48655         this.ddel.innerHTML = this.grid.getDragDropText();
48656         this.proxy.update(this.ddel);
48657         // fire start drag?
48658     },
48659
48660     afterRepair : function(){
48661         this.dragging = false;
48662     },
48663
48664     getRepairXY : function(e, data){
48665         return false;
48666     },
48667
48668     onEndDrag : function(data, e){
48669         // fire end drag?
48670     },
48671
48672     onValidDrop : function(dd, e, id){
48673         // fire drag drop?
48674         this.hideProxy();
48675     },
48676
48677     beforeInvalidDrop : function(e, id){
48678
48679     }
48680 });/*
48681  * Based on:
48682  * Ext JS Library 1.1.1
48683  * Copyright(c) 2006-2007, Ext JS, LLC.
48684  *
48685  * Originally Released Under LGPL - original licence link has changed is not relivant.
48686  *
48687  * Fork - LGPL
48688  * <script type="text/javascript">
48689  */
48690  
48691
48692 /**
48693  * @class Roo.grid.ColumnModel
48694  * @extends Roo.util.Observable
48695  * This is the default implementation of a ColumnModel used by the Grid. It defines
48696  * the columns in the grid.
48697  * <br>Usage:<br>
48698  <pre><code>
48699  var colModel = new Roo.grid.ColumnModel([
48700         {header: "Ticker", width: 60, sortable: true, locked: true},
48701         {header: "Company Name", width: 150, sortable: true},
48702         {header: "Market Cap.", width: 100, sortable: true},
48703         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48704         {header: "Employees", width: 100, sortable: true, resizable: false}
48705  ]);
48706  </code></pre>
48707  * <p>
48708  
48709  * The config options listed for this class are options which may appear in each
48710  * individual column definition.
48711  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48712  * @constructor
48713  * @param {Object} config An Array of column config objects. See this class's
48714  * config objects for details.
48715 */
48716 Roo.grid.ColumnModel = function(config){
48717         /**
48718      * The config passed into the constructor
48719      */
48720     this.config = config;
48721     this.lookup = {};
48722
48723     // if no id, create one
48724     // if the column does not have a dataIndex mapping,
48725     // map it to the order it is in the config
48726     for(var i = 0, len = config.length; i < len; i++){
48727         var c = config[i];
48728         if(typeof c.dataIndex == "undefined"){
48729             c.dataIndex = i;
48730         }
48731         if(typeof c.renderer == "string"){
48732             c.renderer = Roo.util.Format[c.renderer];
48733         }
48734         if(typeof c.id == "undefined"){
48735             c.id = Roo.id();
48736         }
48737         if(c.editor && c.editor.xtype){
48738             c.editor  = Roo.factory(c.editor, Roo.grid);
48739         }
48740         if(c.editor && c.editor.isFormField){
48741             c.editor = new Roo.grid.GridEditor(c.editor);
48742         }
48743         this.lookup[c.id] = c;
48744     }
48745
48746     /**
48747      * The width of columns which have no width specified (defaults to 100)
48748      * @type Number
48749      */
48750     this.defaultWidth = 100;
48751
48752     /**
48753      * Default sortable of columns which have no sortable specified (defaults to false)
48754      * @type Boolean
48755      */
48756     this.defaultSortable = false;
48757
48758     this.addEvents({
48759         /**
48760              * @event widthchange
48761              * Fires when the width of a column changes.
48762              * @param {ColumnModel} this
48763              * @param {Number} columnIndex The column index
48764              * @param {Number} newWidth The new width
48765              */
48766             "widthchange": true,
48767         /**
48768              * @event headerchange
48769              * Fires when the text of a header changes.
48770              * @param {ColumnModel} this
48771              * @param {Number} columnIndex The column index
48772              * @param {Number} newText The new header text
48773              */
48774             "headerchange": true,
48775         /**
48776              * @event hiddenchange
48777              * Fires when a column is hidden or "unhidden".
48778              * @param {ColumnModel} this
48779              * @param {Number} columnIndex The column index
48780              * @param {Boolean} hidden true if hidden, false otherwise
48781              */
48782             "hiddenchange": true,
48783             /**
48784          * @event columnmoved
48785          * Fires when a column is moved.
48786          * @param {ColumnModel} this
48787          * @param {Number} oldIndex
48788          * @param {Number} newIndex
48789          */
48790         "columnmoved" : true,
48791         /**
48792          * @event columlockchange
48793          * Fires when a column's locked state is changed
48794          * @param {ColumnModel} this
48795          * @param {Number} colIndex
48796          * @param {Boolean} locked true if locked
48797          */
48798         "columnlockchange" : true
48799     });
48800     Roo.grid.ColumnModel.superclass.constructor.call(this);
48801 };
48802 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48803     /**
48804      * @cfg {String} header The header text to display in the Grid view.
48805      */
48806     /**
48807      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48808      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48809      * specified, the column's index is used as an index into the Record's data Array.
48810      */
48811     /**
48812      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48813      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48814      */
48815     /**
48816      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48817      * Defaults to the value of the {@link #defaultSortable} property.
48818      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48819      */
48820     /**
48821      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48822      */
48823     /**
48824      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48825      */
48826     /**
48827      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48828      */
48829     /**
48830      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48831      */
48832     /**
48833      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48834      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48835      * default renderer uses the raw data value.
48836      */
48837        /**
48838      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48839      */
48840     /**
48841      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48842      */
48843
48844     /**
48845      * Returns the id of the column at the specified index.
48846      * @param {Number} index The column index
48847      * @return {String} the id
48848      */
48849     getColumnId : function(index){
48850         return this.config[index].id;
48851     },
48852
48853     /**
48854      * Returns the column for a specified id.
48855      * @param {String} id The column id
48856      * @return {Object} the column
48857      */
48858     getColumnById : function(id){
48859         return this.lookup[id];
48860     },
48861
48862     
48863     /**
48864      * Returns the column for a specified dataIndex.
48865      * @param {String} dataIndex The column dataIndex
48866      * @return {Object|Boolean} the column or false if not found
48867      */
48868     getColumnByDataIndex: function(dataIndex){
48869         var index = this.findColumnIndex(dataIndex);
48870         return index > -1 ? this.config[index] : false;
48871     },
48872     
48873     /**
48874      * Returns the index for a specified column id.
48875      * @param {String} id The column id
48876      * @return {Number} the index, or -1 if not found
48877      */
48878     getIndexById : function(id){
48879         for(var i = 0, len = this.config.length; i < len; i++){
48880             if(this.config[i].id == id){
48881                 return i;
48882             }
48883         }
48884         return -1;
48885     },
48886     
48887     /**
48888      * Returns the index for a specified column dataIndex.
48889      * @param {String} dataIndex The column dataIndex
48890      * @return {Number} the index, or -1 if not found
48891      */
48892     
48893     findColumnIndex : function(dataIndex){
48894         for(var i = 0, len = this.config.length; i < len; i++){
48895             if(this.config[i].dataIndex == dataIndex){
48896                 return i;
48897             }
48898         }
48899         return -1;
48900     },
48901     
48902     
48903     moveColumn : function(oldIndex, newIndex){
48904         var c = this.config[oldIndex];
48905         this.config.splice(oldIndex, 1);
48906         this.config.splice(newIndex, 0, c);
48907         this.dataMap = null;
48908         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48909     },
48910
48911     isLocked : function(colIndex){
48912         return this.config[colIndex].locked === true;
48913     },
48914
48915     setLocked : function(colIndex, value, suppressEvent){
48916         if(this.isLocked(colIndex) == value){
48917             return;
48918         }
48919         this.config[colIndex].locked = value;
48920         if(!suppressEvent){
48921             this.fireEvent("columnlockchange", this, colIndex, value);
48922         }
48923     },
48924
48925     getTotalLockedWidth : function(){
48926         var totalWidth = 0;
48927         for(var i = 0; i < this.config.length; i++){
48928             if(this.isLocked(i) && !this.isHidden(i)){
48929                 this.totalWidth += this.getColumnWidth(i);
48930             }
48931         }
48932         return totalWidth;
48933     },
48934
48935     getLockedCount : function(){
48936         for(var i = 0, len = this.config.length; i < len; i++){
48937             if(!this.isLocked(i)){
48938                 return i;
48939             }
48940         }
48941     },
48942
48943     /**
48944      * Returns the number of columns.
48945      * @return {Number}
48946      */
48947     getColumnCount : function(visibleOnly){
48948         if(visibleOnly === true){
48949             var c = 0;
48950             for(var i = 0, len = this.config.length; i < len; i++){
48951                 if(!this.isHidden(i)){
48952                     c++;
48953                 }
48954             }
48955             return c;
48956         }
48957         return this.config.length;
48958     },
48959
48960     /**
48961      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48962      * @param {Function} fn
48963      * @param {Object} scope (optional)
48964      * @return {Array} result
48965      */
48966     getColumnsBy : function(fn, scope){
48967         var r = [];
48968         for(var i = 0, len = this.config.length; i < len; i++){
48969             var c = this.config[i];
48970             if(fn.call(scope||this, c, i) === true){
48971                 r[r.length] = c;
48972             }
48973         }
48974         return r;
48975     },
48976
48977     /**
48978      * Returns true if the specified column is sortable.
48979      * @param {Number} col The column index
48980      * @return {Boolean}
48981      */
48982     isSortable : function(col){
48983         if(typeof this.config[col].sortable == "undefined"){
48984             return this.defaultSortable;
48985         }
48986         return this.config[col].sortable;
48987     },
48988
48989     /**
48990      * Returns the rendering (formatting) function defined for the column.
48991      * @param {Number} col The column index.
48992      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48993      */
48994     getRenderer : function(col){
48995         if(!this.config[col].renderer){
48996             return Roo.grid.ColumnModel.defaultRenderer;
48997         }
48998         return this.config[col].renderer;
48999     },
49000
49001     /**
49002      * Sets the rendering (formatting) function for a column.
49003      * @param {Number} col The column index
49004      * @param {Function} fn The function to use to process the cell's raw data
49005      * to return HTML markup for the grid view. The render function is called with
49006      * the following parameters:<ul>
49007      * <li>Data value.</li>
49008      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49009      * <li>css A CSS style string to apply to the table cell.</li>
49010      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49011      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49012      * <li>Row index</li>
49013      * <li>Column index</li>
49014      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49015      */
49016     setRenderer : function(col, fn){
49017         this.config[col].renderer = fn;
49018     },
49019
49020     /**
49021      * Returns the width for the specified column.
49022      * @param {Number} col The column index
49023      * @return {Number}
49024      */
49025     getColumnWidth : function(col){
49026         return this.config[col].width || this.defaultWidth;
49027     },
49028
49029     /**
49030      * Sets the width for a column.
49031      * @param {Number} col The column index
49032      * @param {Number} width The new width
49033      */
49034     setColumnWidth : function(col, width, suppressEvent){
49035         this.config[col].width = width;
49036         this.totalWidth = null;
49037         if(!suppressEvent){
49038              this.fireEvent("widthchange", this, col, width);
49039         }
49040     },
49041
49042     /**
49043      * Returns the total width of all columns.
49044      * @param {Boolean} includeHidden True to include hidden column widths
49045      * @return {Number}
49046      */
49047     getTotalWidth : function(includeHidden){
49048         if(!this.totalWidth){
49049             this.totalWidth = 0;
49050             for(var i = 0, len = this.config.length; i < len; i++){
49051                 if(includeHidden || !this.isHidden(i)){
49052                     this.totalWidth += this.getColumnWidth(i);
49053                 }
49054             }
49055         }
49056         return this.totalWidth;
49057     },
49058
49059     /**
49060      * Returns the header for the specified column.
49061      * @param {Number} col The column index
49062      * @return {String}
49063      */
49064     getColumnHeader : function(col){
49065         return this.config[col].header;
49066     },
49067
49068     /**
49069      * Sets the header for a column.
49070      * @param {Number} col The column index
49071      * @param {String} header The new header
49072      */
49073     setColumnHeader : function(col, header){
49074         this.config[col].header = header;
49075         this.fireEvent("headerchange", this, col, header);
49076     },
49077
49078     /**
49079      * Returns the tooltip for the specified column.
49080      * @param {Number} col The column index
49081      * @return {String}
49082      */
49083     getColumnTooltip : function(col){
49084             return this.config[col].tooltip;
49085     },
49086     /**
49087      * Sets the tooltip for a column.
49088      * @param {Number} col The column index
49089      * @param {String} tooltip The new tooltip
49090      */
49091     setColumnTooltip : function(col, tooltip){
49092             this.config[col].tooltip = tooltip;
49093     },
49094
49095     /**
49096      * Returns the dataIndex for the specified column.
49097      * @param {Number} col The column index
49098      * @return {Number}
49099      */
49100     getDataIndex : function(col){
49101         return this.config[col].dataIndex;
49102     },
49103
49104     /**
49105      * Sets the dataIndex for a column.
49106      * @param {Number} col The column index
49107      * @param {Number} dataIndex The new dataIndex
49108      */
49109     setDataIndex : function(col, dataIndex){
49110         this.config[col].dataIndex = dataIndex;
49111     },
49112
49113     
49114     
49115     /**
49116      * Returns true if the cell is editable.
49117      * @param {Number} colIndex The column index
49118      * @param {Number} rowIndex The row index
49119      * @return {Boolean}
49120      */
49121     isCellEditable : function(colIndex, rowIndex){
49122         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49123     },
49124
49125     /**
49126      * Returns the editor defined for the cell/column.
49127      * return false or null to disable editing.
49128      * @param {Number} colIndex The column index
49129      * @param {Number} rowIndex The row index
49130      * @return {Object}
49131      */
49132     getCellEditor : function(colIndex, rowIndex){
49133         return this.config[colIndex].editor;
49134     },
49135
49136     /**
49137      * Sets if a column is editable.
49138      * @param {Number} col The column index
49139      * @param {Boolean} editable True if the column is editable
49140      */
49141     setEditable : function(col, editable){
49142         this.config[col].editable = editable;
49143     },
49144
49145
49146     /**
49147      * Returns true if the column is hidden.
49148      * @param {Number} colIndex The column index
49149      * @return {Boolean}
49150      */
49151     isHidden : function(colIndex){
49152         return this.config[colIndex].hidden;
49153     },
49154
49155
49156     /**
49157      * Returns true if the column width cannot be changed
49158      */
49159     isFixed : function(colIndex){
49160         return this.config[colIndex].fixed;
49161     },
49162
49163     /**
49164      * Returns true if the column can be resized
49165      * @return {Boolean}
49166      */
49167     isResizable : function(colIndex){
49168         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49169     },
49170     /**
49171      * Sets if a column is hidden.
49172      * @param {Number} colIndex The column index
49173      * @param {Boolean} hidden True if the column is hidden
49174      */
49175     setHidden : function(colIndex, hidden){
49176         this.config[colIndex].hidden = hidden;
49177         this.totalWidth = null;
49178         this.fireEvent("hiddenchange", this, colIndex, hidden);
49179     },
49180
49181     /**
49182      * Sets the editor for a column.
49183      * @param {Number} col The column index
49184      * @param {Object} editor The editor object
49185      */
49186     setEditor : function(col, editor){
49187         this.config[col].editor = editor;
49188     }
49189 });
49190
49191 Roo.grid.ColumnModel.defaultRenderer = function(value){
49192         if(typeof value == "string" && value.length < 1){
49193             return "&#160;";
49194         }
49195         return value;
49196 };
49197
49198 // Alias for backwards compatibility
49199 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49200 /*
49201  * Based on:
49202  * Ext JS Library 1.1.1
49203  * Copyright(c) 2006-2007, Ext JS, LLC.
49204  *
49205  * Originally Released Under LGPL - original licence link has changed is not relivant.
49206  *
49207  * Fork - LGPL
49208  * <script type="text/javascript">
49209  */
49210
49211 /**
49212  * @class Roo.grid.AbstractSelectionModel
49213  * @extends Roo.util.Observable
49214  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49215  * implemented by descendant classes.  This class should not be directly instantiated.
49216  * @constructor
49217  */
49218 Roo.grid.AbstractSelectionModel = function(){
49219     this.locked = false;
49220     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49221 };
49222
49223 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49224     /** @ignore Called by the grid automatically. Do not call directly. */
49225     init : function(grid){
49226         this.grid = grid;
49227         this.initEvents();
49228     },
49229
49230     /**
49231      * Locks the selections.
49232      */
49233     lock : function(){
49234         this.locked = true;
49235     },
49236
49237     /**
49238      * Unlocks the selections.
49239      */
49240     unlock : function(){
49241         this.locked = false;
49242     },
49243
49244     /**
49245      * Returns true if the selections are locked.
49246      * @return {Boolean}
49247      */
49248     isLocked : function(){
49249         return this.locked;
49250     }
49251 });/*
49252  * Based on:
49253  * Ext JS Library 1.1.1
49254  * Copyright(c) 2006-2007, Ext JS, LLC.
49255  *
49256  * Originally Released Under LGPL - original licence link has changed is not relivant.
49257  *
49258  * Fork - LGPL
49259  * <script type="text/javascript">
49260  */
49261 /**
49262  * @extends Roo.grid.AbstractSelectionModel
49263  * @class Roo.grid.RowSelectionModel
49264  * The default SelectionModel used by {@link Roo.grid.Grid}.
49265  * It supports multiple selections and keyboard selection/navigation. 
49266  * @constructor
49267  * @param {Object} config
49268  */
49269 Roo.grid.RowSelectionModel = function(config){
49270     Roo.apply(this, config);
49271     this.selections = new Roo.util.MixedCollection(false, function(o){
49272         return o.id;
49273     });
49274
49275     this.last = false;
49276     this.lastActive = false;
49277
49278     this.addEvents({
49279         /**
49280              * @event selectionchange
49281              * Fires when the selection changes
49282              * @param {SelectionModel} this
49283              */
49284             "selectionchange" : true,
49285         /**
49286              * @event afterselectionchange
49287              * Fires after the selection changes (eg. by key press or clicking)
49288              * @param {SelectionModel} this
49289              */
49290             "afterselectionchange" : true,
49291         /**
49292              * @event beforerowselect
49293              * Fires when a row is selected being selected, return false to cancel.
49294              * @param {SelectionModel} this
49295              * @param {Number} rowIndex The selected index
49296              * @param {Boolean} keepExisting False if other selections will be cleared
49297              */
49298             "beforerowselect" : true,
49299         /**
49300              * @event rowselect
49301              * Fires when a row is selected.
49302              * @param {SelectionModel} this
49303              * @param {Number} rowIndex The selected index
49304              * @param {Roo.data.Record} r The record
49305              */
49306             "rowselect" : true,
49307         /**
49308              * @event rowdeselect
49309              * Fires when a row is deselected.
49310              * @param {SelectionModel} this
49311              * @param {Number} rowIndex The selected index
49312              */
49313         "rowdeselect" : true
49314     });
49315     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49316     this.locked = false;
49317 };
49318
49319 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49320     /**
49321      * @cfg {Boolean} singleSelect
49322      * True to allow selection of only one row at a time (defaults to false)
49323      */
49324     singleSelect : false,
49325
49326     // private
49327     initEvents : function(){
49328
49329         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49330             this.grid.on("mousedown", this.handleMouseDown, this);
49331         }else{ // allow click to work like normal
49332             this.grid.on("rowclick", this.handleDragableRowClick, this);
49333         }
49334
49335         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49336             "up" : function(e){
49337                 if(!e.shiftKey){
49338                     this.selectPrevious(e.shiftKey);
49339                 }else if(this.last !== false && this.lastActive !== false){
49340                     var last = this.last;
49341                     this.selectRange(this.last,  this.lastActive-1);
49342                     this.grid.getView().focusRow(this.lastActive);
49343                     if(last !== false){
49344                         this.last = last;
49345                     }
49346                 }else{
49347                     this.selectFirstRow();
49348                 }
49349                 this.fireEvent("afterselectionchange", this);
49350             },
49351             "down" : function(e){
49352                 if(!e.shiftKey){
49353                     this.selectNext(e.shiftKey);
49354                 }else if(this.last !== false && this.lastActive !== false){
49355                     var last = this.last;
49356                     this.selectRange(this.last,  this.lastActive+1);
49357                     this.grid.getView().focusRow(this.lastActive);
49358                     if(last !== false){
49359                         this.last = last;
49360                     }
49361                 }else{
49362                     this.selectFirstRow();
49363                 }
49364                 this.fireEvent("afterselectionchange", this);
49365             },
49366             scope: this
49367         });
49368
49369         var view = this.grid.view;
49370         view.on("refresh", this.onRefresh, this);
49371         view.on("rowupdated", this.onRowUpdated, this);
49372         view.on("rowremoved", this.onRemove, this);
49373     },
49374
49375     // private
49376     onRefresh : function(){
49377         var ds = this.grid.dataSource, i, v = this.grid.view;
49378         var s = this.selections;
49379         s.each(function(r){
49380             if((i = ds.indexOfId(r.id)) != -1){
49381                 v.onRowSelect(i);
49382             }else{
49383                 s.remove(r);
49384             }
49385         });
49386     },
49387
49388     // private
49389     onRemove : function(v, index, r){
49390         this.selections.remove(r);
49391     },
49392
49393     // private
49394     onRowUpdated : function(v, index, r){
49395         if(this.isSelected(r)){
49396             v.onRowSelect(index);
49397         }
49398     },
49399
49400     /**
49401      * Select records.
49402      * @param {Array} records The records to select
49403      * @param {Boolean} keepExisting (optional) True to keep existing selections
49404      */
49405     selectRecords : function(records, keepExisting){
49406         if(!keepExisting){
49407             this.clearSelections();
49408         }
49409         var ds = this.grid.dataSource;
49410         for(var i = 0, len = records.length; i < len; i++){
49411             this.selectRow(ds.indexOf(records[i]), true);
49412         }
49413     },
49414
49415     /**
49416      * Gets the number of selected rows.
49417      * @return {Number}
49418      */
49419     getCount : function(){
49420         return this.selections.length;
49421     },
49422
49423     /**
49424      * Selects the first row in the grid.
49425      */
49426     selectFirstRow : function(){
49427         this.selectRow(0);
49428     },
49429
49430     /**
49431      * Select the last row.
49432      * @param {Boolean} keepExisting (optional) True to keep existing selections
49433      */
49434     selectLastRow : function(keepExisting){
49435         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49436     },
49437
49438     /**
49439      * Selects the row immediately following the last selected row.
49440      * @param {Boolean} keepExisting (optional) True to keep existing selections
49441      */
49442     selectNext : function(keepExisting){
49443         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49444             this.selectRow(this.last+1, keepExisting);
49445             this.grid.getView().focusRow(this.last);
49446         }
49447     },
49448
49449     /**
49450      * Selects the row that precedes the last selected row.
49451      * @param {Boolean} keepExisting (optional) True to keep existing selections
49452      */
49453     selectPrevious : function(keepExisting){
49454         if(this.last){
49455             this.selectRow(this.last-1, keepExisting);
49456             this.grid.getView().focusRow(this.last);
49457         }
49458     },
49459
49460     /**
49461      * Returns the selected records
49462      * @return {Array} Array of selected records
49463      */
49464     getSelections : function(){
49465         return [].concat(this.selections.items);
49466     },
49467
49468     /**
49469      * Returns the first selected record.
49470      * @return {Record}
49471      */
49472     getSelected : function(){
49473         return this.selections.itemAt(0);
49474     },
49475
49476
49477     /**
49478      * Clears all selections.
49479      */
49480     clearSelections : function(fast){
49481         if(this.locked) return;
49482         if(fast !== true){
49483             var ds = this.grid.dataSource;
49484             var s = this.selections;
49485             s.each(function(r){
49486                 this.deselectRow(ds.indexOfId(r.id));
49487             }, this);
49488             s.clear();
49489         }else{
49490             this.selections.clear();
49491         }
49492         this.last = false;
49493     },
49494
49495
49496     /**
49497      * Selects all rows.
49498      */
49499     selectAll : function(){
49500         if(this.locked) return;
49501         this.selections.clear();
49502         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49503             this.selectRow(i, true);
49504         }
49505     },
49506
49507     /**
49508      * Returns True if there is a selection.
49509      * @return {Boolean}
49510      */
49511     hasSelection : function(){
49512         return this.selections.length > 0;
49513     },
49514
49515     /**
49516      * Returns True if the specified row is selected.
49517      * @param {Number/Record} record The record or index of the record to check
49518      * @return {Boolean}
49519      */
49520     isSelected : function(index){
49521         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49522         return (r && this.selections.key(r.id) ? true : false);
49523     },
49524
49525     /**
49526      * Returns True if the specified record id is selected.
49527      * @param {String} id The id of record to check
49528      * @return {Boolean}
49529      */
49530     isIdSelected : function(id){
49531         return (this.selections.key(id) ? true : false);
49532     },
49533
49534     // private
49535     handleMouseDown : function(e, t){
49536         var view = this.grid.getView(), rowIndex;
49537         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49538             return;
49539         };
49540         if(e.shiftKey && this.last !== false){
49541             var last = this.last;
49542             this.selectRange(last, rowIndex, e.ctrlKey);
49543             this.last = last; // reset the last
49544             view.focusRow(rowIndex);
49545         }else{
49546             var isSelected = this.isSelected(rowIndex);
49547             if(e.button !== 0 && isSelected){
49548                 view.focusRow(rowIndex);
49549             }else if(e.ctrlKey && isSelected){
49550                 this.deselectRow(rowIndex);
49551             }else if(!isSelected){
49552                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49553                 view.focusRow(rowIndex);
49554             }
49555         }
49556         this.fireEvent("afterselectionchange", this);
49557     },
49558     // private
49559     handleDragableRowClick :  function(grid, rowIndex, e) 
49560     {
49561         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49562             this.selectRow(rowIndex, false);
49563             grid.view.focusRow(rowIndex);
49564              this.fireEvent("afterselectionchange", this);
49565         }
49566     },
49567     
49568     /**
49569      * Selects multiple rows.
49570      * @param {Array} rows Array of the indexes of the row to select
49571      * @param {Boolean} keepExisting (optional) True to keep existing selections
49572      */
49573     selectRows : function(rows, keepExisting){
49574         if(!keepExisting){
49575             this.clearSelections();
49576         }
49577         for(var i = 0, len = rows.length; i < len; i++){
49578             this.selectRow(rows[i], true);
49579         }
49580     },
49581
49582     /**
49583      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49584      * @param {Number} startRow The index of the first row in the range
49585      * @param {Number} endRow The index of the last row in the range
49586      * @param {Boolean} keepExisting (optional) True to retain existing selections
49587      */
49588     selectRange : function(startRow, endRow, keepExisting){
49589         if(this.locked) return;
49590         if(!keepExisting){
49591             this.clearSelections();
49592         }
49593         if(startRow <= endRow){
49594             for(var i = startRow; i <= endRow; i++){
49595                 this.selectRow(i, true);
49596             }
49597         }else{
49598             for(var i = startRow; i >= endRow; i--){
49599                 this.selectRow(i, true);
49600             }
49601         }
49602     },
49603
49604     /**
49605      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49606      * @param {Number} startRow The index of the first row in the range
49607      * @param {Number} endRow The index of the last row in the range
49608      */
49609     deselectRange : function(startRow, endRow, preventViewNotify){
49610         if(this.locked) return;
49611         for(var i = startRow; i <= endRow; i++){
49612             this.deselectRow(i, preventViewNotify);
49613         }
49614     },
49615
49616     /**
49617      * Selects a row.
49618      * @param {Number} row The index of the row to select
49619      * @param {Boolean} keepExisting (optional) True to keep existing selections
49620      */
49621     selectRow : function(index, keepExisting, preventViewNotify){
49622         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49623         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49624             if(!keepExisting || this.singleSelect){
49625                 this.clearSelections();
49626             }
49627             var r = this.grid.dataSource.getAt(index);
49628             this.selections.add(r);
49629             this.last = this.lastActive = index;
49630             if(!preventViewNotify){
49631                 this.grid.getView().onRowSelect(index);
49632             }
49633             this.fireEvent("rowselect", this, index, r);
49634             this.fireEvent("selectionchange", this);
49635         }
49636     },
49637
49638     /**
49639      * Deselects a row.
49640      * @param {Number} row The index of the row to deselect
49641      */
49642     deselectRow : function(index, preventViewNotify){
49643         if(this.locked) return;
49644         if(this.last == index){
49645             this.last = false;
49646         }
49647         if(this.lastActive == index){
49648             this.lastActive = false;
49649         }
49650         var r = this.grid.dataSource.getAt(index);
49651         this.selections.remove(r);
49652         if(!preventViewNotify){
49653             this.grid.getView().onRowDeselect(index);
49654         }
49655         this.fireEvent("rowdeselect", this, index);
49656         this.fireEvent("selectionchange", this);
49657     },
49658
49659     // private
49660     restoreLast : function(){
49661         if(this._last){
49662             this.last = this._last;
49663         }
49664     },
49665
49666     // private
49667     acceptsNav : function(row, col, cm){
49668         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49669     },
49670
49671     // private
49672     onEditorKey : function(field, e){
49673         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49674         if(k == e.TAB){
49675             e.stopEvent();
49676             ed.completeEdit();
49677             if(e.shiftKey){
49678                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49679             }else{
49680                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49681             }
49682         }else if(k == e.ENTER && !e.ctrlKey){
49683             e.stopEvent();
49684             ed.completeEdit();
49685             if(e.shiftKey){
49686                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49687             }else{
49688                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49689             }
49690         }else if(k == e.ESC){
49691             ed.cancelEdit();
49692         }
49693         if(newCell){
49694             g.startEditing(newCell[0], newCell[1]);
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 /**
49708  * @class Roo.grid.CellSelectionModel
49709  * @extends Roo.grid.AbstractSelectionModel
49710  * This class provides the basic implementation for cell selection in a grid.
49711  * @constructor
49712  * @param {Object} config The object containing the configuration of this model.
49713  */
49714 Roo.grid.CellSelectionModel = function(config){
49715     Roo.apply(this, config);
49716
49717     this.selection = null;
49718
49719     this.addEvents({
49720         /**
49721              * @event beforerowselect
49722              * Fires before a cell is selected.
49723              * @param {SelectionModel} this
49724              * @param {Number} rowIndex The selected row index
49725              * @param {Number} colIndex The selected cell index
49726              */
49727             "beforecellselect" : true,
49728         /**
49729              * @event cellselect
49730              * Fires when a cell is selected.
49731              * @param {SelectionModel} this
49732              * @param {Number} rowIndex The selected row index
49733              * @param {Number} colIndex The selected cell index
49734              */
49735             "cellselect" : true,
49736         /**
49737              * @event selectionchange
49738              * Fires when the active selection changes.
49739              * @param {SelectionModel} this
49740              * @param {Object} selection null for no selection or an object (o) with two properties
49741                 <ul>
49742                 <li>o.record: the record object for the row the selection is in</li>
49743                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49744                 </ul>
49745              */
49746             "selectionchange" : true
49747     });
49748     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49749 };
49750
49751 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49752
49753     /** @ignore */
49754     initEvents : function(){
49755         this.grid.on("mousedown", this.handleMouseDown, this);
49756         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49757         var view = this.grid.view;
49758         view.on("refresh", this.onViewChange, this);
49759         view.on("rowupdated", this.onRowUpdated, this);
49760         view.on("beforerowremoved", this.clearSelections, this);
49761         view.on("beforerowsinserted", this.clearSelections, this);
49762         if(this.grid.isEditor){
49763             this.grid.on("beforeedit", this.beforeEdit,  this);
49764         }
49765     },
49766
49767         //private
49768     beforeEdit : function(e){
49769         this.select(e.row, e.column, false, true, e.record);
49770     },
49771
49772         //private
49773     onRowUpdated : function(v, index, r){
49774         if(this.selection && this.selection.record == r){
49775             v.onCellSelect(index, this.selection.cell[1]);
49776         }
49777     },
49778
49779         //private
49780     onViewChange : function(){
49781         this.clearSelections(true);
49782     },
49783
49784         /**
49785          * Returns the currently selected cell,.
49786          * @return {Array} The selected cell (row, column) or null if none selected.
49787          */
49788     getSelectedCell : function(){
49789         return this.selection ? this.selection.cell : null;
49790     },
49791
49792     /**
49793      * Clears all selections.
49794      * @param {Boolean} true to prevent the gridview from being notified about the change.
49795      */
49796     clearSelections : function(preventNotify){
49797         var s = this.selection;
49798         if(s){
49799             if(preventNotify !== true){
49800                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49801             }
49802             this.selection = null;
49803             this.fireEvent("selectionchange", this, null);
49804         }
49805     },
49806
49807     /**
49808      * Returns true if there is a selection.
49809      * @return {Boolean}
49810      */
49811     hasSelection : function(){
49812         return this.selection ? true : false;
49813     },
49814
49815     /** @ignore */
49816     handleMouseDown : function(e, t){
49817         var v = this.grid.getView();
49818         if(this.isLocked()){
49819             return;
49820         };
49821         var row = v.findRowIndex(t);
49822         var cell = v.findCellIndex(t);
49823         if(row !== false && cell !== false){
49824             this.select(row, cell);
49825         }
49826     },
49827
49828     /**
49829      * Selects a cell.
49830      * @param {Number} rowIndex
49831      * @param {Number} collIndex
49832      */
49833     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49834         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49835             this.clearSelections();
49836             r = r || this.grid.dataSource.getAt(rowIndex);
49837             this.selection = {
49838                 record : r,
49839                 cell : [rowIndex, colIndex]
49840             };
49841             if(!preventViewNotify){
49842                 var v = this.grid.getView();
49843                 v.onCellSelect(rowIndex, colIndex);
49844                 if(preventFocus !== true){
49845                     v.focusCell(rowIndex, colIndex);
49846                 }
49847             }
49848             this.fireEvent("cellselect", this, rowIndex, colIndex);
49849             this.fireEvent("selectionchange", this, this.selection);
49850         }
49851     },
49852
49853         //private
49854     isSelectable : function(rowIndex, colIndex, cm){
49855         return !cm.isHidden(colIndex);
49856     },
49857
49858     /** @ignore */
49859     handleKeyDown : function(e){
49860         Roo.log('Cell Sel Model handleKeyDown');
49861         if(!e.isNavKeyPress()){
49862             return;
49863         }
49864         var g = this.grid, s = this.selection;
49865         if(!s){
49866             e.stopEvent();
49867             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49868             if(cell){
49869                 this.select(cell[0], cell[1]);
49870             }
49871             return;
49872         }
49873         var sm = this;
49874         var walk = function(row, col, step){
49875             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49876         };
49877         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49878         var newCell;
49879
49880         switch(k){
49881             case e.TAB:
49882                 // handled by onEditorKey
49883                 if (g.isEditor && g.editing) {
49884                     return;
49885                 }
49886                 if(e.shiftKey){
49887                      newCell = walk(r, c-1, -1);
49888                 }else{
49889                      newCell = walk(r, c+1, 1);
49890                 }
49891              break;
49892              case e.DOWN:
49893                  newCell = walk(r+1, c, 1);
49894              break;
49895              case e.UP:
49896                  newCell = walk(r-1, c, -1);
49897              break;
49898              case e.RIGHT:
49899                  newCell = walk(r, c+1, 1);
49900              break;
49901              case e.LEFT:
49902                  newCell = walk(r, c-1, -1);
49903              break;
49904              case e.ENTER:
49905                  if(g.isEditor && !g.editing){
49906                     g.startEditing(r, c);
49907                     e.stopEvent();
49908                     return;
49909                 }
49910              break;
49911         };
49912         if(newCell){
49913             this.select(newCell[0], newCell[1]);
49914             e.stopEvent();
49915         }
49916     },
49917
49918     acceptsNav : function(row, col, cm){
49919         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49920     },
49921
49922     onEditorKey : function(field, e){
49923         
49924         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49925         ///Roo.log('onEditorKey' + k);
49926         
49927         if(k == e.TAB){
49928             if(e.shiftKey){
49929                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49930             }else{
49931                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49932             }
49933             e.stopEvent();
49934         }else if(k == e.ENTER && !e.ctrlKey){
49935             ed.completeEdit();
49936             e.stopEvent();
49937             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49938         }else if(k == e.ESC){
49939             ed.cancelEdit();
49940         }
49941         
49942         
49943         if(newCell){
49944             //Roo.log('next cell after edit');
49945             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
49946         }
49947     }
49948 });/*
49949  * Based on:
49950  * Ext JS Library 1.1.1
49951  * Copyright(c) 2006-2007, Ext JS, LLC.
49952  *
49953  * Originally Released Under LGPL - original licence link has changed is not relivant.
49954  *
49955  * Fork - LGPL
49956  * <script type="text/javascript">
49957  */
49958  
49959 /**
49960  * @class Roo.grid.EditorGrid
49961  * @extends Roo.grid.Grid
49962  * Class for creating and editable grid.
49963  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49964  * The container MUST have some type of size defined for the grid to fill. The container will be 
49965  * automatically set to position relative if it isn't already.
49966  * @param {Object} dataSource The data model to bind to
49967  * @param {Object} colModel The column model with info about this grid's columns
49968  */
49969 Roo.grid.EditorGrid = function(container, config){
49970     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49971     this.getGridEl().addClass("xedit-grid");
49972
49973     if(!this.selModel){
49974         this.selModel = new Roo.grid.CellSelectionModel();
49975     }
49976
49977     this.activeEditor = null;
49978
49979         this.addEvents({
49980             /**
49981              * @event beforeedit
49982              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49983              * <ul style="padding:5px;padding-left:16px;">
49984              * <li>grid - This grid</li>
49985              * <li>record - The record being edited</li>
49986              * <li>field - The field name being edited</li>
49987              * <li>value - The value for the field being edited.</li>
49988              * <li>row - The grid row index</li>
49989              * <li>column - The grid column index</li>
49990              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49991              * </ul>
49992              * @param {Object} e An edit event (see above for description)
49993              */
49994             "beforeedit" : true,
49995             /**
49996              * @event afteredit
49997              * Fires after a cell is edited. <br />
49998              * <ul style="padding:5px;padding-left:16px;">
49999              * <li>grid - This grid</li>
50000              * <li>record - The record being edited</li>
50001              * <li>field - The field name being edited</li>
50002              * <li>value - The value being set</li>
50003              * <li>originalValue - The original value for the field, before the edit.</li>
50004              * <li>row - The grid row index</li>
50005              * <li>column - The grid column index</li>
50006              * </ul>
50007              * @param {Object} e An edit event (see above for description)
50008              */
50009             "afteredit" : true,
50010             /**
50011              * @event validateedit
50012              * Fires after a cell is edited, but before the value is set in the record. 
50013          * You can use this to modify the value being set in the field, Return false
50014              * to cancel the change. The edit event object has the following properties <br />
50015              * <ul style="padding:5px;padding-left:16px;">
50016          * <li>editor - This editor</li>
50017              * <li>grid - This grid</li>
50018              * <li>record - The record being edited</li>
50019              * <li>field - The field name being edited</li>
50020              * <li>value - The value being set</li>
50021              * <li>originalValue - The original value for the field, before the edit.</li>
50022              * <li>row - The grid row index</li>
50023              * <li>column - The grid column index</li>
50024              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50025              * </ul>
50026              * @param {Object} e An edit event (see above for description)
50027              */
50028             "validateedit" : true
50029         });
50030     this.on("bodyscroll", this.stopEditing,  this);
50031     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50032 };
50033
50034 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50035     /**
50036      * @cfg {Number} clicksToEdit
50037      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50038      */
50039     clicksToEdit: 2,
50040
50041     // private
50042     isEditor : true,
50043     // private
50044     trackMouseOver: false, // causes very odd FF errors
50045
50046     onCellDblClick : function(g, row, col){
50047         this.startEditing(row, col);
50048     },
50049
50050     onEditComplete : function(ed, value, startValue){
50051         this.editing = false;
50052         this.activeEditor = null;
50053         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50054         var r = ed.record;
50055         var field = this.colModel.getDataIndex(ed.col);
50056         var e = {
50057             grid: this,
50058             record: r,
50059             field: field,
50060             originalValue: startValue,
50061             value: value,
50062             row: ed.row,
50063             column: ed.col,
50064             cancel:false,
50065             editor: ed
50066         };
50067         if(String(value) !== String(startValue)){
50068             
50069             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50070                 r.set(field, e.value);
50071                 // if we are dealing with a combo box..
50072                 // then we also set the 'name' colum to be the displayField
50073                 if (ed.field.displayField && ed.field.name) {
50074                     r.set(ed.field.name, ed.field.el.dom.value);
50075                 }
50076                 
50077                 delete e.cancel; //?? why!!!
50078                 this.fireEvent("afteredit", e);
50079             }
50080         } else {
50081             this.fireEvent("afteredit", e); // always fire it!
50082         }
50083         this.view.focusCell(ed.row, ed.col);
50084     },
50085
50086     /**
50087      * Starts editing the specified for the specified row/column
50088      * @param {Number} rowIndex
50089      * @param {Number} colIndex
50090      */
50091     startEditing : function(row, col){
50092         this.stopEditing();
50093         if(this.colModel.isCellEditable(col, row)){
50094             this.view.ensureVisible(row, col, true);
50095             var r = this.dataSource.getAt(row);
50096             var field = this.colModel.getDataIndex(col);
50097             var e = {
50098                 grid: this,
50099                 record: r,
50100                 field: field,
50101                 value: r.data[field],
50102                 row: row,
50103                 column: col,
50104                 cancel:false
50105             };
50106             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50107                 this.editing = true;
50108                 var ed = this.colModel.getCellEditor(col, row);
50109                 
50110                 if (!ed) {
50111                     return;
50112                 }
50113                 if(!ed.rendered){
50114                     ed.render(ed.parentEl || document.body);
50115                 }
50116                 ed.field.reset();
50117                 (function(){ // complex but required for focus issues in safari, ie and opera
50118                     ed.row = row;
50119                     ed.col = col;
50120                     ed.record = r;
50121                     ed.on("complete", this.onEditComplete, this, {single: true});
50122                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50123                     this.activeEditor = ed;
50124                     var v = r.data[field];
50125                     ed.startEdit(this.view.getCell(row, col), v);
50126                     // combo's with 'displayField and name set
50127                     if (ed.field.displayField && ed.field.name) {
50128                         ed.field.el.dom.value = r.data[ed.field.name];
50129                     }
50130                     
50131                     
50132                 }).defer(50, this);
50133             }
50134         }
50135     },
50136         
50137     /**
50138      * Stops any active editing
50139      */
50140     stopEditing : function(){
50141         if(this.activeEditor){
50142             this.activeEditor.completeEdit();
50143         }
50144         this.activeEditor = null;
50145     }
50146 });/*
50147  * Based on:
50148  * Ext JS Library 1.1.1
50149  * Copyright(c) 2006-2007, Ext JS, LLC.
50150  *
50151  * Originally Released Under LGPL - original licence link has changed is not relivant.
50152  *
50153  * Fork - LGPL
50154  * <script type="text/javascript">
50155  */
50156
50157 // private - not really -- you end up using it !
50158 // This is a support class used internally by the Grid components
50159
50160 /**
50161  * @class Roo.grid.GridEditor
50162  * @extends Roo.Editor
50163  * Class for creating and editable grid elements.
50164  * @param {Object} config any settings (must include field)
50165  */
50166 Roo.grid.GridEditor = function(field, config){
50167     if (!config && field.field) {
50168         config = field;
50169         field = Roo.factory(config.field, Roo.form);
50170     }
50171     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50172     field.monitorTab = false;
50173 };
50174
50175 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50176     
50177     /**
50178      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50179      */
50180     
50181     alignment: "tl-tl",
50182     autoSize: "width",
50183     hideEl : false,
50184     cls: "x-small-editor x-grid-editor",
50185     shim:false,
50186     shadow:"frame"
50187 });/*
50188  * Based on:
50189  * Ext JS Library 1.1.1
50190  * Copyright(c) 2006-2007, Ext JS, LLC.
50191  *
50192  * Originally Released Under LGPL - original licence link has changed is not relivant.
50193  *
50194  * Fork - LGPL
50195  * <script type="text/javascript">
50196  */
50197   
50198
50199   
50200 Roo.grid.PropertyRecord = Roo.data.Record.create([
50201     {name:'name',type:'string'},  'value'
50202 ]);
50203
50204
50205 Roo.grid.PropertyStore = function(grid, source){
50206     this.grid = grid;
50207     this.store = new Roo.data.Store({
50208         recordType : Roo.grid.PropertyRecord
50209     });
50210     this.store.on('update', this.onUpdate,  this);
50211     if(source){
50212         this.setSource(source);
50213     }
50214     Roo.grid.PropertyStore.superclass.constructor.call(this);
50215 };
50216
50217
50218
50219 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50220     setSource : function(o){
50221         this.source = o;
50222         this.store.removeAll();
50223         var data = [];
50224         for(var k in o){
50225             if(this.isEditableValue(o[k])){
50226                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50227             }
50228         }
50229         this.store.loadRecords({records: data}, {}, true);
50230     },
50231
50232     onUpdate : function(ds, record, type){
50233         if(type == Roo.data.Record.EDIT){
50234             var v = record.data['value'];
50235             var oldValue = record.modified['value'];
50236             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50237                 this.source[record.id] = v;
50238                 record.commit();
50239                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50240             }else{
50241                 record.reject();
50242             }
50243         }
50244     },
50245
50246     getProperty : function(row){
50247        return this.store.getAt(row);
50248     },
50249
50250     isEditableValue: function(val){
50251         if(val && val instanceof Date){
50252             return true;
50253         }else if(typeof val == 'object' || typeof val == 'function'){
50254             return false;
50255         }
50256         return true;
50257     },
50258
50259     setValue : function(prop, value){
50260         this.source[prop] = value;
50261         this.store.getById(prop).set('value', value);
50262     },
50263
50264     getSource : function(){
50265         return this.source;
50266     }
50267 });
50268
50269 Roo.grid.PropertyColumnModel = function(grid, store){
50270     this.grid = grid;
50271     var g = Roo.grid;
50272     g.PropertyColumnModel.superclass.constructor.call(this, [
50273         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50274         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50275     ]);
50276     this.store = store;
50277     this.bselect = Roo.DomHelper.append(document.body, {
50278         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50279             {tag: 'option', value: 'true', html: 'true'},
50280             {tag: 'option', value: 'false', html: 'false'}
50281         ]
50282     });
50283     Roo.id(this.bselect);
50284     var f = Roo.form;
50285     this.editors = {
50286         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50287         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50288         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50289         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50290         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50291     };
50292     this.renderCellDelegate = this.renderCell.createDelegate(this);
50293     this.renderPropDelegate = this.renderProp.createDelegate(this);
50294 };
50295
50296 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50297     
50298     
50299     nameText : 'Name',
50300     valueText : 'Value',
50301     
50302     dateFormat : 'm/j/Y',
50303     
50304     
50305     renderDate : function(dateVal){
50306         return dateVal.dateFormat(this.dateFormat);
50307     },
50308
50309     renderBool : function(bVal){
50310         return bVal ? 'true' : 'false';
50311     },
50312
50313     isCellEditable : function(colIndex, rowIndex){
50314         return colIndex == 1;
50315     },
50316
50317     getRenderer : function(col){
50318         return col == 1 ?
50319             this.renderCellDelegate : this.renderPropDelegate;
50320     },
50321
50322     renderProp : function(v){
50323         return this.getPropertyName(v);
50324     },
50325
50326     renderCell : function(val){
50327         var rv = val;
50328         if(val instanceof Date){
50329             rv = this.renderDate(val);
50330         }else if(typeof val == 'boolean'){
50331             rv = this.renderBool(val);
50332         }
50333         return Roo.util.Format.htmlEncode(rv);
50334     },
50335
50336     getPropertyName : function(name){
50337         var pn = this.grid.propertyNames;
50338         return pn && pn[name] ? pn[name] : name;
50339     },
50340
50341     getCellEditor : function(colIndex, rowIndex){
50342         var p = this.store.getProperty(rowIndex);
50343         var n = p.data['name'], val = p.data['value'];
50344         
50345         if(typeof(this.grid.customEditors[n]) == 'string'){
50346             return this.editors[this.grid.customEditors[n]];
50347         }
50348         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50349             return this.grid.customEditors[n];
50350         }
50351         if(val instanceof Date){
50352             return this.editors['date'];
50353         }else if(typeof val == 'number'){
50354             return this.editors['number'];
50355         }else if(typeof val == 'boolean'){
50356             return this.editors['boolean'];
50357         }else{
50358             return this.editors['string'];
50359         }
50360     }
50361 });
50362
50363 /**
50364  * @class Roo.grid.PropertyGrid
50365  * @extends Roo.grid.EditorGrid
50366  * This class represents the  interface of a component based property grid control.
50367  * <br><br>Usage:<pre><code>
50368  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50369       
50370  });
50371  // set any options
50372  grid.render();
50373  * </code></pre>
50374   
50375  * @constructor
50376  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50377  * The container MUST have some type of size defined for the grid to fill. The container will be
50378  * automatically set to position relative if it isn't already.
50379  * @param {Object} config A config object that sets properties on this grid.
50380  */
50381 Roo.grid.PropertyGrid = function(container, config){
50382     config = config || {};
50383     var store = new Roo.grid.PropertyStore(this);
50384     this.store = store;
50385     var cm = new Roo.grid.PropertyColumnModel(this, store);
50386     store.store.sort('name', 'ASC');
50387     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50388         ds: store.store,
50389         cm: cm,
50390         enableColLock:false,
50391         enableColumnMove:false,
50392         stripeRows:false,
50393         trackMouseOver: false,
50394         clicksToEdit:1
50395     }, config));
50396     this.getGridEl().addClass('x-props-grid');
50397     this.lastEditRow = null;
50398     this.on('columnresize', this.onColumnResize, this);
50399     this.addEvents({
50400          /**
50401              * @event beforepropertychange
50402              * Fires before a property changes (return false to stop?)
50403              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50404              * @param {String} id Record Id
50405              * @param {String} newval New Value
50406          * @param {String} oldval Old Value
50407              */
50408         "beforepropertychange": true,
50409         /**
50410              * @event propertychange
50411              * Fires after a property changes
50412              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50413              * @param {String} id Record Id
50414              * @param {String} newval New Value
50415          * @param {String} oldval Old Value
50416              */
50417         "propertychange": true
50418     });
50419     this.customEditors = this.customEditors || {};
50420 };
50421 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50422     
50423      /**
50424      * @cfg {Object} customEditors map of colnames=> custom editors.
50425      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50426      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50427      * false disables editing of the field.
50428          */
50429     
50430       /**
50431      * @cfg {Object} propertyNames map of property Names to their displayed value
50432          */
50433     
50434     render : function(){
50435         Roo.grid.PropertyGrid.superclass.render.call(this);
50436         this.autoSize.defer(100, this);
50437     },
50438
50439     autoSize : function(){
50440         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50441         if(this.view){
50442             this.view.fitColumns();
50443         }
50444     },
50445
50446     onColumnResize : function(){
50447         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50448         this.autoSize();
50449     },
50450     /**
50451      * Sets the data for the Grid
50452      * accepts a Key => Value object of all the elements avaiable.
50453      * @param {Object} data  to appear in grid.
50454      */
50455     setSource : function(source){
50456         this.store.setSource(source);
50457         //this.autoSize();
50458     },
50459     /**
50460      * Gets all the data from the grid.
50461      * @return {Object} data  data stored in grid
50462      */
50463     getSource : function(){
50464         return this.store.getSource();
50465     }
50466 });/*
50467  * Based on:
50468  * Ext JS Library 1.1.1
50469  * Copyright(c) 2006-2007, Ext JS, LLC.
50470  *
50471  * Originally Released Under LGPL - original licence link has changed is not relivant.
50472  *
50473  * Fork - LGPL
50474  * <script type="text/javascript">
50475  */
50476  
50477 /**
50478  * @class Roo.LoadMask
50479  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50480  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50481  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50482  * element's UpdateManager load indicator and will be destroyed after the initial load.
50483  * @constructor
50484  * Create a new LoadMask
50485  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50486  * @param {Object} config The config object
50487  */
50488 Roo.LoadMask = function(el, config){
50489     this.el = Roo.get(el);
50490     Roo.apply(this, config);
50491     if(this.store){
50492         this.store.on('beforeload', this.onBeforeLoad, this);
50493         this.store.on('load', this.onLoad, this);
50494         this.store.on('loadexception', this.onLoad, this);
50495         this.removeMask = false;
50496     }else{
50497         var um = this.el.getUpdateManager();
50498         um.showLoadIndicator = false; // disable the default indicator
50499         um.on('beforeupdate', this.onBeforeLoad, this);
50500         um.on('update', this.onLoad, this);
50501         um.on('failure', this.onLoad, this);
50502         this.removeMask = true;
50503     }
50504 };
50505
50506 Roo.LoadMask.prototype = {
50507     /**
50508      * @cfg {Boolean} removeMask
50509      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50510      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50511      */
50512     /**
50513      * @cfg {String} msg
50514      * The text to display in a centered loading message box (defaults to 'Loading...')
50515      */
50516     msg : 'Loading...',
50517     /**
50518      * @cfg {String} msgCls
50519      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50520      */
50521     msgCls : 'x-mask-loading',
50522
50523     /**
50524      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50525      * @type Boolean
50526      */
50527     disabled: false,
50528
50529     /**
50530      * Disables the mask to prevent it from being displayed
50531      */
50532     disable : function(){
50533        this.disabled = true;
50534     },
50535
50536     /**
50537      * Enables the mask so that it can be displayed
50538      */
50539     enable : function(){
50540         this.disabled = false;
50541     },
50542
50543     // private
50544     onLoad : function(){
50545         this.el.unmask(this.removeMask);
50546     },
50547
50548     // private
50549     onBeforeLoad : function(){
50550         if(!this.disabled){
50551             this.el.mask(this.msg, this.msgCls);
50552         }
50553     },
50554
50555     // private
50556     destroy : function(){
50557         if(this.store){
50558             this.store.un('beforeload', this.onBeforeLoad, this);
50559             this.store.un('load', this.onLoad, this);
50560             this.store.un('loadexception', this.onLoad, this);
50561         }else{
50562             var um = this.el.getUpdateManager();
50563             um.un('beforeupdate', this.onBeforeLoad, this);
50564             um.un('update', this.onLoad, this);
50565             um.un('failure', this.onLoad, this);
50566         }
50567     }
50568 };/*
50569  * Based on:
50570  * Ext JS Library 1.1.1
50571  * Copyright(c) 2006-2007, Ext JS, LLC.
50572  *
50573  * Originally Released Under LGPL - original licence link has changed is not relivant.
50574  *
50575  * Fork - LGPL
50576  * <script type="text/javascript">
50577  */
50578 Roo.XTemplate = function(){
50579     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50580     var s = this.html;
50581
50582     s = ['<tpl>', s, '</tpl>'].join('');
50583
50584     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50585
50586     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50587     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50588     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50589     var m, id = 0;
50590     var tpls = [];
50591
50592     while(m = s.match(re)){
50593        var m2 = m[0].match(nameRe);
50594        var m3 = m[0].match(ifRe);
50595        var m4 = m[0].match(execRe);
50596        var exp = null, fn = null, exec = null;
50597        var name = m2 && m2[1] ? m2[1] : '';
50598        if(m3){
50599            exp = m3 && m3[1] ? m3[1] : null;
50600            if(exp){
50601                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50602            }
50603        }
50604        if(m4){
50605            exp = m4 && m4[1] ? m4[1] : null;
50606            if(exp){
50607                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50608            }
50609        }
50610        if(name){
50611            switch(name){
50612                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50613                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50614                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50615            }
50616        }
50617        tpls.push({
50618             id: id,
50619             target: name,
50620             exec: exec,
50621             test: fn,
50622             body: m[1]||''
50623         });
50624        s = s.replace(m[0], '{xtpl'+ id + '}');
50625        ++id;
50626     }
50627     for(var i = tpls.length-1; i >= 0; --i){
50628         this.compileTpl(tpls[i]);
50629     }
50630     this.master = tpls[tpls.length-1];
50631     this.tpls = tpls;
50632 };
50633 Roo.extend(Roo.XTemplate, Roo.Template, {
50634
50635     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50636
50637     applySubTemplate : function(id, values, parent){
50638         var t = this.tpls[id];
50639         if(t.test && !t.test.call(this, values, parent)){
50640             return '';
50641         }
50642         if(t.exec && t.exec.call(this, values, parent)){
50643             return '';
50644         }
50645         var vs = t.target ? t.target.call(this, values, parent) : values;
50646         parent = t.target ? values : parent;
50647         if(t.target && vs instanceof Array){
50648             var buf = [];
50649             for(var i = 0, len = vs.length; i < len; i++){
50650                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50651             }
50652             return buf.join('');
50653         }
50654         return t.compiled.call(this, vs, parent);
50655     },
50656
50657     compileTpl : function(tpl){
50658         var fm = Roo.util.Format;
50659         var useF = this.disableFormats !== true;
50660         var sep = Roo.isGecko ? "+" : ",";
50661         var fn = function(m, name, format, args){
50662             if(name.substr(0, 4) == 'xtpl'){
50663                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50664             }
50665             var v;
50666             if(name.indexOf('.') != -1){
50667                 v = name;
50668             }else{
50669                 v = "values['" + name + "']";
50670             }
50671             if(format && useF){
50672                 args = args ? ',' + args : "";
50673                 if(format.substr(0, 5) != "this."){
50674                     format = "fm." + format + '(';
50675                 }else{
50676                     format = 'this.call("'+ format.substr(5) + '", ';
50677                     args = ", values";
50678                 }
50679             }else{
50680                 args= ''; format = "("+v+" === undefined ? '' : ";
50681             }
50682             return "'"+ sep + format + v + args + ")"+sep+"'";
50683         };
50684         var body;
50685         // branched to use + in gecko and [].join() in others
50686         if(Roo.isGecko){
50687             body = "tpl.compiled = function(values, parent){ return '" +
50688                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50689                     "';};";
50690         }else{
50691             body = ["tpl.compiled = function(values, parent){ return ['"];
50692             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50693             body.push("'].join('');};");
50694             body = body.join('');
50695         }
50696         /** eval:var:zzzzzzz */
50697         eval(body);
50698         return this;
50699     },
50700
50701     applyTemplate : function(values){
50702         return this.master.compiled.call(this, values, {});
50703         var s = this.subs;
50704     },
50705
50706     apply : function(){
50707         return this.applyTemplate.apply(this, arguments);
50708     },
50709
50710     compile : function(){return this;}
50711 });
50712
50713 Roo.XTemplate.from = function(el){
50714     el = Roo.getDom(el);
50715     return new Roo.XTemplate(el.value || el.innerHTML);
50716 };/*
50717  * Original code for Roojs - LGPL
50718  * <script type="text/javascript">
50719  */
50720  
50721 /**
50722  * @class Roo.XComponent
50723  * A delayed Element creator...
50724  * 
50725  * Mypart.xyx = new Roo.XComponent({
50726
50727     parent : 'Mypart.xyz', // empty == document.element.!!
50728     order : '001',
50729     name : 'xxxx'
50730     region : 'xxxx'
50731     disabled : function() {} 
50732      
50733     tree : function() { // return an tree of xtype declared components
50734         var MODULE = this;
50735         return 
50736         {
50737             xtype : 'NestedLayoutPanel',
50738             // technicall
50739         }
50740      ]
50741  *})
50742  * @extends Roo.util.Observable
50743  * @constructor
50744  * @param cfg {Object} configuration of component
50745  * 
50746  */
50747 Roo.XComponent = function(cfg) {
50748     Roo.apply(this, cfg);
50749     this.addEvents({ 
50750         /**
50751              * @event built
50752              * Fires when this the componnt is built
50753              * @param {Roo.XComponent} c the component
50754              */
50755         'built' : true,
50756         /**
50757              * @event buildcomplete
50758              * Fires on the top level element when all elements have been built
50759              * @param {Roo.XComponent} c the top level component.
50760          */
50761         'buildcomplete' : true
50762         
50763     });
50764     
50765     Roo.XComponent.register(this);
50766     this.modules = false;
50767     this.el = false; // where the layout goes..
50768     
50769     
50770 }
50771 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50772     /**
50773      * @property el
50774      * The created element (with Roo.factory())
50775      * @type {Roo.Layout}
50776      */
50777     el  : false,
50778     
50779     /**
50780      * @property el
50781      * for BC  - use el in new code
50782      * @type {Roo.Layout}
50783      */
50784     panel : false,
50785     
50786     /**
50787      * @property layout
50788      * for BC  - use el in new code
50789      * @type {Roo.Layout}
50790      */
50791     layout : false,
50792     
50793      /**
50794      * @cfg {Function|boolean} disabled
50795      * If this module is disabled by some rule, return true from the funtion
50796      */
50797     disabled : false,
50798     
50799     /**
50800      * @cfg {String} parent 
50801      * Name of parent element which it get xtype added to..
50802      */
50803     parent: false,
50804     
50805     /**
50806      * @cfg {String} order
50807      * Used to set the order in which elements are created (usefull for multiple tabs)
50808      */
50809     
50810     order : false,
50811     /**
50812      * @cfg {String} name
50813      * String to display while loading.
50814      */
50815     name : false,
50816     /**
50817      * @cfg {Array} items
50818      * A single item array - the first element is the root of the tree..
50819      * It's done this way to stay compatible with the Xtype system...
50820      */
50821     items : false
50822      
50823      
50824     
50825 });
50826
50827 Roo.apply(Roo.XComponent, {
50828     
50829     /**
50830      * @property  buildCompleted
50831      * True when the builder has completed building the interface.
50832      * @type Boolean
50833      */
50834     buildCompleted : false,
50835      
50836     /**
50837      * @property  topModule
50838      * the upper most module - uses document.element as it's constructor.
50839      * @type Object
50840      */
50841      
50842     topModule  : false,
50843       
50844     /**
50845      * @property  modules
50846      * array of modules to be created by registration system.
50847      * @type Roo.XComponent
50848      */
50849     
50850     modules : [],
50851       
50852     
50853     /**
50854      * Register components to be built later.
50855      *
50856      * This solves the following issues
50857      * - Building is not done on page load, but after an authentication process has occured.
50858      * - Interface elements are registered on page load
50859      * - Parent Interface elements may not be loaded before child, so this handles that..
50860      * 
50861      *
50862      * example:
50863      * 
50864      * MyApp.register({
50865           order : '000001',
50866           module : 'Pman.Tab.projectMgr',
50867           region : 'center',
50868           parent : 'Pman.layout',
50869           disabled : false,  // or use a function..
50870         })
50871      
50872      * * @param {Object} details about module
50873      */
50874     register : function(obj) {
50875         this.modules.push(obj);
50876          
50877     },
50878     /**
50879      * convert a string to an object..
50880      * 
50881      */
50882     
50883     toObject : function(str)
50884     {
50885         if (!str || typeof(str) == 'object') {
50886             return str;
50887         }
50888         var ar = str.split('.');
50889         var rt, o;
50890         rt = ar.shift();
50891             /** eval:var:o */
50892         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50893         if (o === false) {
50894             throw "Module not found : " + str;
50895         }
50896         Roo.each(ar, function(e) {
50897             if (typeof(o[e]) == 'undefined') {
50898                 throw "Module not found : " + str;
50899             }
50900             o = o[e];
50901         });
50902         return o;
50903         
50904     },
50905     
50906     
50907     /**
50908      * move modules into their correct place in the tree..
50909      * 
50910      */
50911     preBuild : function ()
50912     {
50913         
50914         Roo.each(this.modules , function (obj)
50915         {
50916             obj.parent = this.toObject(obj.parent);
50917             
50918             if (!obj.parent) {
50919                 this.topModule = obj;
50920                 return;
50921             }
50922             
50923             if (!obj.parent.modules) {
50924                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50925                     function(o) { return o.order + '' }
50926                 );
50927             }
50928             
50929             obj.parent.modules.add(obj);
50930         }, this);
50931     },
50932     
50933      /**
50934      * make a list of modules to build.
50935      * @return {Array} list of modules. 
50936      */ 
50937     
50938     buildOrder : function()
50939     {
50940         var _this = this;
50941         var cmp = function(a,b) {   
50942             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50943         };
50944         
50945         if (!this.topModule || !this.topModule.modules) {
50946             throw "No top level modules to build";
50947         }
50948        
50949         // make a flat list in order of modules to build.
50950         var mods = [ this.topModule ];
50951         
50952         
50953         // add modules to their parents..
50954         var addMod = function(m) {
50955            // Roo.debug && Roo.log(m.modKey);
50956             
50957             mods.push(m);
50958             if (m.modules) {
50959                 m.modules.keySort('ASC',  cmp );
50960                 m.modules.each(addMod);
50961             }
50962             // not sure if this is used any more..
50963             if (m.finalize) {
50964                 m.finalize.name = m.name + " (clean up) ";
50965                 mods.push(m.finalize);
50966             }
50967             
50968         }
50969         this.topModule.modules.keySort('ASC',  cmp );
50970         this.topModule.modules.each(addMod);
50971         return mods;
50972     },
50973     
50974      /**
50975      * Build the registered modules.
50976      * @param {Object} parent element.
50977      * @param {Function} optional method to call after module has been added.
50978      * 
50979      */ 
50980    
50981     build : function() 
50982     {
50983         
50984         this.preBuild();
50985         var mods = this.buildOrder();
50986       
50987         //this.allmods = mods;
50988         //Roo.debug && Roo.log(mods);
50989         //return;
50990         if (!mods.length) { // should not happen
50991             throw "NO modules!!!";
50992         }
50993         
50994         
50995         
50996         // flash it up as modal - so we store the mask!?
50997         Roo.MessageBox.show({ title: 'loading' });
50998         Roo.MessageBox.show({
50999            title: "Please wait...",
51000            msg: "Building Interface...",
51001            width:450,
51002            progress:true,
51003            closable:false,
51004            modal: false
51005           
51006         });
51007         var total = mods.length;
51008         
51009         var _this = this;
51010         var progressRun = function() {
51011             if (!mods.length) {
51012                 Roo.debug && Roo.log('hide?');
51013                 Roo.MessageBox.hide();
51014                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51015                 return;    
51016             }
51017             
51018             var m = mods.shift();
51019             Roo.debug && Roo.log(m);
51020             if (typeof(m) == 'function') { // not sure if this is supported any more..
51021                 m.call(this);
51022                 return progressRun.defer(10, _this);
51023             } 
51024             
51025             Roo.MessageBox.updateProgress(
51026                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51027                     " of " + total + 
51028                     (m.name ? (' - ' + m.name) : '')
51029                     );
51030             
51031          
51032             
51033             var disabled = (typeof(m.disabled) == 'function') ?
51034                 m.disabled.call(m.module.disabled) : m.disabled;    
51035             
51036             
51037             if (disabled) {
51038                 return progressRun(); // we do not update the display!
51039             }
51040             
51041             if (!m.parent) {
51042                 // it's a top level one..
51043                 var layoutbase = new Ext.BorderLayout(document.body, {
51044                
51045                     center: {
51046                          titlebar: false,
51047                          autoScroll:false,
51048                          closeOnTab: true,
51049                          tabPosition: 'top',
51050                          //resizeTabs: true,
51051                          alwaysShowTabs: true,
51052                          minTabWidth: 140
51053                     }
51054                 });
51055                 var tree = m.tree();
51056                 tree.region = 'center';
51057                 m.el = layoutbase.addxtype(tree);
51058                 m.panel = m.el;
51059                 m.layout = m.panel.layout;    
51060                 return progressRun.defer(10, _this);
51061             }
51062             
51063             var tree = m.tree();
51064             tree.region = tree.region || m.region;
51065             m.el = m.parent.el.addxtype(tree);
51066             m.fireEvent('built', m);
51067             m.panel = m.el;
51068             m.layout = m.panel.layout;    
51069             progressRun.defer(10, _this); 
51070             
51071         }
51072         progressRun.defer(1, _this);
51073      
51074         
51075         
51076     }
51077      
51078    
51079     
51080     
51081 });
51082  //<script type="text/javascript">
51083
51084
51085 /**
51086  * @class Roo.Login
51087  * @extends Roo.LayoutDialog
51088  * A generic Login Dialog..... - only one needed in theory!?!?
51089  *
51090  * Fires XComponent builder on success...
51091  * 
51092  * Sends 
51093  *    username,password, lang = for login actions.
51094  *    check = 1 for periodic checking that sesion is valid.
51095  *    passwordRequest = email request password
51096  *    logout = 1 = to logout
51097  * 
51098  * Affects: (this id="????" elements)
51099  *   loading  (removed) (used to indicate application is loading)
51100  *   loading-mask (hides) (used to hide application when it's building loading)
51101  *   
51102  * 
51103  * Usage: 
51104  *    
51105  * 
51106  * Myapp.login = Roo.Login({
51107      url: xxxx,
51108    
51109      realm : 'Myapp', 
51110      
51111      
51112      method : 'POST',
51113      
51114      
51115      * 
51116  })
51117  * 
51118  * 
51119  * 
51120  **/
51121  
51122 Roo.Login = function(cfg)
51123 {
51124     this.addEvents({
51125         'refreshed' : true
51126     });
51127     
51128     Roo.apply(this,cfg);
51129     
51130     Roo.onReady(function() {
51131         this.onLoad();
51132     }, this);
51133     // call parent..
51134     
51135    
51136     Roo.Login.superclass.constructor.call(this, this);
51137     //this.addxtype(this.items[0]);
51138     
51139     
51140 }
51141
51142
51143 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51144     
51145     /**
51146      * @cfg {String} method
51147      * Method used to query for login details.
51148      */
51149     
51150     method : 'POST',
51151     /**
51152      * @cfg {String} url
51153      * URL to query login data. - eg. baseURL + '/Login.php'
51154      */
51155     url : '',
51156     
51157     /**
51158      * @property user
51159      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51160      * @type {Object} 
51161      */
51162     user : false,
51163     /**
51164      * @property checkFails
51165      * Number of times we have attempted to get authentication check, and failed.
51166      * @type {Number} 
51167      */
51168     checkFails : 0,
51169       /**
51170      * @property intervalID
51171      * The window interval that does the constant login checking.
51172      * @type {Number} 
51173      */
51174     intervalID : 0,
51175     
51176     
51177     onLoad : function() // called on page load...
51178     {
51179         // load 
51180          
51181         if (Roo.get('loading')) { // clear any loading indicator..
51182             Roo.get('loading').remove();
51183         }
51184         
51185         //this.switchLang('en'); // set the language to english..
51186        
51187         this.check({
51188             success:  function(response, opts)  {  // check successfull...
51189             
51190                 var res = this.processResponse(response);
51191                 this.checkFails =0;
51192                 if (!res.success) { // error!
51193                     this.checkFails = 5;
51194                     //console.log('call failure');
51195                     return this.failure(response,opts);
51196                 }
51197                 
51198                 if (!res.data.id) { // id=0 == login failure.
51199                     return this.show();
51200                 }
51201                 
51202                               
51203                         //console.log(success);
51204                 this.fillAuth(res.data);   
51205                 this.checkFails =0;
51206                 Roo.XComponent.build();
51207             },
51208             failure : this.show
51209         });
51210         
51211     }, 
51212     
51213     
51214     check: function(cfg) // called every so often to refresh cookie etc..
51215     {
51216         if (cfg.again) { // could be undefined..
51217             this.checkFails++;
51218         } else {
51219             this.checkFails = 0;
51220         }
51221         var _this = this;
51222         if (this.sending) {
51223             if ( this.checkFails > 4) {
51224                 Roo.MessageBox.alert("Error",  
51225                     "Error getting authentication status. - try reloading, or wait a while", function() {
51226                         _this.sending = false;
51227                     }); 
51228                 return;
51229             }
51230             cfg.again = true;
51231             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51232             return;
51233         }
51234         this.sending = true;
51235         
51236         Roo.Ajax.request({  
51237             url: this.url,
51238             params: {
51239                 getAuthUser: true
51240             },  
51241             method: this.method,
51242             success:  cfg.success || this.success,
51243             failure : cfg.failure || this.failure,
51244             scope : this,
51245             callCfg : cfg
51246               
51247         });  
51248     }, 
51249     
51250     
51251     logout: function()
51252     {
51253         window.onbeforeunload = function() { }; // false does not work for IE..
51254         this.user = false;
51255         var _this = this;
51256         
51257         Roo.Ajax.request({  
51258             url: this.url,
51259             params: {
51260                 logout: 1
51261             },  
51262             method: 'GET',
51263             failure : function() {
51264                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51265                     document.location = document.location.toString() + '?ts=' + Math.random();
51266                 });
51267                 
51268             },
51269             success : function() {
51270                 _this.user = false;
51271                 this.checkFails =0;
51272                 // fixme..
51273                 document.location = document.location.toString() + '?ts=' + Math.random();
51274             }
51275               
51276               
51277         }); 
51278     },
51279     
51280     processResponse : function (response)
51281     {
51282         var res = '';
51283         try {
51284             res = Roo.decode(response.responseText);
51285             // oops...
51286             if (typeof(res) != 'object') {
51287                 res = { success : false, errorMsg : res, errors : true };
51288             }
51289             if (typeof(res.success) == 'undefined') {
51290                 res.success = false;
51291             }
51292             
51293         } catch(e) {
51294             res = { success : false,  errorMsg : response.responseText, errors : true };
51295         }
51296         return res;
51297     },
51298     
51299     success : function(response, opts)  // check successfull...
51300     {  
51301         this.sending = false;
51302         var res = this.processResponse(response);
51303         if (!res.success) {
51304             return this.failure(response, opts);
51305         }
51306         if (!res.data || !res.data.id) {
51307             return this.failure(response,opts);
51308         }
51309         //console.log(res);
51310         this.fillAuth(res.data);
51311         
51312         this.checkFails =0;
51313         
51314     },
51315     
51316     
51317     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51318     {
51319         this.authUser = -1;
51320         this.sending = false;
51321         var res = this.processResponse(response);
51322         //console.log(res);
51323         if ( this.checkFails > 2) {
51324         
51325             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51326                 "Error getting authentication status. - try reloading"); 
51327             return;
51328         }
51329         opts.callCfg.again = true;
51330         this.check.defer(1000, this, [ opts.callCfg ]);
51331         return;  
51332     },
51333     
51334     
51335     
51336     fillAuth: function(au) {
51337         this.startAuthCheck();
51338         this.authUserId = au.id;
51339         this.authUser = au;
51340         this.lastChecked = new Date();
51341         this.fireEvent('refreshed', au);
51342         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51343         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51344         au.lang = au.lang || 'en';
51345         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51346         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51347         this.switchLang(au.lang );
51348         
51349      
51350         // open system... - -on setyp..
51351         if (this.authUserId  < 0) {
51352             Roo.MessageBox.alert("Warning", 
51353                 "This is an open system - please set up a admin user with a password.");  
51354         }
51355          
51356         //Pman.onload(); // which should do nothing if it's a re-auth result...
51357         
51358              
51359     },
51360     
51361     startAuthCheck : function() // starter for timeout checking..
51362     {
51363         if (this.intervalID) { // timer already in place...
51364             return false;
51365         }
51366         var _this = this;
51367         this.intervalID =  window.setInterval(function() {
51368               _this.check(false);
51369             }, 120000); // every 120 secs = 2mins..
51370         
51371         
51372     },
51373          
51374     
51375     switchLang : function (lang) 
51376     {
51377         _T = typeof(_T) == 'undefined' ? false : _T;
51378           if (!_T || !lang.length) {
51379             return;
51380         }
51381         
51382         if (!_T && lang != 'en') {
51383             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51384             return;
51385         }
51386         
51387         if (typeof(_T.en) == 'undefined') {
51388             _T.en = {};
51389             Roo.apply(_T.en, _T);
51390         }
51391         
51392         if (typeof(_T[lang]) == 'undefined') {
51393             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51394             return;
51395         }
51396         
51397         
51398         Roo.apply(_T, _T[lang]);
51399         // just need to set the text values for everything...
51400         var _this = this;
51401         /* this will not work ...
51402         if (this.form) { 
51403             
51404                
51405             function formLabel(name, val) {
51406                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51407             }
51408             
51409             formLabel('password', "Password"+':');
51410             formLabel('username', "Email Address"+':');
51411             formLabel('lang', "Language"+':');
51412             this.dialog.setTitle("Login");
51413             this.dialog.buttons[0].setText("Forgot Password");
51414             this.dialog.buttons[1].setText("Login");
51415         }
51416         */
51417         
51418         
51419     },
51420     
51421     
51422     title: "Login",
51423     modal: true,
51424     width:  350,
51425     //height: 230,
51426     height: 180,
51427     shadow: true,
51428     minWidth:200,
51429     minHeight:180,
51430     //proxyDrag: true,
51431     closable: false,
51432     draggable: false,
51433     collapsible: false,
51434     resizable: false,
51435     center: {  // needed??
51436         autoScroll:false,
51437         titlebar: false,
51438        // tabPosition: 'top',
51439         hideTabs: true,
51440         closeOnTab: true,
51441         alwaysShowTabs: false
51442     } ,
51443     listeners : {
51444         
51445         show  : function(dlg)
51446         {
51447             //console.log(this);
51448             this.form = this.layout.getRegion('center').activePanel.form;
51449             this.form.dialog = dlg;
51450             this.buttons[0].form = this.form;
51451             this.buttons[0].dialog = dlg;
51452             this.buttons[1].form = this.form;
51453             this.buttons[1].dialog = dlg;
51454            
51455            //this.resizeToLogo.defer(1000,this);
51456             // this is all related to resizing for logos..
51457             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51458            //// if (!sz) {
51459              //   this.resizeToLogo.defer(1000,this);
51460              //   return;
51461            // }
51462             //var w = Ext.lib.Dom.getViewWidth() - 100;
51463             //var h = Ext.lib.Dom.getViewHeight() - 100;
51464             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51465             //this.center();
51466             if (this.disabled) {
51467                 this.hide();
51468                 return;
51469             }
51470             
51471             if (this.user.id < 0) { // used for inital setup situations.
51472                 return;
51473             }
51474             
51475             if (this.intervalID) {
51476                 // remove the timer
51477                 window.clearInterval(this.intervalID);
51478                 this.intervalID = false;
51479             }
51480             
51481             
51482             if (Roo.get('loading')) {
51483                 Roo.get('loading').remove();
51484             }
51485             if (Roo.get('loading-mask')) {
51486                 Roo.get('loading-mask').hide();
51487             }
51488             
51489             //incomming._node = tnode;
51490             this.form.reset();
51491             //this.dialog.modal = !modal;
51492             //this.dialog.show();
51493             this.el.unmask(); 
51494             
51495             
51496             this.form.setValues({
51497                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51498                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51499             });
51500             
51501             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51502             if (this.form.findField('username').getValue().length > 0 ){
51503                 this.form.findField('password').focus();
51504             } else {
51505                this.form.findField('username').focus();
51506             }
51507     
51508         }
51509     },
51510     items : [
51511          {
51512        
51513             xtype : 'ContentPanel',
51514             xns : Roo,
51515             region: 'center',
51516             fitToFrame : true,
51517             
51518             items : [
51519     
51520                 {
51521                
51522                     xtype : 'Form',
51523                     xns : Roo.form,
51524                     labelWidth: 100,
51525                     style : 'margin: 10px;',
51526                     
51527                     listeners : {
51528                         actionfailed : function(f, act) {
51529                             // form can return { errors: .... }
51530                                 
51531                             //act.result.errors // invalid form element list...
51532                             //act.result.errorMsg// invalid form element list...
51533                             
51534                             this.dialog.el.unmask();
51535                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51536                                         "Login failed - communication error - try again.");
51537                                       
51538                         },
51539                         actioncomplete: function(re, act) {
51540                              
51541                             Roo.state.Manager.set(
51542                                 this.dialog.realm + '.username',  
51543                                     this.findField('username').getValue()
51544                             );
51545                             Roo.state.Manager.set(
51546                                 this.dialog.realm + '.lang',  
51547                                 this.findField('lang').getValue() 
51548                             );
51549                             
51550                             this.dialog.fillAuth(act.result.data);
51551                               
51552                             this.dialog.hide();
51553                             
51554                             if (Roo.get('loading-mask')) {
51555                                 Roo.get('loading-mask').show();
51556                             }
51557                             Roo.XComponent.build();
51558                             
51559                              
51560                             
51561                         }
51562                     },
51563                     items : [
51564                         {
51565                             xtype : 'TextField',
51566                             xns : Roo.form,
51567                             fieldLabel: "Email Address",
51568                             name: 'username',
51569                             width:200,
51570                             autoCreate : {tag: "input", type: "text", size: "20"}
51571                         },
51572                         {
51573                             xtype : 'TextField',
51574                             xns : Roo.form,
51575                             fieldLabel: "Password",
51576                             inputType: 'password',
51577                             name: 'password',
51578                             width:200,
51579                             autoCreate : {tag: "input", type: "text", size: "20"},
51580                             listeners : {
51581                                 specialkey : function(e,ev) {
51582                                     if (ev.keyCode == 13) {
51583                                         this.form.dialog.el.mask("Logging in");
51584                                         this.form.doAction('submit', {
51585                                             url: this.form.dialog.url,
51586                                             method: this.form.dialog.method
51587                                         });
51588                                     }
51589                                 }
51590                             }  
51591                         },
51592                         {
51593                             xtype : 'ComboBox',
51594                             xns : Roo.form,
51595                             fieldLabel: "Language",
51596                             name : 'langdisp',
51597                             store: {
51598                                 xtype : 'SimpleStore',
51599                                 fields: ['lang', 'ldisp'],
51600                                 data : [
51601                                     [ 'en', 'English' ],
51602                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51603                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51604                                 ]
51605                             },
51606                             
51607                             valueField : 'lang',
51608                             hiddenName:  'lang',
51609                             width: 200,
51610                             displayField:'ldisp',
51611                             typeAhead: false,
51612                             editable: false,
51613                             mode: 'local',
51614                             triggerAction: 'all',
51615                             emptyText:'Select a Language...',
51616                             selectOnFocus:true,
51617                             listeners : {
51618                                 select :  function(cb, rec, ix) {
51619                                     this.form.switchLang(rec.data.lang);
51620                                 }
51621                             }
51622                         
51623                         }
51624                     ]
51625                 }
51626                   
51627                 
51628             ]
51629         }
51630     ],
51631     buttons : [
51632         {
51633             xtype : 'Button',
51634             xns : 'Roo',
51635             text : "Forgot Password",
51636             listeners : {
51637                 click : function() {
51638                     //console.log(this);
51639                     var n = this.form.findField('username').getValue();
51640                     if (!n.length) {
51641                         Roo.MessageBox.alert("Error", "Fill in your email address");
51642                         return;
51643                     }
51644                     Roo.Ajax.request({
51645                         url: this.dialog.url,
51646                         params: {
51647                             passwordRequest: n
51648                         },
51649                         method: this.dialog.method,
51650                         success:  function(response, opts)  {  // check successfull...
51651                         
51652                             var res = this.dialog.processResponse(response);
51653                             if (!res.success) { // error!
51654                                Roo.MessageBox.alert("Error" ,
51655                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51656                                return;
51657                             }
51658                             Roo.MessageBox.alert("Notice" ,
51659                                 "Please check you email for the Password Reset message");
51660                         },
51661                         failure : function() {
51662                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51663                         }
51664                         
51665                     });
51666                 }
51667             }
51668         },
51669         {
51670             xtype : 'Button',
51671             xns : 'Roo',
51672             text : "Login",
51673             listeners : {
51674                 
51675                 click : function () {
51676                         
51677                     this.dialog.el.mask("Logging in");
51678                     this.form.doAction('submit', {
51679                             url: this.dialog.url,
51680                             method: this.dialog.method
51681                     });
51682                 }
51683             }
51684         }
51685     ]
51686   
51687   
51688 })
51689  
51690
51691
51692