Update compiled build
[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          * Selects a single element as a Roo Element
576          * This is about as close as you can get to jQuery's $('do crazy stuff')
577          * @param {String} selector The selector/xpath query
578          * @param {Node} root (optional) The start of the query (defaults to document).
579          * @return {Roo.Element}
580          */
581         selectNode : function(selector, root) 
582         {
583             var node = Roo.DomQuery.selectNode(selector,root);
584             return node ? Roo.get(node) : new Roo.Element(false);
585         }
586         
587     });
588
589
590 })();
591
592 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
593                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
594 /*
595  * Based on:
596  * Ext JS Library 1.1.1
597  * Copyright(c) 2006-2007, Ext JS, LLC.
598  *
599  * Originally Released Under LGPL - original licence link has changed is not relivant.
600  *
601  * Fork - LGPL
602  * <script type="text/javascript">
603  */
604
605 (function() {    
606     // wrappedn so fnCleanup is not in global scope...
607     if(Roo.isIE) {
608         function fnCleanUp() {
609             var p = Function.prototype;
610             delete p.createSequence;
611             delete p.defer;
612             delete p.createDelegate;
613             delete p.createCallback;
614             delete p.createInterceptor;
615
616             window.detachEvent("onunload", fnCleanUp);
617         }
618         window.attachEvent("onunload", fnCleanUp);
619     }
620 })();
621
622
623 /**
624  * @class Function
625  * These functions are available on every Function object (any JavaScript function).
626  */
627 Roo.apply(Function.prototype, {
628      /**
629      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
630      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
631      * Will create a function that is bound to those 2 args.
632      * @return {Function} The new function
633     */
634     createCallback : function(/*args...*/){
635         // make args available, in function below
636         var args = arguments;
637         var method = this;
638         return function() {
639             return method.apply(window, args);
640         };
641     },
642
643     /**
644      * Creates a delegate (callback) that sets the scope to obj.
645      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
646      * Will create a function that is automatically scoped to this.
647      * @param {Object} obj (optional) The object for which the scope is set
648      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
649      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
650      *                                             if a number the args are inserted at the specified position
651      * @return {Function} The new function
652      */
653     createDelegate : function(obj, args, appendArgs){
654         var method = this;
655         return function() {
656             var callArgs = args || arguments;
657             if(appendArgs === true){
658                 callArgs = Array.prototype.slice.call(arguments, 0);
659                 callArgs = callArgs.concat(args);
660             }else if(typeof appendArgs == "number"){
661                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
662                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
663                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
664             }
665             return method.apply(obj || window, callArgs);
666         };
667     },
668
669     /**
670      * Calls this function after the number of millseconds specified.
671      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
672      * @param {Object} obj (optional) The object for which the scope is set
673      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
674      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
675      *                                             if a number the args are inserted at the specified position
676      * @return {Number} The timeout id that can be used with clearTimeout
677      */
678     defer : function(millis, obj, args, appendArgs){
679         var fn = this.createDelegate(obj, args, appendArgs);
680         if(millis){
681             return setTimeout(fn, millis);
682         }
683         fn();
684         return 0;
685     },
686     /**
687      * Create a combined function call sequence of the original function + the passed function.
688      * The resulting function returns the results of the original function.
689      * The passed fcn is called with the parameters of the original function
690      * @param {Function} fcn The function to sequence
691      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
692      * @return {Function} The new function
693      */
694     createSequence : function(fcn, scope){
695         if(typeof fcn != "function"){
696             return this;
697         }
698         var method = this;
699         return function() {
700             var retval = method.apply(this || window, arguments);
701             fcn.apply(scope || this || window, arguments);
702             return retval;
703         };
704     },
705
706     /**
707      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
708      * The resulting function returns the results of the original function.
709      * The passed fcn is called with the parameters of the original function.
710      * @addon
711      * @param {Function} fcn The function to call before the original
712      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
713      * @return {Function} The new function
714      */
715     createInterceptor : function(fcn, scope){
716         if(typeof fcn != "function"){
717             return this;
718         }
719         var method = this;
720         return function() {
721             fcn.target = this;
722             fcn.method = method;
723             if(fcn.apply(scope || this || window, arguments) === false){
724                 return;
725             }
726             return method.apply(this || window, arguments);
727         };
728     }
729 });
730 /*
731  * Based on:
732  * Ext JS Library 1.1.1
733  * Copyright(c) 2006-2007, Ext JS, LLC.
734  *
735  * Originally Released Under LGPL - original licence link has changed is not relivant.
736  *
737  * Fork - LGPL
738  * <script type="text/javascript">
739  */
740
741 Roo.applyIf(String, {
742     
743     /** @scope String */
744     
745     /**
746      * Escapes the passed string for ' and \
747      * @param {String} string The string to escape
748      * @return {String} The escaped string
749      * @static
750      */
751     escape : function(string) {
752         return string.replace(/('|\\)/g, "\\$1");
753     },
754
755     /**
756      * Pads the left side of a string with a specified character.  This is especially useful
757      * for normalizing number and date strings.  Example usage:
758      * <pre><code>
759 var s = String.leftPad('123', 5, '0');
760 // s now contains the string: '00123'
761 </code></pre>
762      * @param {String} string The original string
763      * @param {Number} size The total length of the output string
764      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
765      * @return {String} The padded string
766      * @static
767      */
768     leftPad : function (val, size, ch) {
769         var result = new String(val);
770         if(ch === null || ch === undefined || ch === '') {
771             ch = " ";
772         }
773         while (result.length < size) {
774             result = ch + result;
775         }
776         return result;
777     },
778
779     /**
780      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
781      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
782      * <pre><code>
783 var cls = 'my-class', text = 'Some text';
784 var s = String.format('<div class="{0}">{1}</div>', cls, text);
785 // s now contains the string: '<div class="my-class">Some text</div>'
786 </code></pre>
787      * @param {String} string The tokenized string to be formatted
788      * @param {String} value1 The value to replace token {0}
789      * @param {String} value2 Etc...
790      * @return {String} The formatted string
791      * @static
792      */
793     format : function(format){
794         var args = Array.prototype.slice.call(arguments, 1);
795         return format.replace(/\{(\d+)\}/g, function(m, i){
796             return Roo.util.Format.htmlEncode(args[i]);
797         });
798     }
799 });
800
801 /**
802  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
803  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
804  * they are already different, the first value passed in is returned.  Note that this method returns the new value
805  * but does not change the current string.
806  * <pre><code>
807 // alternate sort directions
808 sort = sort.toggle('ASC', 'DESC');
809
810 // instead of conditional logic:
811 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
812 </code></pre>
813  * @param {String} value The value to compare to the current string
814  * @param {String} other The new value to use if the string already equals the first value passed in
815  * @return {String} The new value
816  */
817  
818 String.prototype.toggle = function(value, other){
819     return this == value ? other : value;
820 };/*
821  * Based on:
822  * Ext JS Library 1.1.1
823  * Copyright(c) 2006-2007, Ext JS, LLC.
824  *
825  * Originally Released Under LGPL - original licence link has changed is not relivant.
826  *
827  * Fork - LGPL
828  * <script type="text/javascript">
829  */
830
831  /**
832  * @class Number
833  */
834 Roo.applyIf(Number.prototype, {
835     /**
836      * Checks whether or not the current number is within a desired range.  If the number is already within the
837      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
838      * exceeded.  Note that this method returns the constrained value but does not change the current number.
839      * @param {Number} min The minimum number in the range
840      * @param {Number} max The maximum number in the range
841      * @return {Number} The constrained value if outside the range, otherwise the current value
842      */
843     constrain : function(min, max){
844         return Math.min(Math.max(this, min), max);
845     }
846 });/*
847  * Based on:
848  * Ext JS Library 1.1.1
849  * Copyright(c) 2006-2007, Ext JS, LLC.
850  *
851  * Originally Released Under LGPL - original licence link has changed is not relivant.
852  *
853  * Fork - LGPL
854  * <script type="text/javascript">
855  */
856  /**
857  * @class Array
858  */
859 Roo.applyIf(Array.prototype, {
860     /**
861      * Checks whether or not the specified object exists in the array.
862      * @param {Object} o The object to check for
863      * @return {Number} The index of o in the array (or -1 if it is not found)
864      */
865     indexOf : function(o){
866        for (var i = 0, len = this.length; i < len; i++){
867               if(this[i] == o) return i;
868        }
869            return -1;
870     },
871
872     /**
873      * Removes the specified object from the array.  If the object is not found nothing happens.
874      * @param {Object} o The object to remove
875      */
876     remove : function(o){
877        var index = this.indexOf(o);
878        if(index != -1){
879            this.splice(index, 1);
880        }
881     },
882     /**
883      * Map (JS 1.6 compatibility)
884      * @param {Function} function  to call
885      */
886     map : function(fun )
887     {
888         var len = this.length >>> 0;
889         if (typeof fun != "function")
890             throw new TypeError();
891
892         var res = new Array(len);
893         var thisp = arguments[1];
894         for (var i = 0; i < len; i++)
895         {
896             if (i in this)
897                 res[i] = fun.call(thisp, this[i], i, this);
898         }
899
900         return res;
901     }
902     
903 });
904
905
906  /*
907  * Based on:
908  * Ext JS Library 1.1.1
909  * Copyright(c) 2006-2007, Ext JS, LLC.
910  *
911  * Originally Released Under LGPL - original licence link has changed is not relivant.
912  *
913  * Fork - LGPL
914  * <script type="text/javascript">
915  */
916
917 /**
918  * @class Date
919  *
920  * The date parsing and format syntax is a subset of
921  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
922  * supported will provide results equivalent to their PHP versions.
923  *
924  * Following is the list of all currently supported formats:
925  *<pre>
926 Sample date:
927 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
928
929 Format  Output      Description
930 ------  ----------  --------------------------------------------------------------
931   d      10         Day of the month, 2 digits with leading zeros
932   D      Wed        A textual representation of a day, three letters
933   j      10         Day of the month without leading zeros
934   l      Wednesday  A full textual representation of the day of the week
935   S      th         English ordinal day of month suffix, 2 chars (use with j)
936   w      3          Numeric representation of the day of the week
937   z      9          The julian date, or day of the year (0-365)
938   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
939   F      January    A full textual representation of the month
940   m      01         Numeric representation of a month, with leading zeros
941   M      Jan        Month name abbreviation, three letters
942   n      1          Numeric representation of a month, without leading zeros
943   t      31         Number of days in the given month
944   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
945   Y      2007       A full numeric representation of a year, 4 digits
946   y      07         A two digit representation of a year
947   a      pm         Lowercase Ante meridiem and Post meridiem
948   A      PM         Uppercase Ante meridiem and Post meridiem
949   g      3          12-hour format of an hour without leading zeros
950   G      15         24-hour format of an hour without leading zeros
951   h      03         12-hour format of an hour with leading zeros
952   H      15         24-hour format of an hour with leading zeros
953   i      05         Minutes with leading zeros
954   s      01         Seconds, with leading zeros
955   O      -0600      Difference to Greenwich time (GMT) in hours
956   T      CST        Timezone setting of the machine running the code
957   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
958 </pre>
959  *
960  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
961  * <pre><code>
962 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
963 document.write(dt.format('Y-m-d'));                         //2007-01-10
964 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
965 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
966  </code></pre>
967  *
968  * Here are some standard date/time patterns that you might find helpful.  They
969  * are not part of the source of Date.js, but to use them you can simply copy this
970  * block of code into any script that is included after Date.js and they will also become
971  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
972  * <pre><code>
973 Date.patterns = {
974     ISO8601Long:"Y-m-d H:i:s",
975     ISO8601Short:"Y-m-d",
976     ShortDate: "n/j/Y",
977     LongDate: "l, F d, Y",
978     FullDateTime: "l, F d, Y g:i:s A",
979     MonthDay: "F d",
980     ShortTime: "g:i A",
981     LongTime: "g:i:s A",
982     SortableDateTime: "Y-m-d\\TH:i:s",
983     UniversalSortableDateTime: "Y-m-d H:i:sO",
984     YearMonth: "F, Y"
985 };
986 </code></pre>
987  *
988  * Example usage:
989  * <pre><code>
990 var dt = new Date();
991 document.write(dt.format(Date.patterns.ShortDate));
992  </code></pre>
993  */
994
995 /*
996  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
997  * They generate precompiled functions from date formats instead of parsing and
998  * processing the pattern every time you format a date.  These functions are available
999  * on every Date object (any javascript function).
1000  *
1001  * The original article and download are here:
1002  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1003  *
1004  */
1005  
1006  
1007  // was in core
1008 /**
1009  Returns the number of milliseconds between this date and date
1010  @param {Date} date (optional) Defaults to now
1011  @return {Number} The diff in milliseconds
1012  @member Date getElapsed
1013  */
1014 Date.prototype.getElapsed = function(date) {
1015         return Math.abs((date || new Date()).getTime()-this.getTime());
1016 };
1017 // was in date file..
1018
1019
1020 // private
1021 Date.parseFunctions = {count:0};
1022 // private
1023 Date.parseRegexes = [];
1024 // private
1025 Date.formatFunctions = {count:0};
1026
1027 // private
1028 Date.prototype.dateFormat = function(format) {
1029     if (Date.formatFunctions[format] == null) {
1030         Date.createNewFormat(format);
1031     }
1032     var func = Date.formatFunctions[format];
1033     return this[func]();
1034 };
1035
1036
1037 /**
1038  * Formats a date given the supplied format string
1039  * @param {String} format The format string
1040  * @return {String} The formatted date
1041  * @method
1042  */
1043 Date.prototype.format = Date.prototype.dateFormat;
1044
1045 // private
1046 Date.createNewFormat = function(format) {
1047     var funcName = "format" + Date.formatFunctions.count++;
1048     Date.formatFunctions[format] = funcName;
1049     var code = "Date.prototype." + funcName + " = function(){return ";
1050     var special = false;
1051     var ch = '';
1052     for (var i = 0; i < format.length; ++i) {
1053         ch = format.charAt(i);
1054         if (!special && ch == "\\") {
1055             special = true;
1056         }
1057         else if (special) {
1058             special = false;
1059             code += "'" + String.escape(ch) + "' + ";
1060         }
1061         else {
1062             code += Date.getFormatCode(ch);
1063         }
1064     }
1065     /** eval:var:zzzzzzzzzzzzz */
1066     eval(code.substring(0, code.length - 3) + ";}");
1067 };
1068
1069 // private
1070 Date.getFormatCode = function(character) {
1071     switch (character) {
1072     case "d":
1073         return "String.leftPad(this.getDate(), 2, '0') + ";
1074     case "D":
1075         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1076     case "j":
1077         return "this.getDate() + ";
1078     case "l":
1079         return "Date.dayNames[this.getDay()] + ";
1080     case "S":
1081         return "this.getSuffix() + ";
1082     case "w":
1083         return "this.getDay() + ";
1084     case "z":
1085         return "this.getDayOfYear() + ";
1086     case "W":
1087         return "this.getWeekOfYear() + ";
1088     case "F":
1089         return "Date.monthNames[this.getMonth()] + ";
1090     case "m":
1091         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1092     case "M":
1093         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1094     case "n":
1095         return "(this.getMonth() + 1) + ";
1096     case "t":
1097         return "this.getDaysInMonth() + ";
1098     case "L":
1099         return "(this.isLeapYear() ? 1 : 0) + ";
1100     case "Y":
1101         return "this.getFullYear() + ";
1102     case "y":
1103         return "('' + this.getFullYear()).substring(2, 4) + ";
1104     case "a":
1105         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1106     case "A":
1107         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1108     case "g":
1109         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1110     case "G":
1111         return "this.getHours() + ";
1112     case "h":
1113         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1114     case "H":
1115         return "String.leftPad(this.getHours(), 2, '0') + ";
1116     case "i":
1117         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1118     case "s":
1119         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1120     case "O":
1121         return "this.getGMTOffset() + ";
1122     case "T":
1123         return "this.getTimezone() + ";
1124     case "Z":
1125         return "(this.getTimezoneOffset() * -60) + ";
1126     default:
1127         return "'" + String.escape(character) + "' + ";
1128     }
1129 };
1130
1131 /**
1132  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1133  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1134  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1135  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1136  * string or the parse operation will fail.
1137  * Example Usage:
1138 <pre><code>
1139 //dt = Fri May 25 2007 (current date)
1140 var dt = new Date();
1141
1142 //dt = Thu May 25 2006 (today's month/day in 2006)
1143 dt = Date.parseDate("2006", "Y");
1144
1145 //dt = Sun Jan 15 2006 (all date parts specified)
1146 dt = Date.parseDate("2006-1-15", "Y-m-d");
1147
1148 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1149 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1150 </code></pre>
1151  * @param {String} input The unparsed date as a string
1152  * @param {String} format The format the date is in
1153  * @return {Date} The parsed date
1154  * @static
1155  */
1156 Date.parseDate = function(input, format) {
1157     if (Date.parseFunctions[format] == null) {
1158         Date.createParser(format);
1159     }
1160     var func = Date.parseFunctions[format];
1161     return Date[func](input);
1162 };
1163 /**
1164  * @private
1165  */
1166 Date.createParser = function(format) {
1167     var funcName = "parse" + Date.parseFunctions.count++;
1168     var regexNum = Date.parseRegexes.length;
1169     var currentGroup = 1;
1170     Date.parseFunctions[format] = funcName;
1171
1172     var code = "Date." + funcName + " = function(input){\n"
1173         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1174         + "var d = new Date();\n"
1175         + "y = d.getFullYear();\n"
1176         + "m = d.getMonth();\n"
1177         + "d = d.getDate();\n"
1178         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1179         + "if (results && results.length > 0) {";
1180     var regex = "";
1181
1182     var special = false;
1183     var ch = '';
1184     for (var i = 0; i < format.length; ++i) {
1185         ch = format.charAt(i);
1186         if (!special && ch == "\\") {
1187             special = true;
1188         }
1189         else if (special) {
1190             special = false;
1191             regex += String.escape(ch);
1192         }
1193         else {
1194             var obj = Date.formatCodeToRegex(ch, currentGroup);
1195             currentGroup += obj.g;
1196             regex += obj.s;
1197             if (obj.g && obj.c) {
1198                 code += obj.c;
1199             }
1200         }
1201     }
1202
1203     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1204         + "{v = new Date(y, m, d, h, i, s);}\n"
1205         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1206         + "{v = new Date(y, m, d, h, i);}\n"
1207         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1208         + "{v = new Date(y, m, d, h);}\n"
1209         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1210         + "{v = new Date(y, m, d);}\n"
1211         + "else if (y >= 0 && m >= 0)\n"
1212         + "{v = new Date(y, m);}\n"
1213         + "else if (y >= 0)\n"
1214         + "{v = new Date(y);}\n"
1215         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1216         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1217         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1218         + ";}";
1219
1220     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1221     /** eval:var:zzzzzzzzzzzzz */
1222     eval(code);
1223 };
1224
1225 // private
1226 Date.formatCodeToRegex = function(character, currentGroup) {
1227     switch (character) {
1228     case "D":
1229         return {g:0,
1230         c:null,
1231         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1232     case "j":
1233         return {g:1,
1234             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1235             s:"(\\d{1,2})"}; // day of month without leading zeroes
1236     case "d":
1237         return {g:1,
1238             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1239             s:"(\\d{2})"}; // day of month with leading zeroes
1240     case "l":
1241         return {g:0,
1242             c:null,
1243             s:"(?:" + Date.dayNames.join("|") + ")"};
1244     case "S":
1245         return {g:0,
1246             c:null,
1247             s:"(?:st|nd|rd|th)"};
1248     case "w":
1249         return {g:0,
1250             c:null,
1251             s:"\\d"};
1252     case "z":
1253         return {g:0,
1254             c:null,
1255             s:"(?:\\d{1,3})"};
1256     case "W":
1257         return {g:0,
1258             c:null,
1259             s:"(?:\\d{2})"};
1260     case "F":
1261         return {g:1,
1262             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1263             s:"(" + Date.monthNames.join("|") + ")"};
1264     case "M":
1265         return {g:1,
1266             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1267             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1268     case "n":
1269         return {g:1,
1270             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1271             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1272     case "m":
1273         return {g:1,
1274             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1275             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1276     case "t":
1277         return {g:0,
1278             c:null,
1279             s:"\\d{1,2}"};
1280     case "L":
1281         return {g:0,
1282             c:null,
1283             s:"(?:1|0)"};
1284     case "Y":
1285         return {g:1,
1286             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{4})"};
1288     case "y":
1289         return {g:1,
1290             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1291                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1292             s:"(\\d{1,2})"};
1293     case "a":
1294         return {g:1,
1295             c:"if (results[" + currentGroup + "] == 'am') {\n"
1296                 + "if (h == 12) { h = 0; }\n"
1297                 + "} else { if (h < 12) { h += 12; }}",
1298             s:"(am|pm)"};
1299     case "A":
1300         return {g:1,
1301             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1302                 + "if (h == 12) { h = 0; }\n"
1303                 + "} else { if (h < 12) { h += 12; }}",
1304             s:"(AM|PM)"};
1305     case "g":
1306     case "G":
1307         return {g:1,
1308             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1309             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1310     case "h":
1311     case "H":
1312         return {g:1,
1313             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1315     case "i":
1316         return {g:1,
1317             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"};
1319     case "s":
1320         return {g:1,
1321             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1322             s:"(\\d{2})"};
1323     case "O":
1324         return {g:1,
1325             c:[
1326                 "o = results[", currentGroup, "];\n",
1327                 "var sn = o.substring(0,1);\n", // get + / - sign
1328                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1329                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1330                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1331                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1332             ].join(""),
1333             s:"([+\-]\\d{4})"};
1334     case "T":
1335         return {g:0,
1336             c:null,
1337             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1338     case "Z":
1339         return {g:1,
1340             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1341                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1342             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1343     default:
1344         return {g:0,
1345             c:null,
1346             s:String.escape(character)};
1347     }
1348 };
1349
1350 /**
1351  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1352  * @return {String} The abbreviated timezone name (e.g. 'CST')
1353  */
1354 Date.prototype.getTimezone = function() {
1355     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1356 };
1357
1358 /**
1359  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1360  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1361  */
1362 Date.prototype.getGMTOffset = function() {
1363     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1364         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1365         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1366 };
1367
1368 /**
1369  * Get the numeric day number of the year, adjusted for leap year.
1370  * @return {Number} 0 through 364 (365 in leap years)
1371  */
1372 Date.prototype.getDayOfYear = function() {
1373     var num = 0;
1374     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1375     for (var i = 0; i < this.getMonth(); ++i) {
1376         num += Date.daysInMonth[i];
1377     }
1378     return num + this.getDate() - 1;
1379 };
1380
1381 /**
1382  * Get the string representation of the numeric week number of the year
1383  * (equivalent to the format specifier 'W').
1384  * @return {String} '00' through '52'
1385  */
1386 Date.prototype.getWeekOfYear = function() {
1387     // Skip to Thursday of this week
1388     var now = this.getDayOfYear() + (4 - this.getDay());
1389     // Find the first Thursday of the year
1390     var jan1 = new Date(this.getFullYear(), 0, 1);
1391     var then = (7 - jan1.getDay() + 4);
1392     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1393 };
1394
1395 /**
1396  * Whether or not the current date is in a leap year.
1397  * @return {Boolean} True if the current date is in a leap year, else false
1398  */
1399 Date.prototype.isLeapYear = function() {
1400     var year = this.getFullYear();
1401     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1402 };
1403
1404 /**
1405  * Get the first 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.getFirstDayOfMonth()]); //output: 'Monday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getFirstDayOfMonth = function() {
1416     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420 /**
1421  * Get the last day of the current month, adjusted for leap year.  The returned value
1422  * is the numeric day index within the week (0-6) which can be used in conjunction with
1423  * the {@link #monthNames} array to retrieve the textual day name.
1424  * Example:
1425  *<pre><code>
1426 var dt = new Date('1/10/2007');
1427 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1428 </code></pre>
1429  * @return {Number} The day number (0-6)
1430  */
1431 Date.prototype.getLastDayOfMonth = function() {
1432     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1433     return (day < 0) ? (day + 7) : day;
1434 };
1435
1436
1437 /**
1438  * Get the first date of this date's month
1439  * @return {Date}
1440  */
1441 Date.prototype.getFirstDateOfMonth = function() {
1442     return new Date(this.getFullYear(), this.getMonth(), 1);
1443 };
1444
1445 /**
1446  * Get the last date of this date's month
1447  * @return {Date}
1448  */
1449 Date.prototype.getLastDateOfMonth = function() {
1450     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1451 };
1452 /**
1453  * Get the number of days in the current month, adjusted for leap year.
1454  * @return {Number} The number of days in the month
1455  */
1456 Date.prototype.getDaysInMonth = function() {
1457     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1458     return Date.daysInMonth[this.getMonth()];
1459 };
1460
1461 /**
1462  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1463  * @return {String} 'st, 'nd', 'rd' or 'th'
1464  */
1465 Date.prototype.getSuffix = function() {
1466     switch (this.getDate()) {
1467         case 1:
1468         case 21:
1469         case 31:
1470             return "st";
1471         case 2:
1472         case 22:
1473             return "nd";
1474         case 3:
1475         case 23:
1476             return "rd";
1477         default:
1478             return "th";
1479     }
1480 };
1481
1482 // private
1483 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1484
1485 /**
1486  * An array of textual month names.
1487  * Override these values for international dates, for example...
1488  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1489  * @type Array
1490  * @static
1491  */
1492 Date.monthNames =
1493    ["January",
1494     "February",
1495     "March",
1496     "April",
1497     "May",
1498     "June",
1499     "July",
1500     "August",
1501     "September",
1502     "October",
1503     "November",
1504     "December"];
1505
1506 /**
1507  * An array of textual day names.
1508  * Override these values for international dates, for example...
1509  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1510  * @type Array
1511  * @static
1512  */
1513 Date.dayNames =
1514    ["Sunday",
1515     "Monday",
1516     "Tuesday",
1517     "Wednesday",
1518     "Thursday",
1519     "Friday",
1520     "Saturday"];
1521
1522 // private
1523 Date.y2kYear = 50;
1524 // private
1525 Date.monthNumbers = {
1526     Jan:0,
1527     Feb:1,
1528     Mar:2,
1529     Apr:3,
1530     May:4,
1531     Jun:5,
1532     Jul:6,
1533     Aug:7,
1534     Sep:8,
1535     Oct:9,
1536     Nov:10,
1537     Dec:11};
1538
1539 /**
1540  * Creates and returns a new Date instance with the exact same date value as the called instance.
1541  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1542  * variable will also be changed.  When the intention is to create a new variable that will not
1543  * modify the original instance, you should create a clone.
1544  *
1545  * Example of correctly cloning a date:
1546  * <pre><code>
1547 //wrong way:
1548 var orig = new Date('10/1/2006');
1549 var copy = orig;
1550 copy.setDate(5);
1551 document.write(orig);  //returns 'Thu Oct 05 2006'!
1552
1553 //correct way:
1554 var orig = new Date('10/1/2006');
1555 var copy = orig.clone();
1556 copy.setDate(5);
1557 document.write(orig);  //returns 'Thu Oct 01 2006'
1558 </code></pre>
1559  * @return {Date} The new Date instance
1560  */
1561 Date.prototype.clone = function() {
1562         return new Date(this.getTime());
1563 };
1564
1565 /**
1566  * Clears any time information from this date
1567  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1568  @return {Date} this or the clone
1569  */
1570 Date.prototype.clearTime = function(clone){
1571     if(clone){
1572         return this.clone().clearTime();
1573     }
1574     this.setHours(0);
1575     this.setMinutes(0);
1576     this.setSeconds(0);
1577     this.setMilliseconds(0);
1578     return this;
1579 };
1580
1581 // private
1582 // safari setMonth is broken
1583 if(Roo.isSafari){
1584     Date.brokenSetMonth = Date.prototype.setMonth;
1585         Date.prototype.setMonth = function(num){
1586                 if(num <= -1){
1587                         var n = Math.ceil(-num);
1588                         var back_year = Math.ceil(n/12);
1589                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1590                         this.setFullYear(this.getFullYear() - back_year);
1591                         return Date.brokenSetMonth.call(this, month);
1592                 } else {
1593                         return Date.brokenSetMonth.apply(this, arguments);
1594                 }
1595         };
1596 }
1597
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.MILLI = "ms";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.SECOND = "s";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.MINUTE = "mi";
1610 /** Date interval constant 
1611 * @static 
1612 * @type String */
1613 Date.HOUR = "h";
1614 /** Date interval constant 
1615 * @static 
1616 * @type String */
1617 Date.DAY = "d";
1618 /** Date interval constant 
1619 * @static 
1620 * @type String */
1621 Date.MONTH = "mo";
1622 /** Date interval constant 
1623 * @static 
1624 * @type String */
1625 Date.YEAR = "y";
1626
1627 /**
1628  * Provides a convenient method of performing basic date arithmetic.  This method
1629  * does not modify the Date instance being called - it creates and returns
1630  * a new Date instance containing the resulting date value.
1631  *
1632  * Examples:
1633  * <pre><code>
1634 //Basic usage:
1635 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1636 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1637
1638 //Negative values will subtract correctly:
1639 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1640 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1641
1642 //You can even chain several calls together in one line!
1643 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1644 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1645  </code></pre>
1646  *
1647  * @param {String} interval   A valid date interval enum value
1648  * @param {Number} value      The amount to add to the current date
1649  * @return {Date} The new Date instance
1650  */
1651 Date.prototype.add = function(interval, value){
1652   var d = this.clone();
1653   if (!interval || value === 0) return d;
1654   switch(interval.toLowerCase()){
1655     case Date.MILLI:
1656       d.setMilliseconds(this.getMilliseconds() + value);
1657       break;
1658     case Date.SECOND:
1659       d.setSeconds(this.getSeconds() + value);
1660       break;
1661     case Date.MINUTE:
1662       d.setMinutes(this.getMinutes() + value);
1663       break;
1664     case Date.HOUR:
1665       d.setHours(this.getHours() + value);
1666       break;
1667     case Date.DAY:
1668       d.setDate(this.getDate() + value);
1669       break;
1670     case Date.MONTH:
1671       var day = this.getDate();
1672       if(day > 28){
1673           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1674       }
1675       d.setDate(day);
1676       d.setMonth(this.getMonth() + value);
1677       break;
1678     case Date.YEAR:
1679       d.setFullYear(this.getFullYear() + value);
1680       break;
1681   }
1682   return d;
1683 };/*
1684  * Based on:
1685  * Ext JS Library 1.1.1
1686  * Copyright(c) 2006-2007, Ext JS, LLC.
1687  *
1688  * Originally Released Under LGPL - original licence link has changed is not relivant.
1689  *
1690  * Fork - LGPL
1691  * <script type="text/javascript">
1692  */
1693
1694 Roo.lib.Dom = {
1695     getViewWidth : function(full) {
1696         return full ? this.getDocumentWidth() : this.getViewportWidth();
1697     },
1698
1699     getViewHeight : function(full) {
1700         return full ? this.getDocumentHeight() : this.getViewportHeight();
1701     },
1702
1703     getDocumentHeight: function() {
1704         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1705         return Math.max(scrollHeight, this.getViewportHeight());
1706     },
1707
1708     getDocumentWidth: function() {
1709         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1710         return Math.max(scrollWidth, this.getViewportWidth());
1711     },
1712
1713     getViewportHeight: function() {
1714         var height = self.innerHeight;
1715         var mode = document.compatMode;
1716
1717         if ((mode || Roo.isIE) && !Roo.isOpera) {
1718             height = (mode == "CSS1Compat") ?
1719                      document.documentElement.clientHeight :
1720                      document.body.clientHeight;
1721         }
1722
1723         return height;
1724     },
1725
1726     getViewportWidth: function() {
1727         var width = self.innerWidth;
1728         var mode = document.compatMode;
1729
1730         if (mode || Roo.isIE) {
1731             width = (mode == "CSS1Compat") ?
1732                     document.documentElement.clientWidth :
1733                     document.body.clientWidth;
1734         }
1735         return width;
1736     },
1737
1738     isAncestor : function(p, c) {
1739         p = Roo.getDom(p);
1740         c = Roo.getDom(c);
1741         if (!p || !c) {
1742             return false;
1743         }
1744
1745         if (p.contains && !Roo.isSafari) {
1746             return p.contains(c);
1747         } else if (p.compareDocumentPosition) {
1748             return !!(p.compareDocumentPosition(c) & 16);
1749         } else {
1750             var parent = c.parentNode;
1751             while (parent) {
1752                 if (parent == p) {
1753                     return true;
1754                 }
1755                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1756                     return false;
1757                 }
1758                 parent = parent.parentNode;
1759             }
1760             return false;
1761         }
1762     },
1763
1764     getRegion : function(el) {
1765         return Roo.lib.Region.getRegion(el);
1766     },
1767
1768     getY : function(el) {
1769         return this.getXY(el)[1];
1770     },
1771
1772     getX : function(el) {
1773         return this.getXY(el)[0];
1774     },
1775
1776     getXY : function(el) {
1777         var p, pe, b, scroll, bd = document.body;
1778         el = Roo.getDom(el);
1779         var fly = Roo.lib.AnimBase.fly;
1780         if (el.getBoundingClientRect) {
1781             b = el.getBoundingClientRect();
1782             scroll = fly(document).getScroll();
1783             return [b.left + scroll.left, b.top + scroll.top];
1784         }
1785         var x = 0, y = 0;
1786
1787         p = el;
1788
1789         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1790
1791         while (p) {
1792
1793             x += p.offsetLeft;
1794             y += p.offsetTop;
1795
1796             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1797                 hasAbsolute = true;
1798             }
1799
1800             if (Roo.isGecko) {
1801                 pe = fly(p);
1802
1803                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1804                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1805
1806
1807                 x += bl;
1808                 y += bt;
1809
1810
1811                 if (p != el && pe.getStyle('overflow') != 'visible') {
1812                     x += bl;
1813                     y += bt;
1814                 }
1815             }
1816             p = p.offsetParent;
1817         }
1818
1819         if (Roo.isSafari && hasAbsolute) {
1820             x -= bd.offsetLeft;
1821             y -= bd.offsetTop;
1822         }
1823
1824         if (Roo.isGecko && !hasAbsolute) {
1825             var dbd = fly(bd);
1826             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1827             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1828         }
1829
1830         p = el.parentNode;
1831         while (p && p != bd) {
1832             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1833                 x -= p.scrollLeft;
1834                 y -= p.scrollTop;
1835             }
1836             p = p.parentNode;
1837         }
1838         return [x, y];
1839     },
1840  
1841   
1842
1843
1844     setXY : function(el, xy) {
1845         el = Roo.fly(el, '_setXY');
1846         el.position();
1847         var pts = el.translatePoints(xy);
1848         if (xy[0] !== false) {
1849             el.dom.style.left = pts.left + "px";
1850         }
1851         if (xy[1] !== false) {
1852             el.dom.style.top = pts.top + "px";
1853         }
1854     },
1855
1856     setX : function(el, x) {
1857         this.setXY(el, [x, false]);
1858     },
1859
1860     setY : function(el, y) {
1861         this.setXY(el, [false, y]);
1862     }
1863 };
1864 /*
1865  * Portions of this file are based on pieces of Yahoo User Interface Library
1866  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1867  * YUI licensed under the BSD License:
1868  * http://developer.yahoo.net/yui/license.txt
1869  * <script type="text/javascript">
1870  *
1871  */
1872
1873 Roo.lib.Event = function() {
1874     var loadComplete = false;
1875     var listeners = [];
1876     var unloadListeners = [];
1877     var retryCount = 0;
1878     var onAvailStack = [];
1879     var counter = 0;
1880     var lastError = null;
1881
1882     return {
1883         POLL_RETRYS: 200,
1884         POLL_INTERVAL: 20,
1885         EL: 0,
1886         TYPE: 1,
1887         FN: 2,
1888         WFN: 3,
1889         OBJ: 3,
1890         ADJ_SCOPE: 4,
1891         _interval: null,
1892
1893         startInterval: function() {
1894             if (!this._interval) {
1895                 var self = this;
1896                 var callback = function() {
1897                     self._tryPreloadAttach();
1898                 };
1899                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1900
1901             }
1902         },
1903
1904         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1905             onAvailStack.push({ id:         p_id,
1906                 fn:         p_fn,
1907                 obj:        p_obj,
1908                 override:   p_override,
1909                 checkReady: false    });
1910
1911             retryCount = this.POLL_RETRYS;
1912             this.startInterval();
1913         },
1914
1915
1916         addListener: function(el, eventName, fn) {
1917             el = Roo.getDom(el);
1918             if (!el || !fn) {
1919                 return false;
1920             }
1921
1922             if ("unload" == eventName) {
1923                 unloadListeners[unloadListeners.length] =
1924                 [el, eventName, fn];
1925                 return true;
1926             }
1927
1928             var wrappedFn = function(e) {
1929                 return fn(Roo.lib.Event.getEvent(e));
1930             };
1931
1932             var li = [el, eventName, fn, wrappedFn];
1933
1934             var index = listeners.length;
1935             listeners[index] = li;
1936
1937             this.doAdd(el, eventName, wrappedFn, false);
1938             return true;
1939
1940         },
1941
1942
1943         removeListener: function(el, eventName, fn) {
1944             var i, len;
1945
1946             el = Roo.getDom(el);
1947
1948             if(!fn) {
1949                 return this.purgeElement(el, false, eventName);
1950             }
1951
1952
1953             if ("unload" == eventName) {
1954
1955                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1956                     var li = unloadListeners[i];
1957                     if (li &&
1958                         li[0] == el &&
1959                         li[1] == eventName &&
1960                         li[2] == fn) {
1961                         unloadListeners.splice(i, 1);
1962                         return true;
1963                     }
1964                 }
1965
1966                 return false;
1967             }
1968
1969             var cacheItem = null;
1970
1971
1972             var index = arguments[3];
1973
1974             if ("undefined" == typeof index) {
1975                 index = this._getCacheIndex(el, eventName, fn);
1976             }
1977
1978             if (index >= 0) {
1979                 cacheItem = listeners[index];
1980             }
1981
1982             if (!el || !cacheItem) {
1983                 return false;
1984             }
1985
1986             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1987
1988             delete listeners[index][this.WFN];
1989             delete listeners[index][this.FN];
1990             listeners.splice(index, 1);
1991
1992             return true;
1993
1994         },
1995
1996
1997         getTarget: function(ev, resolveTextNode) {
1998             ev = ev.browserEvent || ev;
1999             var t = ev.target || ev.srcElement;
2000             return this.resolveTextNode(t);
2001         },
2002
2003
2004         resolveTextNode: function(node) {
2005             if (Roo.isSafari && node && 3 == node.nodeType) {
2006                 return node.parentNode;
2007             } else {
2008                 return node;
2009             }
2010         },
2011
2012
2013         getPageX: function(ev) {
2014             ev = ev.browserEvent || ev;
2015             var x = ev.pageX;
2016             if (!x && 0 !== x) {
2017                 x = ev.clientX || 0;
2018
2019                 if (Roo.isIE) {
2020                     x += this.getScroll()[1];
2021                 }
2022             }
2023
2024             return x;
2025         },
2026
2027
2028         getPageY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             var y = ev.pageY;
2031             if (!y && 0 !== y) {
2032                 y = ev.clientY || 0;
2033
2034                 if (Roo.isIE) {
2035                     y += this.getScroll()[0];
2036                 }
2037             }
2038
2039
2040             return y;
2041         },
2042
2043
2044         getXY: function(ev) {
2045             ev = ev.browserEvent || ev;
2046             return [this.getPageX(ev), this.getPageY(ev)];
2047         },
2048
2049
2050         getRelatedTarget: function(ev) {
2051             ev = ev.browserEvent || ev;
2052             var t = ev.relatedTarget;
2053             if (!t) {
2054                 if (ev.type == "mouseout") {
2055                     t = ev.toElement;
2056                 } else if (ev.type == "mouseover") {
2057                     t = ev.fromElement;
2058                 }
2059             }
2060
2061             return this.resolveTextNode(t);
2062         },
2063
2064
2065         getTime: function(ev) {
2066             ev = ev.browserEvent || ev;
2067             if (!ev.time) {
2068                 var t = new Date().getTime();
2069                 try {
2070                     ev.time = t;
2071                 } catch(ex) {
2072                     this.lastError = ex;
2073                     return t;
2074                 }
2075             }
2076
2077             return ev.time;
2078         },
2079
2080
2081         stopEvent: function(ev) {
2082             this.stopPropagation(ev);
2083             this.preventDefault(ev);
2084         },
2085
2086
2087         stopPropagation: function(ev) {
2088             ev = ev.browserEvent || ev;
2089             if (ev.stopPropagation) {
2090                 ev.stopPropagation();
2091             } else {
2092                 ev.cancelBubble = true;
2093             }
2094         },
2095
2096
2097         preventDefault: function(ev) {
2098             ev = ev.browserEvent || ev;
2099             if(ev.preventDefault) {
2100                 ev.preventDefault();
2101             } else {
2102                 ev.returnValue = false;
2103             }
2104         },
2105
2106
2107         getEvent: function(e) {
2108             var ev = e || window.event;
2109             if (!ev) {
2110                 var c = this.getEvent.caller;
2111                 while (c) {
2112                     ev = c.arguments[0];
2113                     if (ev && Event == ev.constructor) {
2114                         break;
2115                     }
2116                     c = c.caller;
2117                 }
2118             }
2119             return ev;
2120         },
2121
2122
2123         getCharCode: function(ev) {
2124             ev = ev.browserEvent || ev;
2125             return ev.charCode || ev.keyCode || 0;
2126         },
2127
2128
2129         _getCacheIndex: function(el, eventName, fn) {
2130             for (var i = 0,len = listeners.length; i < len; ++i) {
2131                 var li = listeners[i];
2132                 if (li &&
2133                     li[this.FN] == fn &&
2134                     li[this.EL] == el &&
2135                     li[this.TYPE] == eventName) {
2136                     return i;
2137                 }
2138             }
2139
2140             return -1;
2141         },
2142
2143
2144         elCache: {},
2145
2146
2147         getEl: function(id) {
2148             return document.getElementById(id);
2149         },
2150
2151
2152         clearCache: function() {
2153         },
2154
2155
2156         _load: function(e) {
2157             loadComplete = true;
2158             var EU = Roo.lib.Event;
2159
2160
2161             if (Roo.isIE) {
2162                 EU.doRemove(window, "load", EU._load);
2163             }
2164         },
2165
2166
2167         _tryPreloadAttach: function() {
2168
2169             if (this.locked) {
2170                 return false;
2171             }
2172
2173             this.locked = true;
2174
2175
2176             var tryAgain = !loadComplete;
2177             if (!tryAgain) {
2178                 tryAgain = (retryCount > 0);
2179             }
2180
2181
2182             var notAvail = [];
2183             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2184                 var item = onAvailStack[i];
2185                 if (item) {
2186                     var el = this.getEl(item.id);
2187
2188                     if (el) {
2189                         if (!item.checkReady ||
2190                             loadComplete ||
2191                             el.nextSibling ||
2192                             (document && document.body)) {
2193
2194                             var scope = el;
2195                             if (item.override) {
2196                                 if (item.override === true) {
2197                                     scope = item.obj;
2198                                 } else {
2199                                     scope = item.override;
2200                                 }
2201                             }
2202                             item.fn.call(scope, item.obj);
2203                             onAvailStack[i] = null;
2204                         }
2205                     } else {
2206                         notAvail.push(item);
2207                     }
2208                 }
2209             }
2210
2211             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2212
2213             if (tryAgain) {
2214
2215                 this.startInterval();
2216             } else {
2217                 clearInterval(this._interval);
2218                 this._interval = null;
2219             }
2220
2221             this.locked = false;
2222
2223             return true;
2224
2225         },
2226
2227
2228         purgeElement: function(el, recurse, eventName) {
2229             var elListeners = this.getListeners(el, eventName);
2230             if (elListeners) {
2231                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2232                     var l = elListeners[i];
2233                     this.removeListener(el, l.type, l.fn);
2234                 }
2235             }
2236
2237             if (recurse && el && el.childNodes) {
2238                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2239                     this.purgeElement(el.childNodes[i], recurse, eventName);
2240                 }
2241             }
2242         },
2243
2244
2245         getListeners: function(el, eventName) {
2246             var results = [], searchLists;
2247             if (!eventName) {
2248                 searchLists = [listeners, unloadListeners];
2249             } else if (eventName == "unload") {
2250                 searchLists = [unloadListeners];
2251             } else {
2252                 searchLists = [listeners];
2253             }
2254
2255             for (var j = 0; j < searchLists.length; ++j) {
2256                 var searchList = searchLists[j];
2257                 if (searchList && searchList.length > 0) {
2258                     for (var i = 0,len = searchList.length; i < len; ++i) {
2259                         var l = searchList[i];
2260                         if (l && l[this.EL] === el &&
2261                             (!eventName || eventName === l[this.TYPE])) {
2262                             results.push({
2263                                 type:   l[this.TYPE],
2264                                 fn:     l[this.FN],
2265                                 obj:    l[this.OBJ],
2266                                 adjust: l[this.ADJ_SCOPE],
2267                                 index:  i
2268                             });
2269                         }
2270                     }
2271                 }
2272             }
2273
2274             return (results.length) ? results : null;
2275         },
2276
2277
2278         _unload: function(e) {
2279
2280             var EU = Roo.lib.Event, i, j, l, len, index;
2281
2282             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2283                 l = unloadListeners[i];
2284                 if (l) {
2285                     var scope = window;
2286                     if (l[EU.ADJ_SCOPE]) {
2287                         if (l[EU.ADJ_SCOPE] === true) {
2288                             scope = l[EU.OBJ];
2289                         } else {
2290                             scope = l[EU.ADJ_SCOPE];
2291                         }
2292                     }
2293                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2294                     unloadListeners[i] = null;
2295                     l = null;
2296                     scope = null;
2297                 }
2298             }
2299
2300             unloadListeners = null;
2301
2302             if (listeners && listeners.length > 0) {
2303                 j = listeners.length;
2304                 while (j) {
2305                     index = j - 1;
2306                     l = listeners[index];
2307                     if (l) {
2308                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2309                                 l[EU.FN], index);
2310                     }
2311                     j = j - 1;
2312                 }
2313                 l = null;
2314
2315                 EU.clearCache();
2316             }
2317
2318             EU.doRemove(window, "unload", EU._unload);
2319
2320         },
2321
2322
2323         getScroll: function() {
2324             var dd = document.documentElement, db = document.body;
2325             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2326                 return [dd.scrollTop, dd.scrollLeft];
2327             } else if (db) {
2328                 return [db.scrollTop, db.scrollLeft];
2329             } else {
2330                 return [0, 0];
2331             }
2332         },
2333
2334
2335         doAdd: function () {
2336             if (window.addEventListener) {
2337                 return function(el, eventName, fn, capture) {
2338                     el.addEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.attachEvent) {
2341                 return function(el, eventName, fn, capture) {
2342                     el.attachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }(),
2349
2350
2351         doRemove: function() {
2352             if (window.removeEventListener) {
2353                 return function (el, eventName, fn, capture) {
2354                     el.removeEventListener(eventName, fn, (capture));
2355                 };
2356             } else if (window.detachEvent) {
2357                 return function (el, eventName, fn) {
2358                     el.detachEvent("on" + eventName, fn);
2359                 };
2360             } else {
2361                 return function() {
2362                 };
2363             }
2364         }()
2365     };
2366     
2367 }();
2368 (function() {     
2369    
2370     var E = Roo.lib.Event;
2371     E.on = E.addListener;
2372     E.un = E.removeListener;
2373
2374     if (document && document.body) {
2375         E._load();
2376     } else {
2377         E.doAdd(window, "load", E._load);
2378     }
2379     E.doAdd(window, "unload", E._unload);
2380     E._tryPreloadAttach();
2381 })();
2382
2383 /*
2384  * Portions of this file are based on pieces of Yahoo User Interface Library
2385  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2386  * YUI licensed under the BSD License:
2387  * http://developer.yahoo.net/yui/license.txt
2388  * <script type="text/javascript">
2389  *
2390  */
2391
2392 (function() {
2393     
2394     Roo.lib.Ajax = {
2395         request : function(method, uri, cb, data, options) {
2396             if(options){
2397                 var hs = options.headers;
2398                 if(hs){
2399                     for(var h in hs){
2400                         if(hs.hasOwnProperty(h)){
2401                             this.initHeader(h, hs[h], false);
2402                         }
2403                     }
2404                 }
2405                 if(options.xmlData){
2406                     this.initHeader('Content-Type', 'text/xml', false);
2407                     method = 'POST';
2408                     data = options.xmlData;
2409                 }
2410             }
2411
2412             return this.asyncRequest(method, uri, cb, data);
2413         },
2414
2415         serializeForm : function(form) {
2416             if(typeof form == 'string') {
2417                 form = (document.getElementById(form) || document.forms[form]);
2418             }
2419
2420             var el, name, val, disabled, data = '', hasSubmit = false;
2421             for (var i = 0; i < form.elements.length; i++) {
2422                 el = form.elements[i];
2423                 disabled = form.elements[i].disabled;
2424                 name = form.elements[i].name;
2425                 val = form.elements[i].value;
2426
2427                 if (!disabled && name){
2428                     switch (el.type)
2429                             {
2430                         case 'select-one':
2431                         case 'select-multiple':
2432                             for (var j = 0; j < el.options.length; j++) {
2433                                 if (el.options[j].selected) {
2434                                     if (Roo.isIE) {
2435                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2436                                     }
2437                                     else {
2438                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2439                                     }
2440                                 }
2441                             }
2442                             break;
2443                         case 'radio':
2444                         case 'checkbox':
2445                             if (el.checked) {
2446                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2447                             }
2448                             break;
2449                         case 'file':
2450
2451                         case undefined:
2452
2453                         case 'reset':
2454
2455                         case 'button':
2456
2457                             break;
2458                         case 'submit':
2459                             if(hasSubmit == false) {
2460                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2461                                 hasSubmit = true;
2462                             }
2463                             break;
2464                         default:
2465                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2466                             break;
2467                     }
2468                 }
2469             }
2470             data = data.substr(0, data.length - 1);
2471             return data;
2472         },
2473
2474         headers:{},
2475
2476         hasHeaders:false,
2477
2478         useDefaultHeader:true,
2479
2480         defaultPostHeader:'application/x-www-form-urlencoded',
2481
2482         useDefaultXhrHeader:true,
2483
2484         defaultXhrHeader:'XMLHttpRequest',
2485
2486         hasDefaultHeaders:true,
2487
2488         defaultHeaders:{},
2489
2490         poll:{},
2491
2492         timeout:{},
2493
2494         pollInterval:50,
2495
2496         transactionId:0,
2497
2498         setProgId:function(id)
2499         {
2500             this.activeX.unshift(id);
2501         },
2502
2503         setDefaultPostHeader:function(b)
2504         {
2505             this.useDefaultHeader = b;
2506         },
2507
2508         setDefaultXhrHeader:function(b)
2509         {
2510             this.useDefaultXhrHeader = b;
2511         },
2512
2513         setPollingInterval:function(i)
2514         {
2515             if (typeof i == 'number' && isFinite(i)) {
2516                 this.pollInterval = i;
2517             }
2518         },
2519
2520         createXhrObject:function(transactionId)
2521         {
2522             var obj,http;
2523             try
2524             {
2525
2526                 http = new XMLHttpRequest();
2527
2528                 obj = { conn:http, tId:transactionId };
2529             }
2530             catch(e)
2531             {
2532                 for (var i = 0; i < this.activeX.length; ++i) {
2533                     try
2534                     {
2535
2536                         http = new ActiveXObject(this.activeX[i]);
2537
2538                         obj = { conn:http, tId:transactionId };
2539                         break;
2540                     }
2541                     catch(e) {
2542                     }
2543                 }
2544             }
2545             finally
2546             {
2547                 return obj;
2548             }
2549         },
2550
2551         getConnectionObject:function()
2552         {
2553             var o;
2554             var tId = this.transactionId;
2555
2556             try
2557             {
2558                 o = this.createXhrObject(tId);
2559                 if (o) {
2560                     this.transactionId++;
2561                 }
2562             }
2563             catch(e) {
2564             }
2565             finally
2566             {
2567                 return o;
2568             }
2569         },
2570
2571         asyncRequest:function(method, uri, callback, postData)
2572         {
2573             var o = this.getConnectionObject();
2574
2575             if (!o) {
2576                 return null;
2577             }
2578             else {
2579                 o.conn.open(method, uri, true);
2580
2581                 if (this.useDefaultXhrHeader) {
2582                     if (!this.defaultHeaders['X-Requested-With']) {
2583                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2584                     }
2585                 }
2586
2587                 if(postData && this.useDefaultHeader){
2588                     this.initHeader('Content-Type', this.defaultPostHeader);
2589                 }
2590
2591                  if (this.hasDefaultHeaders || this.hasHeaders) {
2592                     this.setHeader(o);
2593                 }
2594
2595                 this.handleReadyState(o, callback);
2596                 o.conn.send(postData || null);
2597
2598                 return o;
2599             }
2600         },
2601
2602         handleReadyState:function(o, callback)
2603         {
2604             var oConn = this;
2605
2606             if (callback && callback.timeout) {
2607                 this.timeout[o.tId] = window.setTimeout(function() {
2608                     oConn.abort(o, callback, true);
2609                 }, callback.timeout);
2610             }
2611
2612             this.poll[o.tId] = window.setInterval(
2613                     function() {
2614                         if (o.conn && o.conn.readyState == 4) {
2615                             window.clearInterval(oConn.poll[o.tId]);
2616                             delete oConn.poll[o.tId];
2617
2618                             if(callback && callback.timeout) {
2619                                 window.clearTimeout(oConn.timeout[o.tId]);
2620                                 delete oConn.timeout[o.tId];
2621                             }
2622
2623                             oConn.handleTransactionResponse(o, callback);
2624                         }
2625                     }
2626                     , this.pollInterval);
2627         },
2628
2629         handleTransactionResponse:function(o, callback, isAbort)
2630         {
2631
2632             if (!callback) {
2633                 this.releaseObject(o);
2634                 return;
2635             }
2636
2637             var httpStatus, responseObject;
2638
2639             try
2640             {
2641                 if (o.conn.status !== undefined && o.conn.status != 0) {
2642                     httpStatus = o.conn.status;
2643                 }
2644                 else {
2645                     httpStatus = 13030;
2646                 }
2647             }
2648             catch(e) {
2649
2650
2651                 httpStatus = 13030;
2652             }
2653
2654             if (httpStatus >= 200 && httpStatus < 300) {
2655                 responseObject = this.createResponseObject(o, callback.argument);
2656                 if (callback.success) {
2657                     if (!callback.scope) {
2658                         callback.success(responseObject);
2659                     }
2660                     else {
2661
2662
2663                         callback.success.apply(callback.scope, [responseObject]);
2664                     }
2665                 }
2666             }
2667             else {
2668                 switch (httpStatus) {
2669
2670                     case 12002:
2671                     case 12029:
2672                     case 12030:
2673                     case 12031:
2674                     case 12152:
2675                     case 13030:
2676                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2677                         if (callback.failure) {
2678                             if (!callback.scope) {
2679                                 callback.failure(responseObject);
2680                             }
2681                             else {
2682                                 callback.failure.apply(callback.scope, [responseObject]);
2683                             }
2684                         }
2685                         break;
2686                     default:
2687                         responseObject = this.createResponseObject(o, callback.argument);
2688                         if (callback.failure) {
2689                             if (!callback.scope) {
2690                                 callback.failure(responseObject);
2691                             }
2692                             else {
2693                                 callback.failure.apply(callback.scope, [responseObject]);
2694                             }
2695                         }
2696                 }
2697             }
2698
2699             this.releaseObject(o);
2700             responseObject = null;
2701         },
2702
2703         createResponseObject:function(o, callbackArg)
2704         {
2705             var obj = {};
2706             var headerObj = {};
2707
2708             try
2709             {
2710                 var headerStr = o.conn.getAllResponseHeaders();
2711                 var header = headerStr.split('\n');
2712                 for (var i = 0; i < header.length; i++) {
2713                     var delimitPos = header[i].indexOf(':');
2714                     if (delimitPos != -1) {
2715                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2716                     }
2717                 }
2718             }
2719             catch(e) {
2720             }
2721
2722             obj.tId = o.tId;
2723             obj.status = o.conn.status;
2724             obj.statusText = o.conn.statusText;
2725             obj.getResponseHeader = headerObj;
2726             obj.getAllResponseHeaders = headerStr;
2727             obj.responseText = o.conn.responseText;
2728             obj.responseXML = o.conn.responseXML;
2729
2730             if (typeof callbackArg !== undefined) {
2731                 obj.argument = callbackArg;
2732             }
2733
2734             return obj;
2735         },
2736
2737         createExceptionObject:function(tId, callbackArg, isAbort)
2738         {
2739             var COMM_CODE = 0;
2740             var COMM_ERROR = 'communication failure';
2741             var ABORT_CODE = -1;
2742             var ABORT_ERROR = 'transaction aborted';
2743
2744             var obj = {};
2745
2746             obj.tId = tId;
2747             if (isAbort) {
2748                 obj.status = ABORT_CODE;
2749                 obj.statusText = ABORT_ERROR;
2750             }
2751             else {
2752                 obj.status = COMM_CODE;
2753                 obj.statusText = COMM_ERROR;
2754             }
2755
2756             if (callbackArg) {
2757                 obj.argument = callbackArg;
2758             }
2759
2760             return obj;
2761         },
2762
2763         initHeader:function(label, value, isDefault)
2764         {
2765             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2766
2767             if (headerObj[label] === undefined) {
2768                 headerObj[label] = value;
2769             }
2770             else {
2771
2772
2773                 headerObj[label] = value + "," + headerObj[label];
2774             }
2775
2776             if (isDefault) {
2777                 this.hasDefaultHeaders = true;
2778             }
2779             else {
2780                 this.hasHeaders = true;
2781             }
2782         },
2783
2784
2785         setHeader:function(o)
2786         {
2787             if (this.hasDefaultHeaders) {
2788                 for (var prop in this.defaultHeaders) {
2789                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2790                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2791                     }
2792                 }
2793             }
2794
2795             if (this.hasHeaders) {
2796                 for (var prop in this.headers) {
2797                     if (this.headers.hasOwnProperty(prop)) {
2798                         o.conn.setRequestHeader(prop, this.headers[prop]);
2799                     }
2800                 }
2801                 this.headers = {};
2802                 this.hasHeaders = false;
2803             }
2804         },
2805
2806         resetDefaultHeaders:function() {
2807             delete this.defaultHeaders;
2808             this.defaultHeaders = {};
2809             this.hasDefaultHeaders = false;
2810         },
2811
2812         abort:function(o, callback, isTimeout)
2813         {
2814             if(this.isCallInProgress(o)) {
2815                 o.conn.abort();
2816                 window.clearInterval(this.poll[o.tId]);
2817                 delete this.poll[o.tId];
2818                 if (isTimeout) {
2819                     delete this.timeout[o.tId];
2820                 }
2821
2822                 this.handleTransactionResponse(o, callback, true);
2823
2824                 return true;
2825             }
2826             else {
2827                 return false;
2828             }
2829         },
2830
2831
2832         isCallInProgress:function(o)
2833         {
2834             if (o && o.conn) {
2835                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2836             }
2837             else {
2838
2839                 return false;
2840             }
2841         },
2842
2843
2844         releaseObject:function(o)
2845         {
2846
2847             o.conn = null;
2848
2849             o = null;
2850         },
2851
2852         activeX:[
2853         'MSXML2.XMLHTTP.3.0',
2854         'MSXML2.XMLHTTP',
2855         'Microsoft.XMLHTTP'
2856         ]
2857
2858
2859     };
2860 })();/*
2861  * Portions of this file are based on pieces of Yahoo User Interface Library
2862  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2863  * YUI licensed under the BSD License:
2864  * http://developer.yahoo.net/yui/license.txt
2865  * <script type="text/javascript">
2866  *
2867  */
2868
2869 Roo.lib.Region = function(t, r, b, l) {
2870     this.top = t;
2871     this[1] = t;
2872     this.right = r;
2873     this.bottom = b;
2874     this.left = l;
2875     this[0] = l;
2876 };
2877
2878
2879 Roo.lib.Region.prototype = {
2880     contains : function(region) {
2881         return ( region.left >= this.left &&
2882                  region.right <= this.right &&
2883                  region.top >= this.top &&
2884                  region.bottom <= this.bottom    );
2885
2886     },
2887
2888     getArea : function() {
2889         return ( (this.bottom - this.top) * (this.right - this.left) );
2890     },
2891
2892     intersect : function(region) {
2893         var t = Math.max(this.top, region.top);
2894         var r = Math.min(this.right, region.right);
2895         var b = Math.min(this.bottom, region.bottom);
2896         var l = Math.max(this.left, region.left);
2897
2898         if (b >= t && r >= l) {
2899             return new Roo.lib.Region(t, r, b, l);
2900         } else {
2901             return null;
2902         }
2903     },
2904     union : function(region) {
2905         var t = Math.min(this.top, region.top);
2906         var r = Math.max(this.right, region.right);
2907         var b = Math.max(this.bottom, region.bottom);
2908         var l = Math.min(this.left, region.left);
2909
2910         return new Roo.lib.Region(t, r, b, l);
2911     },
2912
2913     adjust : function(t, l, b, r) {
2914         this.top += t;
2915         this.left += l;
2916         this.right += r;
2917         this.bottom += b;
2918         return this;
2919     }
2920 };
2921
2922 Roo.lib.Region.getRegion = function(el) {
2923     var p = Roo.lib.Dom.getXY(el);
2924
2925     var t = p[1];
2926     var r = p[0] + el.offsetWidth;
2927     var b = p[1] + el.offsetHeight;
2928     var l = p[0];
2929
2930     return new Roo.lib.Region(t, r, b, l);
2931 };
2932 /*
2933  * Portions of this file are based on pieces of Yahoo User Interface Library
2934  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2935  * YUI licensed under the BSD License:
2936  * http://developer.yahoo.net/yui/license.txt
2937  * <script type="text/javascript">
2938  *
2939  */
2940 //@@dep Roo.lib.Region
2941
2942
2943 Roo.lib.Point = function(x, y) {
2944     if (x instanceof Array) {
2945         y = x[1];
2946         x = x[0];
2947     }
2948     this.x = this.right = this.left = this[0] = x;
2949     this.y = this.top = this.bottom = this[1] = y;
2950 };
2951
2952 Roo.lib.Point.prototype = new Roo.lib.Region();
2953 /*
2954  * Portions of this file are based on pieces of Yahoo User Interface Library
2955  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2956  * YUI licensed under the BSD License:
2957  * http://developer.yahoo.net/yui/license.txt
2958  * <script type="text/javascript">
2959  *
2960  */
2961  
2962 (function() {   
2963
2964     Roo.lib.Anim = {
2965         scroll : function(el, args, duration, easing, cb, scope) {
2966             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2967         },
2968
2969         motion : function(el, args, duration, easing, cb, scope) {
2970             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2971         },
2972
2973         color : function(el, args, duration, easing, cb, scope) {
2974             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2975         },
2976
2977         run : function(el, args, duration, easing, cb, scope, type) {
2978             type = type || Roo.lib.AnimBase;
2979             if (typeof easing == "string") {
2980                 easing = Roo.lib.Easing[easing];
2981             }
2982             var anim = new type(el, args, duration, easing);
2983             anim.animateX(function() {
2984                 Roo.callback(cb, scope);
2985             });
2986             return anim;
2987         }
2988     };
2989 })();/*
2990  * Portions of this file are based on pieces of Yahoo User Interface Library
2991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2992  * YUI licensed under the BSD License:
2993  * http://developer.yahoo.net/yui/license.txt
2994  * <script type="text/javascript">
2995  *
2996  */
2997
2998 (function() {    
2999     var libFlyweight;
3000     
3001     function fly(el) {
3002         if (!libFlyweight) {
3003             libFlyweight = new Roo.Element.Flyweight();
3004         }
3005         libFlyweight.dom = el;
3006         return libFlyweight;
3007     }
3008
3009     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3010     
3011    
3012     
3013     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3014         if (el) {
3015             this.init(el, attributes, duration, method);
3016         }
3017     };
3018
3019     Roo.lib.AnimBase.fly = fly;
3020     
3021     
3022     
3023     Roo.lib.AnimBase.prototype = {
3024
3025         toString: function() {
3026             var el = this.getEl();
3027             var id = el.id || el.tagName;
3028             return ("Anim " + id);
3029         },
3030
3031         patterns: {
3032             noNegatives:        /width|height|opacity|padding/i,
3033             offsetAttribute:  /^((width|height)|(top|left))$/,
3034             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3035             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3036         },
3037
3038
3039         doMethod: function(attr, start, end) {
3040             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3041         },
3042
3043
3044         setAttribute: function(attr, val, unit) {
3045             if (this.patterns.noNegatives.test(attr)) {
3046                 val = (val > 0) ? val : 0;
3047             }
3048
3049             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3050         },
3051
3052
3053         getAttribute: function(attr) {
3054             var el = this.getEl();
3055             var val = fly(el).getStyle(attr);
3056
3057             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3058                 return parseFloat(val);
3059             }
3060
3061             var a = this.patterns.offsetAttribute.exec(attr) || [];
3062             var pos = !!( a[3] );
3063             var box = !!( a[2] );
3064
3065
3066             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3067                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3068             } else {
3069                 val = 0;
3070             }
3071
3072             return val;
3073         },
3074
3075
3076         getDefaultUnit: function(attr) {
3077             if (this.patterns.defaultUnit.test(attr)) {
3078                 return 'px';
3079             }
3080
3081             return '';
3082         },
3083
3084         animateX : function(callback, scope) {
3085             var f = function() {
3086                 this.onComplete.removeListener(f);
3087                 if (typeof callback == "function") {
3088                     callback.call(scope || this, this);
3089                 }
3090             };
3091             this.onComplete.addListener(f, this);
3092             this.animate();
3093         },
3094
3095
3096         setRuntimeAttribute: function(attr) {
3097             var start;
3098             var end;
3099             var attributes = this.attributes;
3100
3101             this.runtimeAttributes[attr] = {};
3102
3103             var isset = function(prop) {
3104                 return (typeof prop !== 'undefined');
3105             };
3106
3107             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3108                 return false;
3109             }
3110
3111             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3112
3113
3114             if (isset(attributes[attr]['to'])) {
3115                 end = attributes[attr]['to'];
3116             } else if (isset(attributes[attr]['by'])) {
3117                 if (start.constructor == Array) {
3118                     end = [];
3119                     for (var i = 0, len = start.length; i < len; ++i) {
3120                         end[i] = start[i] + attributes[attr]['by'][i];
3121                     }
3122                 } else {
3123                     end = start + attributes[attr]['by'];
3124                 }
3125             }
3126
3127             this.runtimeAttributes[attr].start = start;
3128             this.runtimeAttributes[attr].end = end;
3129
3130
3131             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3132         },
3133
3134
3135         init: function(el, attributes, duration, method) {
3136
3137             var isAnimated = false;
3138
3139
3140             var startTime = null;
3141
3142
3143             var actualFrames = 0;
3144
3145
3146             el = Roo.getDom(el);
3147
3148
3149             this.attributes = attributes || {};
3150
3151
3152             this.duration = duration || 1;
3153
3154
3155             this.method = method || Roo.lib.Easing.easeNone;
3156
3157
3158             this.useSeconds = true;
3159
3160
3161             this.currentFrame = 0;
3162
3163
3164             this.totalFrames = Roo.lib.AnimMgr.fps;
3165
3166
3167             this.getEl = function() {
3168                 return el;
3169             };
3170
3171
3172             this.isAnimated = function() {
3173                 return isAnimated;
3174             };
3175
3176
3177             this.getStartTime = function() {
3178                 return startTime;
3179             };
3180
3181             this.runtimeAttributes = {};
3182
3183
3184             this.animate = function() {
3185                 if (this.isAnimated()) {
3186                     return false;
3187                 }
3188
3189                 this.currentFrame = 0;
3190
3191                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3192
3193                 Roo.lib.AnimMgr.registerElement(this);
3194             };
3195
3196
3197             this.stop = function(finish) {
3198                 if (finish) {
3199                     this.currentFrame = this.totalFrames;
3200                     this._onTween.fire();
3201                 }
3202                 Roo.lib.AnimMgr.stop(this);
3203             };
3204
3205             var onStart = function() {
3206                 this.onStart.fire();
3207
3208                 this.runtimeAttributes = {};
3209                 for (var attr in this.attributes) {
3210                     this.setRuntimeAttribute(attr);
3211                 }
3212
3213                 isAnimated = true;
3214                 actualFrames = 0;
3215                 startTime = new Date();
3216             };
3217
3218
3219             var onTween = function() {
3220                 var data = {
3221                     duration: new Date() - this.getStartTime(),
3222                     currentFrame: this.currentFrame
3223                 };
3224
3225                 data.toString = function() {
3226                     return (
3227                             'duration: ' + data.duration +
3228                             ', currentFrame: ' + data.currentFrame
3229                             );
3230                 };
3231
3232                 this.onTween.fire(data);
3233
3234                 var runtimeAttributes = this.runtimeAttributes;
3235
3236                 for (var attr in runtimeAttributes) {
3237                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3238                 }
3239
3240                 actualFrames += 1;
3241             };
3242
3243             var onComplete = function() {
3244                 var actual_duration = (new Date() - startTime) / 1000 ;
3245
3246                 var data = {
3247                     duration: actual_duration,
3248                     frames: actualFrames,
3249                     fps: actualFrames / actual_duration
3250                 };
3251
3252                 data.toString = function() {
3253                     return (
3254                             'duration: ' + data.duration +
3255                             ', frames: ' + data.frames +
3256                             ', fps: ' + data.fps
3257                             );
3258                 };
3259
3260                 isAnimated = false;
3261                 actualFrames = 0;
3262                 this.onComplete.fire(data);
3263             };
3264
3265
3266             this._onStart = new Roo.util.Event(this);
3267             this.onStart = new Roo.util.Event(this);
3268             this.onTween = new Roo.util.Event(this);
3269             this._onTween = new Roo.util.Event(this);
3270             this.onComplete = new Roo.util.Event(this);
3271             this._onComplete = new Roo.util.Event(this);
3272             this._onStart.addListener(onStart);
3273             this._onTween.addListener(onTween);
3274             this._onComplete.addListener(onComplete);
3275         }
3276     };
3277 })();
3278 /*
3279  * Portions of this file are based on pieces of Yahoo User Interface Library
3280  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3281  * YUI licensed under the BSD License:
3282  * http://developer.yahoo.net/yui/license.txt
3283  * <script type="text/javascript">
3284  *
3285  */
3286
3287 Roo.lib.AnimMgr = new function() {
3288
3289         var thread = null;
3290
3291
3292         var queue = [];
3293
3294
3295         var tweenCount = 0;
3296
3297
3298         this.fps = 1000;
3299
3300
3301         this.delay = 1;
3302
3303
3304         this.registerElement = function(tween) {
3305             queue[queue.length] = tween;
3306             tweenCount += 1;
3307             tween._onStart.fire();
3308             this.start();
3309         };
3310
3311
3312         this.unRegister = function(tween, index) {
3313             tween._onComplete.fire();
3314             index = index || getIndex(tween);
3315             if (index != -1) {
3316                 queue.splice(index, 1);
3317             }
3318
3319             tweenCount -= 1;
3320             if (tweenCount <= 0) {
3321                 this.stop();
3322             }
3323         };
3324
3325
3326         this.start = function() {
3327             if (thread === null) {
3328                 thread = setInterval(this.run, this.delay);
3329             }
3330         };
3331
3332
3333         this.stop = function(tween) {
3334             if (!tween) {
3335                 clearInterval(thread);
3336
3337                 for (var i = 0, len = queue.length; i < len; ++i) {
3338                     if (queue[0].isAnimated()) {
3339                         this.unRegister(queue[0], 0);
3340                     }
3341                 }
3342
3343                 queue = [];
3344                 thread = null;
3345                 tweenCount = 0;
3346             }
3347             else {
3348                 this.unRegister(tween);
3349             }
3350         };
3351
3352
3353         this.run = function() {
3354             for (var i = 0, len = queue.length; i < len; ++i) {
3355                 var tween = queue[i];
3356                 if (!tween || !tween.isAnimated()) {
3357                     continue;
3358                 }
3359
3360                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3361                 {
3362                     tween.currentFrame += 1;
3363
3364                     if (tween.useSeconds) {
3365                         correctFrame(tween);
3366                     }
3367                     tween._onTween.fire();
3368                 }
3369                 else {
3370                     Roo.lib.AnimMgr.stop(tween, i);
3371                 }
3372             }
3373         };
3374
3375         var getIndex = function(anim) {
3376             for (var i = 0, len = queue.length; i < len; ++i) {
3377                 if (queue[i] == anim) {
3378                     return i;
3379                 }
3380             }
3381             return -1;
3382         };
3383
3384
3385         var correctFrame = function(tween) {
3386             var frames = tween.totalFrames;
3387             var frame = tween.currentFrame;
3388             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3389             var elapsed = (new Date() - tween.getStartTime());
3390             var tweak = 0;
3391
3392             if (elapsed < tween.duration * 1000) {
3393                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3394             } else {
3395                 tweak = frames - (frame + 1);
3396             }
3397             if (tweak > 0 && isFinite(tweak)) {
3398                 if (tween.currentFrame + tweak >= frames) {
3399                     tweak = frames - (frame + 1);
3400                 }
3401
3402                 tween.currentFrame += tweak;
3403             }
3404         };
3405     };/*
3406  * Portions of this file are based on pieces of Yahoo User Interface Library
3407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3408  * YUI licensed under the BSD License:
3409  * http://developer.yahoo.net/yui/license.txt
3410  * <script type="text/javascript">
3411  *
3412  */
3413 Roo.lib.Bezier = new function() {
3414
3415         this.getPosition = function(points, t) {
3416             var n = points.length;
3417             var tmp = [];
3418
3419             for (var i = 0; i < n; ++i) {
3420                 tmp[i] = [points[i][0], points[i][1]];
3421             }
3422
3423             for (var j = 1; j < n; ++j) {
3424                 for (i = 0; i < n - j; ++i) {
3425                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3426                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3427                 }
3428             }
3429
3430             return [ tmp[0][0], tmp[0][1] ];
3431
3432         };
3433     };/*
3434  * Portions of this file are based on pieces of Yahoo User Interface Library
3435  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3436  * YUI licensed under the BSD License:
3437  * http://developer.yahoo.net/yui/license.txt
3438  * <script type="text/javascript">
3439  *
3440  */
3441 (function() {
3442
3443     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3444         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3445     };
3446
3447     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3448
3449     var fly = Roo.lib.AnimBase.fly;
3450     var Y = Roo.lib;
3451     var superclass = Y.ColorAnim.superclass;
3452     var proto = Y.ColorAnim.prototype;
3453
3454     proto.toString = function() {
3455         var el = this.getEl();
3456         var id = el.id || el.tagName;
3457         return ("ColorAnim " + id);
3458     };
3459
3460     proto.patterns.color = /color$/i;
3461     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3462     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3463     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3464     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3465
3466
3467     proto.parseColor = function(s) {
3468         if (s.length == 3) {
3469             return s;
3470         }
3471
3472         var c = this.patterns.hex.exec(s);
3473         if (c && c.length == 4) {
3474             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3475         }
3476
3477         c = this.patterns.rgb.exec(s);
3478         if (c && c.length == 4) {
3479             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3480         }
3481
3482         c = this.patterns.hex3.exec(s);
3483         if (c && c.length == 4) {
3484             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3485         }
3486
3487         return null;
3488     };
3489     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3490     proto.getAttribute = function(attr) {
3491         var el = this.getEl();
3492         if (this.patterns.color.test(attr)) {
3493             var val = fly(el).getStyle(attr);
3494
3495             if (this.patterns.transparent.test(val)) {
3496                 var parent = el.parentNode;
3497                 val = fly(parent).getStyle(attr);
3498
3499                 while (parent && this.patterns.transparent.test(val)) {
3500                     parent = parent.parentNode;
3501                     val = fly(parent).getStyle(attr);
3502                     if (parent.tagName.toUpperCase() == 'HTML') {
3503                         val = '#fff';
3504                     }
3505                 }
3506             }
3507         } else {
3508             val = superclass.getAttribute.call(this, attr);
3509         }
3510
3511         return val;
3512     };
3513     proto.getAttribute = function(attr) {
3514         var el = this.getEl();
3515         if (this.patterns.color.test(attr)) {
3516             var val = fly(el).getStyle(attr);
3517
3518             if (this.patterns.transparent.test(val)) {
3519                 var parent = el.parentNode;
3520                 val = fly(parent).getStyle(attr);
3521
3522                 while (parent && this.patterns.transparent.test(val)) {
3523                     parent = parent.parentNode;
3524                     val = fly(parent).getStyle(attr);
3525                     if (parent.tagName.toUpperCase() == 'HTML') {
3526                         val = '#fff';
3527                     }
3528                 }
3529             }
3530         } else {
3531             val = superclass.getAttribute.call(this, attr);
3532         }
3533
3534         return val;
3535     };
3536
3537     proto.doMethod = function(attr, start, end) {
3538         var val;
3539
3540         if (this.patterns.color.test(attr)) {
3541             val = [];
3542             for (var i = 0, len = start.length; i < len; ++i) {
3543                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3544             }
3545
3546             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3547         }
3548         else {
3549             val = superclass.doMethod.call(this, attr, start, end);
3550         }
3551
3552         return val;
3553     };
3554
3555     proto.setRuntimeAttribute = function(attr) {
3556         superclass.setRuntimeAttribute.call(this, attr);
3557
3558         if (this.patterns.color.test(attr)) {
3559             var attributes = this.attributes;
3560             var start = this.parseColor(this.runtimeAttributes[attr].start);
3561             var end = this.parseColor(this.runtimeAttributes[attr].end);
3562
3563             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3564                 end = this.parseColor(attributes[attr].by);
3565
3566                 for (var i = 0, len = start.length; i < len; ++i) {
3567                     end[i] = start[i] + end[i];
3568                 }
3569             }
3570
3571             this.runtimeAttributes[attr].start = start;
3572             this.runtimeAttributes[attr].end = end;
3573         }
3574     };
3575 })();
3576
3577 /*
3578  * Portions of this file are based on pieces of Yahoo User Interface Library
3579  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3580  * YUI licensed under the BSD License:
3581  * http://developer.yahoo.net/yui/license.txt
3582  * <script type="text/javascript">
3583  *
3584  */
3585 Roo.lib.Easing = {
3586
3587
3588     easeNone: function (t, b, c, d) {
3589         return c * t / d + b;
3590     },
3591
3592
3593     easeIn: function (t, b, c, d) {
3594         return c * (t /= d) * t + b;
3595     },
3596
3597
3598     easeOut: function (t, b, c, d) {
3599         return -c * (t /= d) * (t - 2) + b;
3600     },
3601
3602
3603     easeBoth: function (t, b, c, d) {
3604         if ((t /= d / 2) < 1) {
3605             return c / 2 * t * t + b;
3606         }
3607
3608         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3609     },
3610
3611
3612     easeInStrong: function (t, b, c, d) {
3613         return c * (t /= d) * t * t * t + b;
3614     },
3615
3616
3617     easeOutStrong: function (t, b, c, d) {
3618         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3619     },
3620
3621
3622     easeBothStrong: function (t, b, c, d) {
3623         if ((t /= d / 2) < 1) {
3624             return c / 2 * t * t * t * t + b;
3625         }
3626
3627         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3628     },
3629
3630
3631
3632     elasticIn: function (t, b, c, d, a, p) {
3633         if (t == 0) {
3634             return b;
3635         }
3636         if ((t /= d) == 1) {
3637             return b + c;
3638         }
3639         if (!p) {
3640             p = d * .3;
3641         }
3642
3643         if (!a || a < Math.abs(c)) {
3644             a = c;
3645             var s = p / 4;
3646         }
3647         else {
3648             var s = p / (2 * Math.PI) * Math.asin(c / a);
3649         }
3650
3651         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3652     },
3653
3654
3655     elasticOut: function (t, b, c, d, a, p) {
3656         if (t == 0) {
3657             return b;
3658         }
3659         if ((t /= d) == 1) {
3660             return b + c;
3661         }
3662         if (!p) {
3663             p = d * .3;
3664         }
3665
3666         if (!a || a < Math.abs(c)) {
3667             a = c;
3668             var s = p / 4;
3669         }
3670         else {
3671             var s = p / (2 * Math.PI) * Math.asin(c / a);
3672         }
3673
3674         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3675     },
3676
3677
3678     elasticBoth: function (t, b, c, d, a, p) {
3679         if (t == 0) {
3680             return b;
3681         }
3682
3683         if ((t /= d / 2) == 2) {
3684             return b + c;
3685         }
3686
3687         if (!p) {
3688             p = d * (.3 * 1.5);
3689         }
3690
3691         if (!a || a < Math.abs(c)) {
3692             a = c;
3693             var s = p / 4;
3694         }
3695         else {
3696             var s = p / (2 * Math.PI) * Math.asin(c / a);
3697         }
3698
3699         if (t < 1) {
3700             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3701                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3702         }
3703         return a * Math.pow(2, -10 * (t -= 1)) *
3704                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3705     },
3706
3707
3708
3709     backIn: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3714     },
3715
3716
3717     backOut: function (t, b, c, d, s) {
3718         if (typeof s == 'undefined') {
3719             s = 1.70158;
3720         }
3721         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3722     },
3723
3724
3725     backBoth: function (t, b, c, d, s) {
3726         if (typeof s == 'undefined') {
3727             s = 1.70158;
3728         }
3729
3730         if ((t /= d / 2 ) < 1) {
3731             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3732         }
3733         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3734     },
3735
3736
3737     bounceIn: function (t, b, c, d) {
3738         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3739     },
3740
3741
3742     bounceOut: function (t, b, c, d) {
3743         if ((t /= d) < (1 / 2.75)) {
3744             return c * (7.5625 * t * t) + b;
3745         } else if (t < (2 / 2.75)) {
3746             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3747         } else if (t < (2.5 / 2.75)) {
3748             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3749         }
3750         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3751     },
3752
3753
3754     bounceBoth: function (t, b, c, d) {
3755         if (t < d / 2) {
3756             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3757         }
3758         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3759     }
3760 };/*
3761  * Portions of this file are based on pieces of Yahoo User Interface Library
3762  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3763  * YUI licensed under the BSD License:
3764  * http://developer.yahoo.net/yui/license.txt
3765  * <script type="text/javascript">
3766  *
3767  */
3768     (function() {
3769         Roo.lib.Motion = function(el, attributes, duration, method) {
3770             if (el) {
3771                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3772             }
3773         };
3774
3775         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3776
3777
3778         var Y = Roo.lib;
3779         var superclass = Y.Motion.superclass;
3780         var proto = Y.Motion.prototype;
3781
3782         proto.toString = function() {
3783             var el = this.getEl();
3784             var id = el.id || el.tagName;
3785             return ("Motion " + id);
3786         };
3787
3788         proto.patterns.points = /^points$/i;
3789
3790         proto.setAttribute = function(attr, val, unit) {
3791             if (this.patterns.points.test(attr)) {
3792                 unit = unit || 'px';
3793                 superclass.setAttribute.call(this, 'left', val[0], unit);
3794                 superclass.setAttribute.call(this, 'top', val[1], unit);
3795             } else {
3796                 superclass.setAttribute.call(this, attr, val, unit);
3797             }
3798         };
3799
3800         proto.getAttribute = function(attr) {
3801             if (this.patterns.points.test(attr)) {
3802                 var val = [
3803                         superclass.getAttribute.call(this, 'left'),
3804                         superclass.getAttribute.call(this, 'top')
3805                         ];
3806             } else {
3807                 val = superclass.getAttribute.call(this, attr);
3808             }
3809
3810             return val;
3811         };
3812
3813         proto.doMethod = function(attr, start, end) {
3814             var val = null;
3815
3816             if (this.patterns.points.test(attr)) {
3817                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3818                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3819             } else {
3820                 val = superclass.doMethod.call(this, attr, start, end);
3821             }
3822             return val;
3823         };
3824
3825         proto.setRuntimeAttribute = function(attr) {
3826             if (this.patterns.points.test(attr)) {
3827                 var el = this.getEl();
3828                 var attributes = this.attributes;
3829                 var start;
3830                 var control = attributes['points']['control'] || [];
3831                 var end;
3832                 var i, len;
3833
3834                 if (control.length > 0 && !(control[0] instanceof Array)) {
3835                     control = [control];
3836                 } else {
3837                     var tmp = [];
3838                     for (i = 0,len = control.length; i < len; ++i) {
3839                         tmp[i] = control[i];
3840                     }
3841                     control = tmp;
3842                 }
3843
3844                 Roo.fly(el).position();
3845
3846                 if (isset(attributes['points']['from'])) {
3847                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3848                 }
3849                 else {
3850                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3851                 }
3852
3853                 start = this.getAttribute('points');
3854
3855
3856                 if (isset(attributes['points']['to'])) {
3857                     end = translateValues.call(this, attributes['points']['to'], start);
3858
3859                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3860                     for (i = 0,len = control.length; i < len; ++i) {
3861                         control[i] = translateValues.call(this, control[i], start);
3862                     }
3863
3864
3865                 } else if (isset(attributes['points']['by'])) {
3866                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3867
3868                     for (i = 0,len = control.length; i < len; ++i) {
3869                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3870                     }
3871                 }
3872
3873                 this.runtimeAttributes[attr] = [start];
3874
3875                 if (control.length > 0) {
3876                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3877                 }
3878
3879                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3880             }
3881             else {
3882                 superclass.setRuntimeAttribute.call(this, attr);
3883             }
3884         };
3885
3886         var translateValues = function(val, start) {
3887             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3888             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3889
3890             return val;
3891         };
3892
3893         var isset = function(prop) {
3894             return (typeof prop !== 'undefined');
3895         };
3896     })();
3897 /*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Scroll = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Scroll.superclass;
3917         var proto = Y.Scroll.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Scroll " + id);
3923         };
3924
3925         proto.doMethod = function(attr, start, end) {
3926             var val = null;
3927
3928             if (attr == 'scroll') {
3929                 val = [
3930                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3931                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3932                         ];
3933
3934             } else {
3935                 val = superclass.doMethod.call(this, attr, start, end);
3936             }
3937             return val;
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             var val = null;
3942             var el = this.getEl();
3943
3944             if (attr == 'scroll') {
3945                 val = [ el.scrollLeft, el.scrollTop ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.setAttribute = function(attr, val, unit) {
3954             var el = this.getEl();
3955
3956             if (attr == 'scroll') {
3957                 el.scrollLeft = val[0];
3958                 el.scrollTop = val[1];
3959             } else {
3960                 superclass.setAttribute.call(this, attr, val, unit);
3961             }
3962         };
3963     })();
3964 /*
3965  * Based on:
3966  * Ext JS Library 1.1.1
3967  * Copyright(c) 2006-2007, Ext JS, LLC.
3968  *
3969  * Originally Released Under LGPL - original licence link has changed is not relivant.
3970  *
3971  * Fork - LGPL
3972  * <script type="text/javascript">
3973  */
3974  
3975
3976 /**
3977  * @class Roo.DomHelper
3978  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3979  * 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>.
3980  * @singleton
3981  */
3982 Roo.DomHelper = function(){
3983     var tempTableEl = null;
3984     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3985     var tableRe = /^table|tbody|tr|td$/i;
3986     var xmlns = {};
3987     // build as innerHTML where available
3988     /** @ignore */
3989     var createHtml = function(o){
3990         if(typeof o == 'string'){
3991             return o;
3992         }
3993         var b = "";
3994         if(!o.tag){
3995             o.tag = "div";
3996         }
3997         b += "<" + o.tag;
3998         for(var attr in o){
3999             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4000             if(attr == "style"){
4001                 var s = o["style"];
4002                 if(typeof s == "function"){
4003                     s = s.call();
4004                 }
4005                 if(typeof s == "string"){
4006                     b += ' style="' + s + '"';
4007                 }else if(typeof s == "object"){
4008                     b += ' style="';
4009                     for(var key in s){
4010                         if(typeof s[key] != "function"){
4011                             b += key + ":" + s[key] + ";";
4012                         }
4013                     }
4014                     b += '"';
4015                 }
4016             }else{
4017                 if(attr == "cls"){
4018                     b += ' class="' + o["cls"] + '"';
4019                 }else if(attr == "htmlFor"){
4020                     b += ' for="' + o["htmlFor"] + '"';
4021                 }else{
4022                     b += " " + attr + '="' + o[attr] + '"';
4023                 }
4024             }
4025         }
4026         if(emptyTags.test(o.tag)){
4027             b += "/>";
4028         }else{
4029             b += ">";
4030             var cn = o.children || o.cn;
4031             if(cn){
4032                 //http://bugs.kde.org/show_bug.cgi?id=71506
4033                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4034                     for(var i = 0, len = cn.length; i < len; i++) {
4035                         b += createHtml(cn[i], b);
4036                     }
4037                 }else{
4038                     b += createHtml(cn, b);
4039                 }
4040             }
4041             if(o.html){
4042                 b += o.html;
4043             }
4044             b += "</" + o.tag + ">";
4045         }
4046         return b;
4047     };
4048
4049     // build as dom
4050     /** @ignore */
4051     var createDom = function(o, parentNode){
4052          
4053         // defininition craeted..
4054         var ns = false;
4055         if (o.ns && o.ns != 'html') {
4056                
4057             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4058                 xmlns[o.ns] = o.xmlns;
4059                 ns = o.xmlns;
4060             }
4061             if (typeof(xmlns[o.ns]) == 'undefined') {
4062                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4063             }
4064             ns = xmlns[o.ns];
4065         }
4066         
4067         
4068         if (typeof(o) == 'string') {
4069             return parentNode.appendChild(document.createTextNode(o));
4070         }
4071         o.tag = o.tag || div;
4072         if (o.ns && Roo.isIE) {
4073             ns = false;
4074             o.tag = o.ns + ':' + o.tag;
4075             
4076         }
4077         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4078         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4079         for(var attr in o){
4080             
4081             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4082                     attr == "style" || typeof o[attr] == "function") continue;
4083                     
4084             if(attr=="cls" && Roo.isIE){
4085                 el.className = o["cls"];
4086             }else{
4087                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4088                 else el[attr] = o[attr];
4089             }
4090         }
4091         Roo.DomHelper.applyStyles(el, o.style);
4092         var cn = o.children || o.cn;
4093         if(cn){
4094             //http://bugs.kde.org/show_bug.cgi?id=71506
4095              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4096                 for(var i = 0, len = cn.length; i < len; i++) {
4097                     createDom(cn[i], el);
4098                 }
4099             }else{
4100                 createDom(cn, el);
4101             }
4102         }
4103         if(o.html){
4104             el.innerHTML = o.html;
4105         }
4106         if(parentNode){
4107            parentNode.appendChild(el);
4108         }
4109         return el;
4110     };
4111
4112     var ieTable = function(depth, s, h, e){
4113         tempTableEl.innerHTML = [s, h, e].join('');
4114         var i = -1, el = tempTableEl;
4115         while(++i < depth){
4116             el = el.firstChild;
4117         }
4118         return el;
4119     };
4120
4121     // kill repeat to save bytes
4122     var ts = '<table>',
4123         te = '</table>',
4124         tbs = ts+'<tbody>',
4125         tbe = '</tbody>'+te,
4126         trs = tbs + '<tr>',
4127         tre = '</tr>'+tbe;
4128
4129     /**
4130      * @ignore
4131      * Nasty code for IE's broken table implementation
4132      */
4133     var insertIntoTable = function(tag, where, el, html){
4134         if(!tempTableEl){
4135             tempTableEl = document.createElement('div');
4136         }
4137         var node;
4138         var before = null;
4139         if(tag == 'td'){
4140             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4141                 return;
4142             }
4143             if(where == 'beforebegin'){
4144                 before = el;
4145                 el = el.parentNode;
4146             } else{
4147                 before = el.nextSibling;
4148                 el = el.parentNode;
4149             }
4150             node = ieTable(4, trs, html, tre);
4151         }
4152         else if(tag == 'tr'){
4153             if(where == 'beforebegin'){
4154                 before = el;
4155                 el = el.parentNode;
4156                 node = ieTable(3, tbs, html, tbe);
4157             } else if(where == 'afterend'){
4158                 before = el.nextSibling;
4159                 el = el.parentNode;
4160                 node = ieTable(3, tbs, html, tbe);
4161             } else{ // INTO a TR
4162                 if(where == 'afterbegin'){
4163                     before = el.firstChild;
4164                 }
4165                 node = ieTable(4, trs, html, tre);
4166             }
4167         } else if(tag == 'tbody'){
4168             if(where == 'beforebegin'){
4169                 before = el;
4170                 el = el.parentNode;
4171                 node = ieTable(2, ts, html, te);
4172             } else if(where == 'afterend'){
4173                 before = el.nextSibling;
4174                 el = el.parentNode;
4175                 node = ieTable(2, ts, html, te);
4176             } else{
4177                 if(where == 'afterbegin'){
4178                     before = el.firstChild;
4179                 }
4180                 node = ieTable(3, tbs, html, tbe);
4181             }
4182         } else{ // TABLE
4183             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4184                 return;
4185             }
4186             if(where == 'afterbegin'){
4187                 before = el.firstChild;
4188             }
4189             node = ieTable(2, ts, html, te);
4190         }
4191         el.insertBefore(node, before);
4192         return node;
4193     };
4194
4195     return {
4196     /** True to force the use of DOM instead of html fragments @type Boolean */
4197     useDom : false,
4198
4199     /**
4200      * Returns the markup for the passed Element(s) config
4201      * @param {Object} o The Dom object spec (and children)
4202      * @return {String}
4203      */
4204     markup : function(o){
4205         return createHtml(o);
4206     },
4207
4208     /**
4209      * Applies a style specification to an element
4210      * @param {String/HTMLElement} el The element to apply styles to
4211      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4212      * a function which returns such a specification.
4213      */
4214     applyStyles : function(el, styles){
4215         if(styles){
4216            el = Roo.fly(el);
4217            if(typeof styles == "string"){
4218                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4219                var matches;
4220                while ((matches = re.exec(styles)) != null){
4221                    el.setStyle(matches[1], matches[2]);
4222                }
4223            }else if (typeof styles == "object"){
4224                for (var style in styles){
4225                   el.setStyle(style, styles[style]);
4226                }
4227            }else if (typeof styles == "function"){
4228                 Roo.DomHelper.applyStyles(el, styles.call());
4229            }
4230         }
4231     },
4232
4233     /**
4234      * Inserts an HTML fragment into the Dom
4235      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4236      * @param {HTMLElement} el The context element
4237      * @param {String} html The HTML fragmenet
4238      * @return {HTMLElement} The new node
4239      */
4240     insertHtml : function(where, el, html){
4241         where = where.toLowerCase();
4242         if(el.insertAdjacentHTML){
4243             if(tableRe.test(el.tagName)){
4244                 var rs;
4245                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4246                     return rs;
4247                 }
4248             }
4249             switch(where){
4250                 case "beforebegin":
4251                     el.insertAdjacentHTML('BeforeBegin', html);
4252                     return el.previousSibling;
4253                 case "afterbegin":
4254                     el.insertAdjacentHTML('AfterBegin', html);
4255                     return el.firstChild;
4256                 case "beforeend":
4257                     el.insertAdjacentHTML('BeforeEnd', html);
4258                     return el.lastChild;
4259                 case "afterend":
4260                     el.insertAdjacentHTML('AfterEnd', html);
4261                     return el.nextSibling;
4262             }
4263             throw 'Illegal insertion point -> "' + where + '"';
4264         }
4265         var range = el.ownerDocument.createRange();
4266         var frag;
4267         switch(where){
4268              case "beforebegin":
4269                 range.setStartBefore(el);
4270                 frag = range.createContextualFragment(html);
4271                 el.parentNode.insertBefore(frag, el);
4272                 return el.previousSibling;
4273              case "afterbegin":
4274                 if(el.firstChild){
4275                     range.setStartBefore(el.firstChild);
4276                     frag = range.createContextualFragment(html);
4277                     el.insertBefore(frag, el.firstChild);
4278                     return el.firstChild;
4279                 }else{
4280                     el.innerHTML = html;
4281                     return el.firstChild;
4282                 }
4283             case "beforeend":
4284                 if(el.lastChild){
4285                     range.setStartAfter(el.lastChild);
4286                     frag = range.createContextualFragment(html);
4287                     el.appendChild(frag);
4288                     return el.lastChild;
4289                 }else{
4290                     el.innerHTML = html;
4291                     return el.lastChild;
4292                 }
4293             case "afterend":
4294                 range.setStartAfter(el);
4295                 frag = range.createContextualFragment(html);
4296                 el.parentNode.insertBefore(frag, el.nextSibling);
4297                 return el.nextSibling;
4298             }
4299             throw 'Illegal insertion point -> "' + where + '"';
4300     },
4301
4302     /**
4303      * Creates new Dom element(s) and inserts them before el
4304      * @param {String/HTMLElement/Element} el The context element
4305      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4306      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4307      * @return {HTMLElement/Roo.Element} The new node
4308      */
4309     insertBefore : function(el, o, returnElement){
4310         return this.doInsert(el, o, returnElement, "beforeBegin");
4311     },
4312
4313     /**
4314      * Creates new Dom element(s) and inserts them after el
4315      * @param {String/HTMLElement/Element} el The context element
4316      * @param {Object} o The Dom object spec (and children)
4317      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4318      * @return {HTMLElement/Roo.Element} The new node
4319      */
4320     insertAfter : function(el, o, returnElement){
4321         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4322     },
4323
4324     /**
4325      * Creates new Dom element(s) and inserts them as the first child of el
4326      * @param {String/HTMLElement/Element} el The context element
4327      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4328      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4329      * @return {HTMLElement/Roo.Element} The new node
4330      */
4331     insertFirst : function(el, o, returnElement){
4332         return this.doInsert(el, o, returnElement, "afterBegin");
4333     },
4334
4335     // private
4336     doInsert : function(el, o, returnElement, pos, sibling){
4337         el = Roo.getDom(el);
4338         var newNode;
4339         if(this.useDom || o.ns){
4340             newNode = createDom(o, null);
4341             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4342         }else{
4343             var html = createHtml(o);
4344             newNode = this.insertHtml(pos, el, html);
4345         }
4346         return returnElement ? Roo.get(newNode, true) : newNode;
4347     },
4348
4349     /**
4350      * Creates new Dom element(s) and appends them to el
4351      * @param {String/HTMLElement/Element} el The context element
4352      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4353      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4354      * @return {HTMLElement/Roo.Element} The new node
4355      */
4356     append : function(el, o, returnElement){
4357         el = Roo.getDom(el);
4358         var newNode;
4359         if(this.useDom || o.ns){
4360             newNode = createDom(o, null);
4361             el.appendChild(newNode);
4362         }else{
4363             var html = createHtml(o);
4364             newNode = this.insertHtml("beforeEnd", el, html);
4365         }
4366         return returnElement ? Roo.get(newNode, true) : newNode;
4367     },
4368
4369     /**
4370      * Creates new Dom element(s) and overwrites the contents of el with them
4371      * @param {String/HTMLElement/Element} el The context element
4372      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4373      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4374      * @return {HTMLElement/Roo.Element} The new node
4375      */
4376     overwrite : function(el, o, returnElement){
4377         el = Roo.getDom(el);
4378         if (o.ns) {
4379           
4380             while (el.childNodes.length) {
4381                 el.removeChild(el.firstChild);
4382             }
4383             createDom(o, el);
4384         } else {
4385             el.innerHTML = createHtml(o);   
4386         }
4387         
4388         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4389     },
4390
4391     /**
4392      * Creates a new Roo.DomHelper.Template from the Dom object spec
4393      * @param {Object} o The Dom object spec (and children)
4394      * @return {Roo.DomHelper.Template} The new template
4395      */
4396     createTemplate : function(o){
4397         var html = createHtml(o);
4398         return new Roo.Template(html);
4399     }
4400     };
4401 }();
4402 /*
4403  * Based on:
4404  * Ext JS Library 1.1.1
4405  * Copyright(c) 2006-2007, Ext JS, LLC.
4406  *
4407  * Originally Released Under LGPL - original licence link has changed is not relivant.
4408  *
4409  * Fork - LGPL
4410  * <script type="text/javascript">
4411  */
4412  
4413 /**
4414 * @class Roo.Template
4415 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4416 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4417 * Usage:
4418 <pre><code>
4419 var t = new Roo.Template({
4420     html :  '&lt;div name="{id}"&gt;' + 
4421         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4422         '&lt;/div&gt;',
4423     myformat: function (value, allValues) {
4424         return 'XX' + value;
4425     }
4426 });
4427 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4428 </code></pre>
4429 * 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>. 
4430 * @constructor
4431 * @param {Object} cfg - Configuration object.
4432 */
4433 Roo.Template = function(cfg){
4434     // BC!
4435     if(cfg instanceof Array){
4436         cfg = cfg.join("");
4437     }else if(arguments.length > 1){
4438         cfg = Array.prototype.join.call(arguments, "");
4439     }
4440     
4441     
4442     if (typeof(cfg) == 'object') {
4443         Roo.apply(this,cfg)
4444     } else {
4445         // bc
4446         this.html = cfg;
4447     }
4448     
4449     
4450 };
4451 Roo.Template.prototype = {
4452     
4453     /**
4454      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4455      */
4456     html : '',
4457     /**
4458      * Returns an HTML fragment of this template with the specified values applied.
4459      * @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'})
4460      * @return {String} The HTML fragment
4461      */
4462     applyTemplate : function(values){
4463         try {
4464             
4465             if(this.compiled){
4466                 return this.compiled(values);
4467             }
4468             var useF = this.disableFormats !== true;
4469             var fm = Roo.util.Format, tpl = this;
4470             var fn = function(m, name, format, args){
4471                 if(format && useF){
4472                     if(format.substr(0, 5) == "this."){
4473                         return tpl.call(format.substr(5), values[name], values);
4474                     }else{
4475                         if(args){
4476                             // quoted values are required for strings in compiled templates, 
4477                             // but for non compiled we need to strip them
4478                             // quoted reversed for jsmin
4479                             var re = /^\s*['"](.*)["']\s*$/;
4480                             args = args.split(',');
4481                             for(var i = 0, len = args.length; i < len; i++){
4482                                 args[i] = args[i].replace(re, "$1");
4483                             }
4484                             args = [values[name]].concat(args);
4485                         }else{
4486                             args = [values[name]];
4487                         }
4488                         return fm[format].apply(fm, args);
4489                     }
4490                 }else{
4491                     return values[name] !== undefined ? values[name] : "";
4492                 }
4493             };
4494             return this.html.replace(this.re, fn);
4495         } catch (e) {
4496             Roo.log(e);
4497             throw e;
4498         }
4499          
4500     },
4501     
4502     /**
4503      * Sets the HTML used as the template and optionally compiles it.
4504      * @param {String} html
4505      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4506      * @return {Roo.Template} this
4507      */
4508     set : function(html, compile){
4509         this.html = html;
4510         this.compiled = null;
4511         if(compile){
4512             this.compile();
4513         }
4514         return this;
4515     },
4516     
4517     /**
4518      * True to disable format functions (defaults to false)
4519      * @type Boolean
4520      */
4521     disableFormats : false,
4522     
4523     /**
4524     * The regular expression used to match template variables 
4525     * @type RegExp
4526     * @property 
4527     */
4528     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4529     
4530     /**
4531      * Compiles the template into an internal function, eliminating the RegEx overhead.
4532      * @return {Roo.Template} this
4533      */
4534     compile : function(){
4535         var fm = Roo.util.Format;
4536         var useF = this.disableFormats !== true;
4537         var sep = Roo.isGecko ? "+" : ",";
4538         var fn = function(m, name, format, args){
4539             if(format && useF){
4540                 args = args ? ',' + args : "";
4541                 if(format.substr(0, 5) != "this."){
4542                     format = "fm." + format + '(';
4543                 }else{
4544                     format = 'this.call("'+ format.substr(5) + '", ';
4545                     args = ", values";
4546                 }
4547             }else{
4548                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4549             }
4550             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4551         };
4552         var body;
4553         // branched to use + in gecko and [].join() in others
4554         if(Roo.isGecko){
4555             body = "this.compiled = function(values){ return '" +
4556                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4557                     "';};";
4558         }else{
4559             body = ["this.compiled = function(values){ return ['"];
4560             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4561             body.push("'].join('');};");
4562             body = body.join('');
4563         }
4564         /**
4565          * eval:var:values
4566          * eval:var:fm
4567          */
4568         eval(body);
4569         return this;
4570     },
4571     
4572     // private function used to call members
4573     call : function(fnName, value, allValues){
4574         return this[fnName](value, allValues);
4575     },
4576     
4577     /**
4578      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4579      * @param {String/HTMLElement/Roo.Element} el The context element
4580      * @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'})
4581      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4582      * @return {HTMLElement/Roo.Element} The new node or Element
4583      */
4584     insertFirst: function(el, values, returnElement){
4585         return this.doInsert('afterBegin', el, values, returnElement);
4586     },
4587
4588     /**
4589      * Applies the supplied values to the template and inserts the new node(s) before el.
4590      * @param {String/HTMLElement/Roo.Element} el The context element
4591      * @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'})
4592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4593      * @return {HTMLElement/Roo.Element} The new node or Element
4594      */
4595     insertBefore: function(el, values, returnElement){
4596         return this.doInsert('beforeBegin', el, values, returnElement);
4597     },
4598
4599     /**
4600      * Applies the supplied values to the template and inserts the new node(s) after el.
4601      * @param {String/HTMLElement/Roo.Element} el The context element
4602      * @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'})
4603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4604      * @return {HTMLElement/Roo.Element} The new node or Element
4605      */
4606     insertAfter : function(el, values, returnElement){
4607         return this.doInsert('afterEnd', el, values, returnElement);
4608     },
4609     
4610     /**
4611      * Applies the supplied values to the template and appends the new node(s) to el.
4612      * @param {String/HTMLElement/Roo.Element} el The context element
4613      * @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'})
4614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4615      * @return {HTMLElement/Roo.Element} The new node or Element
4616      */
4617     append : function(el, values, returnElement){
4618         return this.doInsert('beforeEnd', el, values, returnElement);
4619     },
4620
4621     doInsert : function(where, el, values, returnEl){
4622         el = Roo.getDom(el);
4623         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4624         return returnEl ? Roo.get(newNode, true) : newNode;
4625     },
4626
4627     /**
4628      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4629      * @param {String/HTMLElement/Roo.Element} el The context element
4630      * @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'})
4631      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4632      * @return {HTMLElement/Roo.Element} The new node or Element
4633      */
4634     overwrite : function(el, values, returnElement){
4635         el = Roo.getDom(el);
4636         el.innerHTML = this.applyTemplate(values);
4637         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4638     }
4639 };
4640 /**
4641  * Alias for {@link #applyTemplate}
4642  * @method
4643  */
4644 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4645
4646 // backwards compat
4647 Roo.DomHelper.Template = Roo.Template;
4648
4649 /**
4650  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4651  * @param {String/HTMLElement} el A DOM element or its id
4652  * @returns {Roo.Template} The created template
4653  * @static
4654  */
4655 Roo.Template.from = function(el){
4656     el = Roo.getDom(el);
4657     return new Roo.Template(el.value || el.innerHTML);
4658 };/*
4659  * Based on:
4660  * Ext JS Library 1.1.1
4661  * Copyright(c) 2006-2007, Ext JS, LLC.
4662  *
4663  * Originally Released Under LGPL - original licence link has changed is not relivant.
4664  *
4665  * Fork - LGPL
4666  * <script type="text/javascript">
4667  */
4668  
4669
4670 /*
4671  * This is code is also distributed under MIT license for use
4672  * with jQuery and prototype JavaScript libraries.
4673  */
4674 /**
4675  * @class Roo.DomQuery
4676 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).
4677 <p>
4678 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>
4679
4680 <p>
4681 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.
4682 </p>
4683 <h4>Element Selectors:</h4>
4684 <ul class="list">
4685     <li> <b>*</b> any element</li>
4686     <li> <b>E</b> an element with the tag E</li>
4687     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4688     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4689     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4690     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4691 </ul>
4692 <h4>Attribute Selectors:</h4>
4693 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4694 <ul class="list">
4695     <li> <b>E[foo]</b> has an attribute "foo"</li>
4696     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4697     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4698     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4699     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4700     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4701     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4702 </ul>
4703 <h4>Pseudo Classes:</h4>
4704 <ul class="list">
4705     <li> <b>E:first-child</b> E is the first child of its parent</li>
4706     <li> <b>E:last-child</b> E is the last child of its parent</li>
4707     <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>
4708     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4709     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4710     <li> <b>E:only-child</b> E is the only child of its parent</li>
4711     <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>
4712     <li> <b>E:first</b> the first E in the resultset</li>
4713     <li> <b>E:last</b> the last E in the resultset</li>
4714     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4715     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4716     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4717     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4718     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4719     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4720     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4721     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4722     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4723 </ul>
4724 <h4>CSS Value Selectors:</h4>
4725 <ul class="list">
4726     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4727     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4728     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4729     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4730     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4731     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4732 </ul>
4733  * @singleton
4734  */
4735 Roo.DomQuery = function(){
4736     var cache = {}, simpleCache = {}, valueCache = {};
4737     var nonSpace = /\S/;
4738     var trimRe = /^\s+|\s+$/g;
4739     var tplRe = /\{(\d+)\}/g;
4740     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4741     var tagTokenRe = /^(#)?([\w-\*]+)/;
4742     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4743
4744     function child(p, index){
4745         var i = 0;
4746         var n = p.firstChild;
4747         while(n){
4748             if(n.nodeType == 1){
4749                if(++i == index){
4750                    return n;
4751                }
4752             }
4753             n = n.nextSibling;
4754         }
4755         return null;
4756     };
4757
4758     function next(n){
4759         while((n = n.nextSibling) && n.nodeType != 1);
4760         return n;
4761     };
4762
4763     function prev(n){
4764         while((n = n.previousSibling) && n.nodeType != 1);
4765         return n;
4766     };
4767
4768     function children(d){
4769         var n = d.firstChild, ni = -1;
4770             while(n){
4771                 var nx = n.nextSibling;
4772                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4773                     d.removeChild(n);
4774                 }else{
4775                     n.nodeIndex = ++ni;
4776                 }
4777                 n = nx;
4778             }
4779             return this;
4780         };
4781
4782     function byClassName(c, a, v){
4783         if(!v){
4784             return c;
4785         }
4786         var r = [], ri = -1, cn;
4787         for(var i = 0, ci; ci = c[i]; i++){
4788             if((' '+ci.className+' ').indexOf(v) != -1){
4789                 r[++ri] = ci;
4790             }
4791         }
4792         return r;
4793     };
4794
4795     function attrValue(n, attr){
4796         if(!n.tagName && typeof n.length != "undefined"){
4797             n = n[0];
4798         }
4799         if(!n){
4800             return null;
4801         }
4802         if(attr == "for"){
4803             return n.htmlFor;
4804         }
4805         if(attr == "class" || attr == "className"){
4806             return n.className;
4807         }
4808         return n.getAttribute(attr) || n[attr];
4809
4810     };
4811
4812     function getNodes(ns, mode, tagName){
4813         var result = [], ri = -1, cs;
4814         if(!ns){
4815             return result;
4816         }
4817         tagName = tagName || "*";
4818         if(typeof ns.getElementsByTagName != "undefined"){
4819             ns = [ns];
4820         }
4821         if(!mode){
4822             for(var i = 0, ni; ni = ns[i]; i++){
4823                 cs = ni.getElementsByTagName(tagName);
4824                 for(var j = 0, ci; ci = cs[j]; j++){
4825                     result[++ri] = ci;
4826                 }
4827             }
4828         }else if(mode == "/" || mode == ">"){
4829             var utag = tagName.toUpperCase();
4830             for(var i = 0, ni, cn; ni = ns[i]; i++){
4831                 cn = ni.children || ni.childNodes;
4832                 for(var j = 0, cj; cj = cn[j]; j++){
4833                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4834                         result[++ri] = cj;
4835                     }
4836                 }
4837             }
4838         }else if(mode == "+"){
4839             var utag = tagName.toUpperCase();
4840             for(var i = 0, n; n = ns[i]; i++){
4841                 while((n = n.nextSibling) && n.nodeType != 1);
4842                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4843                     result[++ri] = n;
4844                 }
4845             }
4846         }else if(mode == "~"){
4847             for(var i = 0, n; n = ns[i]; i++){
4848                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4849                 if(n){
4850                     result[++ri] = n;
4851                 }
4852             }
4853         }
4854         return result;
4855     };
4856
4857     function concat(a, b){
4858         if(b.slice){
4859             return a.concat(b);
4860         }
4861         for(var i = 0, l = b.length; i < l; i++){
4862             a[a.length] = b[i];
4863         }
4864         return a;
4865     }
4866
4867     function byTag(cs, tagName){
4868         if(cs.tagName || cs == document){
4869             cs = [cs];
4870         }
4871         if(!tagName){
4872             return cs;
4873         }
4874         var r = [], ri = -1;
4875         tagName = tagName.toLowerCase();
4876         for(var i = 0, ci; ci = cs[i]; i++){
4877             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4878                 r[++ri] = ci;
4879             }
4880         }
4881         return r;
4882     };
4883
4884     function byId(cs, attr, id){
4885         if(cs.tagName || cs == document){
4886             cs = [cs];
4887         }
4888         if(!id){
4889             return cs;
4890         }
4891         var r = [], ri = -1;
4892         for(var i = 0,ci; ci = cs[i]; i++){
4893             if(ci && ci.id == id){
4894                 r[++ri] = ci;
4895                 return r;
4896             }
4897         }
4898         return r;
4899     };
4900
4901     function byAttribute(cs, attr, value, op, custom){
4902         var r = [], ri = -1, st = custom=="{";
4903         var f = Roo.DomQuery.operators[op];
4904         for(var i = 0, ci; ci = cs[i]; i++){
4905             var a;
4906             if(st){
4907                 a = Roo.DomQuery.getStyle(ci, attr);
4908             }
4909             else if(attr == "class" || attr == "className"){
4910                 a = ci.className;
4911             }else if(attr == "for"){
4912                 a = ci.htmlFor;
4913             }else if(attr == "href"){
4914                 a = ci.getAttribute("href", 2);
4915             }else{
4916                 a = ci.getAttribute(attr);
4917             }
4918             if((f && f(a, value)) || (!f && a)){
4919                 r[++ri] = ci;
4920             }
4921         }
4922         return r;
4923     };
4924
4925     function byPseudo(cs, name, value){
4926         return Roo.DomQuery.pseudos[name](cs, value);
4927     };
4928
4929     // This is for IE MSXML which does not support expandos.
4930     // IE runs the same speed using setAttribute, however FF slows way down
4931     // and Safari completely fails so they need to continue to use expandos.
4932     var isIE = window.ActiveXObject ? true : false;
4933
4934     // this eval is stop the compressor from
4935     // renaming the variable to something shorter
4936     
4937     /** eval:var:batch */
4938     var batch = 30803; 
4939
4940     var key = 30803;
4941
4942     function nodupIEXml(cs){
4943         var d = ++key;
4944         cs[0].setAttribute("_nodup", d);
4945         var r = [cs[0]];
4946         for(var i = 1, len = cs.length; i < len; i++){
4947             var c = cs[i];
4948             if(!c.getAttribute("_nodup") != d){
4949                 c.setAttribute("_nodup", d);
4950                 r[r.length] = c;
4951             }
4952         }
4953         for(var i = 0, len = cs.length; i < len; i++){
4954             cs[i].removeAttribute("_nodup");
4955         }
4956         return r;
4957     }
4958
4959     function nodup(cs){
4960         if(!cs){
4961             return [];
4962         }
4963         var len = cs.length, c, i, r = cs, cj, ri = -1;
4964         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4965             return cs;
4966         }
4967         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4968             return nodupIEXml(cs);
4969         }
4970         var d = ++key;
4971         cs[0]._nodup = d;
4972         for(i = 1; c = cs[i]; i++){
4973             if(c._nodup != d){
4974                 c._nodup = d;
4975             }else{
4976                 r = [];
4977                 for(var j = 0; j < i; j++){
4978                     r[++ri] = cs[j];
4979                 }
4980                 for(j = i+1; cj = cs[j]; j++){
4981                     if(cj._nodup != d){
4982                         cj._nodup = d;
4983                         r[++ri] = cj;
4984                     }
4985                 }
4986                 return r;
4987             }
4988         }
4989         return r;
4990     }
4991
4992     function quickDiffIEXml(c1, c2){
4993         var d = ++key;
4994         for(var i = 0, len = c1.length; i < len; i++){
4995             c1[i].setAttribute("_qdiff", d);
4996         }
4997         var r = [];
4998         for(var i = 0, len = c2.length; i < len; i++){
4999             if(c2[i].getAttribute("_qdiff") != d){
5000                 r[r.length] = c2[i];
5001             }
5002         }
5003         for(var i = 0, len = c1.length; i < len; i++){
5004            c1[i].removeAttribute("_qdiff");
5005         }
5006         return r;
5007     }
5008
5009     function quickDiff(c1, c2){
5010         var len1 = c1.length;
5011         if(!len1){
5012             return c2;
5013         }
5014         if(isIE && c1[0].selectSingleNode){
5015             return quickDiffIEXml(c1, c2);
5016         }
5017         var d = ++key;
5018         for(var i = 0; i < len1; i++){
5019             c1[i]._qdiff = d;
5020         }
5021         var r = [];
5022         for(var i = 0, len = c2.length; i < len; i++){
5023             if(c2[i]._qdiff != d){
5024                 r[r.length] = c2[i];
5025             }
5026         }
5027         return r;
5028     }
5029
5030     function quickId(ns, mode, root, id){
5031         if(ns == root){
5032            var d = root.ownerDocument || root;
5033            return d.getElementById(id);
5034         }
5035         ns = getNodes(ns, mode, "*");
5036         return byId(ns, null, id);
5037     }
5038
5039     return {
5040         getStyle : function(el, name){
5041             return Roo.fly(el).getStyle(name);
5042         },
5043         /**
5044          * Compiles a selector/xpath query into a reusable function. The returned function
5045          * takes one parameter "root" (optional), which is the context node from where the query should start.
5046          * @param {String} selector The selector/xpath query
5047          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5048          * @return {Function}
5049          */
5050         compile : function(path, type){
5051             type = type || "select";
5052             
5053             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5054             var q = path, mode, lq;
5055             var tk = Roo.DomQuery.matchers;
5056             var tklen = tk.length;
5057             var mm;
5058
5059             // accept leading mode switch
5060             var lmode = q.match(modeRe);
5061             if(lmode && lmode[1]){
5062                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5063                 q = q.replace(lmode[1], "");
5064             }
5065             // strip leading slashes
5066             while(path.substr(0, 1)=="/"){
5067                 path = path.substr(1);
5068             }
5069
5070             while(q && lq != q){
5071                 lq = q;
5072                 var tm = q.match(tagTokenRe);
5073                 if(type == "select"){
5074                     if(tm){
5075                         if(tm[1] == "#"){
5076                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5077                         }else{
5078                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5079                         }
5080                         q = q.replace(tm[0], "");
5081                     }else if(q.substr(0, 1) != '@'){
5082                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5083                     }
5084                 }else{
5085                     if(tm){
5086                         if(tm[1] == "#"){
5087                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5088                         }else{
5089                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5090                         }
5091                         q = q.replace(tm[0], "");
5092                     }
5093                 }
5094                 while(!(mm = q.match(modeRe))){
5095                     var matched = false;
5096                     for(var j = 0; j < tklen; j++){
5097                         var t = tk[j];
5098                         var m = q.match(t.re);
5099                         if(m){
5100                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5101                                                     return m[i];
5102                                                 });
5103                             q = q.replace(m[0], "");
5104                             matched = true;
5105                             break;
5106                         }
5107                     }
5108                     // prevent infinite loop on bad selector
5109                     if(!matched){
5110                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5111                     }
5112                 }
5113                 if(mm[1]){
5114                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5115                     q = q.replace(mm[1], "");
5116                 }
5117             }
5118             fn[fn.length] = "return nodup(n);\n}";
5119             
5120              /** 
5121               * list of variables that need from compression as they are used by eval.
5122              *  eval:var:batch 
5123              *  eval:var:nodup
5124              *  eval:var:byTag
5125              *  eval:var:ById
5126              *  eval:var:getNodes
5127              *  eval:var:quickId
5128              *  eval:var:mode
5129              *  eval:var:root
5130              *  eval:var:n
5131              *  eval:var:byClassName
5132              *  eval:var:byPseudo
5133              *  eval:var:byAttribute
5134              *  eval:var:attrValue
5135              * 
5136              **/ 
5137             eval(fn.join(""));
5138             return f;
5139         },
5140
5141         /**
5142          * Selects a group of elements.
5143          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5144          * @param {Node} root (optional) The start of the query (defaults to document).
5145          * @return {Array}
5146          */
5147         select : function(path, root, type){
5148             if(!root || root == document){
5149                 root = document;
5150             }
5151             if(typeof root == "string"){
5152                 root = document.getElementById(root);
5153             }
5154             var paths = path.split(",");
5155             var results = [];
5156             for(var i = 0, len = paths.length; i < len; i++){
5157                 var p = paths[i].replace(trimRe, "");
5158                 if(!cache[p]){
5159                     cache[p] = Roo.DomQuery.compile(p);
5160                     if(!cache[p]){
5161                         throw p + " is not a valid selector";
5162                     }
5163                 }
5164                 var result = cache[p](root);
5165                 if(result && result != document){
5166                     results = results.concat(result);
5167                 }
5168             }
5169             if(paths.length > 1){
5170                 return nodup(results);
5171             }
5172             return results;
5173         },
5174
5175         /**
5176          * Selects a single element.
5177          * @param {String} selector The selector/xpath query
5178          * @param {Node} root (optional) The start of the query (defaults to document).
5179          * @return {Element}
5180          */
5181         selectNode : function(path, root){
5182             return Roo.DomQuery.select(path, root)[0];
5183         },
5184
5185         /**
5186          * Selects the value of a node, optionally replacing null with the defaultValue.
5187          * @param {String} selector The selector/xpath query
5188          * @param {Node} root (optional) The start of the query (defaults to document).
5189          * @param {String} defaultValue
5190          */
5191         selectValue : function(path, root, defaultValue){
5192             path = path.replace(trimRe, "");
5193             if(!valueCache[path]){
5194                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5195             }
5196             var n = valueCache[path](root);
5197             n = n[0] ? n[0] : n;
5198             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5199             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5200         },
5201
5202         /**
5203          * Selects the value of a node, parsing integers and floats.
5204          * @param {String} selector The selector/xpath query
5205          * @param {Node} root (optional) The start of the query (defaults to document).
5206          * @param {Number} defaultValue
5207          * @return {Number}
5208          */
5209         selectNumber : function(path, root, defaultValue){
5210             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5211             return parseFloat(v);
5212         },
5213
5214         /**
5215          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5216          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5217          * @param {String} selector The simple selector to test
5218          * @return {Boolean}
5219          */
5220         is : function(el, ss){
5221             if(typeof el == "string"){
5222                 el = document.getElementById(el);
5223             }
5224             var isArray = (el instanceof Array);
5225             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5226             return isArray ? (result.length == el.length) : (result.length > 0);
5227         },
5228
5229         /**
5230          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5231          * @param {Array} el An array of elements to filter
5232          * @param {String} selector The simple selector to test
5233          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5234          * the selector instead of the ones that match
5235          * @return {Array}
5236          */
5237         filter : function(els, ss, nonMatches){
5238             ss = ss.replace(trimRe, "");
5239             if(!simpleCache[ss]){
5240                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5241             }
5242             var result = simpleCache[ss](els);
5243             return nonMatches ? quickDiff(result, els) : result;
5244         },
5245
5246         /**
5247          * Collection of matching regular expressions and code snippets.
5248          */
5249         matchers : [{
5250                 re: /^\.([\w-]+)/,
5251                 select: 'n = byClassName(n, null, " {1} ");'
5252             }, {
5253                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5254                 select: 'n = byPseudo(n, "{1}", "{2}");'
5255             },{
5256                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5257                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5258             }, {
5259                 re: /^#([\w-]+)/,
5260                 select: 'n = byId(n, null, "{1}");'
5261             },{
5262                 re: /^@([\w-]+)/,
5263                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5264             }
5265         ],
5266
5267         /**
5268          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5269          * 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;.
5270          */
5271         operators : {
5272             "=" : function(a, v){
5273                 return a == v;
5274             },
5275             "!=" : function(a, v){
5276                 return a != v;
5277             },
5278             "^=" : function(a, v){
5279                 return a && a.substr(0, v.length) == v;
5280             },
5281             "$=" : function(a, v){
5282                 return a && a.substr(a.length-v.length) == v;
5283             },
5284             "*=" : function(a, v){
5285                 return a && a.indexOf(v) !== -1;
5286             },
5287             "%=" : function(a, v){
5288                 return (a % v) == 0;
5289             },
5290             "|=" : function(a, v){
5291                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5292             },
5293             "~=" : function(a, v){
5294                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5295             }
5296         },
5297
5298         /**
5299          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5300          * and the argument (if any) supplied in the selector.
5301          */
5302         pseudos : {
5303             "first-child" : function(c){
5304                 var r = [], ri = -1, n;
5305                 for(var i = 0, ci; ci = n = c[i]; i++){
5306                     while((n = n.previousSibling) && n.nodeType != 1);
5307                     if(!n){
5308                         r[++ri] = ci;
5309                     }
5310                 }
5311                 return r;
5312             },
5313
5314             "last-child" : function(c){
5315                 var r = [], ri = -1, n;
5316                 for(var i = 0, ci; ci = n = c[i]; i++){
5317                     while((n = n.nextSibling) && n.nodeType != 1);
5318                     if(!n){
5319                         r[++ri] = ci;
5320                     }
5321                 }
5322                 return r;
5323             },
5324
5325             "nth-child" : function(c, a) {
5326                 var r = [], ri = -1;
5327                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5328                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5329                 for(var i = 0, n; n = c[i]; i++){
5330                     var pn = n.parentNode;
5331                     if (batch != pn._batch) {
5332                         var j = 0;
5333                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5334                             if(cn.nodeType == 1){
5335                                cn.nodeIndex = ++j;
5336                             }
5337                         }
5338                         pn._batch = batch;
5339                     }
5340                     if (f == 1) {
5341                         if (l == 0 || n.nodeIndex == l){
5342                             r[++ri] = n;
5343                         }
5344                     } else if ((n.nodeIndex + l) % f == 0){
5345                         r[++ri] = n;
5346                     }
5347                 }
5348
5349                 return r;
5350             },
5351
5352             "only-child" : function(c){
5353                 var r = [], ri = -1;;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(!prev(ci) && !next(ci)){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "empty" : function(c){
5363                 var r = [], ri = -1;
5364                 for(var i = 0, ci; ci = c[i]; i++){
5365                     var cns = ci.childNodes, j = 0, cn, empty = true;
5366                     while(cn = cns[j]){
5367                         ++j;
5368                         if(cn.nodeType == 1 || cn.nodeType == 3){
5369                             empty = false;
5370                             break;
5371                         }
5372                     }
5373                     if(empty){
5374                         r[++ri] = ci;
5375                     }
5376                 }
5377                 return r;
5378             },
5379
5380             "contains" : function(c, v){
5381                 var r = [], ri = -1;
5382                 for(var i = 0, ci; ci = c[i]; i++){
5383                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5384                         r[++ri] = ci;
5385                     }
5386                 }
5387                 return r;
5388             },
5389
5390             "nodeValue" : function(c, v){
5391                 var r = [], ri = -1;
5392                 for(var i = 0, ci; ci = c[i]; i++){
5393                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5394                         r[++ri] = ci;
5395                     }
5396                 }
5397                 return r;
5398             },
5399
5400             "checked" : function(c){
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     if(ci.checked == true){
5404                         r[++ri] = ci;
5405                     }
5406                 }
5407                 return r;
5408             },
5409
5410             "not" : function(c, ss){
5411                 return Roo.DomQuery.filter(c, ss, true);
5412             },
5413
5414             "odd" : function(c){
5415                 return this["nth-child"](c, "odd");
5416             },
5417
5418             "even" : function(c){
5419                 return this["nth-child"](c, "even");
5420             },
5421
5422             "nth" : function(c, a){
5423                 return c[a-1] || [];
5424             },
5425
5426             "first" : function(c){
5427                 return c[0] || [];
5428             },
5429
5430             "last" : function(c){
5431                 return c[c.length-1] || [];
5432             },
5433
5434             "has" : function(c, ss){
5435                 var s = Roo.DomQuery.select;
5436                 var r = [], ri = -1;
5437                 for(var i = 0, ci; ci = c[i]; i++){
5438                     if(s(ss, ci).length > 0){
5439                         r[++ri] = ci;
5440                     }
5441                 }
5442                 return r;
5443             },
5444
5445             "next" : function(c, ss){
5446                 var is = Roo.DomQuery.is;
5447                 var r = [], ri = -1;
5448                 for(var i = 0, ci; ci = c[i]; i++){
5449                     var n = next(ci);
5450                     if(n && is(n, ss)){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "prev" : function(c, ss){
5458                 var is = Roo.DomQuery.is;
5459                 var r = [], ri = -1;
5460                 for(var i = 0, ci; ci = c[i]; i++){
5461                     var n = prev(ci);
5462                     if(n && is(n, ss)){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             }
5468         }
5469     };
5470 }();
5471
5472 /**
5473  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5474  * @param {String} path The selector/xpath query
5475  * @param {Node} root (optional) The start of the query (defaults to document).
5476  * @return {Array}
5477  * @member Roo
5478  * @method query
5479  */
5480 Roo.query = Roo.DomQuery.select;
5481 /*
5482  * Based on:
5483  * Ext JS Library 1.1.1
5484  * Copyright(c) 2006-2007, Ext JS, LLC.
5485  *
5486  * Originally Released Under LGPL - original licence link has changed is not relivant.
5487  *
5488  * Fork - LGPL
5489  * <script type="text/javascript">
5490  */
5491
5492 /**
5493  * @class Roo.util.Observable
5494  * Base class that provides a common interface for publishing events. Subclasses are expected to
5495  * to have a property "events" with all the events defined.<br>
5496  * For example:
5497  * <pre><code>
5498  Employee = function(name){
5499     this.name = name;
5500     this.addEvents({
5501         "fired" : true,
5502         "quit" : true
5503     });
5504  }
5505  Roo.extend(Employee, Roo.util.Observable);
5506 </code></pre>
5507  * @param {Object} config properties to use (incuding events / listeners)
5508  */
5509
5510 Roo.util.Observable = function(cfg){
5511     
5512     cfg = cfg|| {};
5513     this.addEvents(cfg.events || {});
5514     if (cfg.events) {
5515         delete cfg.events; // make sure
5516     }
5517      
5518     Roo.apply(this, cfg);
5519     
5520     if(this.listeners){
5521         this.on(this.listeners);
5522         delete this.listeners;
5523     }
5524 };
5525 Roo.util.Observable.prototype = {
5526     /** 
5527  * @cfg {Object} listeners  list of events and functions to call for this object, 
5528  * For example :
5529  * <pre><code>
5530     listeners :  { 
5531        'click' : function(e) {
5532            ..... 
5533         } ,
5534         .... 
5535     } 
5536   </code></pre>
5537  */
5538     
5539     
5540     /**
5541      * Fires the specified event with the passed parameters (minus the event name).
5542      * @param {String} eventName
5543      * @param {Object...} args Variable number of parameters are passed to handlers
5544      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5545      */
5546     fireEvent : function(){
5547         var ce = this.events[arguments[0].toLowerCase()];
5548         if(typeof ce == "object"){
5549             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5550         }else{
5551             return true;
5552         }
5553     },
5554
5555     // private
5556     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5557
5558     /**
5559      * Appends an event handler to this component
5560      * @param {String}   eventName The type of event to listen for
5561      * @param {Function} handler The method the event invokes
5562      * @param {Object}   scope (optional) The scope in which to execute the handler
5563      * function. The handler function's "this" context.
5564      * @param {Object}   options (optional) An object containing handler configuration
5565      * properties. This may contain any of the following properties:<ul>
5566      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5567      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5568      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5569      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5570      * by the specified number of milliseconds. If the event fires again within that time, the original
5571      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5572      * </ul><br>
5573      * <p>
5574      * <b>Combining Options</b><br>
5575      * Using the options argument, it is possible to combine different types of listeners:<br>
5576      * <br>
5577      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5578                 <pre><code>
5579                 el.on('click', this.onClick, this, {
5580                         single: true,
5581                 delay: 100,
5582                 forumId: 4
5583                 });
5584                 </code></pre>
5585      * <p>
5586      * <b>Attaching multiple handlers in 1 call</b><br>
5587      * The method also allows for a single argument to be passed which is a config object containing properties
5588      * which specify multiple handlers.
5589      * <pre><code>
5590                 el.on({
5591                         'click': {
5592                         fn: this.onClick,
5593                         scope: this,
5594                         delay: 100
5595                 }, 
5596                 'mouseover': {
5597                         fn: this.onMouseOver,
5598                         scope: this
5599                 },
5600                 'mouseout': {
5601                         fn: this.onMouseOut,
5602                         scope: this
5603                 }
5604                 });
5605                 </code></pre>
5606      * <p>
5607      * Or a shorthand syntax which passes the same scope object to all handlers:
5608         <pre><code>
5609                 el.on({
5610                         'click': this.onClick,
5611                 'mouseover': this.onMouseOver,
5612                 'mouseout': this.onMouseOut,
5613                 scope: this
5614                 });
5615                 </code></pre>
5616      */
5617     addListener : function(eventName, fn, scope, o){
5618         if(typeof eventName == "object"){
5619             o = eventName;
5620             for(var e in o){
5621                 if(this.filterOptRe.test(e)){
5622                     continue;
5623                 }
5624                 if(typeof o[e] == "function"){
5625                     // shared options
5626                     this.addListener(e, o[e], o.scope,  o);
5627                 }else{
5628                     // individual options
5629                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5630                 }
5631             }
5632             return;
5633         }
5634         o = (!o || typeof o == "boolean") ? {} : o;
5635         eventName = eventName.toLowerCase();
5636         var ce = this.events[eventName] || true;
5637         if(typeof ce == "boolean"){
5638             ce = new Roo.util.Event(this, eventName);
5639             this.events[eventName] = ce;
5640         }
5641         ce.addListener(fn, scope, o);
5642     },
5643
5644     /**
5645      * Removes a listener
5646      * @param {String}   eventName     The type of event to listen for
5647      * @param {Function} handler        The handler to remove
5648      * @param {Object}   scope  (optional) The scope (this object) for the handler
5649      */
5650     removeListener : function(eventName, fn, scope){
5651         var ce = this.events[eventName.toLowerCase()];
5652         if(typeof ce == "object"){
5653             ce.removeListener(fn, scope);
5654         }
5655     },
5656
5657     /**
5658      * Removes all listeners for this object
5659      */
5660     purgeListeners : function(){
5661         for(var evt in this.events){
5662             if(typeof this.events[evt] == "object"){
5663                  this.events[evt].clearListeners();
5664             }
5665         }
5666     },
5667
5668     relayEvents : function(o, events){
5669         var createHandler = function(ename){
5670             return function(){
5671                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5672             };
5673         };
5674         for(var i = 0, len = events.length; i < len; i++){
5675             var ename = events[i];
5676             if(!this.events[ename]){ this.events[ename] = true; };
5677             o.on(ename, createHandler(ename), this);
5678         }
5679     },
5680
5681     /**
5682      * Used to define events on this Observable
5683      * @param {Object} object The object with the events defined
5684      */
5685     addEvents : function(o){
5686         if(!this.events){
5687             this.events = {};
5688         }
5689         Roo.applyIf(this.events, o);
5690     },
5691
5692     /**
5693      * Checks to see if this object has any listeners for a specified event
5694      * @param {String} eventName The name of the event to check for
5695      * @return {Boolean} True if the event is being listened for, else false
5696      */
5697     hasListener : function(eventName){
5698         var e = this.events[eventName];
5699         return typeof e == "object" && e.listeners.length > 0;
5700     }
5701 };
5702 /**
5703  * Appends an event handler to this element (shorthand for addListener)
5704  * @param {String}   eventName     The type of event to listen for
5705  * @param {Function} handler        The method the event invokes
5706  * @param {Object}   scope (optional) The scope in which to execute the handler
5707  * function. The handler function's "this" context.
5708  * @param {Object}   options  (optional)
5709  * @method
5710  */
5711 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5712 /**
5713  * Removes a listener (shorthand for removeListener)
5714  * @param {String}   eventName     The type of event to listen for
5715  * @param {Function} handler        The handler to remove
5716  * @param {Object}   scope  (optional) The scope (this object) for the handler
5717  * @method
5718  */
5719 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5720
5721 /**
5722  * Starts capture on the specified Observable. All events will be passed
5723  * to the supplied function with the event name + standard signature of the event
5724  * <b>before</b> the event is fired. If the supplied function returns false,
5725  * the event will not fire.
5726  * @param {Observable} o The Observable to capture
5727  * @param {Function} fn The function to call
5728  * @param {Object} scope (optional) The scope (this object) for the fn
5729  * @static
5730  */
5731 Roo.util.Observable.capture = function(o, fn, scope){
5732     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5733 };
5734
5735 /**
5736  * Removes <b>all</b> added captures from the Observable.
5737  * @param {Observable} o The Observable to release
5738  * @static
5739  */
5740 Roo.util.Observable.releaseCapture = function(o){
5741     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5742 };
5743
5744 (function(){
5745
5746     var createBuffered = function(h, o, scope){
5747         var task = new Roo.util.DelayedTask();
5748         return function(){
5749             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5750         };
5751     };
5752
5753     var createSingle = function(h, e, fn, scope){
5754         return function(){
5755             e.removeListener(fn, scope);
5756             return h.apply(scope, arguments);
5757         };
5758     };
5759
5760     var createDelayed = function(h, o, scope){
5761         return function(){
5762             var args = Array.prototype.slice.call(arguments, 0);
5763             setTimeout(function(){
5764                 h.apply(scope, args);
5765             }, o.delay || 10);
5766         };
5767     };
5768
5769     Roo.util.Event = function(obj, name){
5770         this.name = name;
5771         this.obj = obj;
5772         this.listeners = [];
5773     };
5774
5775     Roo.util.Event.prototype = {
5776         addListener : function(fn, scope, options){
5777             var o = options || {};
5778             scope = scope || this.obj;
5779             if(!this.isListening(fn, scope)){
5780                 var l = {fn: fn, scope: scope, options: o};
5781                 var h = fn;
5782                 if(o.delay){
5783                     h = createDelayed(h, o, scope);
5784                 }
5785                 if(o.single){
5786                     h = createSingle(h, this, fn, scope);
5787                 }
5788                 if(o.buffer){
5789                     h = createBuffered(h, o, scope);
5790                 }
5791                 l.fireFn = h;
5792                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5793                     this.listeners.push(l);
5794                 }else{
5795                     this.listeners = this.listeners.slice(0);
5796                     this.listeners.push(l);
5797                 }
5798             }
5799         },
5800
5801         findListener : function(fn, scope){
5802             scope = scope || this.obj;
5803             var ls = this.listeners;
5804             for(var i = 0, len = ls.length; i < len; i++){
5805                 var l = ls[i];
5806                 if(l.fn == fn && l.scope == scope){
5807                     return i;
5808                 }
5809             }
5810             return -1;
5811         },
5812
5813         isListening : function(fn, scope){
5814             return this.findListener(fn, scope) != -1;
5815         },
5816
5817         removeListener : function(fn, scope){
5818             var index;
5819             if((index = this.findListener(fn, scope)) != -1){
5820                 if(!this.firing){
5821                     this.listeners.splice(index, 1);
5822                 }else{
5823                     this.listeners = this.listeners.slice(0);
5824                     this.listeners.splice(index, 1);
5825                 }
5826                 return true;
5827             }
5828             return false;
5829         },
5830
5831         clearListeners : function(){
5832             this.listeners = [];
5833         },
5834
5835         fire : function(){
5836             var ls = this.listeners, scope, len = ls.length;
5837             if(len > 0){
5838                 this.firing = true;
5839                 var args = Array.prototype.slice.call(arguments, 0);
5840                 for(var i = 0; i < len; i++){
5841                     var l = ls[i];
5842                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5843                         this.firing = false;
5844                         return false;
5845                     }
5846                 }
5847                 this.firing = false;
5848             }
5849             return true;
5850         }
5851     };
5852 })();/*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862
5863 /**
5864  * @class Roo.EventManager
5865  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5866  * several useful events directly.
5867  * See {@link Roo.EventObject} for more details on normalized event objects.
5868  * @singleton
5869  */
5870 Roo.EventManager = function(){
5871     var docReadyEvent, docReadyProcId, docReadyState = false;
5872     var resizeEvent, resizeTask, textEvent, textSize;
5873     var E = Roo.lib.Event;
5874     var D = Roo.lib.Dom;
5875
5876
5877     var fireDocReady = function(){
5878         if(!docReadyState){
5879             docReadyState = true;
5880             Roo.isReady = true;
5881             if(docReadyProcId){
5882                 clearInterval(docReadyProcId);
5883             }
5884             if(Roo.isGecko || Roo.isOpera) {
5885                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5886             }
5887             if(Roo.isIE){
5888                 var defer = document.getElementById("ie-deferred-loader");
5889                 if(defer){
5890                     defer.onreadystatechange = null;
5891                     defer.parentNode.removeChild(defer);
5892                 }
5893             }
5894             if(docReadyEvent){
5895                 docReadyEvent.fire();
5896                 docReadyEvent.clearListeners();
5897             }
5898         }
5899     };
5900     
5901     var initDocReady = function(){
5902         docReadyEvent = new Roo.util.Event();
5903         if(Roo.isGecko || Roo.isOpera) {
5904             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5905         }else if(Roo.isIE){
5906             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5907             var defer = document.getElementById("ie-deferred-loader");
5908             defer.onreadystatechange = function(){
5909                 if(this.readyState == "complete"){
5910                     fireDocReady();
5911                 }
5912             };
5913         }else if(Roo.isSafari){ 
5914             docReadyProcId = setInterval(function(){
5915                 var rs = document.readyState;
5916                 if(rs == "complete") {
5917                     fireDocReady();     
5918                  }
5919             }, 10);
5920         }
5921         // no matter what, make sure it fires on load
5922         E.on(window, "load", fireDocReady);
5923     };
5924
5925     var createBuffered = function(h, o){
5926         var task = new Roo.util.DelayedTask(h);
5927         return function(e){
5928             // create new event object impl so new events don't wipe out properties
5929             e = new Roo.EventObjectImpl(e);
5930             task.delay(o.buffer, h, null, [e]);
5931         };
5932     };
5933
5934     var createSingle = function(h, el, ename, fn){
5935         return function(e){
5936             Roo.EventManager.removeListener(el, ename, fn);
5937             h(e);
5938         };
5939     };
5940
5941     var createDelayed = function(h, o){
5942         return function(e){
5943             // create new event object impl so new events don't wipe out properties
5944             e = new Roo.EventObjectImpl(e);
5945             setTimeout(function(){
5946                 h(e);
5947             }, o.delay || 10);
5948         };
5949     };
5950
5951     var listen = function(element, ename, opt, fn, scope){
5952         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5953         fn = fn || o.fn; scope = scope || o.scope;
5954         var el = Roo.getDom(element);
5955         if(!el){
5956             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5957         }
5958         var h = function(e){
5959             e = Roo.EventObject.setEvent(e);
5960             var t;
5961             if(o.delegate){
5962                 t = e.getTarget(o.delegate, el);
5963                 if(!t){
5964                     return;
5965                 }
5966             }else{
5967                 t = e.target;
5968             }
5969             if(o.stopEvent === true){
5970                 e.stopEvent();
5971             }
5972             if(o.preventDefault === true){
5973                e.preventDefault();
5974             }
5975             if(o.stopPropagation === true){
5976                 e.stopPropagation();
5977             }
5978
5979             if(o.normalized === false){
5980                 e = e.browserEvent;
5981             }
5982
5983             fn.call(scope || el, e, t, o);
5984         };
5985         if(o.delay){
5986             h = createDelayed(h, o);
5987         }
5988         if(o.single){
5989             h = createSingle(h, el, ename, fn);
5990         }
5991         if(o.buffer){
5992             h = createBuffered(h, o);
5993         }
5994         fn._handlers = fn._handlers || [];
5995         fn._handlers.push([Roo.id(el), ename, h]);
5996
5997         E.on(el, ename, h);
5998         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5999             el.addEventListener("DOMMouseScroll", h, false);
6000             E.on(window, 'unload', function(){
6001                 el.removeEventListener("DOMMouseScroll", h, false);
6002             });
6003         }
6004         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6005             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6006         }
6007         return h;
6008     };
6009
6010     var stopListening = function(el, ename, fn){
6011         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6012         if(hds){
6013             for(var i = 0, len = hds.length; i < len; i++){
6014                 var h = hds[i];
6015                 if(h[0] == id && h[1] == ename){
6016                     hd = h[2];
6017                     hds.splice(i, 1);
6018                     break;
6019                 }
6020             }
6021         }
6022         E.un(el, ename, hd);
6023         el = Roo.getDom(el);
6024         if(ename == "mousewheel" && el.addEventListener){
6025             el.removeEventListener("DOMMouseScroll", hd, false);
6026         }
6027         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6028             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6029         }
6030     };
6031
6032     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6033     
6034     var pub = {
6035         
6036         
6037         /** 
6038          * Fix for doc tools
6039          * @scope Roo.EventManager
6040          */
6041         
6042         
6043         /** 
6044          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6045          * object with a Roo.EventObject
6046          * @param {Function} fn        The method the event invokes
6047          * @param {Object}   scope    An object that becomes the scope of the handler
6048          * @param {boolean}  override If true, the obj passed in becomes
6049          *                             the execution scope of the listener
6050          * @return {Function} The wrapped function
6051          * @deprecated
6052          */
6053         wrap : function(fn, scope, override){
6054             return function(e){
6055                 Roo.EventObject.setEvent(e);
6056                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6057             };
6058         },
6059         
6060         /**
6061      * Appends an event handler to an element (shorthand for addListener)
6062      * @param {String/HTMLElement}   element        The html element or id to assign the
6063      * @param {String}   eventName The type of event to listen for
6064      * @param {Function} handler The method the event invokes
6065      * @param {Object}   scope (optional) The scope in which to execute the handler
6066      * function. The handler function's "this" context.
6067      * @param {Object}   options (optional) An object containing handler configuration
6068      * properties. This may contain any of the following properties:<ul>
6069      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6070      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6071      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6072      * <li>preventDefault {Boolean} True to prevent the default action</li>
6073      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6074      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6075      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6076      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6077      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6078      * by the specified number of milliseconds. If the event fires again within that time, the original
6079      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6080      * </ul><br>
6081      * <p>
6082      * <b>Combining Options</b><br>
6083      * Using the options argument, it is possible to combine different types of listeners:<br>
6084      * <br>
6085      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6086      * Code:<pre><code>
6087 el.on('click', this.onClick, this, {
6088     single: true,
6089     delay: 100,
6090     stopEvent : true,
6091     forumId: 4
6092 });</code></pre>
6093      * <p>
6094      * <b>Attaching multiple handlers in 1 call</b><br>
6095       * The method also allows for a single argument to be passed which is a config object containing properties
6096      * which specify multiple handlers.
6097      * <p>
6098      * Code:<pre><code>
6099 el.on({
6100     'click' : {
6101         fn: this.onClick
6102         scope: this,
6103         delay: 100
6104     },
6105     'mouseover' : {
6106         fn: this.onMouseOver
6107         scope: this
6108     },
6109     'mouseout' : {
6110         fn: this.onMouseOut
6111         scope: this
6112     }
6113 });</code></pre>
6114      * <p>
6115      * Or a shorthand syntax:<br>
6116      * Code:<pre><code>
6117 el.on({
6118     'click' : this.onClick,
6119     'mouseover' : this.onMouseOver,
6120     'mouseout' : this.onMouseOut
6121     scope: this
6122 });</code></pre>
6123      */
6124         addListener : function(element, eventName, fn, scope, options){
6125             if(typeof eventName == "object"){
6126                 var o = eventName;
6127                 for(var e in o){
6128                     if(propRe.test(e)){
6129                         continue;
6130                     }
6131                     if(typeof o[e] == "function"){
6132                         // shared options
6133                         listen(element, e, o, o[e], o.scope);
6134                     }else{
6135                         // individual options
6136                         listen(element, e, o[e]);
6137                     }
6138                 }
6139                 return;
6140             }
6141             return listen(element, eventName, options, fn, scope);
6142         },
6143         
6144         /**
6145          * Removes an event handler
6146          *
6147          * @param {String/HTMLElement}   element        The id or html element to remove the 
6148          *                             event from
6149          * @param {String}   eventName     The type of event
6150          * @param {Function} fn
6151          * @return {Boolean} True if a listener was actually removed
6152          */
6153         removeListener : function(element, eventName, fn){
6154             return stopListening(element, eventName, fn);
6155         },
6156         
6157         /**
6158          * Fires when the document is ready (before onload and before images are loaded). Can be 
6159          * accessed shorthanded Roo.onReady().
6160          * @param {Function} fn        The method the event invokes
6161          * @param {Object}   scope    An  object that becomes the scope of the handler
6162          * @param {boolean}  options
6163          */
6164         onDocumentReady : function(fn, scope, options){
6165             if(docReadyState){ // if it already fired
6166                 docReadyEvent.addListener(fn, scope, options);
6167                 docReadyEvent.fire();
6168                 docReadyEvent.clearListeners();
6169                 return;
6170             }
6171             if(!docReadyEvent){
6172                 initDocReady();
6173             }
6174             docReadyEvent.addListener(fn, scope, options);
6175         },
6176         
6177         /**
6178          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6179          * @param {Function} fn        The method the event invokes
6180          * @param {Object}   scope    An object that becomes the scope of the handler
6181          * @param {boolean}  options
6182          */
6183         onWindowResize : function(fn, scope, options){
6184             if(!resizeEvent){
6185                 resizeEvent = new Roo.util.Event();
6186                 resizeTask = new Roo.util.DelayedTask(function(){
6187                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6188                 });
6189                 E.on(window, "resize", function(){
6190                     if(Roo.isIE){
6191                         resizeTask.delay(50);
6192                     }else{
6193                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6194                     }
6195                 });
6196             }
6197             resizeEvent.addListener(fn, scope, options);
6198         },
6199
6200         /**
6201          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6202          * @param {Function} fn        The method the event invokes
6203          * @param {Object}   scope    An object that becomes the scope of the handler
6204          * @param {boolean}  options
6205          */
6206         onTextResize : function(fn, scope, options){
6207             if(!textEvent){
6208                 textEvent = new Roo.util.Event();
6209                 var textEl = new Roo.Element(document.createElement('div'));
6210                 textEl.dom.className = 'x-text-resize';
6211                 textEl.dom.innerHTML = 'X';
6212                 textEl.appendTo(document.body);
6213                 textSize = textEl.dom.offsetHeight;
6214                 setInterval(function(){
6215                     if(textEl.dom.offsetHeight != textSize){
6216                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6217                     }
6218                 }, this.textResizeInterval);
6219             }
6220             textEvent.addListener(fn, scope, options);
6221         },
6222
6223         /**
6224          * Removes the passed window resize listener.
6225          * @param {Function} fn        The method the event invokes
6226          * @param {Object}   scope    The scope of handler
6227          */
6228         removeResizeListener : function(fn, scope){
6229             if(resizeEvent){
6230                 resizeEvent.removeListener(fn, scope);
6231             }
6232         },
6233
6234         // private
6235         fireResize : function(){
6236             if(resizeEvent){
6237                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6238             }   
6239         },
6240         /**
6241          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6242          */
6243         ieDeferSrc : false,
6244         /**
6245          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6246          */
6247         textResizeInterval : 50
6248     };
6249     
6250     /**
6251      * Fix for doc tools
6252      * @scopeAlias pub=Roo.EventManager
6253      */
6254     
6255      /**
6256      * Appends an event handler to an element (shorthand for addListener)
6257      * @param {String/HTMLElement}   element        The html element or id to assign the
6258      * @param {String}   eventName The type of event to listen for
6259      * @param {Function} handler The method the event invokes
6260      * @param {Object}   scope (optional) The scope in which to execute the handler
6261      * function. The handler function's "this" context.
6262      * @param {Object}   options (optional) An object containing handler configuration
6263      * properties. This may contain any of the following properties:<ul>
6264      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6265      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6266      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6267      * <li>preventDefault {Boolean} True to prevent the default action</li>
6268      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6269      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6270      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6271      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6272      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6273      * by the specified number of milliseconds. If the event fires again within that time, the original
6274      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6275      * </ul><br>
6276      * <p>
6277      * <b>Combining Options</b><br>
6278      * Using the options argument, it is possible to combine different types of listeners:<br>
6279      * <br>
6280      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6281      * Code:<pre><code>
6282 el.on('click', this.onClick, this, {
6283     single: true,
6284     delay: 100,
6285     stopEvent : true,
6286     forumId: 4
6287 });</code></pre>
6288      * <p>
6289      * <b>Attaching multiple handlers in 1 call</b><br>
6290       * The method also allows for a single argument to be passed which is a config object containing properties
6291      * which specify multiple handlers.
6292      * <p>
6293      * Code:<pre><code>
6294 el.on({
6295     'click' : {
6296         fn: this.onClick
6297         scope: this,
6298         delay: 100
6299     },
6300     'mouseover' : {
6301         fn: this.onMouseOver
6302         scope: this
6303     },
6304     'mouseout' : {
6305         fn: this.onMouseOut
6306         scope: this
6307     }
6308 });</code></pre>
6309      * <p>
6310      * Or a shorthand syntax:<br>
6311      * Code:<pre><code>
6312 el.on({
6313     'click' : this.onClick,
6314     'mouseover' : this.onMouseOver,
6315     'mouseout' : this.onMouseOut
6316     scope: this
6317 });</code></pre>
6318      */
6319     pub.on = pub.addListener;
6320     pub.un = pub.removeListener;
6321
6322     pub.stoppedMouseDownEvent = new Roo.util.Event();
6323     return pub;
6324 }();
6325 /**
6326   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6327   * @param {Function} fn        The method the event invokes
6328   * @param {Object}   scope    An  object that becomes the scope of the handler
6329   * @param {boolean}  override If true, the obj passed in becomes
6330   *                             the execution scope of the listener
6331   * @member Roo
6332   * @method onReady
6333  */
6334 Roo.onReady = Roo.EventManager.onDocumentReady;
6335
6336 Roo.onReady(function(){
6337     var bd = Roo.get(document.body);
6338     if(!bd){ return; }
6339
6340     var cls = [
6341             Roo.isIE ? "roo-ie"
6342             : Roo.isGecko ? "roo-gecko"
6343             : Roo.isOpera ? "roo-opera"
6344             : Roo.isSafari ? "roo-safari" : ""];
6345
6346     if(Roo.isMac){
6347         cls.push("roo-mac");
6348     }
6349     if(Roo.isLinux){
6350         cls.push("roo-linux");
6351     }
6352     if(Roo.isBorderBox){
6353         cls.push('roo-border-box');
6354     }
6355     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6356         var p = bd.dom.parentNode;
6357         if(p){
6358             p.className += ' roo-strict';
6359         }
6360     }
6361     bd.addClass(cls.join(' '));
6362 });
6363
6364 /**
6365  * @class Roo.EventObject
6366  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6367  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6368  * Example:
6369  * <pre><code>
6370  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6371     e.preventDefault();
6372     var target = e.getTarget();
6373     ...
6374  }
6375  var myDiv = Roo.get("myDiv");
6376  myDiv.on("click", handleClick);
6377  //or
6378  Roo.EventManager.on("myDiv", 'click', handleClick);
6379  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6380  </code></pre>
6381  * @singleton
6382  */
6383 Roo.EventObject = function(){
6384     
6385     var E = Roo.lib.Event;
6386     
6387     // safari keypress events for special keys return bad keycodes
6388     var safariKeys = {
6389         63234 : 37, // left
6390         63235 : 39, // right
6391         63232 : 38, // up
6392         63233 : 40, // down
6393         63276 : 33, // page up
6394         63277 : 34, // page down
6395         63272 : 46, // delete
6396         63273 : 36, // home
6397         63275 : 35  // end
6398     };
6399
6400     // normalize button clicks
6401     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6402                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6403
6404     Roo.EventObjectImpl = function(e){
6405         if(e){
6406             this.setEvent(e.browserEvent || e);
6407         }
6408     };
6409     Roo.EventObjectImpl.prototype = {
6410         /**
6411          * Used to fix doc tools.
6412          * @scope Roo.EventObject.prototype
6413          */
6414             
6415
6416         
6417         
6418         /** The normal browser event */
6419         browserEvent : null,
6420         /** The button pressed in a mouse event */
6421         button : -1,
6422         /** True if the shift key was down during the event */
6423         shiftKey : false,
6424         /** True if the control key was down during the event */
6425         ctrlKey : false,
6426         /** True if the alt key was down during the event */
6427         altKey : false,
6428
6429         /** Key constant 
6430         * @type Number */
6431         BACKSPACE : 8,
6432         /** Key constant 
6433         * @type Number */
6434         TAB : 9,
6435         /** Key constant 
6436         * @type Number */
6437         RETURN : 13,
6438         /** Key constant 
6439         * @type Number */
6440         ENTER : 13,
6441         /** Key constant 
6442         * @type Number */
6443         SHIFT : 16,
6444         /** Key constant 
6445         * @type Number */
6446         CONTROL : 17,
6447         /** Key constant 
6448         * @type Number */
6449         ESC : 27,
6450         /** Key constant 
6451         * @type Number */
6452         SPACE : 32,
6453         /** Key constant 
6454         * @type Number */
6455         PAGEUP : 33,
6456         /** Key constant 
6457         * @type Number */
6458         PAGEDOWN : 34,
6459         /** Key constant 
6460         * @type Number */
6461         END : 35,
6462         /** Key constant 
6463         * @type Number */
6464         HOME : 36,
6465         /** Key constant 
6466         * @type Number */
6467         LEFT : 37,
6468         /** Key constant 
6469         * @type Number */
6470         UP : 38,
6471         /** Key constant 
6472         * @type Number */
6473         RIGHT : 39,
6474         /** Key constant 
6475         * @type Number */
6476         DOWN : 40,
6477         /** Key constant 
6478         * @type Number */
6479         DELETE : 46,
6480         /** Key constant 
6481         * @type Number */
6482         F5 : 116,
6483
6484            /** @private */
6485         setEvent : function(e){
6486             if(e == this || (e && e.browserEvent)){ // already wrapped
6487                 return e;
6488             }
6489             this.browserEvent = e;
6490             if(e){
6491                 // normalize buttons
6492                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6493                 if(e.type == 'click' && this.button == -1){
6494                     this.button = 0;
6495                 }
6496                 this.type = e.type;
6497                 this.shiftKey = e.shiftKey;
6498                 // mac metaKey behaves like ctrlKey
6499                 this.ctrlKey = e.ctrlKey || e.metaKey;
6500                 this.altKey = e.altKey;
6501                 // in getKey these will be normalized for the mac
6502                 this.keyCode = e.keyCode;
6503                 // keyup warnings on firefox.
6504                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6505                 // cache the target for the delayed and or buffered events
6506                 this.target = E.getTarget(e);
6507                 // same for XY
6508                 this.xy = E.getXY(e);
6509             }else{
6510                 this.button = -1;
6511                 this.shiftKey = false;
6512                 this.ctrlKey = false;
6513                 this.altKey = false;
6514                 this.keyCode = 0;
6515                 this.charCode =0;
6516                 this.target = null;
6517                 this.xy = [0, 0];
6518             }
6519             return this;
6520         },
6521
6522         /**
6523          * Stop the event (preventDefault and stopPropagation)
6524          */
6525         stopEvent : function(){
6526             if(this.browserEvent){
6527                 if(this.browserEvent.type == 'mousedown'){
6528                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6529                 }
6530                 E.stopEvent(this.browserEvent);
6531             }
6532         },
6533
6534         /**
6535          * Prevents the browsers default handling of the event.
6536          */
6537         preventDefault : function(){
6538             if(this.browserEvent){
6539                 E.preventDefault(this.browserEvent);
6540             }
6541         },
6542
6543         /** @private */
6544         isNavKeyPress : function(){
6545             var k = this.keyCode;
6546             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6547             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6548         },
6549
6550         isSpecialKey : function(){
6551             var k = this.keyCode;
6552             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6553             (k == 16) || (k == 17) ||
6554             (k >= 18 && k <= 20) ||
6555             (k >= 33 && k <= 35) ||
6556             (k >= 36 && k <= 39) ||
6557             (k >= 44 && k <= 45);
6558         },
6559         /**
6560          * Cancels bubbling of the event.
6561          */
6562         stopPropagation : function(){
6563             if(this.browserEvent){
6564                 if(this.type == 'mousedown'){
6565                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6566                 }
6567                 E.stopPropagation(this.browserEvent);
6568             }
6569         },
6570
6571         /**
6572          * Gets the key code for the event.
6573          * @return {Number}
6574          */
6575         getCharCode : function(){
6576             return this.charCode || this.keyCode;
6577         },
6578
6579         /**
6580          * Returns a normalized keyCode for the event.
6581          * @return {Number} The key code
6582          */
6583         getKey : function(){
6584             var k = this.keyCode || this.charCode;
6585             return Roo.isSafari ? (safariKeys[k] || k) : k;
6586         },
6587
6588         /**
6589          * Gets the x coordinate of the event.
6590          * @return {Number}
6591          */
6592         getPageX : function(){
6593             return this.xy[0];
6594         },
6595
6596         /**
6597          * Gets the y coordinate of the event.
6598          * @return {Number}
6599          */
6600         getPageY : function(){
6601             return this.xy[1];
6602         },
6603
6604         /**
6605          * Gets the time of the event.
6606          * @return {Number}
6607          */
6608         getTime : function(){
6609             if(this.browserEvent){
6610                 return E.getTime(this.browserEvent);
6611             }
6612             return null;
6613         },
6614
6615         /**
6616          * Gets the page coordinates of the event.
6617          * @return {Array} The xy values like [x, y]
6618          */
6619         getXY : function(){
6620             return this.xy;
6621         },
6622
6623         /**
6624          * Gets the target for the event.
6625          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6626          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6627                 search as a number or element (defaults to 10 || document.body)
6628          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6629          * @return {HTMLelement}
6630          */
6631         getTarget : function(selector, maxDepth, returnEl){
6632             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6633         },
6634         /**
6635          * Gets the related target.
6636          * @return {HTMLElement}
6637          */
6638         getRelatedTarget : function(){
6639             if(this.browserEvent){
6640                 return E.getRelatedTarget(this.browserEvent);
6641             }
6642             return null;
6643         },
6644
6645         /**
6646          * Normalizes mouse wheel delta across browsers
6647          * @return {Number} The delta
6648          */
6649         getWheelDelta : function(){
6650             var e = this.browserEvent;
6651             var delta = 0;
6652             if(e.wheelDelta){ /* IE/Opera. */
6653                 delta = e.wheelDelta/120;
6654             }else if(e.detail){ /* Mozilla case. */
6655                 delta = -e.detail/3;
6656             }
6657             return delta;
6658         },
6659
6660         /**
6661          * Returns true if the control, meta, shift or alt key was pressed during this event.
6662          * @return {Boolean}
6663          */
6664         hasModifier : function(){
6665             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6666         },
6667
6668         /**
6669          * Returns true if the target of this event equals el or is a child of el
6670          * @param {String/HTMLElement/Element} el
6671          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6672          * @return {Boolean}
6673          */
6674         within : function(el, related){
6675             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6676             return t && Roo.fly(el).contains(t);
6677         },
6678
6679         getPoint : function(){
6680             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6681         }
6682     };
6683
6684     return new Roo.EventObjectImpl();
6685 }();
6686             
6687     /*
6688  * Based on:
6689  * Ext JS Library 1.1.1
6690  * Copyright(c) 2006-2007, Ext JS, LLC.
6691  *
6692  * Originally Released Under LGPL - original licence link has changed is not relivant.
6693  *
6694  * Fork - LGPL
6695  * <script type="text/javascript">
6696  */
6697
6698  
6699 // was in Composite Element!??!?!
6700  
6701 (function(){
6702     var D = Roo.lib.Dom;
6703     var E = Roo.lib.Event;
6704     var A = Roo.lib.Anim;
6705
6706     // local style camelizing for speed
6707     var propCache = {};
6708     var camelRe = /(-[a-z])/gi;
6709     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6710     var view = document.defaultView;
6711
6712 /**
6713  * @class Roo.Element
6714  * Represents an Element in the DOM.<br><br>
6715  * Usage:<br>
6716 <pre><code>
6717 var el = Roo.get("my-div");
6718
6719 // or with getEl
6720 var el = getEl("my-div");
6721
6722 // or with a DOM element
6723 var el = Roo.get(myDivElement);
6724 </code></pre>
6725  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6726  * each call instead of constructing a new one.<br><br>
6727  * <b>Animations</b><br />
6728  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6729  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6730 <pre>
6731 Option    Default   Description
6732 --------- --------  ---------------------------------------------
6733 duration  .35       The duration of the animation in seconds
6734 easing    easeOut   The YUI easing method
6735 callback  none      A function to execute when the anim completes
6736 scope     this      The scope (this) of the callback function
6737 </pre>
6738 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6739 * manipulate the animation. Here's an example:
6740 <pre><code>
6741 var el = Roo.get("my-div");
6742
6743 // no animation
6744 el.setWidth(100);
6745
6746 // default animation
6747 el.setWidth(100, true);
6748
6749 // animation with some options set
6750 el.setWidth(100, {
6751     duration: 1,
6752     callback: this.foo,
6753     scope: this
6754 });
6755
6756 // using the "anim" property to get the Anim object
6757 var opt = {
6758     duration: 1,
6759     callback: this.foo,
6760     scope: this
6761 };
6762 el.setWidth(100, opt);
6763 ...
6764 if(opt.anim.isAnimated()){
6765     opt.anim.stop();
6766 }
6767 </code></pre>
6768 * <b> Composite (Collections of) Elements</b><br />
6769  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6770  * @constructor Create a new Element directly.
6771  * @param {String/HTMLElement} element
6772  * @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).
6773  */
6774     Roo.Element = function(element, forceNew){
6775         var dom = typeof element == "string" ?
6776                 document.getElementById(element) : element;
6777         if(!dom){ // invalid id/element
6778             return null;
6779         }
6780         var id = dom.id;
6781         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6782             return Roo.Element.cache[id];
6783         }
6784
6785         /**
6786          * The DOM element
6787          * @type HTMLElement
6788          */
6789         this.dom = dom;
6790
6791         /**
6792          * The DOM element ID
6793          * @type String
6794          */
6795         this.id = id || Roo.id(dom);
6796     };
6797
6798     var El = Roo.Element;
6799
6800     El.prototype = {
6801         /**
6802          * The element's default display mode  (defaults to "")
6803          * @type String
6804          */
6805         originalDisplay : "",
6806
6807         visibilityMode : 1,
6808         /**
6809          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6810          * @type String
6811          */
6812         defaultUnit : "px",
6813         /**
6814          * Sets the element's visibility mode. When setVisible() is called it
6815          * will use this to determine whether to set the visibility or the display property.
6816          * @param visMode Element.VISIBILITY or Element.DISPLAY
6817          * @return {Roo.Element} this
6818          */
6819         setVisibilityMode : function(visMode){
6820             this.visibilityMode = visMode;
6821             return this;
6822         },
6823         /**
6824          * Convenience method for setVisibilityMode(Element.DISPLAY)
6825          * @param {String} display (optional) What to set display to when visible
6826          * @return {Roo.Element} this
6827          */
6828         enableDisplayMode : function(display){
6829             this.setVisibilityMode(El.DISPLAY);
6830             if(typeof display != "undefined") this.originalDisplay = display;
6831             return this;
6832         },
6833
6834         /**
6835          * 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)
6836          * @param {String} selector The simple selector to test
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6841          */
6842         findParent : function(simpleSelector, maxDepth, returnEl){
6843             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6844             maxDepth = maxDepth || 50;
6845             if(typeof maxDepth != "number"){
6846                 stopEl = Roo.getDom(maxDepth);
6847                 maxDepth = 10;
6848             }
6849             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6850                 if(dq.is(p, simpleSelector)){
6851                     return returnEl ? Roo.get(p) : p;
6852                 }
6853                 depth++;
6854                 p = p.parentNode;
6855             }
6856             return null;
6857         },
6858
6859
6860         /**
6861          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6862          * @param {String} selector The simple selector to test
6863          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6864                 search as a number or element (defaults to 10 || document.body)
6865          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6866          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6867          */
6868         findParentNode : function(simpleSelector, maxDepth, returnEl){
6869             var p = Roo.fly(this.dom.parentNode, '_internal');
6870             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6871         },
6872
6873         /**
6874          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6875          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6876          * @param {String} selector The simple selector to test
6877          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6878                 search as a number or element (defaults to 10 || document.body)
6879          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6880          */
6881         up : function(simpleSelector, maxDepth){
6882             return this.findParentNode(simpleSelector, maxDepth, true);
6883         },
6884
6885
6886
6887         /**
6888          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6889          * @param {String} selector The simple selector to test
6890          * @return {Boolean} True if this element matches the selector, else false
6891          */
6892         is : function(simpleSelector){
6893             return Roo.DomQuery.is(this.dom, simpleSelector);
6894         },
6895
6896         /**
6897          * Perform animation on this element.
6898          * @param {Object} args The YUI animation control args
6899          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6900          * @param {Function} onComplete (optional) Function to call when animation completes
6901          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6902          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6903          * @return {Roo.Element} this
6904          */
6905         animate : function(args, duration, onComplete, easing, animType){
6906             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6907             return this;
6908         },
6909
6910         /*
6911          * @private Internal animation call
6912          */
6913         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6914             animType = animType || 'run';
6915             opt = opt || {};
6916             var anim = Roo.lib.Anim[animType](
6917                 this.dom, args,
6918                 (opt.duration || defaultDur) || .35,
6919                 (opt.easing || defaultEase) || 'easeOut',
6920                 function(){
6921                     Roo.callback(cb, this);
6922                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6923                 },
6924                 this
6925             );
6926             opt.anim = anim;
6927             return anim;
6928         },
6929
6930         // private legacy anim prep
6931         preanim : function(a, i){
6932             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6933         },
6934
6935         /**
6936          * Removes worthless text nodes
6937          * @param {Boolean} forceReclean (optional) By default the element
6938          * keeps track if it has been cleaned already so
6939          * you can call this over and over. However, if you update the element and
6940          * need to force a reclean, you can pass true.
6941          */
6942         clean : function(forceReclean){
6943             if(this.isCleaned && forceReclean !== true){
6944                 return this;
6945             }
6946             var ns = /\S/;
6947             var d = this.dom, n = d.firstChild, ni = -1;
6948             while(n){
6949                 var nx = n.nextSibling;
6950                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6951                     d.removeChild(n);
6952                 }else{
6953                     n.nodeIndex = ++ni;
6954                 }
6955                 n = nx;
6956             }
6957             this.isCleaned = true;
6958             return this;
6959         },
6960
6961         // private
6962         calcOffsetsTo : function(el){
6963             el = Roo.get(el);
6964             var d = el.dom;
6965             var restorePos = false;
6966             if(el.getStyle('position') == 'static'){
6967                 el.position('relative');
6968                 restorePos = true;
6969             }
6970             var x = 0, y =0;
6971             var op = this.dom;
6972             while(op && op != d && op.tagName != 'HTML'){
6973                 x+= op.offsetLeft;
6974                 y+= op.offsetTop;
6975                 op = op.offsetParent;
6976             }
6977             if(restorePos){
6978                 el.position('static');
6979             }
6980             return [x, y];
6981         },
6982
6983         /**
6984          * Scrolls this element into view within the passed container.
6985          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6986          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6987          * @return {Roo.Element} this
6988          */
6989         scrollIntoView : function(container, hscroll){
6990             var c = Roo.getDom(container) || document.body;
6991             var el = this.dom;
6992
6993             var o = this.calcOffsetsTo(c),
6994                 l = o[0],
6995                 t = o[1],
6996                 b = t+el.offsetHeight,
6997                 r = l+el.offsetWidth;
6998
6999             var ch = c.clientHeight;
7000             var ct = parseInt(c.scrollTop, 10);
7001             var cl = parseInt(c.scrollLeft, 10);
7002             var cb = ct + ch;
7003             var cr = cl + c.clientWidth;
7004
7005             if(t < ct){
7006                 c.scrollTop = t;
7007             }else if(b > cb){
7008                 c.scrollTop = b-ch;
7009             }
7010
7011             if(hscroll !== false){
7012                 if(l < cl){
7013                     c.scrollLeft = l;
7014                 }else if(r > cr){
7015                     c.scrollLeft = r-c.clientWidth;
7016                 }
7017             }
7018             return this;
7019         },
7020
7021         // private
7022         scrollChildIntoView : function(child, hscroll){
7023             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7024         },
7025
7026         /**
7027          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7028          * the new height may not be available immediately.
7029          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7030          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7031          * @param {Function} onComplete (optional) Function to call when animation completes
7032          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7033          * @return {Roo.Element} this
7034          */
7035         autoHeight : function(animate, duration, onComplete, easing){
7036             var oldHeight = this.getHeight();
7037             this.clip();
7038             this.setHeight(1); // force clipping
7039             setTimeout(function(){
7040                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7041                 if(!animate){
7042                     this.setHeight(height);
7043                     this.unclip();
7044                     if(typeof onComplete == "function"){
7045                         onComplete();
7046                     }
7047                 }else{
7048                     this.setHeight(oldHeight); // restore original height
7049                     this.setHeight(height, animate, duration, function(){
7050                         this.unclip();
7051                         if(typeof onComplete == "function") onComplete();
7052                     }.createDelegate(this), easing);
7053                 }
7054             }.createDelegate(this), 0);
7055             return this;
7056         },
7057
7058         /**
7059          * Returns true if this element is an ancestor of the passed element
7060          * @param {HTMLElement/String} el The element to check
7061          * @return {Boolean} True if this element is an ancestor of el, else false
7062          */
7063         contains : function(el){
7064             if(!el){return false;}
7065             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7066         },
7067
7068         /**
7069          * Checks whether the element is currently visible using both visibility and display properties.
7070          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7071          * @return {Boolean} True if the element is currently visible, else false
7072          */
7073         isVisible : function(deep) {
7074             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7075             if(deep !== true || !vis){
7076                 return vis;
7077             }
7078             var p = this.dom.parentNode;
7079             while(p && p.tagName.toLowerCase() != "body"){
7080                 if(!Roo.fly(p, '_isVisible').isVisible()){
7081                     return false;
7082                 }
7083                 p = p.parentNode;
7084             }
7085             return true;
7086         },
7087
7088         /**
7089          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7090          * @param {String} selector The CSS selector
7091          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7092          * @return {CompositeElement/CompositeElementLite} The composite element
7093          */
7094         select : function(selector, unique){
7095             return El.select(selector, unique, this.dom);
7096         },
7097
7098         /**
7099          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7100          * @param {String} selector The CSS selector
7101          * @return {Array} An array of the matched nodes
7102          */
7103         query : function(selector, unique){
7104             return Roo.DomQuery.select(selector, this.dom);
7105         },
7106
7107         /**
7108          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7109          * @param {String} selector The CSS selector
7110          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7111          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7112          */
7113         child : function(selector, returnDom){
7114             var n = Roo.DomQuery.selectNode(selector, this.dom);
7115             return returnDom ? n : Roo.get(n);
7116         },
7117
7118         /**
7119          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7120          * @param {String} selector The CSS selector
7121          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7122          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7123          */
7124         down : function(selector, returnDom){
7125             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7126             return returnDom ? n : Roo.get(n);
7127         },
7128
7129         /**
7130          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7131          * @param {String} group The group the DD object is member of
7132          * @param {Object} config The DD config object
7133          * @param {Object} overrides An object containing methods to override/implement on the DD object
7134          * @return {Roo.dd.DD} The DD object
7135          */
7136         initDD : function(group, config, overrides){
7137             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7138             return Roo.apply(dd, overrides);
7139         },
7140
7141         /**
7142          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7143          * @param {String} group The group the DDProxy object is member of
7144          * @param {Object} config The DDProxy config object
7145          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7146          * @return {Roo.dd.DDProxy} The DDProxy object
7147          */
7148         initDDProxy : function(group, config, overrides){
7149             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7150             return Roo.apply(dd, overrides);
7151         },
7152
7153         /**
7154          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7155          * @param {String} group The group the DDTarget object is member of
7156          * @param {Object} config The DDTarget config object
7157          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7158          * @return {Roo.dd.DDTarget} The DDTarget object
7159          */
7160         initDDTarget : function(group, config, overrides){
7161             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7162             return Roo.apply(dd, overrides);
7163         },
7164
7165         /**
7166          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7167          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7168          * @param {Boolean} visible Whether the element is visible
7169          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7170          * @return {Roo.Element} this
7171          */
7172          setVisible : function(visible, animate){
7173             if(!animate || !A){
7174                 if(this.visibilityMode == El.DISPLAY){
7175                     this.setDisplayed(visible);
7176                 }else{
7177                     this.fixDisplay();
7178                     this.dom.style.visibility = visible ? "visible" : "hidden";
7179                 }
7180             }else{
7181                 // closure for composites
7182                 var dom = this.dom;
7183                 var visMode = this.visibilityMode;
7184                 if(visible){
7185                     this.setOpacity(.01);
7186                     this.setVisible(true);
7187                 }
7188                 this.anim({opacity: { to: (visible?1:0) }},
7189                       this.preanim(arguments, 1),
7190                       null, .35, 'easeIn', function(){
7191                          if(!visible){
7192                              if(visMode == El.DISPLAY){
7193                                  dom.style.display = "none";
7194                              }else{
7195                                  dom.style.visibility = "hidden";
7196                              }
7197                              Roo.get(dom).setOpacity(1);
7198                          }
7199                      });
7200             }
7201             return this;
7202         },
7203
7204         /**
7205          * Returns true if display is not "none"
7206          * @return {Boolean}
7207          */
7208         isDisplayed : function() {
7209             return this.getStyle("display") != "none";
7210         },
7211
7212         /**
7213          * Toggles the element's visibility or display, depending on visibility mode.
7214          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7215          * @return {Roo.Element} this
7216          */
7217         toggle : function(animate){
7218             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7219             return this;
7220         },
7221
7222         /**
7223          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7224          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7225          * @return {Roo.Element} this
7226          */
7227         setDisplayed : function(value) {
7228             if(typeof value == "boolean"){
7229                value = value ? this.originalDisplay : "none";
7230             }
7231             this.setStyle("display", value);
7232             return this;
7233         },
7234
7235         /**
7236          * Tries to focus the element. Any exceptions are caught and ignored.
7237          * @return {Roo.Element} this
7238          */
7239         focus : function() {
7240             try{
7241                 this.dom.focus();
7242             }catch(e){}
7243             return this;
7244         },
7245
7246         /**
7247          * Tries to blur the element. Any exceptions are caught and ignored.
7248          * @return {Roo.Element} this
7249          */
7250         blur : function() {
7251             try{
7252                 this.dom.blur();
7253             }catch(e){}
7254             return this;
7255         },
7256
7257         /**
7258          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7259          * @param {String/Array} className The CSS class to add, or an array of classes
7260          * @return {Roo.Element} this
7261          */
7262         addClass : function(className){
7263             if(className instanceof Array){
7264                 for(var i = 0, len = className.length; i < len; i++) {
7265                     this.addClass(className[i]);
7266                 }
7267             }else{
7268                 if(className && !this.hasClass(className)){
7269                     this.dom.className = this.dom.className + " " + className;
7270                 }
7271             }
7272             return this;
7273         },
7274
7275         /**
7276          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7277          * @param {String/Array} className The CSS class to add, or an array of classes
7278          * @return {Roo.Element} this
7279          */
7280         radioClass : function(className){
7281             var siblings = this.dom.parentNode.childNodes;
7282             for(var i = 0; i < siblings.length; i++) {
7283                 var s = siblings[i];
7284                 if(s.nodeType == 1){
7285                     Roo.get(s).removeClass(className);
7286                 }
7287             }
7288             this.addClass(className);
7289             return this;
7290         },
7291
7292         /**
7293          * Removes one or more CSS classes from the element.
7294          * @param {String/Array} className The CSS class to remove, or an array of classes
7295          * @return {Roo.Element} this
7296          */
7297         removeClass : function(className){
7298             if(!className || !this.dom.className){
7299                 return this;
7300             }
7301             if(className instanceof Array){
7302                 for(var i = 0, len = className.length; i < len; i++) {
7303                     this.removeClass(className[i]);
7304                 }
7305             }else{
7306                 if(this.hasClass(className)){
7307                     var re = this.classReCache[className];
7308                     if (!re) {
7309                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7310                        this.classReCache[className] = re;
7311                     }
7312                     this.dom.className =
7313                         this.dom.className.replace(re, " ");
7314                 }
7315             }
7316             return this;
7317         },
7318
7319         // private
7320         classReCache: {},
7321
7322         /**
7323          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7324          * @param {String} className The CSS class to toggle
7325          * @return {Roo.Element} this
7326          */
7327         toggleClass : function(className){
7328             if(this.hasClass(className)){
7329                 this.removeClass(className);
7330             }else{
7331                 this.addClass(className);
7332             }
7333             return this;
7334         },
7335
7336         /**
7337          * Checks if the specified CSS class exists on this element's DOM node.
7338          * @param {String} className The CSS class to check for
7339          * @return {Boolean} True if the class exists, else false
7340          */
7341         hasClass : function(className){
7342             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7343         },
7344
7345         /**
7346          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7347          * @param {String} oldClassName The CSS class to replace
7348          * @param {String} newClassName The replacement CSS class
7349          * @return {Roo.Element} this
7350          */
7351         replaceClass : function(oldClassName, newClassName){
7352             this.removeClass(oldClassName);
7353             this.addClass(newClassName);
7354             return this;
7355         },
7356
7357         /**
7358          * Returns an object with properties matching the styles requested.
7359          * For example, el.getStyles('color', 'font-size', 'width') might return
7360          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7361          * @param {String} style1 A style name
7362          * @param {String} style2 A style name
7363          * @param {String} etc.
7364          * @return {Object} The style object
7365          */
7366         getStyles : function(){
7367             var a = arguments, len = a.length, r = {};
7368             for(var i = 0; i < len; i++){
7369                 r[a[i]] = this.getStyle(a[i]);
7370             }
7371             return r;
7372         },
7373
7374         /**
7375          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7376          * @param {String} property The style property whose value is returned.
7377          * @return {String} The current value of the style property for this element.
7378          */
7379         getStyle : function(){
7380             return view && view.getComputedStyle ?
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'float'){
7384                         prop = "cssFloat";
7385                     }
7386                     if(el.style && (v = el.style[prop])){
7387                         return v;
7388                     }
7389                     if(cs = view.getComputedStyle(el, "")){
7390                         if(!(camel = propCache[prop])){
7391                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7392                         }
7393                         return cs[camel];
7394                     }
7395                     return null;
7396                 } :
7397                 function(prop){
7398                     var el = this.dom, v, cs, camel;
7399                     if(prop == 'opacity'){
7400                         if(typeof el.style.filter == 'string'){
7401                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7402                             if(m){
7403                                 var fv = parseFloat(m[1]);
7404                                 if(!isNaN(fv)){
7405                                     return fv ? fv / 100 : 0;
7406                                 }
7407                             }
7408                         }
7409                         return 1;
7410                     }else if(prop == 'float'){
7411                         prop = "styleFloat";
7412                     }
7413                     if(!(camel = propCache[prop])){
7414                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7415                     }
7416                     if(v = el.style[camel]){
7417                         return v;
7418                     }
7419                     if(cs = el.currentStyle){
7420                         return cs[camel];
7421                     }
7422                     return null;
7423                 };
7424         }(),
7425
7426         /**
7427          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7428          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7429          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7430          * @return {Roo.Element} this
7431          */
7432         setStyle : function(prop, value){
7433             if(typeof prop == "string"){
7434                 
7435                 if (prop == 'float') {
7436                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7437                     return this;
7438                 }
7439                 
7440                 var camel;
7441                 if(!(camel = propCache[prop])){
7442                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7443                 }
7444                 
7445                 if(camel == 'opacity') {
7446                     this.setOpacity(value);
7447                 }else{
7448                     this.dom.style[camel] = value;
7449                 }
7450             }else{
7451                 for(var style in prop){
7452                     if(typeof prop[style] != "function"){
7453                        this.setStyle(style, prop[style]);
7454                     }
7455                 }
7456             }
7457             return this;
7458         },
7459
7460         /**
7461          * More flexible version of {@link #setStyle} for setting style properties.
7462          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7463          * a function which returns such a specification.
7464          * @return {Roo.Element} this
7465          */
7466         applyStyles : function(style){
7467             Roo.DomHelper.applyStyles(this.dom, style);
7468             return this;
7469         },
7470
7471         /**
7472           * 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).
7473           * @return {Number} The X position of the element
7474           */
7475         getX : function(){
7476             return D.getX(this.dom);
7477         },
7478
7479         /**
7480           * 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).
7481           * @return {Number} The Y position of the element
7482           */
7483         getY : function(){
7484             return D.getY(this.dom);
7485         },
7486
7487         /**
7488           * 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).
7489           * @return {Array} The XY position of the element
7490           */
7491         getXY : function(){
7492             return D.getXY(this.dom);
7493         },
7494
7495         /**
7496          * 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).
7497          * @param {Number} The X position of the element
7498          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7499          * @return {Roo.Element} this
7500          */
7501         setX : function(x, animate){
7502             if(!animate || !A){
7503                 D.setX(this.dom, x);
7504             }else{
7505                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7506             }
7507             return this;
7508         },
7509
7510         /**
7511          * 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).
7512          * @param {Number} The Y position of the element
7513          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7514          * @return {Roo.Element} this
7515          */
7516         setY : function(y, animate){
7517             if(!animate || !A){
7518                 D.setY(this.dom, y);
7519             }else{
7520                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7521             }
7522             return this;
7523         },
7524
7525         /**
7526          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7527          * @param {String} left The left CSS property value
7528          * @return {Roo.Element} this
7529          */
7530         setLeft : function(left){
7531             this.setStyle("left", this.addUnits(left));
7532             return this;
7533         },
7534
7535         /**
7536          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7537          * @param {String} top The top CSS property value
7538          * @return {Roo.Element} this
7539          */
7540         setTop : function(top){
7541             this.setStyle("top", this.addUnits(top));
7542             return this;
7543         },
7544
7545         /**
7546          * Sets the element's CSS right style.
7547          * @param {String} right The right CSS property value
7548          * @return {Roo.Element} this
7549          */
7550         setRight : function(right){
7551             this.setStyle("right", this.addUnits(right));
7552             return this;
7553         },
7554
7555         /**
7556          * Sets the element's CSS bottom style.
7557          * @param {String} bottom The bottom CSS property value
7558          * @return {Roo.Element} this
7559          */
7560         setBottom : function(bottom){
7561             this.setStyle("bottom", this.addUnits(bottom));
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 {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7569          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7570          * @return {Roo.Element} this
7571          */
7572         setXY : function(pos, animate){
7573             if(!animate || !A){
7574                 D.setXY(this.dom, pos);
7575             }else{
7576                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7577             }
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7583          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7584          * @param {Number} x X value for new position (coordinates are page-based)
7585          * @param {Number} y Y value for new position (coordinates are page-based)
7586          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7587          * @return {Roo.Element} this
7588          */
7589         setLocation : function(x, y, animate){
7590             this.setXY([x, y], this.preanim(arguments, 2));
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7596          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7597          * @param {Number} x X value for new position (coordinates are page-based)
7598          * @param {Number} y Y value for new position (coordinates are page-based)
7599          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7600          * @return {Roo.Element} this
7601          */
7602         moveTo : function(x, y, animate){
7603             this.setXY([x, y], this.preanim(arguments, 2));
7604             return this;
7605         },
7606
7607         /**
7608          * Returns the region of the given element.
7609          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7610          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7611          */
7612         getRegion : function(){
7613             return D.getRegion(this.dom);
7614         },
7615
7616         /**
7617          * Returns the offset height of the element
7618          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7619          * @return {Number} The element's height
7620          */
7621         getHeight : function(contentHeight){
7622             var h = this.dom.offsetHeight || 0;
7623             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7624         },
7625
7626         /**
7627          * Returns the offset width of the element
7628          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7629          * @return {Number} The element's width
7630          */
7631         getWidth : function(contentWidth){
7632             var w = this.dom.offsetWidth || 0;
7633             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7634         },
7635
7636         /**
7637          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7638          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7639          * if a height has not been set using CSS.
7640          * @return {Number}
7641          */
7642         getComputedHeight : function(){
7643             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7644             if(!h){
7645                 h = parseInt(this.getStyle('height'), 10) || 0;
7646                 if(!this.isBorderBox()){
7647                     h += this.getFrameWidth('tb');
7648                 }
7649             }
7650             return h;
7651         },
7652
7653         /**
7654          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7655          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7656          * if a width has not been set using CSS.
7657          * @return {Number}
7658          */
7659         getComputedWidth : function(){
7660             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7661             if(!w){
7662                 w = parseInt(this.getStyle('width'), 10) || 0;
7663                 if(!this.isBorderBox()){
7664                     w += this.getFrameWidth('lr');
7665                 }
7666             }
7667             return w;
7668         },
7669
7670         /**
7671          * Returns the size of the element.
7672          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7673          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7674          */
7675         getSize : function(contentSize){
7676             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7677         },
7678
7679         /**
7680          * Returns the width and height of the viewport.
7681          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7682          */
7683         getViewSize : function(){
7684             var d = this.dom, doc = document, aw = 0, ah = 0;
7685             if(d == doc || d == doc.body){
7686                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7687             }else{
7688                 return {
7689                     width : d.clientWidth,
7690                     height: d.clientHeight
7691                 };
7692             }
7693         },
7694
7695         /**
7696          * Returns the value of the "value" attribute
7697          * @param {Boolean} asNumber true to parse the value as a number
7698          * @return {String/Number}
7699          */
7700         getValue : function(asNumber){
7701             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7702         },
7703
7704         // private
7705         adjustWidth : function(width){
7706             if(typeof width == "number"){
7707                 if(this.autoBoxAdjust && !this.isBorderBox()){
7708                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7709                 }
7710                 if(width < 0){
7711                     width = 0;
7712                 }
7713             }
7714             return width;
7715         },
7716
7717         // private
7718         adjustHeight : function(height){
7719             if(typeof height == "number"){
7720                if(this.autoBoxAdjust && !this.isBorderBox()){
7721                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7722                }
7723                if(height < 0){
7724                    height = 0;
7725                }
7726             }
7727             return height;
7728         },
7729
7730         /**
7731          * Set the width of the element
7732          * @param {Number} width The new width
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         setWidth : function(width, animate){
7737             width = this.adjustWidth(width);
7738             if(!animate || !A){
7739                 this.dom.style.width = this.addUnits(width);
7740             }else{
7741                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the height of the element
7748          * @param {Number} height The new height
7749          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752          setHeight : function(height, animate){
7753             height = this.adjustHeight(height);
7754             if(!animate || !A){
7755                 this.dom.style.height = this.addUnits(height);
7756             }else{
7757                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7758             }
7759             return this;
7760         },
7761
7762         /**
7763          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7764          * @param {Number} width The new width
7765          * @param {Number} height The new height
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setSize : function(width, height, animate){
7770             if(typeof width == "object"){ // in case of object from getSize()
7771                 height = width.height; width = width.width;
7772             }
7773             width = this.adjustWidth(width); height = this.adjustHeight(height);
7774             if(!animate || !A){
7775                 this.dom.style.width = this.addUnits(width);
7776                 this.dom.style.height = this.addUnits(height);
7777             }else{
7778                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7779             }
7780             return this;
7781         },
7782
7783         /**
7784          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7785          * @param {Number} x X value for new position (coordinates are page-based)
7786          * @param {Number} y Y value for new position (coordinates are page-based)
7787          * @param {Number} width The new width
7788          * @param {Number} height The new height
7789          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7790          * @return {Roo.Element} this
7791          */
7792         setBounds : function(x, y, width, height, animate){
7793             if(!animate || !A){
7794                 this.setSize(width, height);
7795                 this.setLocation(x, y);
7796             }else{
7797                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7798                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7799                               this.preanim(arguments, 4), 'motion');
7800             }
7801             return this;
7802         },
7803
7804         /**
7805          * 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.
7806          * @param {Roo.lib.Region} region The region to fill
7807          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7808          * @return {Roo.Element} this
7809          */
7810         setRegion : function(region, animate){
7811             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7812             return this;
7813         },
7814
7815         /**
7816          * Appends an event handler
7817          *
7818          * @param {String}   eventName     The type of event to append
7819          * @param {Function} fn        The method the event invokes
7820          * @param {Object} scope       (optional) The scope (this object) of the fn
7821          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7822          */
7823         addListener : function(eventName, fn, scope, options){
7824             if (this.dom) {
7825                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7826             }
7827         },
7828
7829         /**
7830          * Removes an event handler from this element
7831          * @param {String} eventName the type of event to remove
7832          * @param {Function} fn the method the event invokes
7833          * @return {Roo.Element} this
7834          */
7835         removeListener : function(eventName, fn){
7836             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7837             return this;
7838         },
7839
7840         /**
7841          * Removes all previous added listeners from this element
7842          * @return {Roo.Element} this
7843          */
7844         removeAllListeners : function(){
7845             E.purgeElement(this.dom);
7846             return this;
7847         },
7848
7849         relayEvent : function(eventName, observable){
7850             this.on(eventName, function(e){
7851                 observable.fireEvent(eventName, e);
7852             });
7853         },
7854
7855         /**
7856          * Set the opacity of the element
7857          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861          setOpacity : function(opacity, animate){
7862             if(!animate || !A){
7863                 var s = this.dom.style;
7864                 if(Roo.isIE){
7865                     s.zoom = 1;
7866                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7867                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7868                 }else{
7869                     s.opacity = opacity;
7870                 }
7871             }else{
7872                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7873             }
7874             return this;
7875         },
7876
7877         /**
7878          * Gets the left X coordinate
7879          * @param {Boolean} local True to get the local css position instead of page coordinate
7880          * @return {Number}
7881          */
7882         getLeft : function(local){
7883             if(!local){
7884                 return this.getX();
7885             }else{
7886                 return parseInt(this.getStyle("left"), 10) || 0;
7887             }
7888         },
7889
7890         /**
7891          * Gets the right X coordinate of the element (element X position + element width)
7892          * @param {Boolean} local True to get the local css position instead of page coordinate
7893          * @return {Number}
7894          */
7895         getRight : function(local){
7896             if(!local){
7897                 return this.getX() + this.getWidth();
7898             }else{
7899                 return (this.getLeft(true) + this.getWidth()) || 0;
7900             }
7901         },
7902
7903         /**
7904          * Gets the top Y coordinate
7905          * @param {Boolean} local True to get the local css position instead of page coordinate
7906          * @return {Number}
7907          */
7908         getTop : function(local) {
7909             if(!local){
7910                 return this.getY();
7911             }else{
7912                 return parseInt(this.getStyle("top"), 10) || 0;
7913             }
7914         },
7915
7916         /**
7917          * Gets the bottom Y coordinate of the element (element Y position + element height)
7918          * @param {Boolean} local True to get the local css position instead of page coordinate
7919          * @return {Number}
7920          */
7921         getBottom : function(local){
7922             if(!local){
7923                 return this.getY() + this.getHeight();
7924             }else{
7925                 return (this.getTop(true) + this.getHeight()) || 0;
7926             }
7927         },
7928
7929         /**
7930         * Initializes positioning on this element. If a desired position is not passed, it will make the
7931         * the element positioned relative IF it is not already positioned.
7932         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7933         * @param {Number} zIndex (optional) The zIndex to apply
7934         * @param {Number} x (optional) Set the page X position
7935         * @param {Number} y (optional) Set the page Y position
7936         */
7937         position : function(pos, zIndex, x, y){
7938             if(!pos){
7939                if(this.getStyle('position') == 'static'){
7940                    this.setStyle('position', 'relative');
7941                }
7942             }else{
7943                 this.setStyle("position", pos);
7944             }
7945             if(zIndex){
7946                 this.setStyle("z-index", zIndex);
7947             }
7948             if(x !== undefined && y !== undefined){
7949                 this.setXY([x, y]);
7950             }else if(x !== undefined){
7951                 this.setX(x);
7952             }else if(y !== undefined){
7953                 this.setY(y);
7954             }
7955         },
7956
7957         /**
7958         * Clear positioning back to the default when the document was loaded
7959         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7960         * @return {Roo.Element} this
7961          */
7962         clearPositioning : function(value){
7963             value = value ||'';
7964             this.setStyle({
7965                 "left": value,
7966                 "right": value,
7967                 "top": value,
7968                 "bottom": value,
7969                 "z-index": "",
7970                 "position" : "static"
7971             });
7972             return this;
7973         },
7974
7975         /**
7976         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7977         * snapshot before performing an update and then restoring the element.
7978         * @return {Object}
7979         */
7980         getPositioning : function(){
7981             var l = this.getStyle("left");
7982             var t = this.getStyle("top");
7983             return {
7984                 "position" : this.getStyle("position"),
7985                 "left" : l,
7986                 "right" : l ? "" : this.getStyle("right"),
7987                 "top" : t,
7988                 "bottom" : t ? "" : this.getStyle("bottom"),
7989                 "z-index" : this.getStyle("z-index")
7990             };
7991         },
7992
7993         /**
7994          * Gets the width of the border(s) for the specified side(s)
7995          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7996          * passing lr would get the border (l)eft width + the border (r)ight width.
7997          * @return {Number} The width of the sides passed added together
7998          */
7999         getBorderWidth : function(side){
8000             return this.addStyles(side, El.borders);
8001         },
8002
8003         /**
8004          * Gets the width of the padding(s) for the specified side(s)
8005          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8006          * passing lr would get the padding (l)eft + the padding (r)ight.
8007          * @return {Number} The padding of the sides passed added together
8008          */
8009         getPadding : function(side){
8010             return this.addStyles(side, El.paddings);
8011         },
8012
8013         /**
8014         * Set positioning with an object returned by getPositioning().
8015         * @param {Object} posCfg
8016         * @return {Roo.Element} this
8017          */
8018         setPositioning : function(pc){
8019             this.applyStyles(pc);
8020             if(pc.right == "auto"){
8021                 this.dom.style.right = "";
8022             }
8023             if(pc.bottom == "auto"){
8024                 this.dom.style.bottom = "";
8025             }
8026             return this;
8027         },
8028
8029         // private
8030         fixDisplay : function(){
8031             if(this.getStyle("display") == "none"){
8032                 this.setStyle("visibility", "hidden");
8033                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8034                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8035                     this.setStyle("display", "block");
8036                 }
8037             }
8038         },
8039
8040         /**
8041          * Quick set left and top adding default units
8042          * @param {String} left The left CSS property value
8043          * @param {String} top The top CSS property value
8044          * @return {Roo.Element} this
8045          */
8046          setLeftTop : function(left, top){
8047             this.dom.style.left = this.addUnits(left);
8048             this.dom.style.top = this.addUnits(top);
8049             return this;
8050         },
8051
8052         /**
8053          * Move this element relative to its current position.
8054          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8055          * @param {Number} distance How far to move the element in pixels
8056          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059          move : function(direction, distance, animate){
8060             var xy = this.getXY();
8061             direction = direction.toLowerCase();
8062             switch(direction){
8063                 case "l":
8064                 case "left":
8065                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8066                     break;
8067                case "r":
8068                case "right":
8069                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8070                     break;
8071                case "t":
8072                case "top":
8073                case "up":
8074                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8075                     break;
8076                case "b":
8077                case "bottom":
8078                case "down":
8079                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8080                     break;
8081             }
8082             return this;
8083         },
8084
8085         /**
8086          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8087          * @return {Roo.Element} this
8088          */
8089         clip : function(){
8090             if(!this.isClipped){
8091                this.isClipped = true;
8092                this.originalClip = {
8093                    "o": this.getStyle("overflow"),
8094                    "x": this.getStyle("overflow-x"),
8095                    "y": this.getStyle("overflow-y")
8096                };
8097                this.setStyle("overflow", "hidden");
8098                this.setStyle("overflow-x", "hidden");
8099                this.setStyle("overflow-y", "hidden");
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          *  Return clipping (overflow) to original clipping before clip() was called
8106          * @return {Roo.Element} this
8107          */
8108         unclip : function(){
8109             if(this.isClipped){
8110                 this.isClipped = false;
8111                 var o = this.originalClip;
8112                 if(o.o){this.setStyle("overflow", o.o);}
8113                 if(o.x){this.setStyle("overflow-x", o.x);}
8114                 if(o.y){this.setStyle("overflow-y", o.y);}
8115             }
8116             return this;
8117         },
8118
8119
8120         /**
8121          * Gets the x,y coordinates specified by the anchor position on the element.
8122          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8123          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8124          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8125          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8126          * @return {Array} [x, y] An array containing the element's x and y coordinates
8127          */
8128         getAnchorXY : function(anchor, local, s){
8129             //Passing a different size is useful for pre-calculating anchors,
8130             //especially for anchored animations that change the el size.
8131
8132             var w, h, vp = false;
8133             if(!s){
8134                 var d = this.dom;
8135                 if(d == document.body || d == document){
8136                     vp = true;
8137                     w = D.getViewWidth(); h = D.getViewHeight();
8138                 }else{
8139                     w = this.getWidth(); h = this.getHeight();
8140                 }
8141             }else{
8142                 w = s.width;  h = s.height;
8143             }
8144             var x = 0, y = 0, r = Math.round;
8145             switch((anchor || "tl").toLowerCase()){
8146                 case "c":
8147                     x = r(w*.5);
8148                     y = r(h*.5);
8149                 break;
8150                 case "t":
8151                     x = r(w*.5);
8152                     y = 0;
8153                 break;
8154                 case "l":
8155                     x = 0;
8156                     y = r(h*.5);
8157                 break;
8158                 case "r":
8159                     x = w;
8160                     y = r(h*.5);
8161                 break;
8162                 case "b":
8163                     x = r(w*.5);
8164                     y = h;
8165                 break;
8166                 case "tl":
8167                     x = 0;
8168                     y = 0;
8169                 break;
8170                 case "bl":
8171                     x = 0;
8172                     y = h;
8173                 break;
8174                 case "br":
8175                     x = w;
8176                     y = h;
8177                 break;
8178                 case "tr":
8179                     x = w;
8180                     y = 0;
8181                 break;
8182             }
8183             if(local === true){
8184                 return [x, y];
8185             }
8186             if(vp){
8187                 var sc = this.getScroll();
8188                 return [x + sc.left, y + sc.top];
8189             }
8190             //Add the element's offset xy
8191             var o = this.getXY();
8192             return [x+o[0], y+o[1]];
8193         },
8194
8195         /**
8196          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8197          * supported position values.
8198          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8199          * @param {String} position The position to align to.
8200          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8201          * @return {Array} [x, y]
8202          */
8203         getAlignToXY : function(el, p, o){
8204             el = Roo.get(el);
8205             var d = this.dom;
8206             if(!el.dom){
8207                 throw "Element.alignTo with an element that doesn't exist";
8208             }
8209             var c = false; //constrain to viewport
8210             var p1 = "", p2 = "";
8211             o = o || [0,0];
8212
8213             if(!p){
8214                 p = "tl-bl";
8215             }else if(p == "?"){
8216                 p = "tl-bl?";
8217             }else if(p.indexOf("-") == -1){
8218                 p = "tl-" + p;
8219             }
8220             p = p.toLowerCase();
8221             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8222             if(!m){
8223                throw "Element.alignTo with an invalid alignment " + p;
8224             }
8225             p1 = m[1]; p2 = m[2]; c = !!m[3];
8226
8227             //Subtract the aligned el's internal xy from the target's offset xy
8228             //plus custom offset to get the aligned el's new offset xy
8229             var a1 = this.getAnchorXY(p1, true);
8230             var a2 = el.getAnchorXY(p2, false);
8231             var x = a2[0] - a1[0] + o[0];
8232             var y = a2[1] - a1[1] + o[1];
8233             if(c){
8234                 //constrain the aligned el to viewport if necessary
8235                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8236                 // 5px of margin for ie
8237                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8238
8239                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8240                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8241                 //otherwise swap the aligned el to the opposite border of the target.
8242                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8243                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8244                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8245                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8246
8247                var doc = document;
8248                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8249                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8250
8251                if((x+w) > dw + scrollX){
8252                     x = swapX ? r.left-w : dw+scrollX-w;
8253                 }
8254                if(x < scrollX){
8255                    x = swapX ? r.right : scrollX;
8256                }
8257                if((y+h) > dh + scrollY){
8258                     y = swapY ? r.top-h : dh+scrollY-h;
8259                 }
8260                if (y < scrollY){
8261                    y = swapY ? r.bottom : scrollY;
8262                }
8263             }
8264             return [x,y];
8265         },
8266
8267         // private
8268         getConstrainToXY : function(){
8269             var os = {top:0, left:0, bottom:0, right: 0};
8270
8271             return function(el, local, offsets, proposedXY){
8272                 el = Roo.get(el);
8273                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8274
8275                 var vw, vh, vx = 0, vy = 0;
8276                 if(el.dom == document.body || el.dom == document){
8277                     vw = Roo.lib.Dom.getViewWidth();
8278                     vh = Roo.lib.Dom.getViewHeight();
8279                 }else{
8280                     vw = el.dom.clientWidth;
8281                     vh = el.dom.clientHeight;
8282                     if(!local){
8283                         var vxy = el.getXY();
8284                         vx = vxy[0];
8285                         vy = vxy[1];
8286                     }
8287                 }
8288
8289                 var s = el.getScroll();
8290
8291                 vx += offsets.left + s.left;
8292                 vy += offsets.top + s.top;
8293
8294                 vw -= offsets.right;
8295                 vh -= offsets.bottom;
8296
8297                 var vr = vx+vw;
8298                 var vb = vy+vh;
8299
8300                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8301                 var x = xy[0], y = xy[1];
8302                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8303
8304                 // only move it if it needs it
8305                 var moved = false;
8306
8307                 // first validate right/bottom
8308                 if((x + w) > vr){
8309                     x = vr - w;
8310                     moved = true;
8311                 }
8312                 if((y + h) > vb){
8313                     y = vb - h;
8314                     moved = true;
8315                 }
8316                 // then make sure top/left isn't negative
8317                 if(x < vx){
8318                     x = vx;
8319                     moved = true;
8320                 }
8321                 if(y < vy){
8322                     y = vy;
8323                     moved = true;
8324                 }
8325                 return moved ? [x, y] : false;
8326             };
8327         }(),
8328
8329         // private
8330         adjustForConstraints : function(xy, parent, offsets){
8331             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8332         },
8333
8334         /**
8335          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8336          * document it aligns it to the viewport.
8337          * The position parameter is optional, and can be specified in any one of the following formats:
8338          * <ul>
8339          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8340          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8341          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8342          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8343          *   <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
8344          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8345          * </ul>
8346          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8347          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8348          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8349          * that specified in order to enforce the viewport constraints.
8350          * Following are all of the supported anchor positions:
8351     <pre>
8352     Value  Description
8353     -----  -----------------------------
8354     tl     The top left corner (default)
8355     t      The center of the top edge
8356     tr     The top right corner
8357     l      The center of the left edge
8358     c      In the center of the element
8359     r      The center of the right edge
8360     bl     The bottom left corner
8361     b      The center of the bottom edge
8362     br     The bottom right corner
8363     </pre>
8364     Example Usage:
8365     <pre><code>
8366     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8367     el.alignTo("other-el");
8368
8369     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8370     el.alignTo("other-el", "tr?");
8371
8372     // align the bottom right corner of el with the center left edge of other-el
8373     el.alignTo("other-el", "br-l?");
8374
8375     // align the center of el with the bottom left corner of other-el and
8376     // adjust the x position by -6 pixels (and the y position by 0)
8377     el.alignTo("other-el", "c-bl", [-6, 0]);
8378     </code></pre>
8379          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8380          * @param {String} position The position to align to.
8381          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8382          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8383          * @return {Roo.Element} this
8384          */
8385         alignTo : function(element, position, offsets, animate){
8386             var xy = this.getAlignToXY(element, position, offsets);
8387             this.setXY(xy, this.preanim(arguments, 3));
8388             return this;
8389         },
8390
8391         /**
8392          * Anchors an element to another element and realigns it when the window is resized.
8393          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8394          * @param {String} position The position to align to.
8395          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8396          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8397          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8398          * is a number, it is used as the buffer delay (defaults to 50ms).
8399          * @param {Function} callback The function to call after the animation finishes
8400          * @return {Roo.Element} this
8401          */
8402         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8403             var action = function(){
8404                 this.alignTo(el, alignment, offsets, animate);
8405                 Roo.callback(callback, this);
8406             };
8407             Roo.EventManager.onWindowResize(action, this);
8408             var tm = typeof monitorScroll;
8409             if(tm != 'undefined'){
8410                 Roo.EventManager.on(window, 'scroll', action, this,
8411                     {buffer: tm == 'number' ? monitorScroll : 50});
8412             }
8413             action.call(this); // align immediately
8414             return this;
8415         },
8416         /**
8417          * Clears any opacity settings from this element. Required in some cases for IE.
8418          * @return {Roo.Element} this
8419          */
8420         clearOpacity : function(){
8421             if (window.ActiveXObject) {
8422                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8423                     this.dom.style.filter = "";
8424                 }
8425             } else {
8426                 this.dom.style.opacity = "";
8427                 this.dom.style["-moz-opacity"] = "";
8428                 this.dom.style["-khtml-opacity"] = "";
8429             }
8430             return this;
8431         },
8432
8433         /**
8434          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8435          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8436          * @return {Roo.Element} this
8437          */
8438         hide : function(animate){
8439             this.setVisible(false, this.preanim(arguments, 0));
8440             return this;
8441         },
8442
8443         /**
8444         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8445         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         show : function(animate){
8449             this.setVisible(true, this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * @private Test if size has a unit, otherwise appends the default
8455          */
8456         addUnits : function(size){
8457             return Roo.Element.addUnits(size, this.defaultUnit);
8458         },
8459
8460         /**
8461          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8462          * @return {Roo.Element} this
8463          */
8464         beginMeasure : function(){
8465             var el = this.dom;
8466             if(el.offsetWidth || el.offsetHeight){
8467                 return this; // offsets work already
8468             }
8469             var changed = [];
8470             var p = this.dom, b = document.body; // start with this element
8471             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8472                 var pe = Roo.get(p);
8473                 if(pe.getStyle('display') == 'none'){
8474                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8475                     p.style.visibility = "hidden";
8476                     p.style.display = "block";
8477                 }
8478                 p = p.parentNode;
8479             }
8480             this._measureChanged = changed;
8481             return this;
8482
8483         },
8484
8485         /**
8486          * Restores displays to before beginMeasure was called
8487          * @return {Roo.Element} this
8488          */
8489         endMeasure : function(){
8490             var changed = this._measureChanged;
8491             if(changed){
8492                 for(var i = 0, len = changed.length; i < len; i++) {
8493                     var r = changed[i];
8494                     r.el.style.visibility = r.visibility;
8495                     r.el.style.display = "none";
8496                 }
8497                 this._measureChanged = null;
8498             }
8499             return this;
8500         },
8501
8502         /**
8503         * Update the innerHTML of this element, optionally searching for and processing scripts
8504         * @param {String} html The new HTML
8505         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8506         * @param {Function} callback For async script loading you can be noticed when the update completes
8507         * @return {Roo.Element} this
8508          */
8509         update : function(html, loadScripts, callback){
8510             if(typeof html == "undefined"){
8511                 html = "";
8512             }
8513             if(loadScripts !== true){
8514                 this.dom.innerHTML = html;
8515                 if(typeof callback == "function"){
8516                     callback();
8517                 }
8518                 return this;
8519             }
8520             var id = Roo.id();
8521             var dom = this.dom;
8522
8523             html += '<span id="' + id + '"></span>';
8524
8525             E.onAvailable(id, function(){
8526                 var hd = document.getElementsByTagName("head")[0];
8527                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8528                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8529                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8530
8531                 var match;
8532                 while(match = re.exec(html)){
8533                     var attrs = match[1];
8534                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8535                     if(srcMatch && srcMatch[2]){
8536                        var s = document.createElement("script");
8537                        s.src = srcMatch[2];
8538                        var typeMatch = attrs.match(typeRe);
8539                        if(typeMatch && typeMatch[2]){
8540                            s.type = typeMatch[2];
8541                        }
8542                        hd.appendChild(s);
8543                     }else if(match[2] && match[2].length > 0){
8544                         if(window.execScript) {
8545                            window.execScript(match[2]);
8546                         } else {
8547                             /**
8548                              * eval:var:id
8549                              * eval:var:dom
8550                              * eval:var:html
8551                              * 
8552                              */
8553                            window.eval(match[2]);
8554                         }
8555                     }
8556                 }
8557                 var el = document.getElementById(id);
8558                 if(el){el.parentNode.removeChild(el);}
8559                 if(typeof callback == "function"){
8560                     callback();
8561                 }
8562             });
8563             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8564             return this;
8565         },
8566
8567         /**
8568          * Direct access to the UpdateManager update() method (takes the same parameters).
8569          * @param {String/Function} url The url for this request or a function to call to get the url
8570          * @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}
8571          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8572          * @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.
8573          * @return {Roo.Element} this
8574          */
8575         load : function(){
8576             var um = this.getUpdateManager();
8577             um.update.apply(um, arguments);
8578             return this;
8579         },
8580
8581         /**
8582         * Gets this element's UpdateManager
8583         * @return {Roo.UpdateManager} The UpdateManager
8584         */
8585         getUpdateManager : function(){
8586             if(!this.updateManager){
8587                 this.updateManager = new Roo.UpdateManager(this);
8588             }
8589             return this.updateManager;
8590         },
8591
8592         /**
8593          * Disables text selection for this element (normalized across browsers)
8594          * @return {Roo.Element} this
8595          */
8596         unselectable : function(){
8597             this.dom.unselectable = "on";
8598             this.swallowEvent("selectstart", true);
8599             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8600             this.addClass("x-unselectable");
8601             return this;
8602         },
8603
8604         /**
8605         * Calculates the x, y to center this element on the screen
8606         * @return {Array} The x, y values [x, y]
8607         */
8608         getCenterXY : function(){
8609             return this.getAlignToXY(document, 'c-c');
8610         },
8611
8612         /**
8613         * Centers the Element in either the viewport, or another Element.
8614         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8615         */
8616         center : function(centerIn){
8617             this.alignTo(centerIn || document, 'c-c');
8618             return this;
8619         },
8620
8621         /**
8622          * Tests various css rules/browsers to determine if this element uses a border box
8623          * @return {Boolean}
8624          */
8625         isBorderBox : function(){
8626             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8627         },
8628
8629         /**
8630          * Return a box {x, y, width, height} that can be used to set another elements
8631          * size/location to match this element.
8632          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8633          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8634          * @return {Object} box An object in the format {x, y, width, height}
8635          */
8636         getBox : function(contentBox, local){
8637             var xy;
8638             if(!local){
8639                 xy = this.getXY();
8640             }else{
8641                 var left = parseInt(this.getStyle("left"), 10) || 0;
8642                 var top = parseInt(this.getStyle("top"), 10) || 0;
8643                 xy = [left, top];
8644             }
8645             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8646             if(!contentBox){
8647                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8648             }else{
8649                 var l = this.getBorderWidth("l")+this.getPadding("l");
8650                 var r = this.getBorderWidth("r")+this.getPadding("r");
8651                 var t = this.getBorderWidth("t")+this.getPadding("t");
8652                 var b = this.getBorderWidth("b")+this.getPadding("b");
8653                 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)};
8654             }
8655             bx.right = bx.x + bx.width;
8656             bx.bottom = bx.y + bx.height;
8657             return bx;
8658         },
8659
8660         /**
8661          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8662          for more information about the sides.
8663          * @param {String} sides
8664          * @return {Number}
8665          */
8666         getFrameWidth : function(sides, onlyContentBox){
8667             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8668         },
8669
8670         /**
8671          * 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.
8672          * @param {Object} box The box to fill {x, y, width, height}
8673          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8675          * @return {Roo.Element} this
8676          */
8677         setBox : function(box, adjust, animate){
8678             var w = box.width, h = box.height;
8679             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8680                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8681                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8682             }
8683             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8684             return this;
8685         },
8686
8687         /**
8688          * Forces the browser to repaint this element
8689          * @return {Roo.Element} this
8690          */
8691          repaint : function(){
8692             var dom = this.dom;
8693             this.addClass("x-repaint");
8694             setTimeout(function(){
8695                 Roo.get(dom).removeClass("x-repaint");
8696             }, 1);
8697             return this;
8698         },
8699
8700         /**
8701          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8702          * then it returns the calculated width of the sides (see getPadding)
8703          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8704          * @return {Object/Number}
8705          */
8706         getMargins : function(side){
8707             if(!side){
8708                 return {
8709                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8710                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8711                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8712                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8713                 };
8714             }else{
8715                 return this.addStyles(side, El.margins);
8716              }
8717         },
8718
8719         // private
8720         addStyles : function(sides, styles){
8721             var val = 0, v, w;
8722             for(var i = 0, len = sides.length; i < len; i++){
8723                 v = this.getStyle(styles[sides.charAt(i)]);
8724                 if(v){
8725                      w = parseInt(v, 10);
8726                      if(w){ val += w; }
8727                 }
8728             }
8729             return val;
8730         },
8731
8732         /**
8733          * Creates a proxy element of this element
8734          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8735          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8736          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8737          * @return {Roo.Element} The new proxy element
8738          */
8739         createProxy : function(config, renderTo, matchBox){
8740             if(renderTo){
8741                 renderTo = Roo.getDom(renderTo);
8742             }else{
8743                 renderTo = document.body;
8744             }
8745             config = typeof config == "object" ?
8746                 config : {tag : "div", cls: config};
8747             var proxy = Roo.DomHelper.append(renderTo, config, true);
8748             if(matchBox){
8749                proxy.setBox(this.getBox());
8750             }
8751             return proxy;
8752         },
8753
8754         /**
8755          * Puts a mask over this element to disable user interaction. Requires core.css.
8756          * This method can only be applied to elements which accept child nodes.
8757          * @param {String} msg (optional) A message to display in the mask
8758          * @param {String} msgCls (optional) A css class to apply to the msg element
8759          * @return {Element} The mask  element
8760          */
8761         mask : function(msg, msgCls){
8762             if(this.getStyle("position") == "static"){
8763                 this.setStyle("position", "relative");
8764             }
8765             if(!this._mask){
8766                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8767             }
8768             this.addClass("x-masked");
8769             this._mask.setDisplayed(true);
8770             if(typeof msg == 'string'){
8771                 if(!this._maskMsg){
8772                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8773                 }
8774                 var mm = this._maskMsg;
8775                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8776                 mm.dom.firstChild.innerHTML = msg;
8777                 mm.setDisplayed(true);
8778                 mm.center(this);
8779             }
8780             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8781                 this._mask.setHeight(this.getHeight());
8782             }
8783             return this._mask;
8784         },
8785
8786         /**
8787          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8788          * it is cached for reuse.
8789          */
8790         unmask : function(removeEl){
8791             if(this._mask){
8792                 if(removeEl === true){
8793                     this._mask.remove();
8794                     delete this._mask;
8795                     if(this._maskMsg){
8796                         this._maskMsg.remove();
8797                         delete this._maskMsg;
8798                     }
8799                 }else{
8800                     this._mask.setDisplayed(false);
8801                     if(this._maskMsg){
8802                         this._maskMsg.setDisplayed(false);
8803                     }
8804                 }
8805             }
8806             this.removeClass("x-masked");
8807         },
8808
8809         /**
8810          * Returns true if this element is masked
8811          * @return {Boolean}
8812          */
8813         isMasked : function(){
8814             return this._mask && this._mask.isVisible();
8815         },
8816
8817         /**
8818          * Creates an iframe shim for this element to keep selects and other windowed objects from
8819          * showing through.
8820          * @return {Roo.Element} The new shim element
8821          */
8822         createShim : function(){
8823             var el = document.createElement('iframe');
8824             el.frameBorder = 'no';
8825             el.className = 'roo-shim';
8826             if(Roo.isIE && Roo.isSecure){
8827                 el.src = Roo.SSL_SECURE_URL;
8828             }
8829             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8830             shim.autoBoxAdjust = false;
8831             return shim;
8832         },
8833
8834         /**
8835          * Removes this element from the DOM and deletes it from the cache
8836          */
8837         remove : function(){
8838             if(this.dom.parentNode){
8839                 this.dom.parentNode.removeChild(this.dom);
8840             }
8841             delete El.cache[this.dom.id];
8842         },
8843
8844         /**
8845          * Sets up event handlers to add and remove a css class when the mouse is over this element
8846          * @param {String} className
8847          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8848          * mouseout events for children elements
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnOver : function(className, preventFlicker){
8852             this.on("mouseover", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             var removeFn = function(e){
8856                 if(preventFlicker !== true || !e.within(this, true)){
8857                     Roo.fly(this, '_internal').removeClass(className);
8858                 }
8859             };
8860             this.on("mouseout", removeFn, this.dom);
8861             return this;
8862         },
8863
8864         /**
8865          * Sets up event handlers to add and remove a css class when this element has the focus
8866          * @param {String} className
8867          * @return {Roo.Element} this
8868          */
8869         addClassOnFocus : function(className){
8870             this.on("focus", function(){
8871                 Roo.fly(this, '_internal').addClass(className);
8872             }, this.dom);
8873             this.on("blur", function(){
8874                 Roo.fly(this, '_internal').removeClass(className);
8875             }, this.dom);
8876             return this;
8877         },
8878         /**
8879          * 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)
8880          * @param {String} className
8881          * @return {Roo.Element} this
8882          */
8883         addClassOnClick : function(className){
8884             var dom = this.dom;
8885             this.on("mousedown", function(){
8886                 Roo.fly(dom, '_internal').addClass(className);
8887                 var d = Roo.get(document);
8888                 var fn = function(){
8889                     Roo.fly(dom, '_internal').removeClass(className);
8890                     d.removeListener("mouseup", fn);
8891                 };
8892                 d.on("mouseup", fn);
8893             });
8894             return this;
8895         },
8896
8897         /**
8898          * Stops the specified event from bubbling and optionally prevents the default action
8899          * @param {String} eventName
8900          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8901          * @return {Roo.Element} this
8902          */
8903         swallowEvent : function(eventName, preventDefault){
8904             var fn = function(e){
8905                 e.stopPropagation();
8906                 if(preventDefault){
8907                     e.preventDefault();
8908                 }
8909             };
8910             if(eventName instanceof Array){
8911                 for(var i = 0, len = eventName.length; i < len; i++){
8912                      this.on(eventName[i], fn);
8913                 }
8914                 return this;
8915             }
8916             this.on(eventName, fn);
8917             return this;
8918         },
8919
8920         /**
8921          * @private
8922          */
8923       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8924
8925         /**
8926          * Sizes this element to its parent element's dimensions performing
8927          * neccessary box adjustments.
8928          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8929          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8930          * @return {Roo.Element} this
8931          */
8932         fitToParent : function(monitorResize, targetParent) {
8933           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8934           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8935           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8936             return;
8937           }
8938           var p = Roo.get(targetParent || this.dom.parentNode);
8939           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8940           if (monitorResize === true) {
8941             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8942             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8943           }
8944           return this;
8945         },
8946
8947         /**
8948          * Gets the next sibling, skipping text nodes
8949          * @return {HTMLElement} The next sibling or null
8950          */
8951         getNextSibling : function(){
8952             var n = this.dom.nextSibling;
8953             while(n && n.nodeType != 1){
8954                 n = n.nextSibling;
8955             }
8956             return n;
8957         },
8958
8959         /**
8960          * Gets the previous sibling, skipping text nodes
8961          * @return {HTMLElement} The previous sibling or null
8962          */
8963         getPrevSibling : function(){
8964             var n = this.dom.previousSibling;
8965             while(n && n.nodeType != 1){
8966                 n = n.previousSibling;
8967             }
8968             return n;
8969         },
8970
8971
8972         /**
8973          * Appends the passed element(s) to this element
8974          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8975          * @return {Roo.Element} this
8976          */
8977         appendChild: function(el){
8978             el = Roo.get(el);
8979             el.appendTo(this);
8980             return this;
8981         },
8982
8983         /**
8984          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8985          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8986          * automatically generated with the specified attributes.
8987          * @param {HTMLElement} insertBefore (optional) a child element of this element
8988          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8989          * @return {Roo.Element} The new child element
8990          */
8991         createChild: function(config, insertBefore, returnDom){
8992             config = config || {tag:'div'};
8993             if(insertBefore){
8994                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8995             }
8996             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8997         },
8998
8999         /**
9000          * Appends this element to the passed element
9001          * @param {String/HTMLElement/Element} el The new parent element
9002          * @return {Roo.Element} this
9003          */
9004         appendTo: function(el){
9005             el = Roo.getDom(el);
9006             el.appendChild(this.dom);
9007             return this;
9008         },
9009
9010         /**
9011          * Inserts this element before the passed element in the DOM
9012          * @param {String/HTMLElement/Element} el The element to insert before
9013          * @return {Roo.Element} this
9014          */
9015         insertBefore: function(el){
9016             el = Roo.getDom(el);
9017             el.parentNode.insertBefore(this.dom, el);
9018             return this;
9019         },
9020
9021         /**
9022          * Inserts this element after the passed element in the DOM
9023          * @param {String/HTMLElement/Element} el The element to insert after
9024          * @return {Roo.Element} this
9025          */
9026         insertAfter: function(el){
9027             el = Roo.getDom(el);
9028             el.parentNode.insertBefore(this.dom, el.nextSibling);
9029             return this;
9030         },
9031
9032         /**
9033          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9034          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9035          * @return {Roo.Element} The new child
9036          */
9037         insertFirst: function(el, returnDom){
9038             el = el || {};
9039             if(typeof el == 'object' && !el.nodeType){ // dh config
9040                 return this.createChild(el, this.dom.firstChild, returnDom);
9041             }else{
9042                 el = Roo.getDom(el);
9043                 this.dom.insertBefore(el, this.dom.firstChild);
9044                 return !returnDom ? Roo.get(el) : el;
9045             }
9046         },
9047
9048         /**
9049          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9050          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9051          * @param {String} where (optional) 'before' or 'after' defaults to before
9052          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9053          * @return {Roo.Element} the inserted Element
9054          */
9055         insertSibling: function(el, where, returnDom){
9056             where = where ? where.toLowerCase() : 'before';
9057             el = el || {};
9058             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9059
9060             if(typeof el == 'object' && !el.nodeType){ // dh config
9061                 if(where == 'after' && !this.dom.nextSibling){
9062                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9063                 }else{
9064                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9065                 }
9066
9067             }else{
9068                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9069                             where == 'before' ? this.dom : this.dom.nextSibling);
9070                 if(!returnDom){
9071                     rt = Roo.get(rt);
9072                 }
9073             }
9074             return rt;
9075         },
9076
9077         /**
9078          * Creates and wraps this element with another element
9079          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9080          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9081          * @return {HTMLElement/Element} The newly created wrapper element
9082          */
9083         wrap: function(config, returnDom){
9084             if(!config){
9085                 config = {tag: "div"};
9086             }
9087             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9088             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9089             return newEl;
9090         },
9091
9092         /**
9093          * Replaces the passed element with this element
9094          * @param {String/HTMLElement/Element} el The element to replace
9095          * @return {Roo.Element} this
9096          */
9097         replace: function(el){
9098             el = Roo.get(el);
9099             this.insertBefore(el);
9100             el.remove();
9101             return this;
9102         },
9103
9104         /**
9105          * Inserts an html fragment into this element
9106          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9107          * @param {String} html The HTML fragment
9108          * @param {Boolean} returnEl True to return an Roo.Element
9109          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9110          */
9111         insertHtml : function(where, html, returnEl){
9112             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9113             return returnEl ? Roo.get(el) : el;
9114         },
9115
9116         /**
9117          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9118          * @param {Object} o The object with the attributes
9119          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9120          * @return {Roo.Element} this
9121          */
9122         set : function(o, useSet){
9123             var el = this.dom;
9124             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9125             for(var attr in o){
9126                 if(attr == "style" || typeof o[attr] == "function") continue;
9127                 if(attr=="cls"){
9128                     el.className = o["cls"];
9129                 }else{
9130                     if(useSet) el.setAttribute(attr, o[attr]);
9131                     else el[attr] = o[attr];
9132                 }
9133             }
9134             if(o.style){
9135                 Roo.DomHelper.applyStyles(el, o.style);
9136             }
9137             return this;
9138         },
9139
9140         /**
9141          * Convenience method for constructing a KeyMap
9142          * @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:
9143          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9144          * @param {Function} fn The function to call
9145          * @param {Object} scope (optional) The scope of the function
9146          * @return {Roo.KeyMap} The KeyMap created
9147          */
9148         addKeyListener : function(key, fn, scope){
9149             var config;
9150             if(typeof key != "object" || key instanceof Array){
9151                 config = {
9152                     key: key,
9153                     fn: fn,
9154                     scope: scope
9155                 };
9156             }else{
9157                 config = {
9158                     key : key.key,
9159                     shift : key.shift,
9160                     ctrl : key.ctrl,
9161                     alt : key.alt,
9162                     fn: fn,
9163                     scope: scope
9164                 };
9165             }
9166             return new Roo.KeyMap(this, config);
9167         },
9168
9169         /**
9170          * Creates a KeyMap for this element
9171          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9172          * @return {Roo.KeyMap} The KeyMap created
9173          */
9174         addKeyMap : function(config){
9175             return new Roo.KeyMap(this, config);
9176         },
9177
9178         /**
9179          * Returns true if this element is scrollable.
9180          * @return {Boolean}
9181          */
9182          isScrollable : function(){
9183             var dom = this.dom;
9184             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9185         },
9186
9187         /**
9188          * 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().
9189          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9190          * @param {Number} value The new scroll value
9191          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9192          * @return {Element} this
9193          */
9194
9195         scrollTo : function(side, value, animate){
9196             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9197             if(!animate || !A){
9198                 this.dom[prop] = value;
9199             }else{
9200                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9201                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9202             }
9203             return this;
9204         },
9205
9206         /**
9207          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9208          * within this element's scrollable range.
9209          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9210          * @param {Number} distance How far to scroll the element in pixels
9211          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9212          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9213          * was scrolled as far as it could go.
9214          */
9215          scroll : function(direction, distance, animate){
9216              if(!this.isScrollable()){
9217                  return;
9218              }
9219              var el = this.dom;
9220              var l = el.scrollLeft, t = el.scrollTop;
9221              var w = el.scrollWidth, h = el.scrollHeight;
9222              var cw = el.clientWidth, ch = el.clientHeight;
9223              direction = direction.toLowerCase();
9224              var scrolled = false;
9225              var a = this.preanim(arguments, 2);
9226              switch(direction){
9227                  case "l":
9228                  case "left":
9229                      if(w - l > cw){
9230                          var v = Math.min(l + distance, w-cw);
9231                          this.scrollTo("left", v, a);
9232                          scrolled = true;
9233                      }
9234                      break;
9235                 case "r":
9236                 case "right":
9237                      if(l > 0){
9238                          var v = Math.max(l - distance, 0);
9239                          this.scrollTo("left", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243                 case "t":
9244                 case "top":
9245                 case "up":
9246                      if(t > 0){
9247                          var v = Math.max(t - distance, 0);
9248                          this.scrollTo("top", v, a);
9249                          scrolled = true;
9250                      }
9251                      break;
9252                 case "b":
9253                 case "bottom":
9254                 case "down":
9255                      if(h - t > ch){
9256                          var v = Math.min(t + distance, h-ch);
9257                          this.scrollTo("top", v, a);
9258                          scrolled = true;
9259                      }
9260                      break;
9261              }
9262              return scrolled;
9263         },
9264
9265         /**
9266          * Translates the passed page coordinates into left/top css values for this element
9267          * @param {Number/Array} x The page x or an array containing [x, y]
9268          * @param {Number} y The page y
9269          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9270          */
9271         translatePoints : function(x, y){
9272             if(typeof x == 'object' || x instanceof Array){
9273                 y = x[1]; x = x[0];
9274             }
9275             var p = this.getStyle('position');
9276             var o = this.getXY();
9277
9278             var l = parseInt(this.getStyle('left'), 10);
9279             var t = parseInt(this.getStyle('top'), 10);
9280
9281             if(isNaN(l)){
9282                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9283             }
9284             if(isNaN(t)){
9285                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9286             }
9287
9288             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9289         },
9290
9291         /**
9292          * Returns the current scroll position of the element.
9293          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9294          */
9295         getScroll : function(){
9296             var d = this.dom, doc = document;
9297             if(d == doc || d == doc.body){
9298                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9299                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9300                 return {left: l, top: t};
9301             }else{
9302                 return {left: d.scrollLeft, top: d.scrollTop};
9303             }
9304         },
9305
9306         /**
9307          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9308          * are convert to standard 6 digit hex color.
9309          * @param {String} attr The css attribute
9310          * @param {String} defaultValue The default value to use when a valid color isn't found
9311          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9312          * YUI color anims.
9313          */
9314         getColor : function(attr, defaultValue, prefix){
9315             var v = this.getStyle(attr);
9316             if(!v || v == "transparent" || v == "inherit") {
9317                 return defaultValue;
9318             }
9319             var color = typeof prefix == "undefined" ? "#" : prefix;
9320             if(v.substr(0, 4) == "rgb("){
9321                 var rvs = v.slice(4, v.length -1).split(",");
9322                 for(var i = 0; i < 3; i++){
9323                     var h = parseInt(rvs[i]).toString(16);
9324                     if(h < 16){
9325                         h = "0" + h;
9326                     }
9327                     color += h;
9328                 }
9329             } else {
9330                 if(v.substr(0, 1) == "#"){
9331                     if(v.length == 4) {
9332                         for(var i = 1; i < 4; i++){
9333                             var c = v.charAt(i);
9334                             color +=  c + c;
9335                         }
9336                     }else if(v.length == 7){
9337                         color += v.substr(1);
9338                     }
9339                 }
9340             }
9341             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9342         },
9343
9344         /**
9345          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9346          * gradient background, rounded corners and a 4-way shadow.
9347          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9348          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9349          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9350          * @return {Roo.Element} this
9351          */
9352         boxWrap : function(cls){
9353             cls = cls || 'x-box';
9354             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9355             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9356             return el;
9357         },
9358
9359         /**
9360          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9361          * @param {String} namespace The namespace in which to look for the attribute
9362          * @param {String} name The attribute name
9363          * @return {String} The attribute value
9364          */
9365         getAttributeNS : Roo.isIE ? function(ns, name){
9366             var d = this.dom;
9367             var type = typeof d[ns+":"+name];
9368             if(type != 'undefined' && type != 'unknown'){
9369                 return d[ns+":"+name];
9370             }
9371             return d[name];
9372         } : function(ns, name){
9373             var d = this.dom;
9374             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9375         }
9376     };
9377
9378     var ep = El.prototype;
9379
9380     /**
9381      * Appends an event handler (Shorthand for addListener)
9382      * @param {String}   eventName     The type of event to append
9383      * @param {Function} fn        The method the event invokes
9384      * @param {Object} scope       (optional) The scope (this object) of the fn
9385      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9386      * @method
9387      */
9388     ep.on = ep.addListener;
9389         // backwards compat
9390     ep.mon = ep.addListener;
9391
9392     /**
9393      * Removes an event handler from this element (shorthand for removeListener)
9394      * @param {String} eventName the type of event to remove
9395      * @param {Function} fn the method the event invokes
9396      * @return {Roo.Element} this
9397      * @method
9398      */
9399     ep.un = ep.removeListener;
9400
9401     /**
9402      * true to automatically adjust width and height settings for box-model issues (default to true)
9403      */
9404     ep.autoBoxAdjust = true;
9405
9406     // private
9407     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9408
9409     // private
9410     El.addUnits = function(v, defaultUnit){
9411         if(v === "" || v == "auto"){
9412             return v;
9413         }
9414         if(v === undefined){
9415             return '';
9416         }
9417         if(typeof v == "number" || !El.unitPattern.test(v)){
9418             return v + (defaultUnit || 'px');
9419         }
9420         return v;
9421     };
9422
9423     // special markup used throughout Roo when box wrapping elements
9424     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>';
9425     /**
9426      * Visibility mode constant - Use visibility to hide element
9427      * @static
9428      * @type Number
9429      */
9430     El.VISIBILITY = 1;
9431     /**
9432      * Visibility mode constant - Use display to hide element
9433      * @static
9434      * @type Number
9435      */
9436     El.DISPLAY = 2;
9437
9438     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9439     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9440     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9441
9442
9443
9444     /**
9445      * @private
9446      */
9447     El.cache = {};
9448
9449     var docEl;
9450
9451     /**
9452      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9453      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9454      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9455      * @return {Element} The Element object
9456      * @static
9457      */
9458     El.get = function(el){
9459         var ex, elm, id;
9460         if(!el){ return null; }
9461         if(typeof el == "string"){ // element id
9462             if(!(elm = document.getElementById(el))){
9463                 return null;
9464             }
9465             if(ex = El.cache[el]){
9466                 ex.dom = elm;
9467             }else{
9468                 ex = El.cache[el] = new El(elm);
9469             }
9470             return ex;
9471         }else if(el.tagName){ // dom element
9472             if(!(id = el.id)){
9473                 id = Roo.id(el);
9474             }
9475             if(ex = El.cache[id]){
9476                 ex.dom = el;
9477             }else{
9478                 ex = El.cache[id] = new El(el);
9479             }
9480             return ex;
9481         }else if(el instanceof El){
9482             if(el != docEl){
9483                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9484                                                               // catch case where it hasn't been appended
9485                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9486             }
9487             return el;
9488         }else if(el.isComposite){
9489             return el;
9490         }else if(el instanceof Array){
9491             return El.select(el);
9492         }else if(el == document){
9493             // create a bogus element object representing the document object
9494             if(!docEl){
9495                 var f = function(){};
9496                 f.prototype = El.prototype;
9497                 docEl = new f();
9498                 docEl.dom = document;
9499             }
9500             return docEl;
9501         }
9502         return null;
9503     };
9504
9505     // private
9506     El.uncache = function(el){
9507         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9508             if(a[i]){
9509                 delete El.cache[a[i].id || a[i]];
9510             }
9511         }
9512     };
9513
9514     // private
9515     // Garbage collection - uncache elements/purge listeners on orphaned elements
9516     // so we don't hold a reference and cause the browser to retain them
9517     El.garbageCollect = function(){
9518         if(!Roo.enableGarbageCollector){
9519             clearInterval(El.collectorThread);
9520             return;
9521         }
9522         for(var eid in El.cache){
9523             var el = El.cache[eid], d = el.dom;
9524             // -------------------------------------------------------
9525             // Determining what is garbage:
9526             // -------------------------------------------------------
9527             // !d
9528             // dom node is null, definitely garbage
9529             // -------------------------------------------------------
9530             // !d.parentNode
9531             // no parentNode == direct orphan, definitely garbage
9532             // -------------------------------------------------------
9533             // !d.offsetParent && !document.getElementById(eid)
9534             // display none elements have no offsetParent so we will
9535             // also try to look it up by it's id. However, check
9536             // offsetParent first so we don't do unneeded lookups.
9537             // This enables collection of elements that are not orphans
9538             // directly, but somewhere up the line they have an orphan
9539             // parent.
9540             // -------------------------------------------------------
9541             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9542                 delete El.cache[eid];
9543                 if(d && Roo.enableListenerCollection){
9544                     E.purgeElement(d);
9545                 }
9546             }
9547         }
9548     }
9549     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9550
9551
9552     // dom is optional
9553     El.Flyweight = function(dom){
9554         this.dom = dom;
9555     };
9556     El.Flyweight.prototype = El.prototype;
9557
9558     El._flyweights = {};
9559     /**
9560      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9561      * the dom node can be overwritten by other code.
9562      * @param {String/HTMLElement} el The dom node or id
9563      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9564      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9565      * @static
9566      * @return {Element} The shared Element object
9567      */
9568     El.fly = function(el, named){
9569         named = named || '_global';
9570         el = Roo.getDom(el);
9571         if(!el){
9572             return null;
9573         }
9574         if(!El._flyweights[named]){
9575             El._flyweights[named] = new El.Flyweight();
9576         }
9577         El._flyweights[named].dom = el;
9578         return El._flyweights[named];
9579     };
9580
9581     /**
9582      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9583      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9584      * Shorthand of {@link Roo.Element#get}
9585      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9586      * @return {Element} The Element object
9587      * @member Roo
9588      * @method get
9589      */
9590     Roo.get = El.get;
9591     /**
9592      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9593      * the dom node can be overwritten by other code.
9594      * Shorthand of {@link Roo.Element#fly}
9595      * @param {String/HTMLElement} el The dom node or id
9596      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9597      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9598      * @static
9599      * @return {Element} The shared Element object
9600      * @member Roo
9601      * @method fly
9602      */
9603     Roo.fly = El.fly;
9604
9605     // speedy lookup for elements never to box adjust
9606     var noBoxAdjust = Roo.isStrict ? {
9607         select:1
9608     } : {
9609         input:1, select:1, textarea:1
9610     };
9611     if(Roo.isIE || Roo.isGecko){
9612         noBoxAdjust['button'] = 1;
9613     }
9614
9615
9616     Roo.EventManager.on(window, 'unload', function(){
9617         delete El.cache;
9618         delete El._flyweights;
9619     });
9620 })();
9621
9622
9623
9624
9625 if(Roo.DomQuery){
9626     Roo.Element.selectorFunction = Roo.DomQuery.select;
9627 }
9628
9629 Roo.Element.select = function(selector, unique, root){
9630     var els;
9631     if(typeof selector == "string"){
9632         els = Roo.Element.selectorFunction(selector, root);
9633     }else if(selector.length !== undefined){
9634         els = selector;
9635     }else{
9636         throw "Invalid selector";
9637     }
9638     if(unique === true){
9639         return new Roo.CompositeElement(els);
9640     }else{
9641         return new Roo.CompositeElementLite(els);
9642     }
9643 };
9644 /**
9645  * Selects elements based on the passed CSS selector to enable working on them as 1.
9646  * @param {String/Array} selector The CSS selector or an array of elements
9647  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9648  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9649  * @return {CompositeElementLite/CompositeElement}
9650  * @member Roo
9651  * @method select
9652  */
9653 Roo.select = Roo.Element.select;
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666
9667
9668 /*
9669  * Based on:
9670  * Ext JS Library 1.1.1
9671  * Copyright(c) 2006-2007, Ext JS, LLC.
9672  *
9673  * Originally Released Under LGPL - original licence link has changed is not relivant.
9674  *
9675  * Fork - LGPL
9676  * <script type="text/javascript">
9677  */
9678
9679
9680
9681 //Notifies Element that fx methods are available
9682 Roo.enableFx = true;
9683
9684 /**
9685  * @class Roo.Fx
9686  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9687  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9688  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9689  * Element effects to work.</p><br/>
9690  *
9691  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9692  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9693  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9694  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9695  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9696  * expected results and should be done with care.</p><br/>
9697  *
9698  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9699  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9700 <pre>
9701 Value  Description
9702 -----  -----------------------------
9703 tl     The top left corner
9704 t      The center of the top edge
9705 tr     The top right corner
9706 l      The center of the left edge
9707 r      The center of the right edge
9708 bl     The bottom left corner
9709 b      The center of the bottom edge
9710 br     The bottom right corner
9711 </pre>
9712  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9713  * below are common options that can be passed to any Fx method.</b>
9714  * @cfg {Function} callback A function called when the effect is finished
9715  * @cfg {Object} scope The scope of the effect function
9716  * @cfg {String} easing A valid Easing value for the effect
9717  * @cfg {String} afterCls A css class to apply after the effect
9718  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9719  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9720  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9721  * effects that end with the element being visually hidden, ignored otherwise)
9722  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9723  * a function which returns such a specification that will be applied to the Element after the effect finishes
9724  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9725  * @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
9726  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9727  */
9728 Roo.Fx = {
9729         /**
9730          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9731          * origin for the slide effect.  This function automatically handles wrapping the element with
9732          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9733          * Usage:
9734          *<pre><code>
9735 // default: slide the element in from the top
9736 el.slideIn();
9737
9738 // custom: slide the element in from the right with a 2-second duration
9739 el.slideIn('r', { duration: 2 });
9740
9741 // common config options shown with default values
9742 el.slideIn('t', {
9743     easing: 'easeOut',
9744     duration: .5
9745 });
9746 </code></pre>
9747          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9748          * @param {Object} options (optional) Object literal with any of the Fx config options
9749          * @return {Roo.Element} The Element
9750          */
9751     slideIn : function(anchor, o){
9752         var el = this.getFxEl();
9753         o = o || {};
9754
9755         el.queueFx(o, function(){
9756
9757             anchor = anchor || "t";
9758
9759             // fix display to visibility
9760             this.fixDisplay();
9761
9762             // restore values after effect
9763             var r = this.getFxRestore();
9764             var b = this.getBox();
9765             // fixed size for slide
9766             this.setSize(b);
9767
9768             // wrap if needed
9769             var wrap = this.fxWrap(r.pos, o, "hidden");
9770
9771             var st = this.dom.style;
9772             st.visibility = "visible";
9773             st.position = "absolute";
9774
9775             // clear out temp styles after slide and unwrap
9776             var after = function(){
9777                 el.fxUnwrap(wrap, r.pos, o);
9778                 st.width = r.width;
9779                 st.height = r.height;
9780                 el.afterFx(o);
9781             };
9782             // time to calc the positions
9783             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9784
9785             switch(anchor.toLowerCase()){
9786                 case "t":
9787                     wrap.setSize(b.width, 0);
9788                     st.left = st.bottom = "0";
9789                     a = {height: bh};
9790                 break;
9791                 case "l":
9792                     wrap.setSize(0, b.height);
9793                     st.right = st.top = "0";
9794                     a = {width: bw};
9795                 break;
9796                 case "r":
9797                     wrap.setSize(0, b.height);
9798                     wrap.setX(b.right);
9799                     st.left = st.top = "0";
9800                     a = {width: bw, points: pt};
9801                 break;
9802                 case "b":
9803                     wrap.setSize(b.width, 0);
9804                     wrap.setY(b.bottom);
9805                     st.left = st.top = "0";
9806                     a = {height: bh, points: pt};
9807                 break;
9808                 case "tl":
9809                     wrap.setSize(0, 0);
9810                     st.right = st.bottom = "0";
9811                     a = {width: bw, height: bh};
9812                 break;
9813                 case "bl":
9814                     wrap.setSize(0, 0);
9815                     wrap.setY(b.y+b.height);
9816                     st.right = st.top = "0";
9817                     a = {width: bw, height: bh, points: pt};
9818                 break;
9819                 case "br":
9820                     wrap.setSize(0, 0);
9821                     wrap.setXY([b.right, b.bottom]);
9822                     st.left = st.top = "0";
9823                     a = {width: bw, height: bh, points: pt};
9824                 break;
9825                 case "tr":
9826                     wrap.setSize(0, 0);
9827                     wrap.setX(b.x+b.width);
9828                     st.left = st.bottom = "0";
9829                     a = {width: bw, height: bh, points: pt};
9830                 break;
9831             }
9832             this.dom.style.visibility = "visible";
9833             wrap.show();
9834
9835             arguments.callee.anim = wrap.fxanim(a,
9836                 o,
9837                 'motion',
9838                 .5,
9839                 'easeOut', after);
9840         });
9841         return this;
9842     },
9843     
9844         /**
9845          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9846          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9847          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9848          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9849          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9850          * Usage:
9851          *<pre><code>
9852 // default: slide the element out to the top
9853 el.slideOut();
9854
9855 // custom: slide the element out to the right with a 2-second duration
9856 el.slideOut('r', { duration: 2 });
9857
9858 // common config options shown with default values
9859 el.slideOut('t', {
9860     easing: 'easeOut',
9861     duration: .5,
9862     remove: false,
9863     useDisplay: false
9864 });
9865 </code></pre>
9866          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9867          * @param {Object} options (optional) Object literal with any of the Fx config options
9868          * @return {Roo.Element} The Element
9869          */
9870     slideOut : function(anchor, o){
9871         var el = this.getFxEl();
9872         o = o || {};
9873
9874         el.queueFx(o, function(){
9875
9876             anchor = anchor || "t";
9877
9878             // restore values after effect
9879             var r = this.getFxRestore();
9880             
9881             var b = this.getBox();
9882             // fixed size for slide
9883             this.setSize(b);
9884
9885             // wrap if needed
9886             var wrap = this.fxWrap(r.pos, o, "visible");
9887
9888             var st = this.dom.style;
9889             st.visibility = "visible";
9890             st.position = "absolute";
9891
9892             wrap.setSize(b);
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.fxUnwrap(wrap, r.pos, o);
9902
9903                 st.width = r.width;
9904                 st.height = r.height;
9905
9906                 el.afterFx(o);
9907             };
9908
9909             var a, zero = {to: 0};
9910             switch(anchor.toLowerCase()){
9911                 case "t":
9912                     st.left = st.bottom = "0";
9913                     a = {height: zero};
9914                 break;
9915                 case "l":
9916                     st.right = st.top = "0";
9917                     a = {width: zero};
9918                 break;
9919                 case "r":
9920                     st.left = st.top = "0";
9921                     a = {width: zero, points: {to:[b.right, b.y]}};
9922                 break;
9923                 case "b":
9924                     st.left = st.top = "0";
9925                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9926                 break;
9927                 case "tl":
9928                     st.right = st.bottom = "0";
9929                     a = {width: zero, height: zero};
9930                 break;
9931                 case "bl":
9932                     st.right = st.top = "0";
9933                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9934                 break;
9935                 case "br":
9936                     st.left = st.top = "0";
9937                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9938                 break;
9939                 case "tr":
9940                     st.left = st.bottom = "0";
9941                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9942                 break;
9943             }
9944
9945             arguments.callee.anim = wrap.fxanim(a,
9946                 o,
9947                 'motion',
9948                 .5,
9949                 "easeOut", after);
9950         });
9951         return this;
9952     },
9953
9954         /**
9955          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9956          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9957          * The element must be removed from the DOM using the 'remove' config option if desired.
9958          * Usage:
9959          *<pre><code>
9960 // default
9961 el.puff();
9962
9963 // common config options shown with default values
9964 el.puff({
9965     easing: 'easeOut',
9966     duration: .5,
9967     remove: false,
9968     useDisplay: false
9969 });
9970 </code></pre>
9971          * @param {Object} options (optional) Object literal with any of the Fx config options
9972          * @return {Roo.Element} The Element
9973          */
9974     puff : function(o){
9975         var el = this.getFxEl();
9976         o = o || {};
9977
9978         el.queueFx(o, function(){
9979             this.clearOpacity();
9980             this.show();
9981
9982             // restore values after effect
9983             var r = this.getFxRestore();
9984             var st = this.dom.style;
9985
9986             var after = function(){
9987                 if(o.useDisplay){
9988                     el.setDisplayed(false);
9989                 }else{
9990                     el.hide();
9991                 }
9992
9993                 el.clearOpacity();
9994
9995                 el.setPositioning(r.pos);
9996                 st.width = r.width;
9997                 st.height = r.height;
9998                 st.fontSize = '';
9999                 el.afterFx(o);
10000             };
10001
10002             var width = this.getWidth();
10003             var height = this.getHeight();
10004
10005             arguments.callee.anim = this.fxanim({
10006                     width : {to: this.adjustWidth(width * 2)},
10007                     height : {to: this.adjustHeight(height * 2)},
10008                     points : {by: [-(width * .5), -(height * .5)]},
10009                     opacity : {to: 0},
10010                     fontSize: {to:200, unit: "%"}
10011                 },
10012                 o,
10013                 'motion',
10014                 .5,
10015                 "easeOut", after);
10016         });
10017         return this;
10018     },
10019
10020         /**
10021          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10022          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10023          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10024          * Usage:
10025          *<pre><code>
10026 // default
10027 el.switchOff();
10028
10029 // all config options shown with default values
10030 el.switchOff({
10031     easing: 'easeIn',
10032     duration: .3,
10033     remove: false,
10034     useDisplay: false
10035 });
10036 </code></pre>
10037          * @param {Object} options (optional) Object literal with any of the Fx config options
10038          * @return {Roo.Element} The Element
10039          */
10040     switchOff : function(o){
10041         var el = this.getFxEl();
10042         o = o || {};
10043
10044         el.queueFx(o, function(){
10045             this.clearOpacity();
10046             this.clip();
10047
10048             // restore values after effect
10049             var r = this.getFxRestore();
10050             var st = this.dom.style;
10051
10052             var after = function(){
10053                 if(o.useDisplay){
10054                     el.setDisplayed(false);
10055                 }else{
10056                     el.hide();
10057                 }
10058
10059                 el.clearOpacity();
10060                 el.setPositioning(r.pos);
10061                 st.width = r.width;
10062                 st.height = r.height;
10063
10064                 el.afterFx(o);
10065             };
10066
10067             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10068                 this.clearOpacity();
10069                 (function(){
10070                     this.fxanim({
10071                         height:{to:1},
10072                         points:{by:[0, this.getHeight() * .5]}
10073                     }, o, 'motion', 0.3, 'easeIn', after);
10074                 }).defer(100, this);
10075             });
10076         });
10077         return this;
10078     },
10079
10080     /**
10081      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10082      * changed using the "attr" config option) and then fading back to the original color. If no original
10083      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10084      * Usage:
10085 <pre><code>
10086 // default: highlight background to yellow
10087 el.highlight();
10088
10089 // custom: highlight foreground text to blue for 2 seconds
10090 el.highlight("0000ff", { attr: 'color', duration: 2 });
10091
10092 // common config options shown with default values
10093 el.highlight("ffff9c", {
10094     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10095     endColor: (current color) or "ffffff",
10096     easing: 'easeIn',
10097     duration: 1
10098 });
10099 </code></pre>
10100      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10101      * @param {Object} options (optional) Object literal with any of the Fx config options
10102      * @return {Roo.Element} The Element
10103      */ 
10104     highlight : function(color, o){
10105         var el = this.getFxEl();
10106         o = o || {};
10107
10108         el.queueFx(o, function(){
10109             color = color || "ffff9c";
10110             attr = o.attr || "backgroundColor";
10111
10112             this.clearOpacity();
10113             this.show();
10114
10115             var origColor = this.getColor(attr);
10116             var restoreColor = this.dom.style[attr];
10117             endColor = (o.endColor || origColor) || "ffffff";
10118
10119             var after = function(){
10120                 el.dom.style[attr] = restoreColor;
10121                 el.afterFx(o);
10122             };
10123
10124             var a = {};
10125             a[attr] = {from: color, to: endColor};
10126             arguments.callee.anim = this.fxanim(a,
10127                 o,
10128                 'color',
10129                 1,
10130                 'easeIn', after);
10131         });
10132         return this;
10133     },
10134
10135    /**
10136     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10137     * Usage:
10138 <pre><code>
10139 // default: a single light blue ripple
10140 el.frame();
10141
10142 // custom: 3 red ripples lasting 3 seconds total
10143 el.frame("ff0000", 3, { duration: 3 });
10144
10145 // common config options shown with default values
10146 el.frame("C3DAF9", 1, {
10147     duration: 1 //duration of entire animation (not each individual ripple)
10148     // Note: Easing is not configurable and will be ignored if included
10149 });
10150 </code></pre>
10151     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10152     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10153     * @param {Object} options (optional) Object literal with any of the Fx config options
10154     * @return {Roo.Element} The Element
10155     */
10156     frame : function(color, count, o){
10157         var el = this.getFxEl();
10158         o = o || {};
10159
10160         el.queueFx(o, function(){
10161             color = color || "#C3DAF9";
10162             if(color.length == 6){
10163                 color = "#" + color;
10164             }
10165             count = count || 1;
10166             duration = o.duration || 1;
10167             this.show();
10168
10169             var b = this.getBox();
10170             var animFn = function(){
10171                 var proxy = this.createProxy({
10172
10173                      style:{
10174                         visbility:"hidden",
10175                         position:"absolute",
10176                         "z-index":"35000", // yee haw
10177                         border:"0px solid " + color
10178                      }
10179                   });
10180                 var scale = Roo.isBorderBox ? 2 : 1;
10181                 proxy.animate({
10182                     top:{from:b.y, to:b.y - 20},
10183                     left:{from:b.x, to:b.x - 20},
10184                     borderWidth:{from:0, to:10},
10185                     opacity:{from:1, to:0},
10186                     height:{from:b.height, to:(b.height + (20*scale))},
10187                     width:{from:b.width, to:(b.width + (20*scale))}
10188                 }, duration, function(){
10189                     proxy.remove();
10190                 });
10191                 if(--count > 0){
10192                      animFn.defer((duration/2)*1000, this);
10193                 }else{
10194                     el.afterFx(o);
10195                 }
10196             };
10197             animFn.call(this);
10198         });
10199         return this;
10200     },
10201
10202    /**
10203     * Creates a pause before any subsequent queued effects begin.  If there are
10204     * no effects queued after the pause it will have no effect.
10205     * Usage:
10206 <pre><code>
10207 el.pause(1);
10208 </code></pre>
10209     * @param {Number} seconds The length of time to pause (in seconds)
10210     * @return {Roo.Element} The Element
10211     */
10212     pause : function(seconds){
10213         var el = this.getFxEl();
10214         var o = {};
10215
10216         el.queueFx(o, function(){
10217             setTimeout(function(){
10218                 el.afterFx(o);
10219             }, seconds * 1000);
10220         });
10221         return this;
10222     },
10223
10224    /**
10225     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10226     * using the "endOpacity" config option.
10227     * Usage:
10228 <pre><code>
10229 // default: fade in from opacity 0 to 100%
10230 el.fadeIn();
10231
10232 // custom: fade in from opacity 0 to 75% over 2 seconds
10233 el.fadeIn({ endOpacity: .75, duration: 2});
10234
10235 // common config options shown with default values
10236 el.fadeIn({
10237     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10238     easing: 'easeOut',
10239     duration: .5
10240 });
10241 </code></pre>
10242     * @param {Object} options (optional) Object literal with any of the Fx config options
10243     * @return {Roo.Element} The Element
10244     */
10245     fadeIn : function(o){
10246         var el = this.getFxEl();
10247         o = o || {};
10248         el.queueFx(o, function(){
10249             this.setOpacity(0);
10250             this.fixDisplay();
10251             this.dom.style.visibility = 'visible';
10252             var to = o.endOpacity || 1;
10253             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10254                 o, null, .5, "easeOut", function(){
10255                 if(to == 1){
10256                     this.clearOpacity();
10257                 }
10258                 el.afterFx(o);
10259             });
10260         });
10261         return this;
10262     },
10263
10264    /**
10265     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10266     * using the "endOpacity" config option.
10267     * Usage:
10268 <pre><code>
10269 // default: fade out from the element's current opacity to 0
10270 el.fadeOut();
10271
10272 // custom: fade out from the element's current opacity to 25% over 2 seconds
10273 el.fadeOut({ endOpacity: .25, duration: 2});
10274
10275 // common config options shown with default values
10276 el.fadeOut({
10277     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10278     easing: 'easeOut',
10279     duration: .5
10280     remove: false,
10281     useDisplay: false
10282 });
10283 </code></pre>
10284     * @param {Object} options (optional) Object literal with any of the Fx config options
10285     * @return {Roo.Element} The Element
10286     */
10287     fadeOut : function(o){
10288         var el = this.getFxEl();
10289         o = o || {};
10290         el.queueFx(o, function(){
10291             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10292                 o, null, .5, "easeOut", function(){
10293                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10294                      this.dom.style.display = "none";
10295                 }else{
10296                      this.dom.style.visibility = "hidden";
10297                 }
10298                 this.clearOpacity();
10299                 el.afterFx(o);
10300             });
10301         });
10302         return this;
10303     },
10304
10305    /**
10306     * Animates the transition of an element's dimensions from a starting height/width
10307     * to an ending height/width.
10308     * Usage:
10309 <pre><code>
10310 // change height and width to 100x100 pixels
10311 el.scale(100, 100);
10312
10313 // common config options shown with default values.  The height and width will default to
10314 // the element's existing values if passed as null.
10315 el.scale(
10316     [element's width],
10317     [element's height], {
10318     easing: 'easeOut',
10319     duration: .35
10320 });
10321 </code></pre>
10322     * @param {Number} width  The new width (pass undefined to keep the original width)
10323     * @param {Number} height  The new height (pass undefined to keep the original height)
10324     * @param {Object} options (optional) Object literal with any of the Fx config options
10325     * @return {Roo.Element} The Element
10326     */
10327     scale : function(w, h, o){
10328         this.shift(Roo.apply({}, o, {
10329             width: w,
10330             height: h
10331         }));
10332         return this;
10333     },
10334
10335    /**
10336     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10337     * Any of these properties not specified in the config object will not be changed.  This effect 
10338     * requires that at least one new dimension, position or opacity setting must be passed in on
10339     * the config object in order for the function to have any effect.
10340     * Usage:
10341 <pre><code>
10342 // slide the element horizontally to x position 200 while changing the height and opacity
10343 el.shift({ x: 200, height: 50, opacity: .8 });
10344
10345 // common config options shown with default values.
10346 el.shift({
10347     width: [element's width],
10348     height: [element's height],
10349     x: [element's x position],
10350     y: [element's y position],
10351     opacity: [element's opacity],
10352     easing: 'easeOut',
10353     duration: .35
10354 });
10355 </code></pre>
10356     * @param {Object} options  Object literal with any of the Fx config options
10357     * @return {Roo.Element} The Element
10358     */
10359     shift : function(o){
10360         var el = this.getFxEl();
10361         o = o || {};
10362         el.queueFx(o, function(){
10363             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10364             if(w !== undefined){
10365                 a.width = {to: this.adjustWidth(w)};
10366             }
10367             if(h !== undefined){
10368                 a.height = {to: this.adjustHeight(h)};
10369             }
10370             if(x !== undefined || y !== undefined){
10371                 a.points = {to: [
10372                     x !== undefined ? x : this.getX(),
10373                     y !== undefined ? y : this.getY()
10374                 ]};
10375             }
10376             if(op !== undefined){
10377                 a.opacity = {to: op};
10378             }
10379             if(o.xy !== undefined){
10380                 a.points = {to: o.xy};
10381             }
10382             arguments.callee.anim = this.fxanim(a,
10383                 o, 'motion', .35, "easeOut", function(){
10384                 el.afterFx(o);
10385             });
10386         });
10387         return this;
10388     },
10389
10390         /**
10391          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10392          * ending point of the effect.
10393          * Usage:
10394          *<pre><code>
10395 // default: slide the element downward while fading out
10396 el.ghost();
10397
10398 // custom: slide the element out to the right with a 2-second duration
10399 el.ghost('r', { duration: 2 });
10400
10401 // common config options shown with default values
10402 el.ghost('b', {
10403     easing: 'easeOut',
10404     duration: .5
10405     remove: false,
10406     useDisplay: false
10407 });
10408 </code></pre>
10409          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10410          * @param {Object} options (optional) Object literal with any of the Fx config options
10411          * @return {Roo.Element} The Element
10412          */
10413     ghost : function(anchor, o){
10414         var el = this.getFxEl();
10415         o = o || {};
10416
10417         el.queueFx(o, function(){
10418             anchor = anchor || "b";
10419
10420             // restore values after effect
10421             var r = this.getFxRestore();
10422             var w = this.getWidth(),
10423                 h = this.getHeight();
10424
10425             var st = this.dom.style;
10426
10427             var after = function(){
10428                 if(o.useDisplay){
10429                     el.setDisplayed(false);
10430                 }else{
10431                     el.hide();
10432                 }
10433
10434                 el.clearOpacity();
10435                 el.setPositioning(r.pos);
10436                 st.width = r.width;
10437                 st.height = r.height;
10438
10439                 el.afterFx(o);
10440             };
10441
10442             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10443             switch(anchor.toLowerCase()){
10444                 case "t":
10445                     pt.by = [0, -h];
10446                 break;
10447                 case "l":
10448                     pt.by = [-w, 0];
10449                 break;
10450                 case "r":
10451                     pt.by = [w, 0];
10452                 break;
10453                 case "b":
10454                     pt.by = [0, h];
10455                 break;
10456                 case "tl":
10457                     pt.by = [-w, -h];
10458                 break;
10459                 case "bl":
10460                     pt.by = [-w, h];
10461                 break;
10462                 case "br":
10463                     pt.by = [w, h];
10464                 break;
10465                 case "tr":
10466                     pt.by = [w, -h];
10467                 break;
10468             }
10469
10470             arguments.callee.anim = this.fxanim(a,
10471                 o,
10472                 'motion',
10473                 .5,
10474                 "easeOut", after);
10475         });
10476         return this;
10477     },
10478
10479         /**
10480          * Ensures that all effects queued after syncFx is called on the element are
10481          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10482          * @return {Roo.Element} The Element
10483          */
10484     syncFx : function(){
10485         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10486             block : false,
10487             concurrent : true,
10488             stopFx : false
10489         });
10490         return this;
10491     },
10492
10493         /**
10494          * Ensures that all effects queued after sequenceFx is called on the element are
10495          * run in sequence.  This is the opposite of {@link #syncFx}.
10496          * @return {Roo.Element} The Element
10497          */
10498     sequenceFx : function(){
10499         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10500             block : false,
10501             concurrent : false,
10502             stopFx : false
10503         });
10504         return this;
10505     },
10506
10507         /* @private */
10508     nextFx : function(){
10509         var ef = this.fxQueue[0];
10510         if(ef){
10511             ef.call(this);
10512         }
10513     },
10514
10515         /**
10516          * Returns true if the element has any effects actively running or queued, else returns false.
10517          * @return {Boolean} True if element has active effects, else false
10518          */
10519     hasActiveFx : function(){
10520         return this.fxQueue && this.fxQueue[0];
10521     },
10522
10523         /**
10524          * Stops any running effects and clears the element's internal effects queue if it contains
10525          * any additional effects that haven't started yet.
10526          * @return {Roo.Element} The Element
10527          */
10528     stopFx : function(){
10529         if(this.hasActiveFx()){
10530             var cur = this.fxQueue[0];
10531             if(cur && cur.anim && cur.anim.isAnimated()){
10532                 this.fxQueue = [cur]; // clear out others
10533                 cur.anim.stop(true);
10534             }
10535         }
10536         return this;
10537     },
10538
10539         /* @private */
10540     beforeFx : function(o){
10541         if(this.hasActiveFx() && !o.concurrent){
10542            if(o.stopFx){
10543                this.stopFx();
10544                return true;
10545            }
10546            return false;
10547         }
10548         return true;
10549     },
10550
10551         /**
10552          * Returns true if the element is currently blocking so that no other effect can be queued
10553          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10554          * used to ensure that an effect initiated by a user action runs to completion prior to the
10555          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10556          * @return {Boolean} True if blocking, else false
10557          */
10558     hasFxBlock : function(){
10559         var q = this.fxQueue;
10560         return q && q[0] && q[0].block;
10561     },
10562
10563         /* @private */
10564     queueFx : function(o, fn){
10565         if(!this.fxQueue){
10566             this.fxQueue = [];
10567         }
10568         if(!this.hasFxBlock()){
10569             Roo.applyIf(o, this.fxDefaults);
10570             if(!o.concurrent){
10571                 var run = this.beforeFx(o);
10572                 fn.block = o.block;
10573                 this.fxQueue.push(fn);
10574                 if(run){
10575                     this.nextFx();
10576                 }
10577             }else{
10578                 fn.call(this);
10579             }
10580         }
10581         return this;
10582     },
10583
10584         /* @private */
10585     fxWrap : function(pos, o, vis){
10586         var wrap;
10587         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10588             var wrapXY;
10589             if(o.fixPosition){
10590                 wrapXY = this.getXY();
10591             }
10592             var div = document.createElement("div");
10593             div.style.visibility = vis;
10594             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10595             wrap.setPositioning(pos);
10596             if(wrap.getStyle("position") == "static"){
10597                 wrap.position("relative");
10598             }
10599             this.clearPositioning('auto');
10600             wrap.clip();
10601             wrap.dom.appendChild(this.dom);
10602             if(wrapXY){
10603                 wrap.setXY(wrapXY);
10604             }
10605         }
10606         return wrap;
10607     },
10608
10609         /* @private */
10610     fxUnwrap : function(wrap, pos, o){
10611         this.clearPositioning();
10612         this.setPositioning(pos);
10613         if(!o.wrap){
10614             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10615             wrap.remove();
10616         }
10617     },
10618
10619         /* @private */
10620     getFxRestore : function(){
10621         var st = this.dom.style;
10622         return {pos: this.getPositioning(), width: st.width, height : st.height};
10623     },
10624
10625         /* @private */
10626     afterFx : function(o){
10627         if(o.afterStyle){
10628             this.applyStyles(o.afterStyle);
10629         }
10630         if(o.afterCls){
10631             this.addClass(o.afterCls);
10632         }
10633         if(o.remove === true){
10634             this.remove();
10635         }
10636         Roo.callback(o.callback, o.scope, [this]);
10637         if(!o.concurrent){
10638             this.fxQueue.shift();
10639             this.nextFx();
10640         }
10641     },
10642
10643         /* @private */
10644     getFxEl : function(){ // support for composite element fx
10645         return Roo.get(this.dom);
10646     },
10647
10648         /* @private */
10649     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10650         animType = animType || 'run';
10651         opt = opt || {};
10652         var anim = Roo.lib.Anim[animType](
10653             this.dom, args,
10654             (opt.duration || defaultDur) || .35,
10655             (opt.easing || defaultEase) || 'easeOut',
10656             function(){
10657                 Roo.callback(cb, this);
10658             },
10659             this
10660         );
10661         opt.anim = anim;
10662         return anim;
10663     }
10664 };
10665
10666 // backwords compat
10667 Roo.Fx.resize = Roo.Fx.scale;
10668
10669 //When included, Roo.Fx is automatically applied to Element so that all basic
10670 //effects are available directly via the Element API
10671 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10672  * Based on:
10673  * Ext JS Library 1.1.1
10674  * Copyright(c) 2006-2007, Ext JS, LLC.
10675  *
10676  * Originally Released Under LGPL - original licence link has changed is not relivant.
10677  *
10678  * Fork - LGPL
10679  * <script type="text/javascript">
10680  */
10681
10682
10683 /**
10684  * @class Roo.CompositeElement
10685  * Standard composite class. Creates a Roo.Element for every element in the collection.
10686  * <br><br>
10687  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10688  * actions will be performed on all the elements in this collection.</b>
10689  * <br><br>
10690  * All methods return <i>this</i> and can be chained.
10691  <pre><code>
10692  var els = Roo.select("#some-el div.some-class", true);
10693  // or select directly from an existing element
10694  var el = Roo.get('some-el');
10695  el.select('div.some-class', true);
10696
10697  els.setWidth(100); // all elements become 100 width
10698  els.hide(true); // all elements fade out and hide
10699  // or
10700  els.setWidth(100).hide(true);
10701  </code></pre>
10702  */
10703 Roo.CompositeElement = function(els){
10704     this.elements = [];
10705     this.addElements(els);
10706 };
10707 Roo.CompositeElement.prototype = {
10708     isComposite: true,
10709     addElements : function(els){
10710         if(!els) return this;
10711         if(typeof els == "string"){
10712             els = Roo.Element.selectorFunction(els);
10713         }
10714         var yels = this.elements;
10715         var index = yels.length-1;
10716         for(var i = 0, len = els.length; i < len; i++) {
10717                 yels[++index] = Roo.get(els[i]);
10718         }
10719         return this;
10720     },
10721
10722     /**
10723     * Clears this composite and adds the elements returned by the passed selector.
10724     * @param {String/Array} els A string CSS selector, an array of elements or an element
10725     * @return {CompositeElement} this
10726     */
10727     fill : function(els){
10728         this.elements = [];
10729         this.add(els);
10730         return this;
10731     },
10732
10733     /**
10734     * Filters this composite to only elements that match the passed selector.
10735     * @param {String} selector A string CSS selector
10736     * @return {CompositeElement} this
10737     */
10738     filter : function(selector){
10739         var els = [];
10740         this.each(function(el){
10741             if(el.is(selector)){
10742                 els[els.length] = el.dom;
10743             }
10744         });
10745         this.fill(els);
10746         return this;
10747     },
10748
10749     invoke : function(fn, args){
10750         var els = this.elements;
10751         for(var i = 0, len = els.length; i < len; i++) {
10752                 Roo.Element.prototype[fn].apply(els[i], args);
10753         }
10754         return this;
10755     },
10756     /**
10757     * Adds elements to this composite.
10758     * @param {String/Array} els A string CSS selector, an array of elements or an element
10759     * @return {CompositeElement} this
10760     */
10761     add : function(els){
10762         if(typeof els == "string"){
10763             this.addElements(Roo.Element.selectorFunction(els));
10764         }else if(els.length !== undefined){
10765             this.addElements(els);
10766         }else{
10767             this.addElements([els]);
10768         }
10769         return this;
10770     },
10771     /**
10772     * Calls the passed function passing (el, this, index) for each element in this composite.
10773     * @param {Function} fn The function to call
10774     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10775     * @return {CompositeElement} this
10776     */
10777     each : function(fn, scope){
10778         var els = this.elements;
10779         for(var i = 0, len = els.length; i < len; i++){
10780             if(fn.call(scope || els[i], els[i], this, i) === false) {
10781                 break;
10782             }
10783         }
10784         return this;
10785     },
10786
10787     /**
10788      * Returns the Element object at the specified index
10789      * @param {Number} index
10790      * @return {Roo.Element}
10791      */
10792     item : function(index){
10793         return this.elements[index] || null;
10794     },
10795
10796     /**
10797      * Returns the first Element
10798      * @return {Roo.Element}
10799      */
10800     first : function(){
10801         return this.item(0);
10802     },
10803
10804     /**
10805      * Returns the last Element
10806      * @return {Roo.Element}
10807      */
10808     last : function(){
10809         return this.item(this.elements.length-1);
10810     },
10811
10812     /**
10813      * Returns the number of elements in this composite
10814      * @return Number
10815      */
10816     getCount : function(){
10817         return this.elements.length;
10818     },
10819
10820     /**
10821      * Returns true if this composite contains the passed element
10822      * @return Boolean
10823      */
10824     contains : function(el){
10825         return this.indexOf(el) !== -1;
10826     },
10827
10828     /**
10829      * Returns true if this composite contains the passed element
10830      * @return Boolean
10831      */
10832     indexOf : function(el){
10833         return this.elements.indexOf(Roo.get(el));
10834     },
10835
10836
10837     /**
10838     * Removes the specified element(s).
10839     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10840     * or an array of any of those.
10841     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10842     * @return {CompositeElement} this
10843     */
10844     removeElement : function(el, removeDom){
10845         if(el instanceof Array){
10846             for(var i = 0, len = el.length; i < len; i++){
10847                 this.removeElement(el[i]);
10848             }
10849             return this;
10850         }
10851         var index = typeof el == 'number' ? el : this.indexOf(el);
10852         if(index !== -1){
10853             if(removeDom){
10854                 var d = this.elements[index];
10855                 if(d.dom){
10856                     d.remove();
10857                 }else{
10858                     d.parentNode.removeChild(d);
10859                 }
10860             }
10861             this.elements.splice(index, 1);
10862         }
10863         return this;
10864     },
10865
10866     /**
10867     * Replaces the specified element with the passed element.
10868     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10869     * to replace.
10870     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10871     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10872     * @return {CompositeElement} this
10873     */
10874     replaceElement : function(el, replacement, domReplace){
10875         var index = typeof el == 'number' ? el : this.indexOf(el);
10876         if(index !== -1){
10877             if(domReplace){
10878                 this.elements[index].replaceWith(replacement);
10879             }else{
10880                 this.elements.splice(index, 1, Roo.get(replacement))
10881             }
10882         }
10883         return this;
10884     },
10885
10886     /**
10887      * Removes all elements.
10888      */
10889     clear : function(){
10890         this.elements = [];
10891     }
10892 };
10893 (function(){
10894     Roo.CompositeElement.createCall = function(proto, fnName){
10895         if(!proto[fnName]){
10896             proto[fnName] = function(){
10897                 return this.invoke(fnName, arguments);
10898             };
10899         }
10900     };
10901     for(var fnName in Roo.Element.prototype){
10902         if(typeof Roo.Element.prototype[fnName] == "function"){
10903             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10904         }
10905     };
10906 })();
10907 /*
10908  * Based on:
10909  * Ext JS Library 1.1.1
10910  * Copyright(c) 2006-2007, Ext JS, LLC.
10911  *
10912  * Originally Released Under LGPL - original licence link has changed is not relivant.
10913  *
10914  * Fork - LGPL
10915  * <script type="text/javascript">
10916  */
10917
10918 /**
10919  * @class Roo.CompositeElementLite
10920  * @extends Roo.CompositeElement
10921  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10922  <pre><code>
10923  var els = Roo.select("#some-el div.some-class");
10924  // or select directly from an existing element
10925  var el = Roo.get('some-el');
10926  el.select('div.some-class');
10927
10928  els.setWidth(100); // all elements become 100 width
10929  els.hide(true); // all elements fade out and hide
10930  // or
10931  els.setWidth(100).hide(true);
10932  </code></pre><br><br>
10933  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10934  * actions will be performed on all the elements in this collection.</b>
10935  */
10936 Roo.CompositeElementLite = function(els){
10937     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10938     this.el = new Roo.Element.Flyweight();
10939 };
10940 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10941     addElements : function(els){
10942         if(els){
10943             if(els instanceof Array){
10944                 this.elements = this.elements.concat(els);
10945             }else{
10946                 var yels = this.elements;
10947                 var index = yels.length-1;
10948                 for(var i = 0, len = els.length; i < len; i++) {
10949                     yels[++index] = els[i];
10950                 }
10951             }
10952         }
10953         return this;
10954     },
10955     invoke : function(fn, args){
10956         var els = this.elements;
10957         var el = this.el;
10958         for(var i = 0, len = els.length; i < len; i++) {
10959             el.dom = els[i];
10960                 Roo.Element.prototype[fn].apply(el, args);
10961         }
10962         return this;
10963     },
10964     /**
10965      * Returns a flyweight Element of the dom element object at the specified index
10966      * @param {Number} index
10967      * @return {Roo.Element}
10968      */
10969     item : function(index){
10970         if(!this.elements[index]){
10971             return null;
10972         }
10973         this.el.dom = this.elements[index];
10974         return this.el;
10975     },
10976
10977     // fixes scope with flyweight
10978     addListener : function(eventName, handler, scope, opt){
10979         var els = this.elements;
10980         for(var i = 0, len = els.length; i < len; i++) {
10981             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10982         }
10983         return this;
10984     },
10985
10986     /**
10987     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10988     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10989     * a reference to the dom node, use el.dom.</b>
10990     * @param {Function} fn The function to call
10991     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10992     * @return {CompositeElement} this
10993     */
10994     each : function(fn, scope){
10995         var els = this.elements;
10996         var el = this.el;
10997         for(var i = 0, len = els.length; i < len; i++){
10998             el.dom = els[i];
10999                 if(fn.call(scope || el, el, this, i) === false){
11000                 break;
11001             }
11002         }
11003         return this;
11004     },
11005
11006     indexOf : function(el){
11007         return this.elements.indexOf(Roo.getDom(el));
11008     },
11009
11010     replaceElement : function(el, replacement, domReplace){
11011         var index = typeof el == 'number' ? el : this.indexOf(el);
11012         if(index !== -1){
11013             replacement = Roo.getDom(replacement);
11014             if(domReplace){
11015                 var d = this.elements[index];
11016                 d.parentNode.insertBefore(replacement, d);
11017                 d.parentNode.removeChild(d);
11018             }
11019             this.elements.splice(index, 1, replacement);
11020         }
11021         return this;
11022     }
11023 });
11024 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11025
11026 /*
11027  * Based on:
11028  * Ext JS Library 1.1.1
11029  * Copyright(c) 2006-2007, Ext JS, LLC.
11030  *
11031  * Originally Released Under LGPL - original licence link has changed is not relivant.
11032  *
11033  * Fork - LGPL
11034  * <script type="text/javascript">
11035  */
11036
11037  
11038
11039 /**
11040  * @class Roo.data.Connection
11041  * @extends Roo.util.Observable
11042  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11043  * either to a configured URL, or to a URL specified at request time.<br><br>
11044  * <p>
11045  * Requests made by this class are asynchronous, and will return immediately. No data from
11046  * the server will be available to the statement immediately following the {@link #request} call.
11047  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11048  * <p>
11049  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11050  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11051  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11052  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11053  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11054  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11055  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11056  * standard DOM methods.
11057  * @constructor
11058  * @param {Object} config a configuration object.
11059  */
11060 Roo.data.Connection = function(config){
11061     Roo.apply(this, config);
11062     this.addEvents({
11063         /**
11064          * @event beforerequest
11065          * Fires before a network request is made to retrieve a data object.
11066          * @param {Connection} conn This Connection object.
11067          * @param {Object} options The options config object passed to the {@link #request} method.
11068          */
11069         "beforerequest" : true,
11070         /**
11071          * @event requestcomplete
11072          * Fires if the request was successfully completed.
11073          * @param {Connection} conn This Connection object.
11074          * @param {Object} response The XHR object containing the response data.
11075          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11076          * @param {Object} options The options config object passed to the {@link #request} method.
11077          */
11078         "requestcomplete" : true,
11079         /**
11080          * @event requestexception
11081          * Fires if an error HTTP status was returned from the server.
11082          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11083          * @param {Connection} conn This Connection object.
11084          * @param {Object} response The XHR object containing the response data.
11085          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11086          * @param {Object} options The options config object passed to the {@link #request} method.
11087          */
11088         "requestexception" : true
11089     });
11090     Roo.data.Connection.superclass.constructor.call(this);
11091 };
11092
11093 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11094     /**
11095      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11096      */
11097     /**
11098      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11099      * extra parameters to each request made by this object. (defaults to undefined)
11100      */
11101     /**
11102      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11103      *  to each request made by this object. (defaults to undefined)
11104      */
11105     /**
11106      * @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)
11107      */
11108     /**
11109      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11110      */
11111     timeout : 30000,
11112     /**
11113      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11114      * @type Boolean
11115      */
11116     autoAbort:false,
11117
11118     /**
11119      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11120      * @type Boolean
11121      */
11122     disableCaching: true,
11123
11124     /**
11125      * Sends an HTTP request to a remote server.
11126      * @param {Object} options An object which may contain the following properties:<ul>
11127      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11128      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11129      * request, a url encoded string or a function to call to get either.</li>
11130      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11131      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11132      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11133      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11134      * <li>options {Object} The parameter to the request call.</li>
11135      * <li>success {Boolean} True if the request succeeded.</li>
11136      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11137      * </ul></li>
11138      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11139      * The callback is passed the following parameters:<ul>
11140      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11141      * <li>options {Object} The parameter to the request call.</li>
11142      * </ul></li>
11143      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11144      * The callback is passed the following parameters:<ul>
11145      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11146      * <li>options {Object} The parameter to the request call.</li>
11147      * </ul></li>
11148      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11149      * for the callback function. Defaults to the browser window.</li>
11150      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11151      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11152      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11153      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11154      * params for the post data. Any params will be appended to the URL.</li>
11155      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11156      * </ul>
11157      * @return {Number} transactionId
11158      */
11159     request : function(o){
11160         if(this.fireEvent("beforerequest", this, o) !== false){
11161             var p = o.params;
11162
11163             if(typeof p == "function"){
11164                 p = p.call(o.scope||window, o);
11165             }
11166             if(typeof p == "object"){
11167                 p = Roo.urlEncode(o.params);
11168             }
11169             if(this.extraParams){
11170                 var extras = Roo.urlEncode(this.extraParams);
11171                 p = p ? (p + '&' + extras) : extras;
11172             }
11173
11174             var url = o.url || this.url;
11175             if(typeof url == 'function'){
11176                 url = url.call(o.scope||window, o);
11177             }
11178
11179             if(o.form){
11180                 var form = Roo.getDom(o.form);
11181                 url = url || form.action;
11182
11183                 var enctype = form.getAttribute("enctype");
11184                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11185                     return this.doFormUpload(o, p, url);
11186                 }
11187                 var f = Roo.lib.Ajax.serializeForm(form);
11188                 p = p ? (p + '&' + f) : f;
11189             }
11190
11191             var hs = o.headers;
11192             if(this.defaultHeaders){
11193                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11194                 if(!o.headers){
11195                     o.headers = hs;
11196                 }
11197             }
11198
11199             var cb = {
11200                 success: this.handleResponse,
11201                 failure: this.handleFailure,
11202                 scope: this,
11203                 argument: {options: o},
11204                 timeout : this.timeout
11205             };
11206
11207             var method = o.method||this.method||(p ? "POST" : "GET");
11208
11209             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11210                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11211             }
11212
11213             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11214                 if(o.autoAbort){
11215                     this.abort();
11216                 }
11217             }else if(this.autoAbort !== false){
11218                 this.abort();
11219             }
11220
11221             if((method == 'GET' && p) || o.xmlData){
11222                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11223                 p = '';
11224             }
11225             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11226             return this.transId;
11227         }else{
11228             Roo.callback(o.callback, o.scope, [o, null, null]);
11229             return null;
11230         }
11231     },
11232
11233     /**
11234      * Determine whether this object has a request outstanding.
11235      * @param {Number} transactionId (Optional) defaults to the last transaction
11236      * @return {Boolean} True if there is an outstanding request.
11237      */
11238     isLoading : function(transId){
11239         if(transId){
11240             return Roo.lib.Ajax.isCallInProgress(transId);
11241         }else{
11242             return this.transId ? true : false;
11243         }
11244     },
11245
11246     /**
11247      * Aborts any outstanding request.
11248      * @param {Number} transactionId (Optional) defaults to the last transaction
11249      */
11250     abort : function(transId){
11251         if(transId || this.isLoading()){
11252             Roo.lib.Ajax.abort(transId || this.transId);
11253         }
11254     },
11255
11256     // private
11257     handleResponse : function(response){
11258         this.transId = false;
11259         var options = response.argument.options;
11260         response.argument = options ? options.argument : null;
11261         this.fireEvent("requestcomplete", this, response, options);
11262         Roo.callback(options.success, options.scope, [response, options]);
11263         Roo.callback(options.callback, options.scope, [options, true, response]);
11264     },
11265
11266     // private
11267     handleFailure : function(response, e){
11268         this.transId = false;
11269         var options = response.argument.options;
11270         response.argument = options ? options.argument : null;
11271         this.fireEvent("requestexception", this, response, options, e);
11272         Roo.callback(options.failure, options.scope, [response, options]);
11273         Roo.callback(options.callback, options.scope, [options, false, response]);
11274     },
11275
11276     // private
11277     doFormUpload : function(o, ps, url){
11278         var id = Roo.id();
11279         var frame = document.createElement('iframe');
11280         frame.id = id;
11281         frame.name = id;
11282         frame.className = 'x-hidden';
11283         if(Roo.isIE){
11284             frame.src = Roo.SSL_SECURE_URL;
11285         }
11286         document.body.appendChild(frame);
11287
11288         if(Roo.isIE){
11289            document.frames[id].name = id;
11290         }
11291
11292         var form = Roo.getDom(o.form);
11293         form.target = id;
11294         form.method = 'POST';
11295         form.enctype = form.encoding = 'multipart/form-data';
11296         if(url){
11297             form.action = url;
11298         }
11299
11300         var hiddens, hd;
11301         if(ps){ // add dynamic params
11302             hiddens = [];
11303             ps = Roo.urlDecode(ps, false);
11304             for(var k in ps){
11305                 if(ps.hasOwnProperty(k)){
11306                     hd = document.createElement('input');
11307                     hd.type = 'hidden';
11308                     hd.name = k;
11309                     hd.value = ps[k];
11310                     form.appendChild(hd);
11311                     hiddens.push(hd);
11312                 }
11313             }
11314         }
11315
11316         function cb(){
11317             var r = {  // bogus response object
11318                 responseText : '',
11319                 responseXML : null
11320             };
11321
11322             r.argument = o ? o.argument : null;
11323
11324             try { //
11325                 var doc;
11326                 if(Roo.isIE){
11327                     doc = frame.contentWindow.document;
11328                 }else {
11329                     doc = (frame.contentDocument || window.frames[id].document);
11330                 }
11331                 if(doc && doc.body){
11332                     r.responseText = doc.body.innerHTML;
11333                 }
11334                 if(doc && doc.XMLDocument){
11335                     r.responseXML = doc.XMLDocument;
11336                 }else {
11337                     r.responseXML = doc;
11338                 }
11339             }
11340             catch(e) {
11341                 // ignore
11342             }
11343
11344             Roo.EventManager.removeListener(frame, 'load', cb, this);
11345
11346             this.fireEvent("requestcomplete", this, r, o);
11347             Roo.callback(o.success, o.scope, [r, o]);
11348             Roo.callback(o.callback, o.scope, [o, true, r]);
11349
11350             setTimeout(function(){document.body.removeChild(frame);}, 100);
11351         }
11352
11353         Roo.EventManager.on(frame, 'load', cb, this);
11354         form.submit();
11355
11356         if(hiddens){ // remove dynamic params
11357             for(var i = 0, len = hiddens.length; i < len; i++){
11358                 form.removeChild(hiddens[i]);
11359             }
11360         }
11361     }
11362 });
11363
11364 /**
11365  * @class Roo.Ajax
11366  * @extends Roo.data.Connection
11367  * Global Ajax request class.
11368  *
11369  * @singleton
11370  */
11371 Roo.Ajax = new Roo.data.Connection({
11372     // fix up the docs
11373    /**
11374      * @cfg {String} url @hide
11375      */
11376     /**
11377      * @cfg {Object} extraParams @hide
11378      */
11379     /**
11380      * @cfg {Object} defaultHeaders @hide
11381      */
11382     /**
11383      * @cfg {String} method (Optional) @hide
11384      */
11385     /**
11386      * @cfg {Number} timeout (Optional) @hide
11387      */
11388     /**
11389      * @cfg {Boolean} autoAbort (Optional) @hide
11390      */
11391
11392     /**
11393      * @cfg {Boolean} disableCaching (Optional) @hide
11394      */
11395
11396     /**
11397      * @property  disableCaching
11398      * True to add a unique cache-buster param to GET requests. (defaults to true)
11399      * @type Boolean
11400      */
11401     /**
11402      * @property  url
11403      * The default URL to be used for requests to the server. (defaults to undefined)
11404      * @type String
11405      */
11406     /**
11407      * @property  extraParams
11408      * An object containing properties which are used as
11409      * extra parameters to each request made by this object. (defaults to undefined)
11410      * @type Object
11411      */
11412     /**
11413      * @property  defaultHeaders
11414      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11415      * @type Object
11416      */
11417     /**
11418      * @property  method
11419      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11420      * @type String
11421      */
11422     /**
11423      * @property  timeout
11424      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11425      * @type Number
11426      */
11427
11428     /**
11429      * @property  autoAbort
11430      * Whether a new request should abort any pending requests. (defaults to false)
11431      * @type Boolean
11432      */
11433     autoAbort : false,
11434
11435     /**
11436      * Serialize the passed form into a url encoded string
11437      * @param {String/HTMLElement} form
11438      * @return {String}
11439      */
11440     serializeForm : function(form){
11441         return Roo.lib.Ajax.serializeForm(form);
11442     }
11443 });/*
11444  * Based on:
11445  * Ext JS Library 1.1.1
11446  * Copyright(c) 2006-2007, Ext JS, LLC.
11447  *
11448  * Originally Released Under LGPL - original licence link has changed is not relivant.
11449  *
11450  * Fork - LGPL
11451  * <script type="text/javascript">
11452  */
11453  
11454 /**
11455  * @class Roo.Ajax
11456  * @extends Roo.data.Connection
11457  * Global Ajax request class.
11458  *
11459  * @instanceOf  Roo.data.Connection
11460  */
11461 Roo.Ajax = new Roo.data.Connection({
11462     // fix up the docs
11463     
11464     /**
11465      * fix up scoping
11466      * @scope Roo.Ajax
11467      */
11468     
11469    /**
11470      * @cfg {String} url @hide
11471      */
11472     /**
11473      * @cfg {Object} extraParams @hide
11474      */
11475     /**
11476      * @cfg {Object} defaultHeaders @hide
11477      */
11478     /**
11479      * @cfg {String} method (Optional) @hide
11480      */
11481     /**
11482      * @cfg {Number} timeout (Optional) @hide
11483      */
11484     /**
11485      * @cfg {Boolean} autoAbort (Optional) @hide
11486      */
11487
11488     /**
11489      * @cfg {Boolean} disableCaching (Optional) @hide
11490      */
11491
11492     /**
11493      * @property  disableCaching
11494      * True to add a unique cache-buster param to GET requests. (defaults to true)
11495      * @type Boolean
11496      */
11497     /**
11498      * @property  url
11499      * The default URL to be used for requests to the server. (defaults to undefined)
11500      * @type String
11501      */
11502     /**
11503      * @property  extraParams
11504      * An object containing properties which are used as
11505      * extra parameters to each request made by this object. (defaults to undefined)
11506      * @type Object
11507      */
11508     /**
11509      * @property  defaultHeaders
11510      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11511      * @type Object
11512      */
11513     /**
11514      * @property  method
11515      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11516      * @type String
11517      */
11518     /**
11519      * @property  timeout
11520      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11521      * @type Number
11522      */
11523
11524     /**
11525      * @property  autoAbort
11526      * Whether a new request should abort any pending requests. (defaults to false)
11527      * @type Boolean
11528      */
11529     autoAbort : false,
11530
11531     /**
11532      * Serialize the passed form into a url encoded string
11533      * @param {String/HTMLElement} form
11534      * @return {String}
11535      */
11536     serializeForm : function(form){
11537         return Roo.lib.Ajax.serializeForm(form);
11538     }
11539 });/*
11540  * Based on:
11541  * Ext JS Library 1.1.1
11542  * Copyright(c) 2006-2007, Ext JS, LLC.
11543  *
11544  * Originally Released Under LGPL - original licence link has changed is not relivant.
11545  *
11546  * Fork - LGPL
11547  * <script type="text/javascript">
11548  */
11549
11550  
11551 /**
11552  * @class Roo.UpdateManager
11553  * @extends Roo.util.Observable
11554  * Provides AJAX-style update for Element object.<br><br>
11555  * Usage:<br>
11556  * <pre><code>
11557  * // Get it from a Roo.Element object
11558  * var el = Roo.get("foo");
11559  * var mgr = el.getUpdateManager();
11560  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11561  * ...
11562  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11563  * <br>
11564  * // or directly (returns the same UpdateManager instance)
11565  * var mgr = new Roo.UpdateManager("myElementId");
11566  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11567  * mgr.on("update", myFcnNeedsToKnow);
11568  * <br>
11569    // short handed call directly from the element object
11570    Roo.get("foo").load({
11571         url: "bar.php",
11572         scripts:true,
11573         params: "for=bar",
11574         text: "Loading Foo..."
11575    });
11576  * </code></pre>
11577  * @constructor
11578  * Create new UpdateManager directly.
11579  * @param {String/HTMLElement/Roo.Element} el The element to update
11580  * @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).
11581  */
11582 Roo.UpdateManager = function(el, forceNew){
11583     el = Roo.get(el);
11584     if(!forceNew && el.updateManager){
11585         return el.updateManager;
11586     }
11587     /**
11588      * The Element object
11589      * @type Roo.Element
11590      */
11591     this.el = el;
11592     /**
11593      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11594      * @type String
11595      */
11596     this.defaultUrl = null;
11597
11598     this.addEvents({
11599         /**
11600          * @event beforeupdate
11601          * Fired before an update is made, return false from your handler and the update is cancelled.
11602          * @param {Roo.Element} el
11603          * @param {String/Object/Function} url
11604          * @param {String/Object} params
11605          */
11606         "beforeupdate": true,
11607         /**
11608          * @event update
11609          * Fired after successful update is made.
11610          * @param {Roo.Element} el
11611          * @param {Object} oResponseObject The response Object
11612          */
11613         "update": true,
11614         /**
11615          * @event failure
11616          * Fired on update failure.
11617          * @param {Roo.Element} el
11618          * @param {Object} oResponseObject The response Object
11619          */
11620         "failure": true
11621     });
11622     var d = Roo.UpdateManager.defaults;
11623     /**
11624      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11625      * @type String
11626      */
11627     this.sslBlankUrl = d.sslBlankUrl;
11628     /**
11629      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11630      * @type Boolean
11631      */
11632     this.disableCaching = d.disableCaching;
11633     /**
11634      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11635      * @type String
11636      */
11637     this.indicatorText = d.indicatorText;
11638     /**
11639      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11640      * @type String
11641      */
11642     this.showLoadIndicator = d.showLoadIndicator;
11643     /**
11644      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11645      * @type Number
11646      */
11647     this.timeout = d.timeout;
11648
11649     /**
11650      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11651      * @type Boolean
11652      */
11653     this.loadScripts = d.loadScripts;
11654
11655     /**
11656      * Transaction object of current executing transaction
11657      */
11658     this.transaction = null;
11659
11660     /**
11661      * @private
11662      */
11663     this.autoRefreshProcId = null;
11664     /**
11665      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11666      * @type Function
11667      */
11668     this.refreshDelegate = this.refresh.createDelegate(this);
11669     /**
11670      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11671      * @type Function
11672      */
11673     this.updateDelegate = this.update.createDelegate(this);
11674     /**
11675      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11676      * @type Function
11677      */
11678     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11679     /**
11680      * @private
11681      */
11682     this.successDelegate = this.processSuccess.createDelegate(this);
11683     /**
11684      * @private
11685      */
11686     this.failureDelegate = this.processFailure.createDelegate(this);
11687
11688     if(!this.renderer){
11689      /**
11690       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11691       */
11692     this.renderer = new Roo.UpdateManager.BasicRenderer();
11693     }
11694     
11695     Roo.UpdateManager.superclass.constructor.call(this);
11696 };
11697
11698 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11699     /**
11700      * Get the Element this UpdateManager is bound to
11701      * @return {Roo.Element} The element
11702      */
11703     getEl : function(){
11704         return this.el;
11705     },
11706     /**
11707      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11708      * @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:
11709 <pre><code>
11710 um.update({<br/>
11711     url: "your-url.php",<br/>
11712     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11713     callback: yourFunction,<br/>
11714     scope: yourObject, //(optional scope)  <br/>
11715     discardUrl: false, <br/>
11716     nocache: false,<br/>
11717     text: "Loading...",<br/>
11718     timeout: 30,<br/>
11719     scripts: false<br/>
11720 });
11721 </code></pre>
11722      * The only required property is url. The optional properties nocache, text and scripts
11723      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11724      * @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}
11725      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11726      * @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.
11727      */
11728     update : function(url, params, callback, discardUrl){
11729         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11730             var method = this.method, cfg;
11731             if(typeof url == "object"){ // must be config object
11732                 cfg = url;
11733                 url = cfg.url;
11734                 params = params || cfg.params;
11735                 callback = callback || cfg.callback;
11736                 discardUrl = discardUrl || cfg.discardUrl;
11737                 if(callback && cfg.scope){
11738                     callback = callback.createDelegate(cfg.scope);
11739                 }
11740                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11741                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11742                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11743                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11744                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11745             }
11746             this.showLoading();
11747             if(!discardUrl){
11748                 this.defaultUrl = url;
11749             }
11750             if(typeof url == "function"){
11751                 url = url.call(this);
11752             }
11753
11754             method = method || (params ? "POST" : "GET");
11755             if(method == "GET"){
11756                 url = this.prepareUrl(url);
11757             }
11758
11759             var o = Roo.apply(cfg ||{}, {
11760                 url : url,
11761                 params: params,
11762                 success: this.successDelegate,
11763                 failure: this.failureDelegate,
11764                 callback: undefined,
11765                 timeout: (this.timeout*1000),
11766                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11767             });
11768
11769             this.transaction = Roo.Ajax.request(o);
11770         }
11771     },
11772
11773     /**
11774      * 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.
11775      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11776      * @param {String/HTMLElement} form The form Id or form element
11777      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11778      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      */
11781     formUpdate : function(form, url, reset, callback){
11782         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11783             if(typeof url == "function"){
11784                 url = url.call(this);
11785             }
11786             form = Roo.getDom(form);
11787             this.transaction = Roo.Ajax.request({
11788                 form: form,
11789                 url:url,
11790                 success: this.successDelegate,
11791                 failure: this.failureDelegate,
11792                 timeout: (this.timeout*1000),
11793                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11794             });
11795             this.showLoading.defer(1, this);
11796         }
11797     },
11798
11799     /**
11800      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11801      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11802      */
11803     refresh : function(callback){
11804         if(this.defaultUrl == null){
11805             return;
11806         }
11807         this.update(this.defaultUrl, null, callback, true);
11808     },
11809
11810     /**
11811      * Set this element to auto refresh.
11812      * @param {Number} interval How often to update (in seconds).
11813      * @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)
11814      * @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}
11815      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11816      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11817      */
11818     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11819         if(refreshNow){
11820             this.update(url || this.defaultUrl, params, callback, true);
11821         }
11822         if(this.autoRefreshProcId){
11823             clearInterval(this.autoRefreshProcId);
11824         }
11825         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11826     },
11827
11828     /**
11829      * Stop auto refresh on this element.
11830      */
11831      stopAutoRefresh : function(){
11832         if(this.autoRefreshProcId){
11833             clearInterval(this.autoRefreshProcId);
11834             delete this.autoRefreshProcId;
11835         }
11836     },
11837
11838     isAutoRefreshing : function(){
11839        return this.autoRefreshProcId ? true : false;
11840     },
11841     /**
11842      * Called to update the element to "Loading" state. Override to perform custom action.
11843      */
11844     showLoading : function(){
11845         if(this.showLoadIndicator){
11846             this.el.update(this.indicatorText);
11847         }
11848     },
11849
11850     /**
11851      * Adds unique parameter to query string if disableCaching = true
11852      * @private
11853      */
11854     prepareUrl : function(url){
11855         if(this.disableCaching){
11856             var append = "_dc=" + (new Date().getTime());
11857             if(url.indexOf("?") !== -1){
11858                 url += "&" + append;
11859             }else{
11860                 url += "?" + append;
11861             }
11862         }
11863         return url;
11864     },
11865
11866     /**
11867      * @private
11868      */
11869     processSuccess : function(response){
11870         this.transaction = null;
11871         if(response.argument.form && response.argument.reset){
11872             try{ // put in try/catch since some older FF releases had problems with this
11873                 response.argument.form.reset();
11874             }catch(e){}
11875         }
11876         if(this.loadScripts){
11877             this.renderer.render(this.el, response, this,
11878                 this.updateComplete.createDelegate(this, [response]));
11879         }else{
11880             this.renderer.render(this.el, response, this);
11881             this.updateComplete(response);
11882         }
11883     },
11884
11885     updateComplete : function(response){
11886         this.fireEvent("update", this.el, response);
11887         if(typeof response.argument.callback == "function"){
11888             response.argument.callback(this.el, true, response);
11889         }
11890     },
11891
11892     /**
11893      * @private
11894      */
11895     processFailure : function(response){
11896         this.transaction = null;
11897         this.fireEvent("failure", this.el, response);
11898         if(typeof response.argument.callback == "function"){
11899             response.argument.callback(this.el, false, response);
11900         }
11901     },
11902
11903     /**
11904      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11905      * @param {Object} renderer The object implementing the render() method
11906      */
11907     setRenderer : function(renderer){
11908         this.renderer = renderer;
11909     },
11910
11911     getRenderer : function(){
11912        return this.renderer;
11913     },
11914
11915     /**
11916      * Set the defaultUrl used for updates
11917      * @param {String/Function} defaultUrl The url or a function to call to get the url
11918      */
11919     setDefaultUrl : function(defaultUrl){
11920         this.defaultUrl = defaultUrl;
11921     },
11922
11923     /**
11924      * Aborts the executing transaction
11925      */
11926     abort : function(){
11927         if(this.transaction){
11928             Roo.Ajax.abort(this.transaction);
11929         }
11930     },
11931
11932     /**
11933      * Returns true if an update is in progress
11934      * @return {Boolean}
11935      */
11936     isUpdating : function(){
11937         if(this.transaction){
11938             return Roo.Ajax.isLoading(this.transaction);
11939         }
11940         return false;
11941     }
11942 });
11943
11944 /**
11945  * @class Roo.UpdateManager.defaults
11946  * @static (not really - but it helps the doc tool)
11947  * The defaults collection enables customizing the default properties of UpdateManager
11948  */
11949    Roo.UpdateManager.defaults = {
11950        /**
11951          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11952          * @type Number
11953          */
11954          timeout : 30,
11955
11956          /**
11957          * True to process scripts by default (Defaults to false).
11958          * @type Boolean
11959          */
11960         loadScripts : false,
11961
11962         /**
11963         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11964         * @type String
11965         */
11966         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11967         /**
11968          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11969          * @type Boolean
11970          */
11971         disableCaching : false,
11972         /**
11973          * Whether to show indicatorText when loading (Defaults to true).
11974          * @type Boolean
11975          */
11976         showLoadIndicator : true,
11977         /**
11978          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11979          * @type String
11980          */
11981         indicatorText : '<div class="loading-indicator">Loading...</div>'
11982    };
11983
11984 /**
11985  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11986  *Usage:
11987  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11988  * @param {String/HTMLElement/Roo.Element} el The element to update
11989  * @param {String} url The url
11990  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11991  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11992  * @static
11993  * @deprecated
11994  * @member Roo.UpdateManager
11995  */
11996 Roo.UpdateManager.updateElement = function(el, url, params, options){
11997     var um = Roo.get(el, true).getUpdateManager();
11998     Roo.apply(um, options);
11999     um.update(url, params, options ? options.callback : null);
12000 };
12001 // alias for backwards compat
12002 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12003 /**
12004  * @class Roo.UpdateManager.BasicRenderer
12005  * Default Content renderer. Updates the elements innerHTML with the responseText.
12006  */
12007 Roo.UpdateManager.BasicRenderer = function(){};
12008
12009 Roo.UpdateManager.BasicRenderer.prototype = {
12010     /**
12011      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12012      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12013      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12014      * @param {Roo.Element} el The element being rendered
12015      * @param {Object} response The YUI Connect response object
12016      * @param {UpdateManager} updateManager The calling update manager
12017      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12018      */
12019      render : function(el, response, updateManager, callback){
12020         el.update(response.responseText, updateManager.loadScripts, callback);
12021     }
12022 };
12023 /*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033
12034 /**
12035  * @class Roo.util.DelayedTask
12036  * Provides a convenient method of performing setTimeout where a new
12037  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12038  * You can use this class to buffer
12039  * the keypress events for a certain number of milliseconds, and perform only if they stop
12040  * for that amount of time.
12041  * @constructor The parameters to this constructor serve as defaults and are not required.
12042  * @param {Function} fn (optional) The default function to timeout
12043  * @param {Object} scope (optional) The default scope of that timeout
12044  * @param {Array} args (optional) The default Array of arguments
12045  */
12046 Roo.util.DelayedTask = function(fn, scope, args){
12047     var id = null, d, t;
12048
12049     var call = function(){
12050         var now = new Date().getTime();
12051         if(now - t >= d){
12052             clearInterval(id);
12053             id = null;
12054             fn.apply(scope, args || []);
12055         }
12056     };
12057     /**
12058      * Cancels any pending timeout and queues a new one
12059      * @param {Number} delay The milliseconds to delay
12060      * @param {Function} newFn (optional) Overrides function passed to constructor
12061      * @param {Object} newScope (optional) Overrides scope passed to constructor
12062      * @param {Array} newArgs (optional) Overrides args passed to constructor
12063      */
12064     this.delay = function(delay, newFn, newScope, newArgs){
12065         if(id && delay != d){
12066             this.cancel();
12067         }
12068         d = delay;
12069         t = new Date().getTime();
12070         fn = newFn || fn;
12071         scope = newScope || scope;
12072         args = newArgs || args;
12073         if(!id){
12074             id = setInterval(call, d);
12075         }
12076     };
12077
12078     /**
12079      * Cancel the last queued timeout
12080      */
12081     this.cancel = function(){
12082         if(id){
12083             clearInterval(id);
12084             id = null;
12085         }
12086     };
12087 };/*
12088  * Based on:
12089  * Ext JS Library 1.1.1
12090  * Copyright(c) 2006-2007, Ext JS, LLC.
12091  *
12092  * Originally Released Under LGPL - original licence link has changed is not relivant.
12093  *
12094  * Fork - LGPL
12095  * <script type="text/javascript">
12096  */
12097  
12098  
12099 Roo.util.TaskRunner = function(interval){
12100     interval = interval || 10;
12101     var tasks = [], removeQueue = [];
12102     var id = 0;
12103     var running = false;
12104
12105     var stopThread = function(){
12106         running = false;
12107         clearInterval(id);
12108         id = 0;
12109     };
12110
12111     var startThread = function(){
12112         if(!running){
12113             running = true;
12114             id = setInterval(runTasks, interval);
12115         }
12116     };
12117
12118     var removeTask = function(task){
12119         removeQueue.push(task);
12120         if(task.onStop){
12121             task.onStop();
12122         }
12123     };
12124
12125     var runTasks = function(){
12126         if(removeQueue.length > 0){
12127             for(var i = 0, len = removeQueue.length; i < len; i++){
12128                 tasks.remove(removeQueue[i]);
12129             }
12130             removeQueue = [];
12131             if(tasks.length < 1){
12132                 stopThread();
12133                 return;
12134             }
12135         }
12136         var now = new Date().getTime();
12137         for(var i = 0, len = tasks.length; i < len; ++i){
12138             var t = tasks[i];
12139             var itime = now - t.taskRunTime;
12140             if(t.interval <= itime){
12141                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12142                 t.taskRunTime = now;
12143                 if(rt === false || t.taskRunCount === t.repeat){
12144                     removeTask(t);
12145                     return;
12146                 }
12147             }
12148             if(t.duration && t.duration <= (now - t.taskStartTime)){
12149                 removeTask(t);
12150             }
12151         }
12152     };
12153
12154     /**
12155      * Queues a new task.
12156      * @param {Object} task
12157      */
12158     this.start = function(task){
12159         tasks.push(task);
12160         task.taskStartTime = new Date().getTime();
12161         task.taskRunTime = 0;
12162         task.taskRunCount = 0;
12163         startThread();
12164         return task;
12165     };
12166
12167     this.stop = function(task){
12168         removeTask(task);
12169         return task;
12170     };
12171
12172     this.stopAll = function(){
12173         stopThread();
12174         for(var i = 0, len = tasks.length; i < len; i++){
12175             if(tasks[i].onStop){
12176                 tasks[i].onStop();
12177             }
12178         }
12179         tasks = [];
12180         removeQueue = [];
12181     };
12182 };
12183
12184 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194
12195  
12196 /**
12197  * @class Roo.util.MixedCollection
12198  * @extends Roo.util.Observable
12199  * A Collection class that maintains both numeric indexes and keys and exposes events.
12200  * @constructor
12201  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12202  * collection (defaults to false)
12203  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12204  * and return the key value for that item.  This is used when available to look up the key on items that
12205  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12206  * equivalent to providing an implementation for the {@link #getKey} method.
12207  */
12208 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12209     this.items = [];
12210     this.map = {};
12211     this.keys = [];
12212     this.length = 0;
12213     this.addEvents({
12214         /**
12215          * @event clear
12216          * Fires when the collection is cleared.
12217          */
12218         "clear" : true,
12219         /**
12220          * @event add
12221          * Fires when an item is added to the collection.
12222          * @param {Number} index The index at which the item was added.
12223          * @param {Object} o The item added.
12224          * @param {String} key The key associated with the added item.
12225          */
12226         "add" : true,
12227         /**
12228          * @event replace
12229          * Fires when an item is replaced in the collection.
12230          * @param {String} key he key associated with the new added.
12231          * @param {Object} old The item being replaced.
12232          * @param {Object} new The new item.
12233          */
12234         "replace" : true,
12235         /**
12236          * @event remove
12237          * Fires when an item is removed from the collection.
12238          * @param {Object} o The item being removed.
12239          * @param {String} key (optional) The key associated with the removed item.
12240          */
12241         "remove" : true,
12242         "sort" : true
12243     });
12244     this.allowFunctions = allowFunctions === true;
12245     if(keyFn){
12246         this.getKey = keyFn;
12247     }
12248     Roo.util.MixedCollection.superclass.constructor.call(this);
12249 };
12250
12251 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12252     allowFunctions : false,
12253     
12254 /**
12255  * Adds an item to the collection.
12256  * @param {String} key The key to associate with the item
12257  * @param {Object} o The item to add.
12258  * @return {Object} The item added.
12259  */
12260     add : function(key, o){
12261         if(arguments.length == 1){
12262             o = arguments[0];
12263             key = this.getKey(o);
12264         }
12265         if(typeof key == "undefined" || key === null){
12266             this.length++;
12267             this.items.push(o);
12268             this.keys.push(null);
12269         }else{
12270             var old = this.map[key];
12271             if(old){
12272                 return this.replace(key, o);
12273             }
12274             this.length++;
12275             this.items.push(o);
12276             this.map[key] = o;
12277             this.keys.push(key);
12278         }
12279         this.fireEvent("add", this.length-1, o, key);
12280         return o;
12281     },
12282        
12283 /**
12284   * MixedCollection has a generic way to fetch keys if you implement getKey.
12285 <pre><code>
12286 // normal way
12287 var mc = new Roo.util.MixedCollection();
12288 mc.add(someEl.dom.id, someEl);
12289 mc.add(otherEl.dom.id, otherEl);
12290 //and so on
12291
12292 // using getKey
12293 var mc = new Roo.util.MixedCollection();
12294 mc.getKey = function(el){
12295    return el.dom.id;
12296 };
12297 mc.add(someEl);
12298 mc.add(otherEl);
12299
12300 // or via the constructor
12301 var mc = new Roo.util.MixedCollection(false, function(el){
12302    return el.dom.id;
12303 });
12304 mc.add(someEl);
12305 mc.add(otherEl);
12306 </code></pre>
12307  * @param o {Object} The item for which to find the key.
12308  * @return {Object} The key for the passed item.
12309  */
12310     getKey : function(o){
12311          return o.id; 
12312     },
12313    
12314 /**
12315  * Replaces an item in the collection.
12316  * @param {String} key The key associated with the item to replace, or the item to replace.
12317  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12318  * @return {Object}  The new item.
12319  */
12320     replace : function(key, o){
12321         if(arguments.length == 1){
12322             o = arguments[0];
12323             key = this.getKey(o);
12324         }
12325         var old = this.item(key);
12326         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12327              return this.add(key, o);
12328         }
12329         var index = this.indexOfKey(key);
12330         this.items[index] = o;
12331         this.map[key] = o;
12332         this.fireEvent("replace", key, old, o);
12333         return o;
12334     },
12335    
12336 /**
12337  * Adds all elements of an Array or an Object to the collection.
12338  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12339  * an Array of values, each of which are added to the collection.
12340  */
12341     addAll : function(objs){
12342         if(arguments.length > 1 || objs instanceof Array){
12343             var args = arguments.length > 1 ? arguments : objs;
12344             for(var i = 0, len = args.length; i < len; i++){
12345                 this.add(args[i]);
12346             }
12347         }else{
12348             for(var key in objs){
12349                 if(this.allowFunctions || typeof objs[key] != "function"){
12350                     this.add(key, objs[key]);
12351                 }
12352             }
12353         }
12354     },
12355    
12356 /**
12357  * Executes the specified function once for every item in the collection, passing each
12358  * item as the first and only parameter. returning false from the function will stop the iteration.
12359  * @param {Function} fn The function to execute for each item.
12360  * @param {Object} scope (optional) The scope in which to execute the function.
12361  */
12362     each : function(fn, scope){
12363         var items = [].concat(this.items); // each safe for removal
12364         for(var i = 0, len = items.length; i < len; i++){
12365             if(fn.call(scope || items[i], items[i], i, len) === false){
12366                 break;
12367             }
12368         }
12369     },
12370    
12371 /**
12372  * Executes the specified function once for every key in the collection, passing each
12373  * key, and its associated item as the first two parameters.
12374  * @param {Function} fn The function to execute for each item.
12375  * @param {Object} scope (optional) The scope in which to execute the function.
12376  */
12377     eachKey : function(fn, scope){
12378         for(var i = 0, len = this.keys.length; i < len; i++){
12379             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12380         }
12381     },
12382    
12383 /**
12384  * Returns the first item in the collection which elicits a true return value from the
12385  * passed selection function.
12386  * @param {Function} fn The selection function to execute for each item.
12387  * @param {Object} scope (optional) The scope in which to execute the function.
12388  * @return {Object} The first item in the collection which returned true from the selection function.
12389  */
12390     find : function(fn, scope){
12391         for(var i = 0, len = this.items.length; i < len; i++){
12392             if(fn.call(scope || window, this.items[i], this.keys[i])){
12393                 return this.items[i];
12394             }
12395         }
12396         return null;
12397     },
12398    
12399 /**
12400  * Inserts an item at the specified index in the collection.
12401  * @param {Number} index The index to insert the item at.
12402  * @param {String} key The key to associate with the new item, or the item itself.
12403  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12404  * @return {Object} The item inserted.
12405  */
12406     insert : function(index, key, o){
12407         if(arguments.length == 2){
12408             o = arguments[1];
12409             key = this.getKey(o);
12410         }
12411         if(index >= this.length){
12412             return this.add(key, o);
12413         }
12414         this.length++;
12415         this.items.splice(index, 0, o);
12416         if(typeof key != "undefined" && key != null){
12417             this.map[key] = o;
12418         }
12419         this.keys.splice(index, 0, key);
12420         this.fireEvent("add", index, o, key);
12421         return o;
12422     },
12423    
12424 /**
12425  * Removed an item from the collection.
12426  * @param {Object} o The item to remove.
12427  * @return {Object} The item removed.
12428  */
12429     remove : function(o){
12430         return this.removeAt(this.indexOf(o));
12431     },
12432    
12433 /**
12434  * Remove an item from a specified index in the collection.
12435  * @param {Number} index The index within the collection of the item to remove.
12436  */
12437     removeAt : function(index){
12438         if(index < this.length && index >= 0){
12439             this.length--;
12440             var o = this.items[index];
12441             this.items.splice(index, 1);
12442             var key = this.keys[index];
12443             if(typeof key != "undefined"){
12444                 delete this.map[key];
12445             }
12446             this.keys.splice(index, 1);
12447             this.fireEvent("remove", o, key);
12448         }
12449     },
12450    
12451 /**
12452  * Removed an item associated with the passed key fom the collection.
12453  * @param {String} key The key of the item to remove.
12454  */
12455     removeKey : function(key){
12456         return this.removeAt(this.indexOfKey(key));
12457     },
12458    
12459 /**
12460  * Returns the number of items in the collection.
12461  * @return {Number} the number of items in the collection.
12462  */
12463     getCount : function(){
12464         return this.length; 
12465     },
12466    
12467 /**
12468  * Returns index within the collection of the passed Object.
12469  * @param {Object} o The item to find the index of.
12470  * @return {Number} index of the item.
12471  */
12472     indexOf : function(o){
12473         if(!this.items.indexOf){
12474             for(var i = 0, len = this.items.length; i < len; i++){
12475                 if(this.items[i] == o) return i;
12476             }
12477             return -1;
12478         }else{
12479             return this.items.indexOf(o);
12480         }
12481     },
12482    
12483 /**
12484  * Returns index within the collection of the passed key.
12485  * @param {String} key The key to find the index of.
12486  * @return {Number} index of the key.
12487  */
12488     indexOfKey : function(key){
12489         if(!this.keys.indexOf){
12490             for(var i = 0, len = this.keys.length; i < len; i++){
12491                 if(this.keys[i] == key) return i;
12492             }
12493             return -1;
12494         }else{
12495             return this.keys.indexOf(key);
12496         }
12497     },
12498    
12499 /**
12500  * Returns the item associated with the passed key OR index. Key has priority over index.
12501  * @param {String/Number} key The key or index of the item.
12502  * @return {Object} The item associated with the passed key.
12503  */
12504     item : function(key){
12505         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12506         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12507     },
12508     
12509 /**
12510  * Returns the item at the specified index.
12511  * @param {Number} index The index of the item.
12512  * @return {Object}
12513  */
12514     itemAt : function(index){
12515         return this.items[index];
12516     },
12517     
12518 /**
12519  * Returns the item associated with the passed key.
12520  * @param {String/Number} key The key of the item.
12521  * @return {Object} The item associated with the passed key.
12522  */
12523     key : function(key){
12524         return this.map[key];
12525     },
12526    
12527 /**
12528  * Returns true if the collection contains the passed Object as an item.
12529  * @param {Object} o  The Object to look for in the collection.
12530  * @return {Boolean} True if the collection contains the Object as an item.
12531  */
12532     contains : function(o){
12533         return this.indexOf(o) != -1;
12534     },
12535    
12536 /**
12537  * Returns true if the collection contains the passed Object as a key.
12538  * @param {String} key The key to look for in the collection.
12539  * @return {Boolean} True if the collection contains the Object as a key.
12540  */
12541     containsKey : function(key){
12542         return typeof this.map[key] != "undefined";
12543     },
12544    
12545 /**
12546  * Removes all items from the collection.
12547  */
12548     clear : function(){
12549         this.length = 0;
12550         this.items = [];
12551         this.keys = [];
12552         this.map = {};
12553         this.fireEvent("clear");
12554     },
12555    
12556 /**
12557  * Returns the first item in the collection.
12558  * @return {Object} the first item in the collection..
12559  */
12560     first : function(){
12561         return this.items[0]; 
12562     },
12563    
12564 /**
12565  * Returns the last item in the collection.
12566  * @return {Object} the last item in the collection..
12567  */
12568     last : function(){
12569         return this.items[this.length-1];   
12570     },
12571     
12572     _sort : function(property, dir, fn){
12573         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12574         fn = fn || function(a, b){
12575             return a-b;
12576         };
12577         var c = [], k = this.keys, items = this.items;
12578         for(var i = 0, len = items.length; i < len; i++){
12579             c[c.length] = {key: k[i], value: items[i], index: i};
12580         }
12581         c.sort(function(a, b){
12582             var v = fn(a[property], b[property]) * dsc;
12583             if(v == 0){
12584                 v = (a.index < b.index ? -1 : 1);
12585             }
12586             return v;
12587         });
12588         for(var i = 0, len = c.length; i < len; i++){
12589             items[i] = c[i].value;
12590             k[i] = c[i].key;
12591         }
12592         this.fireEvent("sort", this);
12593     },
12594     
12595     /**
12596      * Sorts this collection with the passed comparison function
12597      * @param {String} direction (optional) "ASC" or "DESC"
12598      * @param {Function} fn (optional) comparison function
12599      */
12600     sort : function(dir, fn){
12601         this._sort("value", dir, fn);
12602     },
12603     
12604     /**
12605      * Sorts this collection by keys
12606      * @param {String} direction (optional) "ASC" or "DESC"
12607      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12608      */
12609     keySort : function(dir, fn){
12610         this._sort("key", dir, fn || function(a, b){
12611             return String(a).toUpperCase()-String(b).toUpperCase();
12612         });
12613     },
12614     
12615     /**
12616      * Returns a range of items in this collection
12617      * @param {Number} startIndex (optional) defaults to 0
12618      * @param {Number} endIndex (optional) default to the last item
12619      * @return {Array} An array of items
12620      */
12621     getRange : function(start, end){
12622         var items = this.items;
12623         if(items.length < 1){
12624             return [];
12625         }
12626         start = start || 0;
12627         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12628         var r = [];
12629         if(start <= end){
12630             for(var i = start; i <= end; i++) {
12631                     r[r.length] = items[i];
12632             }
12633         }else{
12634             for(var i = start; i >= end; i--) {
12635                     r[r.length] = items[i];
12636             }
12637         }
12638         return r;
12639     },
12640         
12641     /**
12642      * Filter the <i>objects</i> in this collection by a specific property. 
12643      * Returns a new collection that has been filtered.
12644      * @param {String} property A property on your objects
12645      * @param {String/RegExp} value Either string that the property values 
12646      * should start with or a RegExp to test against the property
12647      * @return {MixedCollection} The new filtered collection
12648      */
12649     filter : function(property, value){
12650         if(!value.exec){ // not a regex
12651             value = String(value);
12652             if(value.length == 0){
12653                 return this.clone();
12654             }
12655             value = new RegExp("^" + Roo.escapeRe(value), "i");
12656         }
12657         return this.filterBy(function(o){
12658             return o && value.test(o[property]);
12659         });
12660         },
12661     
12662     /**
12663      * Filter by a function. * Returns a new collection that has been filtered.
12664      * The passed function will be called with each 
12665      * object in the collection. If the function returns true, the value is included 
12666      * otherwise it is filtered.
12667      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12668      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12669      * @return {MixedCollection} The new filtered collection
12670      */
12671     filterBy : function(fn, scope){
12672         var r = new Roo.util.MixedCollection();
12673         r.getKey = this.getKey;
12674         var k = this.keys, it = this.items;
12675         for(var i = 0, len = it.length; i < len; i++){
12676             if(fn.call(scope||this, it[i], k[i])){
12677                                 r.add(k[i], it[i]);
12678                         }
12679         }
12680         return r;
12681     },
12682     
12683     /**
12684      * Creates a duplicate of this collection
12685      * @return {MixedCollection}
12686      */
12687     clone : function(){
12688         var r = new Roo.util.MixedCollection();
12689         var k = this.keys, it = this.items;
12690         for(var i = 0, len = it.length; i < len; i++){
12691             r.add(k[i], it[i]);
12692         }
12693         r.getKey = this.getKey;
12694         return r;
12695     }
12696 });
12697 /**
12698  * Returns the item associated with the passed key or index.
12699  * @method
12700  * @param {String/Number} key The key or index of the item.
12701  * @return {Object} The item associated with the passed key.
12702  */
12703 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12704  * Based on:
12705  * Ext JS Library 1.1.1
12706  * Copyright(c) 2006-2007, Ext JS, LLC.
12707  *
12708  * Originally Released Under LGPL - original licence link has changed is not relivant.
12709  *
12710  * Fork - LGPL
12711  * <script type="text/javascript">
12712  */
12713 /**
12714  * @class Roo.util.JSON
12715  * Modified version of Douglas Crockford"s json.js that doesn"t
12716  * mess with the Object prototype 
12717  * http://www.json.org/js.html
12718  * @singleton
12719  */
12720 Roo.util.JSON = new (function(){
12721     var useHasOwn = {}.hasOwnProperty ? true : false;
12722     
12723     // crashes Safari in some instances
12724     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12725     
12726     var pad = function(n) {
12727         return n < 10 ? "0" + n : n;
12728     };
12729     
12730     var m = {
12731         "\b": '\\b',
12732         "\t": '\\t',
12733         "\n": '\\n',
12734         "\f": '\\f',
12735         "\r": '\\r',
12736         '"' : '\\"',
12737         "\\": '\\\\'
12738     };
12739
12740     var encodeString = function(s){
12741         if (/["\\\x00-\x1f]/.test(s)) {
12742             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12743                 var c = m[b];
12744                 if(c){
12745                     return c;
12746                 }
12747                 c = b.charCodeAt();
12748                 return "\\u00" +
12749                     Math.floor(c / 16).toString(16) +
12750                     (c % 16).toString(16);
12751             }) + '"';
12752         }
12753         return '"' + s + '"';
12754     };
12755     
12756     var encodeArray = function(o){
12757         var a = ["["], b, i, l = o.length, v;
12758             for (i = 0; i < l; i += 1) {
12759                 v = o[i];
12760                 switch (typeof v) {
12761                     case "undefined":
12762                     case "function":
12763                     case "unknown":
12764                         break;
12765                     default:
12766                         if (b) {
12767                             a.push(',');
12768                         }
12769                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12770                         b = true;
12771                 }
12772             }
12773             a.push("]");
12774             return a.join("");
12775     };
12776     
12777     var encodeDate = function(o){
12778         return '"' + o.getFullYear() + "-" +
12779                 pad(o.getMonth() + 1) + "-" +
12780                 pad(o.getDate()) + "T" +
12781                 pad(o.getHours()) + ":" +
12782                 pad(o.getMinutes()) + ":" +
12783                 pad(o.getSeconds()) + '"';
12784     };
12785     
12786     /**
12787      * Encodes an Object, Array or other value
12788      * @param {Mixed} o The variable to encode
12789      * @return {String} The JSON string
12790      */
12791     this.encode = function(o)
12792     {
12793         // should this be extended to fully wrap stringify..
12794         
12795         if(typeof o == "undefined" || o === null){
12796             return "null";
12797         }else if(o instanceof Array){
12798             return encodeArray(o);
12799         }else if(o instanceof Date){
12800             return encodeDate(o);
12801         }else if(typeof o == "string"){
12802             return encodeString(o);
12803         }else if(typeof o == "number"){
12804             return isFinite(o) ? String(o) : "null";
12805         }else if(typeof o == "boolean"){
12806             return String(o);
12807         }else {
12808             var a = ["{"], b, i, v;
12809             for (i in o) {
12810                 if(!useHasOwn || o.hasOwnProperty(i)) {
12811                     v = o[i];
12812                     switch (typeof v) {
12813                     case "undefined":
12814                     case "function":
12815                     case "unknown":
12816                         break;
12817                     default:
12818                         if(b){
12819                             a.push(',');
12820                         }
12821                         a.push(this.encode(i), ":",
12822                                 v === null ? "null" : this.encode(v));
12823                         b = true;
12824                     }
12825                 }
12826             }
12827             a.push("}");
12828             return a.join("");
12829         }
12830     };
12831     
12832     /**
12833      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12834      * @param {String} json The JSON string
12835      * @return {Object} The resulting object
12836      */
12837     this.decode = function(json){
12838         
12839         return  /** eval:var:json */ eval("(" + json + ')');
12840     };
12841 })();
12842 /** 
12843  * Shorthand for {@link Roo.util.JSON#encode}
12844  * @member Roo encode 
12845  * @method */
12846 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12847 /** 
12848  * Shorthand for {@link Roo.util.JSON#decode}
12849  * @member Roo decode 
12850  * @method */
12851 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12852 /*
12853  * Based on:
12854  * Ext JS Library 1.1.1
12855  * Copyright(c) 2006-2007, Ext JS, LLC.
12856  *
12857  * Originally Released Under LGPL - original licence link has changed is not relivant.
12858  *
12859  * Fork - LGPL
12860  * <script type="text/javascript">
12861  */
12862  
12863 /**
12864  * @class Roo.util.Format
12865  * Reusable data formatting functions
12866  * @singleton
12867  */
12868 Roo.util.Format = function(){
12869     var trimRe = /^\s+|\s+$/g;
12870     return {
12871         /**
12872          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12873          * @param {String} value The string to truncate
12874          * @param {Number} length The maximum length to allow before truncating
12875          * @return {String} The converted text
12876          */
12877         ellipsis : function(value, len){
12878             if(value && value.length > len){
12879                 return value.substr(0, len-3)+"...";
12880             }
12881             return value;
12882         },
12883
12884         /**
12885          * Checks a reference and converts it to empty string if it is undefined
12886          * @param {Mixed} value Reference to check
12887          * @return {Mixed} Empty string if converted, otherwise the original value
12888          */
12889         undef : function(value){
12890             return typeof value != "undefined" ? value : "";
12891         },
12892
12893         /**
12894          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12895          * @param {String} value The string to encode
12896          * @return {String} The encoded text
12897          */
12898         htmlEncode : function(value){
12899             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12900         },
12901
12902         /**
12903          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12904          * @param {String} value The string to decode
12905          * @return {String} The decoded text
12906          */
12907         htmlDecode : function(value){
12908             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12909         },
12910
12911         /**
12912          * Trims any whitespace from either side of a string
12913          * @param {String} value The text to trim
12914          * @return {String} The trimmed text
12915          */
12916         trim : function(value){
12917             return String(value).replace(trimRe, "");
12918         },
12919
12920         /**
12921          * Returns a substring from within an original string
12922          * @param {String} value The original text
12923          * @param {Number} start The start index of the substring
12924          * @param {Number} length The length of the substring
12925          * @return {String} The substring
12926          */
12927         substr : function(value, start, length){
12928             return String(value).substr(start, length);
12929         },
12930
12931         /**
12932          * Converts a string to all lower case letters
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         lowercase : function(value){
12937             return String(value).toLowerCase();
12938         },
12939
12940         /**
12941          * Converts a string to all upper case letters
12942          * @param {String} value The text to convert
12943          * @return {String} The converted text
12944          */
12945         uppercase : function(value){
12946             return String(value).toUpperCase();
12947         },
12948
12949         /**
12950          * Converts the first character only of a string to upper case
12951          * @param {String} value The text to convert
12952          * @return {String} The converted text
12953          */
12954         capitalize : function(value){
12955             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12956         },
12957
12958         // private
12959         call : function(value, fn){
12960             if(arguments.length > 2){
12961                 var args = Array.prototype.slice.call(arguments, 2);
12962                 args.unshift(value);
12963                  
12964                 return /** eval:var:value */  eval(fn).apply(window, args);
12965             }else{
12966                 /** eval:var:value */
12967                 return /** eval:var:value */ eval(fn).call(window, value);
12968             }
12969         },
12970
12971        
12972         /**
12973          * safer version of Math.toFixed..??/
12974          * @param {Number/String} value The numeric value to format
12975          * @param {Number/String} value Decimal places 
12976          * @return {String} The formatted currency string
12977          */
12978         toFixed : function(v, n)
12979         {
12980             // why not use to fixed - precision is buggered???
12981             if (!n) {
12982                 return Math.round(v-0);
12983             }
12984             var fact = Math.pow(10,n+1);
12985             v = (Math.round((v-0)*fact))/fact;
12986             var z = (''+fact).substring(2);
12987             if (v == Math.floor(v)) {
12988                 return Math.floor(v) + '.' + z;
12989             }
12990             
12991             // now just padd decimals..
12992             var ps = String(v).split('.');
12993             var fd = (ps[1] + z);
12994             var r = fd.substring(0,n); 
12995             var rm = fd.substring(n); 
12996             if (rm < 5) {
12997                 return ps[0] + '.' + r;
12998             }
12999             r*=1; // turn it into a number;
13000             r++;
13001             if (String(r).length != n) {
13002                 ps[0]*=1;
13003                 ps[0]++;
13004                 r = String(r).substring(1); // chop the end off.
13005             }
13006             
13007             return ps[0] + '.' + r;
13008              
13009         },
13010         
13011         /**
13012          * Format a number as US currency
13013          * @param {Number/String} value The numeric value to format
13014          * @return {String} The formatted currency string
13015          */
13016         usMoney : function(v){
13017             v = (Math.round((v-0)*100))/100;
13018             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13019             v = String(v);
13020             var ps = v.split('.');
13021             var whole = ps[0];
13022             var sub = ps[1] ? '.'+ ps[1] : '.00';
13023             var r = /(\d+)(\d{3})/;
13024             while (r.test(whole)) {
13025                 whole = whole.replace(r, '$1' + ',' + '$2');
13026             }
13027             return "$" + whole + sub ;
13028         },
13029         
13030         /**
13031          * Parse a value into a formatted date using the specified format pattern.
13032          * @param {Mixed} value The value to format
13033          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13034          * @return {String} The formatted date string
13035          */
13036         date : function(v, format){
13037             if(!v){
13038                 return "";
13039             }
13040             if(!(v instanceof Date)){
13041                 v = new Date(Date.parse(v));
13042             }
13043             return v.dateFormat(format || "m/d/Y");
13044         },
13045
13046         /**
13047          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13048          * @param {String} format Any valid date format string
13049          * @return {Function} The date formatting function
13050          */
13051         dateRenderer : function(format){
13052             return function(v){
13053                 return Roo.util.Format.date(v, format);  
13054             };
13055         },
13056
13057         // private
13058         stripTagsRE : /<\/?[^>]+>/gi,
13059         
13060         /**
13061          * Strips all HTML tags
13062          * @param {Mixed} value The text from which to strip tags
13063          * @return {String} The stripped text
13064          */
13065         stripTags : function(v){
13066             return !v ? v : String(v).replace(this.stripTagsRE, "");
13067         }
13068     };
13069 }();/*
13070  * Based on:
13071  * Ext JS Library 1.1.1
13072  * Copyright(c) 2006-2007, Ext JS, LLC.
13073  *
13074  * Originally Released Under LGPL - original licence link has changed is not relivant.
13075  *
13076  * Fork - LGPL
13077  * <script type="text/javascript">
13078  */
13079
13080
13081  
13082
13083 /**
13084  * @class Roo.MasterTemplate
13085  * @extends Roo.Template
13086  * Provides a template that can have child templates. The syntax is:
13087 <pre><code>
13088 var t = new Roo.MasterTemplate(
13089         '&lt;select name="{name}"&gt;',
13090                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13091         '&lt;/select&gt;'
13092 );
13093 t.add('options', {value: 'foo', text: 'bar'});
13094 // or you can add multiple child elements in one shot
13095 t.addAll('options', [
13096     {value: 'foo', text: 'bar'},
13097     {value: 'foo2', text: 'bar2'},
13098     {value: 'foo3', text: 'bar3'}
13099 ]);
13100 // then append, applying the master template values
13101 t.append('my-form', {name: 'my-select'});
13102 </code></pre>
13103 * A name attribute for the child template is not required if you have only one child
13104 * template or you want to refer to them by index.
13105  */
13106 Roo.MasterTemplate = function(){
13107     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13108     this.originalHtml = this.html;
13109     var st = {};
13110     var m, re = this.subTemplateRe;
13111     re.lastIndex = 0;
13112     var subIndex = 0;
13113     while(m = re.exec(this.html)){
13114         var name = m[1], content = m[2];
13115         st[subIndex] = {
13116             name: name,
13117             index: subIndex,
13118             buffer: [],
13119             tpl : new Roo.Template(content)
13120         };
13121         if(name){
13122             st[name] = st[subIndex];
13123         }
13124         st[subIndex].tpl.compile();
13125         st[subIndex].tpl.call = this.call.createDelegate(this);
13126         subIndex++;
13127     }
13128     this.subCount = subIndex;
13129     this.subs = st;
13130 };
13131 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13132     /**
13133     * The regular expression used to match sub templates
13134     * @type RegExp
13135     * @property
13136     */
13137     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13138
13139     /**
13140      * Applies the passed values to a child template.
13141      * @param {String/Number} name (optional) The name or index of the child template
13142      * @param {Array/Object} values The values to be applied to the template
13143      * @return {MasterTemplate} this
13144      */
13145      add : function(name, values){
13146         if(arguments.length == 1){
13147             values = arguments[0];
13148             name = 0;
13149         }
13150         var s = this.subs[name];
13151         s.buffer[s.buffer.length] = s.tpl.apply(values);
13152         return this;
13153     },
13154
13155     /**
13156      * Applies all the passed values to a child template.
13157      * @param {String/Number} name (optional) The name or index of the child template
13158      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13159      * @param {Boolean} reset (optional) True to reset the template first
13160      * @return {MasterTemplate} this
13161      */
13162     fill : function(name, values, reset){
13163         var a = arguments;
13164         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13165             values = a[0];
13166             name = 0;
13167             reset = a[1];
13168         }
13169         if(reset){
13170             this.reset();
13171         }
13172         for(var i = 0, len = values.length; i < len; i++){
13173             this.add(name, values[i]);
13174         }
13175         return this;
13176     },
13177
13178     /**
13179      * Resets the template for reuse
13180      * @return {MasterTemplate} this
13181      */
13182      reset : function(){
13183         var s = this.subs;
13184         for(var i = 0; i < this.subCount; i++){
13185             s[i].buffer = [];
13186         }
13187         return this;
13188     },
13189
13190     applyTemplate : function(values){
13191         var s = this.subs;
13192         var replaceIndex = -1;
13193         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13194             return s[++replaceIndex].buffer.join("");
13195         });
13196         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13197     },
13198
13199     apply : function(){
13200         return this.applyTemplate.apply(this, arguments);
13201     },
13202
13203     compile : function(){return this;}
13204 });
13205
13206 /**
13207  * Alias for fill().
13208  * @method
13209  */
13210 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13211  /**
13212  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13213  * var tpl = Roo.MasterTemplate.from('element-id');
13214  * @param {String/HTMLElement} el
13215  * @param {Object} config
13216  * @static
13217  */
13218 Roo.MasterTemplate.from = function(el, config){
13219     el = Roo.getDom(el);
13220     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13221 };/*
13222  * Based on:
13223  * Ext JS Library 1.1.1
13224  * Copyright(c) 2006-2007, Ext JS, LLC.
13225  *
13226  * Originally Released Under LGPL - original licence link has changed is not relivant.
13227  *
13228  * Fork - LGPL
13229  * <script type="text/javascript">
13230  */
13231
13232  
13233 /**
13234  * @class Roo.util.CSS
13235  * Utility class for manipulating CSS rules
13236  * @singleton
13237  */
13238 Roo.util.CSS = function(){
13239         var rules = null;
13240         var doc = document;
13241
13242     var camelRe = /(-[a-z])/gi;
13243     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13244
13245    return {
13246    /**
13247     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13248     * tag and appended to the HEAD of the document.
13249     * @param {String|Object} cssText The text containing the css rules
13250     * @param {String} id An id to add to the stylesheet for later removal
13251     * @return {StyleSheet}
13252     */
13253     createStyleSheet : function(cssText, id){
13254         var ss;
13255         var head = doc.getElementsByTagName("head")[0];
13256         var nrules = doc.createElement("style");
13257         nrules.setAttribute("type", "text/css");
13258         if(id){
13259             nrules.setAttribute("id", id);
13260         }
13261         if (typeof(cssText) != 'string') {
13262             // support object maps..
13263             // not sure if this a good idea.. 
13264             // perhaps it should be merged with the general css handling
13265             // and handle js style props.
13266             var cssTextNew = [];
13267             for(var n in cssText) {
13268                 var citems = [];
13269                 for(var k in cssText[n]) {
13270                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13271                 }
13272                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13273                 
13274             }
13275             cssText = cssTextNew.join("\n");
13276             
13277         }
13278        
13279        
13280        if(Roo.isIE){
13281            head.appendChild(nrules);
13282            ss = nrules.styleSheet;
13283            ss.cssText = cssText;
13284        }else{
13285            try{
13286                 nrules.appendChild(doc.createTextNode(cssText));
13287            }catch(e){
13288                nrules.cssText = cssText; 
13289            }
13290            head.appendChild(nrules);
13291            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13292        }
13293        this.cacheStyleSheet(ss);
13294        return ss;
13295    },
13296
13297    /**
13298     * Removes a style or link tag by id
13299     * @param {String} id The id of the tag
13300     */
13301    removeStyleSheet : function(id){
13302        var existing = doc.getElementById(id);
13303        if(existing){
13304            existing.parentNode.removeChild(existing);
13305        }
13306    },
13307
13308    /**
13309     * Dynamically swaps an existing stylesheet reference for a new one
13310     * @param {String} id The id of an existing link tag to remove
13311     * @param {String} url The href of the new stylesheet to include
13312     */
13313    swapStyleSheet : function(id, url){
13314        this.removeStyleSheet(id);
13315        var ss = doc.createElement("link");
13316        ss.setAttribute("rel", "stylesheet");
13317        ss.setAttribute("type", "text/css");
13318        ss.setAttribute("id", id);
13319        ss.setAttribute("href", url);
13320        doc.getElementsByTagName("head")[0].appendChild(ss);
13321    },
13322    
13323    /**
13324     * Refresh the rule cache if you have dynamically added stylesheets
13325     * @return {Object} An object (hash) of rules indexed by selector
13326     */
13327    refreshCache : function(){
13328        return this.getRules(true);
13329    },
13330
13331    // private
13332    cacheStyleSheet : function(stylesheet){
13333        if(!rules){
13334            rules = {};
13335        }
13336        try{// try catch for cross domain access issue
13337            var ssRules = stylesheet.cssRules || stylesheet.rules;
13338            for(var j = ssRules.length-1; j >= 0; --j){
13339                rules[ssRules[j].selectorText] = ssRules[j];
13340            }
13341        }catch(e){}
13342    },
13343    
13344    /**
13345     * Gets all css rules for the document
13346     * @param {Boolean} refreshCache true to refresh the internal cache
13347     * @return {Object} An object (hash) of rules indexed by selector
13348     */
13349    getRules : function(refreshCache){
13350                 if(rules == null || refreshCache){
13351                         rules = {};
13352                         var ds = doc.styleSheets;
13353                         for(var i =0, len = ds.length; i < len; i++){
13354                             try{
13355                         this.cacheStyleSheet(ds[i]);
13356                     }catch(e){} 
13357                 }
13358                 }
13359                 return rules;
13360         },
13361         
13362         /**
13363     * Gets an an individual CSS rule by selector(s)
13364     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13365     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13366     * @return {CSSRule} The CSS rule or null if one is not found
13367     */
13368    getRule : function(selector, refreshCache){
13369                 var rs = this.getRules(refreshCache);
13370                 if(!(selector instanceof Array)){
13371                     return rs[selector];
13372                 }
13373                 for(var i = 0; i < selector.length; i++){
13374                         if(rs[selector[i]]){
13375                                 return rs[selector[i]];
13376                         }
13377                 }
13378                 return null;
13379         },
13380         
13381         
13382         /**
13383     * Updates a rule property
13384     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13385     * @param {String} property The css property
13386     * @param {String} value The new value for the property
13387     * @return {Boolean} true If a rule was found and updated
13388     */
13389    updateRule : function(selector, property, value){
13390                 if(!(selector instanceof Array)){
13391                         var rule = this.getRule(selector);
13392                         if(rule){
13393                                 rule.style[property.replace(camelRe, camelFn)] = value;
13394                                 return true;
13395                         }
13396                 }else{
13397                         for(var i = 0; i < selector.length; i++){
13398                                 if(this.updateRule(selector[i], property, value)){
13399                                         return true;
13400                                 }
13401                         }
13402                 }
13403                 return false;
13404         }
13405    };   
13406 }();/*
13407  * Based on:
13408  * Ext JS Library 1.1.1
13409  * Copyright(c) 2006-2007, Ext JS, LLC.
13410  *
13411  * Originally Released Under LGPL - original licence link has changed is not relivant.
13412  *
13413  * Fork - LGPL
13414  * <script type="text/javascript">
13415  */
13416
13417  
13418
13419 /**
13420  * @class Roo.util.ClickRepeater
13421  * @extends Roo.util.Observable
13422  * 
13423  * A wrapper class which can be applied to any element. Fires a "click" event while the
13424  * mouse is pressed. The interval between firings may be specified in the config but
13425  * defaults to 10 milliseconds.
13426  * 
13427  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13428  * 
13429  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13430  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13431  * Similar to an autorepeat key delay.
13432  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13433  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13434  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13435  *           "interval" and "delay" are ignored. "immediate" is honored.
13436  * @cfg {Boolean} preventDefault True to prevent the default click event
13437  * @cfg {Boolean} stopDefault True to stop the default click event
13438  * 
13439  * @history
13440  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13441  *     2007-02-02 jvs Renamed to ClickRepeater
13442  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13443  *
13444  *  @constructor
13445  * @param {String/HTMLElement/Element} el The element to listen on
13446  * @param {Object} config
13447  **/
13448 Roo.util.ClickRepeater = function(el, config)
13449 {
13450     this.el = Roo.get(el);
13451     this.el.unselectable();
13452
13453     Roo.apply(this, config);
13454
13455     this.addEvents({
13456     /**
13457      * @event mousedown
13458      * Fires when the mouse button is depressed.
13459      * @param {Roo.util.ClickRepeater} this
13460      */
13461         "mousedown" : true,
13462     /**
13463      * @event click
13464      * Fires on a specified interval during the time the element is pressed.
13465      * @param {Roo.util.ClickRepeater} this
13466      */
13467         "click" : true,
13468     /**
13469      * @event mouseup
13470      * Fires when the mouse key is released.
13471      * @param {Roo.util.ClickRepeater} this
13472      */
13473         "mouseup" : true
13474     });
13475
13476     this.el.on("mousedown", this.handleMouseDown, this);
13477     if(this.preventDefault || this.stopDefault){
13478         this.el.on("click", function(e){
13479             if(this.preventDefault){
13480                 e.preventDefault();
13481             }
13482             if(this.stopDefault){
13483                 e.stopEvent();
13484             }
13485         }, this);
13486     }
13487
13488     // allow inline handler
13489     if(this.handler){
13490         this.on("click", this.handler,  this.scope || this);
13491     }
13492
13493     Roo.util.ClickRepeater.superclass.constructor.call(this);
13494 };
13495
13496 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13497     interval : 20,
13498     delay: 250,
13499     preventDefault : true,
13500     stopDefault : false,
13501     timer : 0,
13502
13503     // private
13504     handleMouseDown : function(){
13505         clearTimeout(this.timer);
13506         this.el.blur();
13507         if(this.pressClass){
13508             this.el.addClass(this.pressClass);
13509         }
13510         this.mousedownTime = new Date();
13511
13512         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13513         this.el.on("mouseout", this.handleMouseOut, this);
13514
13515         this.fireEvent("mousedown", this);
13516         this.fireEvent("click", this);
13517         
13518         this.timer = this.click.defer(this.delay || this.interval, this);
13519     },
13520
13521     // private
13522     click : function(){
13523         this.fireEvent("click", this);
13524         this.timer = this.click.defer(this.getInterval(), this);
13525     },
13526
13527     // private
13528     getInterval: function(){
13529         if(!this.accelerate){
13530             return this.interval;
13531         }
13532         var pressTime = this.mousedownTime.getElapsed();
13533         if(pressTime < 500){
13534             return 400;
13535         }else if(pressTime < 1700){
13536             return 320;
13537         }else if(pressTime < 2600){
13538             return 250;
13539         }else if(pressTime < 3500){
13540             return 180;
13541         }else if(pressTime < 4400){
13542             return 140;
13543         }else if(pressTime < 5300){
13544             return 80;
13545         }else if(pressTime < 6200){
13546             return 50;
13547         }else{
13548             return 10;
13549         }
13550     },
13551
13552     // private
13553     handleMouseOut : function(){
13554         clearTimeout(this.timer);
13555         if(this.pressClass){
13556             this.el.removeClass(this.pressClass);
13557         }
13558         this.el.on("mouseover", this.handleMouseReturn, this);
13559     },
13560
13561     // private
13562     handleMouseReturn : function(){
13563         this.el.un("mouseover", this.handleMouseReturn);
13564         if(this.pressClass){
13565             this.el.addClass(this.pressClass);
13566         }
13567         this.click();
13568     },
13569
13570     // private
13571     handleMouseUp : function(){
13572         clearTimeout(this.timer);
13573         this.el.un("mouseover", this.handleMouseReturn);
13574         this.el.un("mouseout", this.handleMouseOut);
13575         Roo.get(document).un("mouseup", this.handleMouseUp);
13576         this.el.removeClass(this.pressClass);
13577         this.fireEvent("mouseup", this);
13578     }
13579 });/*
13580  * Based on:
13581  * Ext JS Library 1.1.1
13582  * Copyright(c) 2006-2007, Ext JS, LLC.
13583  *
13584  * Originally Released Under LGPL - original licence link has changed is not relivant.
13585  *
13586  * Fork - LGPL
13587  * <script type="text/javascript">
13588  */
13589
13590  
13591 /**
13592  * @class Roo.KeyNav
13593  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13594  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13595  * way to implement custom navigation schemes for any UI component.</p>
13596  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13597  * pageUp, pageDown, del, home, end.  Usage:</p>
13598  <pre><code>
13599 var nav = new Roo.KeyNav("my-element", {
13600     "left" : function(e){
13601         this.moveLeft(e.ctrlKey);
13602     },
13603     "right" : function(e){
13604         this.moveRight(e.ctrlKey);
13605     },
13606     "enter" : function(e){
13607         this.save();
13608     },
13609     scope : this
13610 });
13611 </code></pre>
13612  * @constructor
13613  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13614  * @param {Object} config The config
13615  */
13616 Roo.KeyNav = function(el, config){
13617     this.el = Roo.get(el);
13618     Roo.apply(this, config);
13619     if(!this.disabled){
13620         this.disabled = true;
13621         this.enable();
13622     }
13623 };
13624
13625 Roo.KeyNav.prototype = {
13626     /**
13627      * @cfg {Boolean} disabled
13628      * True to disable this KeyNav instance (defaults to false)
13629      */
13630     disabled : false,
13631     /**
13632      * @cfg {String} defaultEventAction
13633      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13634      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13635      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13636      */
13637     defaultEventAction: "stopEvent",
13638     /**
13639      * @cfg {Boolean} forceKeyDown
13640      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13641      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13642      * handle keydown instead of keypress.
13643      */
13644     forceKeyDown : false,
13645
13646     // private
13647     prepareEvent : function(e){
13648         var k = e.getKey();
13649         var h = this.keyToHandler[k];
13650         //if(h && this[h]){
13651         //    e.stopPropagation();
13652         //}
13653         if(Roo.isSafari && h && k >= 37 && k <= 40){
13654             e.stopEvent();
13655         }
13656     },
13657
13658     // private
13659     relay : function(e){
13660         var k = e.getKey();
13661         var h = this.keyToHandler[k];
13662         if(h && this[h]){
13663             if(this.doRelay(e, this[h], h) !== true){
13664                 e[this.defaultEventAction]();
13665             }
13666         }
13667     },
13668
13669     // private
13670     doRelay : function(e, h, hname){
13671         return h.call(this.scope || this, e);
13672     },
13673
13674     // possible handlers
13675     enter : false,
13676     left : false,
13677     right : false,
13678     up : false,
13679     down : false,
13680     tab : false,
13681     esc : false,
13682     pageUp : false,
13683     pageDown : false,
13684     del : false,
13685     home : false,
13686     end : false,
13687
13688     // quick lookup hash
13689     keyToHandler : {
13690         37 : "left",
13691         39 : "right",
13692         38 : "up",
13693         40 : "down",
13694         33 : "pageUp",
13695         34 : "pageDown",
13696         46 : "del",
13697         36 : "home",
13698         35 : "end",
13699         13 : "enter",
13700         27 : "esc",
13701         9  : "tab"
13702     },
13703
13704         /**
13705          * Enable this KeyNav
13706          */
13707         enable: function(){
13708                 if(this.disabled){
13709             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13710             // the EventObject will normalize Safari automatically
13711             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13712                 this.el.on("keydown", this.relay,  this);
13713             }else{
13714                 this.el.on("keydown", this.prepareEvent,  this);
13715                 this.el.on("keypress", this.relay,  this);
13716             }
13717                     this.disabled = false;
13718                 }
13719         },
13720
13721         /**
13722          * Disable this KeyNav
13723          */
13724         disable: function(){
13725                 if(!this.disabled){
13726                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13727                 this.el.un("keydown", this.relay);
13728             }else{
13729                 this.el.un("keydown", this.prepareEvent);
13730                 this.el.un("keypress", this.relay);
13731             }
13732                     this.disabled = true;
13733                 }
13734         }
13735 };/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745
13746  
13747 /**
13748  * @class Roo.KeyMap
13749  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13750  * The constructor accepts the same config object as defined by {@link #addBinding}.
13751  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13752  * combination it will call the function with this signature (if the match is a multi-key
13753  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13754  * A KeyMap can also handle a string representation of keys.<br />
13755  * Usage:
13756  <pre><code>
13757 // map one key by key code
13758 var map = new Roo.KeyMap("my-element", {
13759     key: 13, // or Roo.EventObject.ENTER
13760     fn: myHandler,
13761     scope: myObject
13762 });
13763
13764 // map multiple keys to one action by string
13765 var map = new Roo.KeyMap("my-element", {
13766     key: "a\r\n\t",
13767     fn: myHandler,
13768     scope: myObject
13769 });
13770
13771 // map multiple keys to multiple actions by strings and array of codes
13772 var map = new Roo.KeyMap("my-element", [
13773     {
13774         key: [10,13],
13775         fn: function(){ alert("Return was pressed"); }
13776     }, {
13777         key: "abc",
13778         fn: function(){ alert('a, b or c was pressed'); }
13779     }, {
13780         key: "\t",
13781         ctrl:true,
13782         shift:true,
13783         fn: function(){ alert('Control + shift + tab was pressed.'); }
13784     }
13785 ]);
13786 </code></pre>
13787  * <b>Note: A KeyMap starts enabled</b>
13788  * @constructor
13789  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13790  * @param {Object} config The config (see {@link #addBinding})
13791  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13792  */
13793 Roo.KeyMap = function(el, config, eventName){
13794     this.el  = Roo.get(el);
13795     this.eventName = eventName || "keydown";
13796     this.bindings = [];
13797     if(config){
13798         this.addBinding(config);
13799     }
13800     this.enable();
13801 };
13802
13803 Roo.KeyMap.prototype = {
13804     /**
13805      * True to stop the event from bubbling and prevent the default browser action if the
13806      * key was handled by the KeyMap (defaults to false)
13807      * @type Boolean
13808      */
13809     stopEvent : false,
13810
13811     /**
13812      * Add a new binding to this KeyMap. The following config object properties are supported:
13813      * <pre>
13814 Property    Type             Description
13815 ----------  ---------------  ----------------------------------------------------------------------
13816 key         String/Array     A single keycode or an array of keycodes to handle
13817 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13818 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13819 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13820 fn          Function         The function to call when KeyMap finds the expected key combination
13821 scope       Object           The scope of the callback function
13822 </pre>
13823      *
13824      * Usage:
13825      * <pre><code>
13826 // Create a KeyMap
13827 var map = new Roo.KeyMap(document, {
13828     key: Roo.EventObject.ENTER,
13829     fn: handleKey,
13830     scope: this
13831 });
13832
13833 //Add a new binding to the existing KeyMap later
13834 map.addBinding({
13835     key: 'abc',
13836     shift: true,
13837     fn: handleKey,
13838     scope: this
13839 });
13840 </code></pre>
13841      * @param {Object/Array} config A single KeyMap config or an array of configs
13842      */
13843         addBinding : function(config){
13844         if(config instanceof Array){
13845             for(var i = 0, len = config.length; i < len; i++){
13846                 this.addBinding(config[i]);
13847             }
13848             return;
13849         }
13850         var keyCode = config.key,
13851             shift = config.shift, 
13852             ctrl = config.ctrl, 
13853             alt = config.alt,
13854             fn = config.fn,
13855             scope = config.scope;
13856         if(typeof keyCode == "string"){
13857             var ks = [];
13858             var keyString = keyCode.toUpperCase();
13859             for(var j = 0, len = keyString.length; j < len; j++){
13860                 ks.push(keyString.charCodeAt(j));
13861             }
13862             keyCode = ks;
13863         }
13864         var keyArray = keyCode instanceof Array;
13865         var handler = function(e){
13866             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13867                 var k = e.getKey();
13868                 if(keyArray){
13869                     for(var i = 0, len = keyCode.length; i < len; i++){
13870                         if(keyCode[i] == k){
13871                           if(this.stopEvent){
13872                               e.stopEvent();
13873                           }
13874                           fn.call(scope || window, k, e);
13875                           return;
13876                         }
13877                     }
13878                 }else{
13879                     if(k == keyCode){
13880                         if(this.stopEvent){
13881                            e.stopEvent();
13882                         }
13883                         fn.call(scope || window, k, e);
13884                     }
13885                 }
13886             }
13887         };
13888         this.bindings.push(handler);  
13889         },
13890
13891     /**
13892      * Shorthand for adding a single key listener
13893      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13894      * following options:
13895      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13896      * @param {Function} fn The function to call
13897      * @param {Object} scope (optional) The scope of the function
13898      */
13899     on : function(key, fn, scope){
13900         var keyCode, shift, ctrl, alt;
13901         if(typeof key == "object" && !(key instanceof Array)){
13902             keyCode = key.key;
13903             shift = key.shift;
13904             ctrl = key.ctrl;
13905             alt = key.alt;
13906         }else{
13907             keyCode = key;
13908         }
13909         this.addBinding({
13910             key: keyCode,
13911             shift: shift,
13912             ctrl: ctrl,
13913             alt: alt,
13914             fn: fn,
13915             scope: scope
13916         })
13917     },
13918
13919     // private
13920     handleKeyDown : function(e){
13921             if(this.enabled){ //just in case
13922             var b = this.bindings;
13923             for(var i = 0, len = b.length; i < len; i++){
13924                 b[i].call(this, e);
13925             }
13926             }
13927         },
13928         
13929         /**
13930          * Returns true if this KeyMap is enabled
13931          * @return {Boolean} 
13932          */
13933         isEnabled : function(){
13934             return this.enabled;  
13935         },
13936         
13937         /**
13938          * Enables this KeyMap
13939          */
13940         enable: function(){
13941                 if(!this.enabled){
13942                     this.el.on(this.eventName, this.handleKeyDown, this);
13943                     this.enabled = true;
13944                 }
13945         },
13946
13947         /**
13948          * Disable this KeyMap
13949          */
13950         disable: function(){
13951                 if(this.enabled){
13952                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13953                     this.enabled = false;
13954                 }
13955         }
13956 };/*
13957  * Based on:
13958  * Ext JS Library 1.1.1
13959  * Copyright(c) 2006-2007, Ext JS, LLC.
13960  *
13961  * Originally Released Under LGPL - original licence link has changed is not relivant.
13962  *
13963  * Fork - LGPL
13964  * <script type="text/javascript">
13965  */
13966
13967  
13968 /**
13969  * @class Roo.util.TextMetrics
13970  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13971  * wide, in pixels, a given block of text will be.
13972  * @singleton
13973  */
13974 Roo.util.TextMetrics = function(){
13975     var shared;
13976     return {
13977         /**
13978          * Measures the size of the specified text
13979          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13980          * that can affect the size of the rendered text
13981          * @param {String} text The text to measure
13982          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13983          * in order to accurately measure the text height
13984          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13985          */
13986         measure : function(el, text, fixedWidth){
13987             if(!shared){
13988                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13989             }
13990             shared.bind(el);
13991             shared.setFixedWidth(fixedWidth || 'auto');
13992             return shared.getSize(text);
13993         },
13994
13995         /**
13996          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13997          * the overhead of multiple calls to initialize the style properties on each measurement.
13998          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13999          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14000          * in order to accurately measure the text height
14001          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14002          */
14003         createInstance : function(el, fixedWidth){
14004             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14005         }
14006     };
14007 }();
14008
14009  
14010
14011 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14012     var ml = new Roo.Element(document.createElement('div'));
14013     document.body.appendChild(ml.dom);
14014     ml.position('absolute');
14015     ml.setLeftTop(-1000, -1000);
14016     ml.hide();
14017
14018     if(fixedWidth){
14019         ml.setWidth(fixedWidth);
14020     }
14021      
14022     var instance = {
14023         /**
14024          * Returns the size of the specified text based on the internal element's style and width properties
14025          * @memberOf Roo.util.TextMetrics.Instance#
14026          * @param {String} text The text to measure
14027          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14028          */
14029         getSize : function(text){
14030             ml.update(text);
14031             var s = ml.getSize();
14032             ml.update('');
14033             return s;
14034         },
14035
14036         /**
14037          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14038          * that can affect the size of the rendered text
14039          * @memberOf Roo.util.TextMetrics.Instance#
14040          * @param {String/HTMLElement} el The element, dom node or id
14041          */
14042         bind : function(el){
14043             ml.setStyle(
14044                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14045             );
14046         },
14047
14048         /**
14049          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14050          * to set a fixed width in order to accurately measure the text height.
14051          * @memberOf Roo.util.TextMetrics.Instance#
14052          * @param {Number} width The width to set on the element
14053          */
14054         setFixedWidth : function(width){
14055             ml.setWidth(width);
14056         },
14057
14058         /**
14059          * Returns the measured width of the specified text
14060          * @memberOf Roo.util.TextMetrics.Instance#
14061          * @param {String} text The text to measure
14062          * @return {Number} width The width in pixels
14063          */
14064         getWidth : function(text){
14065             ml.dom.style.width = 'auto';
14066             return this.getSize(text).width;
14067         },
14068
14069         /**
14070          * Returns the measured height of the specified text.  For multiline text, be sure to call
14071          * {@link #setFixedWidth} if necessary.
14072          * @memberOf Roo.util.TextMetrics.Instance#
14073          * @param {String} text The text to measure
14074          * @return {Number} height The height in pixels
14075          */
14076         getHeight : function(text){
14077             return this.getSize(text).height;
14078         }
14079     };
14080
14081     instance.bind(bindTo);
14082
14083     return instance;
14084 };
14085
14086 // backwards compat
14087 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097
14098 /**
14099  * @class Roo.state.Provider
14100  * Abstract base class for state provider implementations. This class provides methods
14101  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14102  * Provider interface.
14103  */
14104 Roo.state.Provider = function(){
14105     /**
14106      * @event statechange
14107      * Fires when a state change occurs.
14108      * @param {Provider} this This state provider
14109      * @param {String} key The state key which was changed
14110      * @param {String} value The encoded value for the state
14111      */
14112     this.addEvents({
14113         "statechange": true
14114     });
14115     this.state = {};
14116     Roo.state.Provider.superclass.constructor.call(this);
14117 };
14118 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14119     /**
14120      * Returns the current value for a key
14121      * @param {String} name The key name
14122      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14123      * @return {Mixed} The state data
14124      */
14125     get : function(name, defaultValue){
14126         return typeof this.state[name] == "undefined" ?
14127             defaultValue : this.state[name];
14128     },
14129     
14130     /**
14131      * Clears a value from the state
14132      * @param {String} name The key name
14133      */
14134     clear : function(name){
14135         delete this.state[name];
14136         this.fireEvent("statechange", this, name, null);
14137     },
14138     
14139     /**
14140      * Sets the value for a key
14141      * @param {String} name The key name
14142      * @param {Mixed} value The value to set
14143      */
14144     set : function(name, value){
14145         this.state[name] = value;
14146         this.fireEvent("statechange", this, name, value);
14147     },
14148     
14149     /**
14150      * Decodes a string previously encoded with {@link #encodeValue}.
14151      * @param {String} value The value to decode
14152      * @return {Mixed} The decoded value
14153      */
14154     decodeValue : function(cookie){
14155         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14156         var matches = re.exec(unescape(cookie));
14157         if(!matches || !matches[1]) return; // non state cookie
14158         var type = matches[1];
14159         var v = matches[2];
14160         switch(type){
14161             case "n":
14162                 return parseFloat(v);
14163             case "d":
14164                 return new Date(Date.parse(v));
14165             case "b":
14166                 return (v == "1");
14167             case "a":
14168                 var all = [];
14169                 var values = v.split("^");
14170                 for(var i = 0, len = values.length; i < len; i++){
14171                     all.push(this.decodeValue(values[i]));
14172                 }
14173                 return all;
14174            case "o":
14175                 var all = {};
14176                 var values = v.split("^");
14177                 for(var i = 0, len = values.length; i < len; i++){
14178                     var kv = values[i].split("=");
14179                     all[kv[0]] = this.decodeValue(kv[1]);
14180                 }
14181                 return all;
14182            default:
14183                 return v;
14184         }
14185     },
14186     
14187     /**
14188      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14189      * @param {Mixed} value The value to encode
14190      * @return {String} The encoded value
14191      */
14192     encodeValue : function(v){
14193         var enc;
14194         if(typeof v == "number"){
14195             enc = "n:" + v;
14196         }else if(typeof v == "boolean"){
14197             enc = "b:" + (v ? "1" : "0");
14198         }else if(v instanceof Date){
14199             enc = "d:" + v.toGMTString();
14200         }else if(v instanceof Array){
14201             var flat = "";
14202             for(var i = 0, len = v.length; i < len; i++){
14203                 flat += this.encodeValue(v[i]);
14204                 if(i != len-1) flat += "^";
14205             }
14206             enc = "a:" + flat;
14207         }else if(typeof v == "object"){
14208             var flat = "";
14209             for(var key in v){
14210                 if(typeof v[key] != "function"){
14211                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14212                 }
14213             }
14214             enc = "o:" + flat.substring(0, flat.length-1);
14215         }else{
14216             enc = "s:" + v;
14217         }
14218         return escape(enc);        
14219     }
14220 });
14221
14222 /*
14223  * Based on:
14224  * Ext JS Library 1.1.1
14225  * Copyright(c) 2006-2007, Ext JS, LLC.
14226  *
14227  * Originally Released Under LGPL - original licence link has changed is not relivant.
14228  *
14229  * Fork - LGPL
14230  * <script type="text/javascript">
14231  */
14232 /**
14233  * @class Roo.state.Manager
14234  * This is the global state manager. By default all components that are "state aware" check this class
14235  * for state information if you don't pass them a custom state provider. In order for this class
14236  * to be useful, it must be initialized with a provider when your application initializes.
14237  <pre><code>
14238 // in your initialization function
14239 init : function(){
14240    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14241    ...
14242    // supposed you have a {@link Roo.BorderLayout}
14243    var layout = new Roo.BorderLayout(...);
14244    layout.restoreState();
14245    // or a {Roo.BasicDialog}
14246    var dialog = new Roo.BasicDialog(...);
14247    dialog.restoreState();
14248  </code></pre>
14249  * @singleton
14250  */
14251 Roo.state.Manager = function(){
14252     var provider = new Roo.state.Provider();
14253     
14254     return {
14255         /**
14256          * Configures the default state provider for your application
14257          * @param {Provider} stateProvider The state provider to set
14258          */
14259         setProvider : function(stateProvider){
14260             provider = stateProvider;
14261         },
14262         
14263         /**
14264          * Returns the current value for a key
14265          * @param {String} name The key name
14266          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14267          * @return {Mixed} The state data
14268          */
14269         get : function(key, defaultValue){
14270             return provider.get(key, defaultValue);
14271         },
14272         
14273         /**
14274          * Sets the value for a key
14275          * @param {String} name The key name
14276          * @param {Mixed} value The state data
14277          */
14278          set : function(key, value){
14279             provider.set(key, value);
14280         },
14281         
14282         /**
14283          * Clears a value from the state
14284          * @param {String} name The key name
14285          */
14286         clear : function(key){
14287             provider.clear(key);
14288         },
14289         
14290         /**
14291          * Gets the currently configured state provider
14292          * @return {Provider} The state provider
14293          */
14294         getProvider : function(){
14295             return provider;
14296         }
14297     };
14298 }();
14299 /*
14300  * Based on:
14301  * Ext JS Library 1.1.1
14302  * Copyright(c) 2006-2007, Ext JS, LLC.
14303  *
14304  * Originally Released Under LGPL - original licence link has changed is not relivant.
14305  *
14306  * Fork - LGPL
14307  * <script type="text/javascript">
14308  */
14309 /**
14310  * @class Roo.state.CookieProvider
14311  * @extends Roo.state.Provider
14312  * The default Provider implementation which saves state via cookies.
14313  * <br />Usage:
14314  <pre><code>
14315    var cp = new Roo.state.CookieProvider({
14316        path: "/cgi-bin/",
14317        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14318        domain: "roojs.com"
14319    })
14320    Roo.state.Manager.setProvider(cp);
14321  </code></pre>
14322  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14323  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14324  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14325  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14326  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14327  * domain the page is running on including the 'www' like 'www.roojs.com')
14328  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14329  * @constructor
14330  * Create a new CookieProvider
14331  * @param {Object} config The configuration object
14332  */
14333 Roo.state.CookieProvider = function(config){
14334     Roo.state.CookieProvider.superclass.constructor.call(this);
14335     this.path = "/";
14336     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14337     this.domain = null;
14338     this.secure = false;
14339     Roo.apply(this, config);
14340     this.state = this.readCookies();
14341 };
14342
14343 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14344     // private
14345     set : function(name, value){
14346         if(typeof value == "undefined" || value === null){
14347             this.clear(name);
14348             return;
14349         }
14350         this.setCookie(name, value);
14351         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14352     },
14353
14354     // private
14355     clear : function(name){
14356         this.clearCookie(name);
14357         Roo.state.CookieProvider.superclass.clear.call(this, name);
14358     },
14359
14360     // private
14361     readCookies : function(){
14362         var cookies = {};
14363         var c = document.cookie + ";";
14364         var re = /\s?(.*?)=(.*?);/g;
14365         var matches;
14366         while((matches = re.exec(c)) != null){
14367             var name = matches[1];
14368             var value = matches[2];
14369             if(name && name.substring(0,3) == "ys-"){
14370                 cookies[name.substr(3)] = this.decodeValue(value);
14371             }
14372         }
14373         return cookies;
14374     },
14375
14376     // private
14377     setCookie : function(name, value){
14378         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14379            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14380            ((this.path == null) ? "" : ("; path=" + this.path)) +
14381            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14382            ((this.secure == true) ? "; secure" : "");
14383     },
14384
14385     // private
14386     clearCookie : function(name){
14387         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14388            ((this.path == null) ? "" : ("; path=" + this.path)) +
14389            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14390            ((this.secure == true) ? "; secure" : "");
14391     }
14392 });/*
14393  * Based on:
14394  * Ext JS Library 1.1.1
14395  * Copyright(c) 2006-2007, Ext JS, LLC.
14396  *
14397  * Originally Released Under LGPL - original licence link has changed is not relivant.
14398  *
14399  * Fork - LGPL
14400  * <script type="text/javascript">
14401  */
14402
14403
14404
14405 /*
14406  * These classes are derivatives of the similarly named classes in the YUI Library.
14407  * The original license:
14408  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14409  * Code licensed under the BSD License:
14410  * http://developer.yahoo.net/yui/license.txt
14411  */
14412
14413 (function() {
14414
14415 var Event=Roo.EventManager;
14416 var Dom=Roo.lib.Dom;
14417
14418 /**
14419  * @class Roo.dd.DragDrop
14420  * @extends Roo.util.Observable
14421  * Defines the interface and base operation of items that that can be
14422  * dragged or can be drop targets.  It was designed to be extended, overriding
14423  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14424  * Up to three html elements can be associated with a DragDrop instance:
14425  * <ul>
14426  * <li>linked element: the element that is passed into the constructor.
14427  * This is the element which defines the boundaries for interaction with
14428  * other DragDrop objects.</li>
14429  * <li>handle element(s): The drag operation only occurs if the element that
14430  * was clicked matches a handle element.  By default this is the linked
14431  * element, but there are times that you will want only a portion of the
14432  * linked element to initiate the drag operation, and the setHandleElId()
14433  * method provides a way to define this.</li>
14434  * <li>drag element: this represents the element that would be moved along
14435  * with the cursor during a drag operation.  By default, this is the linked
14436  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14437  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14438  * </li>
14439  * </ul>
14440  * This class should not be instantiated until the onload event to ensure that
14441  * the associated elements are available.
14442  * The following would define a DragDrop obj that would interact with any
14443  * other DragDrop obj in the "group1" group:
14444  * <pre>
14445  *  dd = new Roo.dd.DragDrop("div1", "group1");
14446  * </pre>
14447  * Since none of the event handlers have been implemented, nothing would
14448  * actually happen if you were to run the code above.  Normally you would
14449  * override this class or one of the default implementations, but you can
14450  * also override the methods you want on an instance of the class...
14451  * <pre>
14452  *  dd.onDragDrop = function(e, id) {
14453  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14454  *  }
14455  * </pre>
14456  * @constructor
14457  * @param {String} id of the element that is linked to this instance
14458  * @param {String} sGroup the group of related DragDrop objects
14459  * @param {object} config an object containing configurable attributes
14460  *                Valid properties for DragDrop:
14461  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14462  */
14463 Roo.dd.DragDrop = function(id, sGroup, config) {
14464     if (id) {
14465         this.init(id, sGroup, config);
14466     }
14467     
14468 };
14469
14470 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14471
14472     /**
14473      * The id of the element associated with this object.  This is what we
14474      * refer to as the "linked element" because the size and position of
14475      * this element is used to determine when the drag and drop objects have
14476      * interacted.
14477      * @property id
14478      * @type String
14479      */
14480     id: null,
14481
14482     /**
14483      * Configuration attributes passed into the constructor
14484      * @property config
14485      * @type object
14486      */
14487     config: null,
14488
14489     /**
14490      * The id of the element that will be dragged.  By default this is same
14491      * as the linked element , but could be changed to another element. Ex:
14492      * Roo.dd.DDProxy
14493      * @property dragElId
14494      * @type String
14495      * @private
14496      */
14497     dragElId: null,
14498
14499     /**
14500      * the id of the element that initiates the drag operation.  By default
14501      * this is the linked element, but could be changed to be a child of this
14502      * element.  This lets us do things like only starting the drag when the
14503      * header element within the linked html element is clicked.
14504      * @property handleElId
14505      * @type String
14506      * @private
14507      */
14508     handleElId: null,
14509
14510     /**
14511      * An associative array of HTML tags that will be ignored if clicked.
14512      * @property invalidHandleTypes
14513      * @type {string: string}
14514      */
14515     invalidHandleTypes: null,
14516
14517     /**
14518      * An associative array of ids for elements that will be ignored if clicked
14519      * @property invalidHandleIds
14520      * @type {string: string}
14521      */
14522     invalidHandleIds: null,
14523
14524     /**
14525      * An indexted array of css class names for elements that will be ignored
14526      * if clicked.
14527      * @property invalidHandleClasses
14528      * @type string[]
14529      */
14530     invalidHandleClasses: null,
14531
14532     /**
14533      * The linked element's absolute X position at the time the drag was
14534      * started
14535      * @property startPageX
14536      * @type int
14537      * @private
14538      */
14539     startPageX: 0,
14540
14541     /**
14542      * The linked element's absolute X position at the time the drag was
14543      * started
14544      * @property startPageY
14545      * @type int
14546      * @private
14547      */
14548     startPageY: 0,
14549
14550     /**
14551      * The group defines a logical collection of DragDrop objects that are
14552      * related.  Instances only get events when interacting with other
14553      * DragDrop object in the same group.  This lets us define multiple
14554      * groups using a single DragDrop subclass if we want.
14555      * @property groups
14556      * @type {string: string}
14557      */
14558     groups: null,
14559
14560     /**
14561      * Individual drag/drop instances can be locked.  This will prevent
14562      * onmousedown start drag.
14563      * @property locked
14564      * @type boolean
14565      * @private
14566      */
14567     locked: false,
14568
14569     /**
14570      * Lock this instance
14571      * @method lock
14572      */
14573     lock: function() { this.locked = true; },
14574
14575     /**
14576      * Unlock this instace
14577      * @method unlock
14578      */
14579     unlock: function() { this.locked = false; },
14580
14581     /**
14582      * By default, all insances can be a drop target.  This can be disabled by
14583      * setting isTarget to false.
14584      * @method isTarget
14585      * @type boolean
14586      */
14587     isTarget: true,
14588
14589     /**
14590      * The padding configured for this drag and drop object for calculating
14591      * the drop zone intersection with this object.
14592      * @method padding
14593      * @type int[]
14594      */
14595     padding: null,
14596
14597     /**
14598      * Cached reference to the linked element
14599      * @property _domRef
14600      * @private
14601      */
14602     _domRef: null,
14603
14604     /**
14605      * Internal typeof flag
14606      * @property __ygDragDrop
14607      * @private
14608      */
14609     __ygDragDrop: true,
14610
14611     /**
14612      * Set to true when horizontal contraints are applied
14613      * @property constrainX
14614      * @type boolean
14615      * @private
14616      */
14617     constrainX: false,
14618
14619     /**
14620      * Set to true when vertical contraints are applied
14621      * @property constrainY
14622      * @type boolean
14623      * @private
14624      */
14625     constrainY: false,
14626
14627     /**
14628      * The left constraint
14629      * @property minX
14630      * @type int
14631      * @private
14632      */
14633     minX: 0,
14634
14635     /**
14636      * The right constraint
14637      * @property maxX
14638      * @type int
14639      * @private
14640      */
14641     maxX: 0,
14642
14643     /**
14644      * The up constraint
14645      * @property minY
14646      * @type int
14647      * @type int
14648      * @private
14649      */
14650     minY: 0,
14651
14652     /**
14653      * The down constraint
14654      * @property maxY
14655      * @type int
14656      * @private
14657      */
14658     maxY: 0,
14659
14660     /**
14661      * Maintain offsets when we resetconstraints.  Set to true when you want
14662      * the position of the element relative to its parent to stay the same
14663      * when the page changes
14664      *
14665      * @property maintainOffset
14666      * @type boolean
14667      */
14668     maintainOffset: false,
14669
14670     /**
14671      * Array of pixel locations the element will snap to if we specified a
14672      * horizontal graduation/interval.  This array is generated automatically
14673      * when you define a tick interval.
14674      * @property xTicks
14675      * @type int[]
14676      */
14677     xTicks: null,
14678
14679     /**
14680      * Array of pixel locations the element will snap to if we specified a
14681      * vertical graduation/interval.  This array is generated automatically
14682      * when you define a tick interval.
14683      * @property yTicks
14684      * @type int[]
14685      */
14686     yTicks: null,
14687
14688     /**
14689      * By default the drag and drop instance will only respond to the primary
14690      * button click (left button for a right-handed mouse).  Set to true to
14691      * allow drag and drop to start with any mouse click that is propogated
14692      * by the browser
14693      * @property primaryButtonOnly
14694      * @type boolean
14695      */
14696     primaryButtonOnly: true,
14697
14698     /**
14699      * The availabe property is false until the linked dom element is accessible.
14700      * @property available
14701      * @type boolean
14702      */
14703     available: false,
14704
14705     /**
14706      * By default, drags can only be initiated if the mousedown occurs in the
14707      * region the linked element is.  This is done in part to work around a
14708      * bug in some browsers that mis-report the mousedown if the previous
14709      * mouseup happened outside of the window.  This property is set to true
14710      * if outer handles are defined.
14711      *
14712      * @property hasOuterHandles
14713      * @type boolean
14714      * @default false
14715      */
14716     hasOuterHandles: false,
14717
14718     /**
14719      * Code that executes immediately before the startDrag event
14720      * @method b4StartDrag
14721      * @private
14722      */
14723     b4StartDrag: function(x, y) { },
14724
14725     /**
14726      * Abstract method called after a drag/drop object is clicked
14727      * and the drag or mousedown time thresholds have beeen met.
14728      * @method startDrag
14729      * @param {int} X click location
14730      * @param {int} Y click location
14731      */
14732     startDrag: function(x, y) { /* override this */ },
14733
14734     /**
14735      * Code that executes immediately before the onDrag event
14736      * @method b4Drag
14737      * @private
14738      */
14739     b4Drag: function(e) { },
14740
14741     /**
14742      * Abstract method called during the onMouseMove event while dragging an
14743      * object.
14744      * @method onDrag
14745      * @param {Event} e the mousemove event
14746      */
14747     onDrag: function(e) { /* override this */ },
14748
14749     /**
14750      * Abstract method called when this element fist begins hovering over
14751      * another DragDrop obj
14752      * @method onDragEnter
14753      * @param {Event} e the mousemove event
14754      * @param {String|DragDrop[]} id In POINT mode, the element
14755      * id this is hovering over.  In INTERSECT mode, an array of one or more
14756      * dragdrop items being hovered over.
14757      */
14758     onDragEnter: function(e, id) { /* override this */ },
14759
14760     /**
14761      * Code that executes immediately before the onDragOver event
14762      * @method b4DragOver
14763      * @private
14764      */
14765     b4DragOver: function(e) { },
14766
14767     /**
14768      * Abstract method called when this element is hovering over another
14769      * DragDrop obj
14770      * @method onDragOver
14771      * @param {Event} e the mousemove event
14772      * @param {String|DragDrop[]} id In POINT mode, the element
14773      * id this is hovering over.  In INTERSECT mode, an array of dd items
14774      * being hovered over.
14775      */
14776     onDragOver: function(e, id) { /* override this */ },
14777
14778     /**
14779      * Code that executes immediately before the onDragOut event
14780      * @method b4DragOut
14781      * @private
14782      */
14783     b4DragOut: function(e) { },
14784
14785     /**
14786      * Abstract method called when we are no longer hovering over an element
14787      * @method onDragOut
14788      * @param {Event} e the mousemove event
14789      * @param {String|DragDrop[]} id In POINT mode, the element
14790      * id this was hovering over.  In INTERSECT mode, an array of dd items
14791      * that the mouse is no longer over.
14792      */
14793     onDragOut: function(e, id) { /* override this */ },
14794
14795     /**
14796      * Code that executes immediately before the onDragDrop event
14797      * @method b4DragDrop
14798      * @private
14799      */
14800     b4DragDrop: function(e) { },
14801
14802     /**
14803      * Abstract method called when this item is dropped on another DragDrop
14804      * obj
14805      * @method onDragDrop
14806      * @param {Event} e the mouseup event
14807      * @param {String|DragDrop[]} id In POINT mode, the element
14808      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14809      * was dropped on.
14810      */
14811     onDragDrop: function(e, id) { /* override this */ },
14812
14813     /**
14814      * Abstract method called when this item is dropped on an area with no
14815      * drop target
14816      * @method onInvalidDrop
14817      * @param {Event} e the mouseup event
14818      */
14819     onInvalidDrop: function(e) { /* override this */ },
14820
14821     /**
14822      * Code that executes immediately before the endDrag event
14823      * @method b4EndDrag
14824      * @private
14825      */
14826     b4EndDrag: function(e) { },
14827
14828     /**
14829      * Fired when we are done dragging the object
14830      * @method endDrag
14831      * @param {Event} e the mouseup event
14832      */
14833     endDrag: function(e) { /* override this */ },
14834
14835     /**
14836      * Code executed immediately before the onMouseDown event
14837      * @method b4MouseDown
14838      * @param {Event} e the mousedown event
14839      * @private
14840      */
14841     b4MouseDown: function(e) {  },
14842
14843     /**
14844      * Event handler that fires when a drag/drop obj gets a mousedown
14845      * @method onMouseDown
14846      * @param {Event} e the mousedown event
14847      */
14848     onMouseDown: function(e) { /* override this */ },
14849
14850     /**
14851      * Event handler that fires when a drag/drop obj gets a mouseup
14852      * @method onMouseUp
14853      * @param {Event} e the mouseup event
14854      */
14855     onMouseUp: function(e) { /* override this */ },
14856
14857     /**
14858      * Override the onAvailable method to do what is needed after the initial
14859      * position was determined.
14860      * @method onAvailable
14861      */
14862     onAvailable: function () {
14863     },
14864
14865     /*
14866      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14867      * @type Object
14868      */
14869     defaultPadding : {left:0, right:0, top:0, bottom:0},
14870
14871     /*
14872      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14873  *
14874  * Usage:
14875  <pre><code>
14876  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14877                 { dragElId: "existingProxyDiv" });
14878  dd.startDrag = function(){
14879      this.constrainTo("parent-id");
14880  };
14881  </code></pre>
14882  * Or you can initalize it using the {@link Roo.Element} object:
14883  <pre><code>
14884  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14885      startDrag : function(){
14886          this.constrainTo("parent-id");
14887      }
14888  });
14889  </code></pre>
14890      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14891      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14892      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14893      * an object containing the sides to pad. For example: {right:10, bottom:10}
14894      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14895      */
14896     constrainTo : function(constrainTo, pad, inContent){
14897         if(typeof pad == "number"){
14898             pad = {left: pad, right:pad, top:pad, bottom:pad};
14899         }
14900         pad = pad || this.defaultPadding;
14901         var b = Roo.get(this.getEl()).getBox();
14902         var ce = Roo.get(constrainTo);
14903         var s = ce.getScroll();
14904         var c, cd = ce.dom;
14905         if(cd == document.body){
14906             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14907         }else{
14908             xy = ce.getXY();
14909             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14910         }
14911
14912
14913         var topSpace = b.y - c.y;
14914         var leftSpace = b.x - c.x;
14915
14916         this.resetConstraints();
14917         this.setXConstraint(leftSpace - (pad.left||0), // left
14918                 c.width - leftSpace - b.width - (pad.right||0) //right
14919         );
14920         this.setYConstraint(topSpace - (pad.top||0), //top
14921                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14922         );
14923     },
14924
14925     /**
14926      * Returns a reference to the linked element
14927      * @method getEl
14928      * @return {HTMLElement} the html element
14929      */
14930     getEl: function() {
14931         if (!this._domRef) {
14932             this._domRef = Roo.getDom(this.id);
14933         }
14934
14935         return this._domRef;
14936     },
14937
14938     /**
14939      * Returns a reference to the actual element to drag.  By default this is
14940      * the same as the html element, but it can be assigned to another
14941      * element. An example of this can be found in Roo.dd.DDProxy
14942      * @method getDragEl
14943      * @return {HTMLElement} the html element
14944      */
14945     getDragEl: function() {
14946         return Roo.getDom(this.dragElId);
14947     },
14948
14949     /**
14950      * Sets up the DragDrop object.  Must be called in the constructor of any
14951      * Roo.dd.DragDrop subclass
14952      * @method init
14953      * @param id the id of the linked element
14954      * @param {String} sGroup the group of related items
14955      * @param {object} config configuration attributes
14956      */
14957     init: function(id, sGroup, config) {
14958         this.initTarget(id, sGroup, config);
14959         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14960         // Event.on(this.id, "selectstart", Event.preventDefault);
14961     },
14962
14963     /**
14964      * Initializes Targeting functionality only... the object does not
14965      * get a mousedown handler.
14966      * @method initTarget
14967      * @param id the id of the linked element
14968      * @param {String} sGroup the group of related items
14969      * @param {object} config configuration attributes
14970      */
14971     initTarget: function(id, sGroup, config) {
14972
14973         // configuration attributes
14974         this.config = config || {};
14975
14976         // create a local reference to the drag and drop manager
14977         this.DDM = Roo.dd.DDM;
14978         // initialize the groups array
14979         this.groups = {};
14980
14981         // assume that we have an element reference instead of an id if the
14982         // parameter is not a string
14983         if (typeof id !== "string") {
14984             id = Roo.id(id);
14985         }
14986
14987         // set the id
14988         this.id = id;
14989
14990         // add to an interaction group
14991         this.addToGroup((sGroup) ? sGroup : "default");
14992
14993         // We don't want to register this as the handle with the manager
14994         // so we just set the id rather than calling the setter.
14995         this.handleElId = id;
14996
14997         // the linked element is the element that gets dragged by default
14998         this.setDragElId(id);
14999
15000         // by default, clicked anchors will not start drag operations.
15001         this.invalidHandleTypes = { A: "A" };
15002         this.invalidHandleIds = {};
15003         this.invalidHandleClasses = [];
15004
15005         this.applyConfig();
15006
15007         this.handleOnAvailable();
15008     },
15009
15010     /**
15011      * Applies the configuration parameters that were passed into the constructor.
15012      * This is supposed to happen at each level through the inheritance chain.  So
15013      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15014      * DragDrop in order to get all of the parameters that are available in
15015      * each object.
15016      * @method applyConfig
15017      */
15018     applyConfig: function() {
15019
15020         // configurable properties:
15021         //    padding, isTarget, maintainOffset, primaryButtonOnly
15022         this.padding           = this.config.padding || [0, 0, 0, 0];
15023         this.isTarget          = (this.config.isTarget !== false);
15024         this.maintainOffset    = (this.config.maintainOffset);
15025         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15026
15027     },
15028
15029     /**
15030      * Executed when the linked element is available
15031      * @method handleOnAvailable
15032      * @private
15033      */
15034     handleOnAvailable: function() {
15035         this.available = true;
15036         this.resetConstraints();
15037         this.onAvailable();
15038     },
15039
15040      /**
15041      * Configures the padding for the target zone in px.  Effectively expands
15042      * (or reduces) the virtual object size for targeting calculations.
15043      * Supports css-style shorthand; if only one parameter is passed, all sides
15044      * will have that padding, and if only two are passed, the top and bottom
15045      * will have the first param, the left and right the second.
15046      * @method setPadding
15047      * @param {int} iTop    Top pad
15048      * @param {int} iRight  Right pad
15049      * @param {int} iBot    Bot pad
15050      * @param {int} iLeft   Left pad
15051      */
15052     setPadding: function(iTop, iRight, iBot, iLeft) {
15053         // this.padding = [iLeft, iRight, iTop, iBot];
15054         if (!iRight && 0 !== iRight) {
15055             this.padding = [iTop, iTop, iTop, iTop];
15056         } else if (!iBot && 0 !== iBot) {
15057             this.padding = [iTop, iRight, iTop, iRight];
15058         } else {
15059             this.padding = [iTop, iRight, iBot, iLeft];
15060         }
15061     },
15062
15063     /**
15064      * Stores the initial placement of the linked element.
15065      * @method setInitialPosition
15066      * @param {int} diffX   the X offset, default 0
15067      * @param {int} diffY   the Y offset, default 0
15068      */
15069     setInitPosition: function(diffX, diffY) {
15070         var el = this.getEl();
15071
15072         if (!this.DDM.verifyEl(el)) {
15073             return;
15074         }
15075
15076         var dx = diffX || 0;
15077         var dy = diffY || 0;
15078
15079         var p = Dom.getXY( el );
15080
15081         this.initPageX = p[0] - dx;
15082         this.initPageY = p[1] - dy;
15083
15084         this.lastPageX = p[0];
15085         this.lastPageY = p[1];
15086
15087
15088         this.setStartPosition(p);
15089     },
15090
15091     /**
15092      * Sets the start position of the element.  This is set when the obj
15093      * is initialized, the reset when a drag is started.
15094      * @method setStartPosition
15095      * @param pos current position (from previous lookup)
15096      * @private
15097      */
15098     setStartPosition: function(pos) {
15099         var p = pos || Dom.getXY( this.getEl() );
15100         this.deltaSetXY = null;
15101
15102         this.startPageX = p[0];
15103         this.startPageY = p[1];
15104     },
15105
15106     /**
15107      * Add this instance to a group of related drag/drop objects.  All
15108      * instances belong to at least one group, and can belong to as many
15109      * groups as needed.
15110      * @method addToGroup
15111      * @param sGroup {string} the name of the group
15112      */
15113     addToGroup: function(sGroup) {
15114         this.groups[sGroup] = true;
15115         this.DDM.regDragDrop(this, sGroup);
15116     },
15117
15118     /**
15119      * Remove's this instance from the supplied interaction group
15120      * @method removeFromGroup
15121      * @param {string}  sGroup  The group to drop
15122      */
15123     removeFromGroup: function(sGroup) {
15124         if (this.groups[sGroup]) {
15125             delete this.groups[sGroup];
15126         }
15127
15128         this.DDM.removeDDFromGroup(this, sGroup);
15129     },
15130
15131     /**
15132      * Allows you to specify that an element other than the linked element
15133      * will be moved with the cursor during a drag
15134      * @method setDragElId
15135      * @param id {string} the id of the element that will be used to initiate the drag
15136      */
15137     setDragElId: function(id) {
15138         this.dragElId = id;
15139     },
15140
15141     /**
15142      * Allows you to specify a child of the linked element that should be
15143      * used to initiate the drag operation.  An example of this would be if
15144      * you have a content div with text and links.  Clicking anywhere in the
15145      * content area would normally start the drag operation.  Use this method
15146      * to specify that an element inside of the content div is the element
15147      * that starts the drag operation.
15148      * @method setHandleElId
15149      * @param id {string} the id of the element that will be used to
15150      * initiate the drag.
15151      */
15152     setHandleElId: function(id) {
15153         if (typeof id !== "string") {
15154             id = Roo.id(id);
15155         }
15156         this.handleElId = id;
15157         this.DDM.regHandle(this.id, id);
15158     },
15159
15160     /**
15161      * Allows you to set an element outside of the linked element as a drag
15162      * handle
15163      * @method setOuterHandleElId
15164      * @param id the id of the element that will be used to initiate the drag
15165      */
15166     setOuterHandleElId: function(id) {
15167         if (typeof id !== "string") {
15168             id = Roo.id(id);
15169         }
15170         Event.on(id, "mousedown",
15171                 this.handleMouseDown, this);
15172         this.setHandleElId(id);
15173
15174         this.hasOuterHandles = true;
15175     },
15176
15177     /**
15178      * Remove all drag and drop hooks for this element
15179      * @method unreg
15180      */
15181     unreg: function() {
15182         Event.un(this.id, "mousedown",
15183                 this.handleMouseDown);
15184         this._domRef = null;
15185         this.DDM._remove(this);
15186     },
15187
15188     destroy : function(){
15189         this.unreg();
15190     },
15191
15192     /**
15193      * Returns true if this instance is locked, or the drag drop mgr is locked
15194      * (meaning that all drag/drop is disabled on the page.)
15195      * @method isLocked
15196      * @return {boolean} true if this obj or all drag/drop is locked, else
15197      * false
15198      */
15199     isLocked: function() {
15200         return (this.DDM.isLocked() || this.locked);
15201     },
15202
15203     /**
15204      * Fired when this object is clicked
15205      * @method handleMouseDown
15206      * @param {Event} e
15207      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15208      * @private
15209      */
15210     handleMouseDown: function(e, oDD){
15211         if (this.primaryButtonOnly && e.button != 0) {
15212             return;
15213         }
15214
15215         if (this.isLocked()) {
15216             return;
15217         }
15218
15219         this.DDM.refreshCache(this.groups);
15220
15221         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15222         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15223         } else {
15224             if (this.clickValidator(e)) {
15225
15226                 // set the initial element position
15227                 this.setStartPosition();
15228
15229
15230                 this.b4MouseDown(e);
15231                 this.onMouseDown(e);
15232
15233                 this.DDM.handleMouseDown(e, this);
15234
15235                 this.DDM.stopEvent(e);
15236             } else {
15237
15238
15239             }
15240         }
15241     },
15242
15243     clickValidator: function(e) {
15244         var target = e.getTarget();
15245         return ( this.isValidHandleChild(target) &&
15246                     (this.id == this.handleElId ||
15247                         this.DDM.handleWasClicked(target, this.id)) );
15248     },
15249
15250     /**
15251      * Allows you to specify a tag name that should not start a drag operation
15252      * when clicked.  This is designed to facilitate embedding links within a
15253      * drag handle that do something other than start the drag.
15254      * @method addInvalidHandleType
15255      * @param {string} tagName the type of element to exclude
15256      */
15257     addInvalidHandleType: function(tagName) {
15258         var type = tagName.toUpperCase();
15259         this.invalidHandleTypes[type] = type;
15260     },
15261
15262     /**
15263      * Lets you to specify an element id for a child of a drag handle
15264      * that should not initiate a drag
15265      * @method addInvalidHandleId
15266      * @param {string} id the element id of the element you wish to ignore
15267      */
15268     addInvalidHandleId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         this.invalidHandleIds[id] = id;
15273     },
15274
15275     /**
15276      * Lets you specify a css class of elements that will not initiate a drag
15277      * @method addInvalidHandleClass
15278      * @param {string} cssClass the class of the elements you wish to ignore
15279      */
15280     addInvalidHandleClass: function(cssClass) {
15281         this.invalidHandleClasses.push(cssClass);
15282     },
15283
15284     /**
15285      * Unsets an excluded tag name set by addInvalidHandleType
15286      * @method removeInvalidHandleType
15287      * @param {string} tagName the type of element to unexclude
15288      */
15289     removeInvalidHandleType: function(tagName) {
15290         var type = tagName.toUpperCase();
15291         // this.invalidHandleTypes[type] = null;
15292         delete this.invalidHandleTypes[type];
15293     },
15294
15295     /**
15296      * Unsets an invalid handle id
15297      * @method removeInvalidHandleId
15298      * @param {string} id the id of the element to re-enable
15299      */
15300     removeInvalidHandleId: function(id) {
15301         if (typeof id !== "string") {
15302             id = Roo.id(id);
15303         }
15304         delete this.invalidHandleIds[id];
15305     },
15306
15307     /**
15308      * Unsets an invalid css class
15309      * @method removeInvalidHandleClass
15310      * @param {string} cssClass the class of the element(s) you wish to
15311      * re-enable
15312      */
15313     removeInvalidHandleClass: function(cssClass) {
15314         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15315             if (this.invalidHandleClasses[i] == cssClass) {
15316                 delete this.invalidHandleClasses[i];
15317             }
15318         }
15319     },
15320
15321     /**
15322      * Checks the tag exclusion list to see if this click should be ignored
15323      * @method isValidHandleChild
15324      * @param {HTMLElement} node the HTMLElement to evaluate
15325      * @return {boolean} true if this is a valid tag type, false if not
15326      */
15327     isValidHandleChild: function(node) {
15328
15329         var valid = true;
15330         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15331         var nodeName;
15332         try {
15333             nodeName = node.nodeName.toUpperCase();
15334         } catch(e) {
15335             nodeName = node.nodeName;
15336         }
15337         valid = valid && !this.invalidHandleTypes[nodeName];
15338         valid = valid && !this.invalidHandleIds[node.id];
15339
15340         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15341             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15342         }
15343
15344
15345         return valid;
15346
15347     },
15348
15349     /**
15350      * Create the array of horizontal tick marks if an interval was specified
15351      * in setXConstraint().
15352      * @method setXTicks
15353      * @private
15354      */
15355     setXTicks: function(iStartX, iTickSize) {
15356         this.xTicks = [];
15357         this.xTickSize = iTickSize;
15358
15359         var tickMap = {};
15360
15361         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15362             if (!tickMap[i]) {
15363                 this.xTicks[this.xTicks.length] = i;
15364                 tickMap[i] = true;
15365             }
15366         }
15367
15368         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15369             if (!tickMap[i]) {
15370                 this.xTicks[this.xTicks.length] = i;
15371                 tickMap[i] = true;
15372             }
15373         }
15374
15375         this.xTicks.sort(this.DDM.numericSort) ;
15376     },
15377
15378     /**
15379      * Create the array of vertical tick marks if an interval was specified in
15380      * setYConstraint().
15381      * @method setYTicks
15382      * @private
15383      */
15384     setYTicks: function(iStartY, iTickSize) {
15385         this.yTicks = [];
15386         this.yTickSize = iTickSize;
15387
15388         var tickMap = {};
15389
15390         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15391             if (!tickMap[i]) {
15392                 this.yTicks[this.yTicks.length] = i;
15393                 tickMap[i] = true;
15394             }
15395         }
15396
15397         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15398             if (!tickMap[i]) {
15399                 this.yTicks[this.yTicks.length] = i;
15400                 tickMap[i] = true;
15401             }
15402         }
15403
15404         this.yTicks.sort(this.DDM.numericSort) ;
15405     },
15406
15407     /**
15408      * By default, the element can be dragged any place on the screen.  Use
15409      * this method to limit the horizontal travel of the element.  Pass in
15410      * 0,0 for the parameters if you want to lock the drag to the y axis.
15411      * @method setXConstraint
15412      * @param {int} iLeft the number of pixels the element can move to the left
15413      * @param {int} iRight the number of pixels the element can move to the
15414      * right
15415      * @param {int} iTickSize optional parameter for specifying that the
15416      * element
15417      * should move iTickSize pixels at a time.
15418      */
15419     setXConstraint: function(iLeft, iRight, iTickSize) {
15420         this.leftConstraint = iLeft;
15421         this.rightConstraint = iRight;
15422
15423         this.minX = this.initPageX - iLeft;
15424         this.maxX = this.initPageX + iRight;
15425         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15426
15427         this.constrainX = true;
15428     },
15429
15430     /**
15431      * Clears any constraints applied to this instance.  Also clears ticks
15432      * since they can't exist independent of a constraint at this time.
15433      * @method clearConstraints
15434      */
15435     clearConstraints: function() {
15436         this.constrainX = false;
15437         this.constrainY = false;
15438         this.clearTicks();
15439     },
15440
15441     /**
15442      * Clears any tick interval defined for this instance
15443      * @method clearTicks
15444      */
15445     clearTicks: function() {
15446         this.xTicks = null;
15447         this.yTicks = null;
15448         this.xTickSize = 0;
15449         this.yTickSize = 0;
15450     },
15451
15452     /**
15453      * By default, the element can be dragged any place on the screen.  Set
15454      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15455      * parameters if you want to lock the drag to the x axis.
15456      * @method setYConstraint
15457      * @param {int} iUp the number of pixels the element can move up
15458      * @param {int} iDown the number of pixels the element can move down
15459      * @param {int} iTickSize optional parameter for specifying that the
15460      * element should move iTickSize pixels at a time.
15461      */
15462     setYConstraint: function(iUp, iDown, iTickSize) {
15463         this.topConstraint = iUp;
15464         this.bottomConstraint = iDown;
15465
15466         this.minY = this.initPageY - iUp;
15467         this.maxY = this.initPageY + iDown;
15468         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15469
15470         this.constrainY = true;
15471
15472     },
15473
15474     /**
15475      * resetConstraints must be called if you manually reposition a dd element.
15476      * @method resetConstraints
15477      * @param {boolean} maintainOffset
15478      */
15479     resetConstraints: function() {
15480
15481
15482         // Maintain offsets if necessary
15483         if (this.initPageX || this.initPageX === 0) {
15484             // figure out how much this thing has moved
15485             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15486             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15487
15488             this.setInitPosition(dx, dy);
15489
15490         // This is the first time we have detected the element's position
15491         } else {
15492             this.setInitPosition();
15493         }
15494
15495         if (this.constrainX) {
15496             this.setXConstraint( this.leftConstraint,
15497                                  this.rightConstraint,
15498                                  this.xTickSize        );
15499         }
15500
15501         if (this.constrainY) {
15502             this.setYConstraint( this.topConstraint,
15503                                  this.bottomConstraint,
15504                                  this.yTickSize         );
15505         }
15506     },
15507
15508     /**
15509      * Normally the drag element is moved pixel by pixel, but we can specify
15510      * that it move a number of pixels at a time.  This method resolves the
15511      * location when we have it set up like this.
15512      * @method getTick
15513      * @param {int} val where we want to place the object
15514      * @param {int[]} tickArray sorted array of valid points
15515      * @return {int} the closest tick
15516      * @private
15517      */
15518     getTick: function(val, tickArray) {
15519
15520         if (!tickArray) {
15521             // If tick interval is not defined, it is effectively 1 pixel,
15522             // so we return the value passed to us.
15523             return val;
15524         } else if (tickArray[0] >= val) {
15525             // The value is lower than the first tick, so we return the first
15526             // tick.
15527             return tickArray[0];
15528         } else {
15529             for (var i=0, len=tickArray.length; i<len; ++i) {
15530                 var next = i + 1;
15531                 if (tickArray[next] && tickArray[next] >= val) {
15532                     var diff1 = val - tickArray[i];
15533                     var diff2 = tickArray[next] - val;
15534                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15535                 }
15536             }
15537
15538             // The value is larger than the last tick, so we return the last
15539             // tick.
15540             return tickArray[tickArray.length - 1];
15541         }
15542     },
15543
15544     /**
15545      * toString method
15546      * @method toString
15547      * @return {string} string representation of the dd obj
15548      */
15549     toString: function() {
15550         return ("DragDrop " + this.id);
15551     }
15552
15553 });
15554
15555 })();
15556 /*
15557  * Based on:
15558  * Ext JS Library 1.1.1
15559  * Copyright(c) 2006-2007, Ext JS, LLC.
15560  *
15561  * Originally Released Under LGPL - original licence link has changed is not relivant.
15562  *
15563  * Fork - LGPL
15564  * <script type="text/javascript">
15565  */
15566
15567
15568 /**
15569  * The drag and drop utility provides a framework for building drag and drop
15570  * applications.  In addition to enabling drag and drop for specific elements,
15571  * the drag and drop elements are tracked by the manager class, and the
15572  * interactions between the various elements are tracked during the drag and
15573  * the implementing code is notified about these important moments.
15574  */
15575
15576 // Only load the library once.  Rewriting the manager class would orphan
15577 // existing drag and drop instances.
15578 if (!Roo.dd.DragDropMgr) {
15579
15580 /**
15581  * @class Roo.dd.DragDropMgr
15582  * DragDropMgr is a singleton that tracks the element interaction for
15583  * all DragDrop items in the window.  Generally, you will not call
15584  * this class directly, but it does have helper methods that could
15585  * be useful in your DragDrop implementations.
15586  * @singleton
15587  */
15588 Roo.dd.DragDropMgr = function() {
15589
15590     var Event = Roo.EventManager;
15591
15592     return {
15593
15594         /**
15595          * Two dimensional Array of registered DragDrop objects.  The first
15596          * dimension is the DragDrop item group, the second the DragDrop
15597          * object.
15598          * @property ids
15599          * @type {string: string}
15600          * @private
15601          * @static
15602          */
15603         ids: {},
15604
15605         /**
15606          * Array of element ids defined as drag handles.  Used to determine
15607          * if the element that generated the mousedown event is actually the
15608          * handle and not the html element itself.
15609          * @property handleIds
15610          * @type {string: string}
15611          * @private
15612          * @static
15613          */
15614         handleIds: {},
15615
15616         /**
15617          * the DragDrop object that is currently being dragged
15618          * @property dragCurrent
15619          * @type DragDrop
15620          * @private
15621          * @static
15622          **/
15623         dragCurrent: null,
15624
15625         /**
15626          * the DragDrop object(s) that are being hovered over
15627          * @property dragOvers
15628          * @type Array
15629          * @private
15630          * @static
15631          */
15632         dragOvers: {},
15633
15634         /**
15635          * the X distance between the cursor and the object being dragged
15636          * @property deltaX
15637          * @type int
15638          * @private
15639          * @static
15640          */
15641         deltaX: 0,
15642
15643         /**
15644          * the Y distance between the cursor and the object being dragged
15645          * @property deltaY
15646          * @type int
15647          * @private
15648          * @static
15649          */
15650         deltaY: 0,
15651
15652         /**
15653          * Flag to determine if we should prevent the default behavior of the
15654          * events we define. By default this is true, but this can be set to
15655          * false if you need the default behavior (not recommended)
15656          * @property preventDefault
15657          * @type boolean
15658          * @static
15659          */
15660         preventDefault: true,
15661
15662         /**
15663          * Flag to determine if we should stop the propagation of the events
15664          * we generate. This is true by default but you may want to set it to
15665          * false if the html element contains other features that require the
15666          * mouse click.
15667          * @property stopPropagation
15668          * @type boolean
15669          * @static
15670          */
15671         stopPropagation: true,
15672
15673         /**
15674          * Internal flag that is set to true when drag and drop has been
15675          * intialized
15676          * @property initialized
15677          * @private
15678          * @static
15679          */
15680         initalized: false,
15681
15682         /**
15683          * All drag and drop can be disabled.
15684          * @property locked
15685          * @private
15686          * @static
15687          */
15688         locked: false,
15689
15690         /**
15691          * Called the first time an element is registered.
15692          * @method init
15693          * @private
15694          * @static
15695          */
15696         init: function() {
15697             this.initialized = true;
15698         },
15699
15700         /**
15701          * In point mode, drag and drop interaction is defined by the
15702          * location of the cursor during the drag/drop
15703          * @property POINT
15704          * @type int
15705          * @static
15706          */
15707         POINT: 0,
15708
15709         /**
15710          * In intersect mode, drag and drop interactio nis defined by the
15711          * overlap of two or more drag and drop objects.
15712          * @property INTERSECT
15713          * @type int
15714          * @static
15715          */
15716         INTERSECT: 1,
15717
15718         /**
15719          * The current drag and drop mode.  Default: POINT
15720          * @property mode
15721          * @type int
15722          * @static
15723          */
15724         mode: 0,
15725
15726         /**
15727          * Runs method on all drag and drop objects
15728          * @method _execOnAll
15729          * @private
15730          * @static
15731          */
15732         _execOnAll: function(sMethod, args) {
15733             for (var i in this.ids) {
15734                 for (var j in this.ids[i]) {
15735                     var oDD = this.ids[i][j];
15736                     if (! this.isTypeOfDD(oDD)) {
15737                         continue;
15738                     }
15739                     oDD[sMethod].apply(oDD, args);
15740                 }
15741             }
15742         },
15743
15744         /**
15745          * Drag and drop initialization.  Sets up the global event handlers
15746          * @method _onLoad
15747          * @private
15748          * @static
15749          */
15750         _onLoad: function() {
15751
15752             this.init();
15753
15754
15755             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15756             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15757             Event.on(window,   "unload",    this._onUnload, this, true);
15758             Event.on(window,   "resize",    this._onResize, this, true);
15759             // Event.on(window,   "mouseout",    this._test);
15760
15761         },
15762
15763         /**
15764          * Reset constraints on all drag and drop objs
15765          * @method _onResize
15766          * @private
15767          * @static
15768          */
15769         _onResize: function(e) {
15770             this._execOnAll("resetConstraints", []);
15771         },
15772
15773         /**
15774          * Lock all drag and drop functionality
15775          * @method lock
15776          * @static
15777          */
15778         lock: function() { this.locked = true; },
15779
15780         /**
15781          * Unlock all drag and drop functionality
15782          * @method unlock
15783          * @static
15784          */
15785         unlock: function() { this.locked = false; },
15786
15787         /**
15788          * Is drag and drop locked?
15789          * @method isLocked
15790          * @return {boolean} True if drag and drop is locked, false otherwise.
15791          * @static
15792          */
15793         isLocked: function() { return this.locked; },
15794
15795         /**
15796          * Location cache that is set for all drag drop objects when a drag is
15797          * initiated, cleared when the drag is finished.
15798          * @property locationCache
15799          * @private
15800          * @static
15801          */
15802         locationCache: {},
15803
15804         /**
15805          * Set useCache to false if you want to force object the lookup of each
15806          * drag and drop linked element constantly during a drag.
15807          * @property useCache
15808          * @type boolean
15809          * @static
15810          */
15811         useCache: true,
15812
15813         /**
15814          * The number of pixels that the mouse needs to move after the
15815          * mousedown before the drag is initiated.  Default=3;
15816          * @property clickPixelThresh
15817          * @type int
15818          * @static
15819          */
15820         clickPixelThresh: 3,
15821
15822         /**
15823          * The number of milliseconds after the mousedown event to initiate the
15824          * drag if we don't get a mouseup event. Default=1000
15825          * @property clickTimeThresh
15826          * @type int
15827          * @static
15828          */
15829         clickTimeThresh: 350,
15830
15831         /**
15832          * Flag that indicates that either the drag pixel threshold or the
15833          * mousdown time threshold has been met
15834          * @property dragThreshMet
15835          * @type boolean
15836          * @private
15837          * @static
15838          */
15839         dragThreshMet: false,
15840
15841         /**
15842          * Timeout used for the click time threshold
15843          * @property clickTimeout
15844          * @type Object
15845          * @private
15846          * @static
15847          */
15848         clickTimeout: null,
15849
15850         /**
15851          * The X position of the mousedown event stored for later use when a
15852          * drag threshold is met.
15853          * @property startX
15854          * @type int
15855          * @private
15856          * @static
15857          */
15858         startX: 0,
15859
15860         /**
15861          * The Y position of the mousedown event stored for later use when a
15862          * drag threshold is met.
15863          * @property startY
15864          * @type int
15865          * @private
15866          * @static
15867          */
15868         startY: 0,
15869
15870         /**
15871          * Each DragDrop instance must be registered with the DragDropMgr.
15872          * This is executed in DragDrop.init()
15873          * @method regDragDrop
15874          * @param {DragDrop} oDD the DragDrop object to register
15875          * @param {String} sGroup the name of the group this element belongs to
15876          * @static
15877          */
15878         regDragDrop: function(oDD, sGroup) {
15879             if (!this.initialized) { this.init(); }
15880
15881             if (!this.ids[sGroup]) {
15882                 this.ids[sGroup] = {};
15883             }
15884             this.ids[sGroup][oDD.id] = oDD;
15885         },
15886
15887         /**
15888          * Removes the supplied dd instance from the supplied group. Executed
15889          * by DragDrop.removeFromGroup, so don't call this function directly.
15890          * @method removeDDFromGroup
15891          * @private
15892          * @static
15893          */
15894         removeDDFromGroup: function(oDD, sGroup) {
15895             if (!this.ids[sGroup]) {
15896                 this.ids[sGroup] = {};
15897             }
15898
15899             var obj = this.ids[sGroup];
15900             if (obj && obj[oDD.id]) {
15901                 delete obj[oDD.id];
15902             }
15903         },
15904
15905         /**
15906          * Unregisters a drag and drop item.  This is executed in
15907          * DragDrop.unreg, use that method instead of calling this directly.
15908          * @method _remove
15909          * @private
15910          * @static
15911          */
15912         _remove: function(oDD) {
15913             for (var g in oDD.groups) {
15914                 if (g && this.ids[g][oDD.id]) {
15915                     delete this.ids[g][oDD.id];
15916                 }
15917             }
15918             delete this.handleIds[oDD.id];
15919         },
15920
15921         /**
15922          * Each DragDrop handle element must be registered.  This is done
15923          * automatically when executing DragDrop.setHandleElId()
15924          * @method regHandle
15925          * @param {String} sDDId the DragDrop id this element is a handle for
15926          * @param {String} sHandleId the id of the element that is the drag
15927          * handle
15928          * @static
15929          */
15930         regHandle: function(sDDId, sHandleId) {
15931             if (!this.handleIds[sDDId]) {
15932                 this.handleIds[sDDId] = {};
15933             }
15934             this.handleIds[sDDId][sHandleId] = sHandleId;
15935         },
15936
15937         /**
15938          * Utility function to determine if a given element has been
15939          * registered as a drag drop item.
15940          * @method isDragDrop
15941          * @param {String} id the element id to check
15942          * @return {boolean} true if this element is a DragDrop item,
15943          * false otherwise
15944          * @static
15945          */
15946         isDragDrop: function(id) {
15947             return ( this.getDDById(id) ) ? true : false;
15948         },
15949
15950         /**
15951          * Returns the drag and drop instances that are in all groups the
15952          * passed in instance belongs to.
15953          * @method getRelated
15954          * @param {DragDrop} p_oDD the obj to get related data for
15955          * @param {boolean} bTargetsOnly if true, only return targetable objs
15956          * @return {DragDrop[]} the related instances
15957          * @static
15958          */
15959         getRelated: function(p_oDD, bTargetsOnly) {
15960             var oDDs = [];
15961             for (var i in p_oDD.groups) {
15962                 for (j in this.ids[i]) {
15963                     var dd = this.ids[i][j];
15964                     if (! this.isTypeOfDD(dd)) {
15965                         continue;
15966                     }
15967                     if (!bTargetsOnly || dd.isTarget) {
15968                         oDDs[oDDs.length] = dd;
15969                     }
15970                 }
15971             }
15972
15973             return oDDs;
15974         },
15975
15976         /**
15977          * Returns true if the specified dd target is a legal target for
15978          * the specifice drag obj
15979          * @method isLegalTarget
15980          * @param {DragDrop} the drag obj
15981          * @param {DragDrop} the target
15982          * @return {boolean} true if the target is a legal target for the
15983          * dd obj
15984          * @static
15985          */
15986         isLegalTarget: function (oDD, oTargetDD) {
15987             var targets = this.getRelated(oDD, true);
15988             for (var i=0, len=targets.length;i<len;++i) {
15989                 if (targets[i].id == oTargetDD.id) {
15990                     return true;
15991                 }
15992             }
15993
15994             return false;
15995         },
15996
15997         /**
15998          * My goal is to be able to transparently determine if an object is
15999          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16000          * returns "object", oDD.constructor.toString() always returns
16001          * "DragDrop" and not the name of the subclass.  So for now it just
16002          * evaluates a well-known variable in DragDrop.
16003          * @method isTypeOfDD
16004          * @param {Object} the object to evaluate
16005          * @return {boolean} true if typeof oDD = DragDrop
16006          * @static
16007          */
16008         isTypeOfDD: function (oDD) {
16009             return (oDD && oDD.__ygDragDrop);
16010         },
16011
16012         /**
16013          * Utility function to determine if a given element has been
16014          * registered as a drag drop handle for the given Drag Drop object.
16015          * @method isHandle
16016          * @param {String} id the element id to check
16017          * @return {boolean} true if this element is a DragDrop handle, false
16018          * otherwise
16019          * @static
16020          */
16021         isHandle: function(sDDId, sHandleId) {
16022             return ( this.handleIds[sDDId] &&
16023                             this.handleIds[sDDId][sHandleId] );
16024         },
16025
16026         /**
16027          * Returns the DragDrop instance for a given id
16028          * @method getDDById
16029          * @param {String} id the id of the DragDrop object
16030          * @return {DragDrop} the drag drop object, null if it is not found
16031          * @static
16032          */
16033         getDDById: function(id) {
16034             for (var i in this.ids) {
16035                 if (this.ids[i][id]) {
16036                     return this.ids[i][id];
16037                 }
16038             }
16039             return null;
16040         },
16041
16042         /**
16043          * Fired after a registered DragDrop object gets the mousedown event.
16044          * Sets up the events required to track the object being dragged
16045          * @method handleMouseDown
16046          * @param {Event} e the event
16047          * @param oDD the DragDrop object being dragged
16048          * @private
16049          * @static
16050          */
16051         handleMouseDown: function(e, oDD) {
16052             if(Roo.QuickTips){
16053                 Roo.QuickTips.disable();
16054             }
16055             this.currentTarget = e.getTarget();
16056
16057             this.dragCurrent = oDD;
16058
16059             var el = oDD.getEl();
16060
16061             // track start position
16062             this.startX = e.getPageX();
16063             this.startY = e.getPageY();
16064
16065             this.deltaX = this.startX - el.offsetLeft;
16066             this.deltaY = this.startY - el.offsetTop;
16067
16068             this.dragThreshMet = false;
16069
16070             this.clickTimeout = setTimeout(
16071                     function() {
16072                         var DDM = Roo.dd.DDM;
16073                         DDM.startDrag(DDM.startX, DDM.startY);
16074                     },
16075                     this.clickTimeThresh );
16076         },
16077
16078         /**
16079          * Fired when either the drag pixel threshol or the mousedown hold
16080          * time threshold has been met.
16081          * @method startDrag
16082          * @param x {int} the X position of the original mousedown
16083          * @param y {int} the Y position of the original mousedown
16084          * @static
16085          */
16086         startDrag: function(x, y) {
16087             clearTimeout(this.clickTimeout);
16088             if (this.dragCurrent) {
16089                 this.dragCurrent.b4StartDrag(x, y);
16090                 this.dragCurrent.startDrag(x, y);
16091             }
16092             this.dragThreshMet = true;
16093         },
16094
16095         /**
16096          * Internal function to handle the mouseup event.  Will be invoked
16097          * from the context of the document.
16098          * @method handleMouseUp
16099          * @param {Event} e the event
16100          * @private
16101          * @static
16102          */
16103         handleMouseUp: function(e) {
16104
16105             if(Roo.QuickTips){
16106                 Roo.QuickTips.enable();
16107             }
16108             if (! this.dragCurrent) {
16109                 return;
16110             }
16111
16112             clearTimeout(this.clickTimeout);
16113
16114             if (this.dragThreshMet) {
16115                 this.fireEvents(e, true);
16116             } else {
16117             }
16118
16119             this.stopDrag(e);
16120
16121             this.stopEvent(e);
16122         },
16123
16124         /**
16125          * Utility to stop event propagation and event default, if these
16126          * features are turned on.
16127          * @method stopEvent
16128          * @param {Event} e the event as returned by this.getEvent()
16129          * @static
16130          */
16131         stopEvent: function(e){
16132             if(this.stopPropagation) {
16133                 e.stopPropagation();
16134             }
16135
16136             if (this.preventDefault) {
16137                 e.preventDefault();
16138             }
16139         },
16140
16141         /**
16142          * Internal function to clean up event handlers after the drag
16143          * operation is complete
16144          * @method stopDrag
16145          * @param {Event} e the event
16146          * @private
16147          * @static
16148          */
16149         stopDrag: function(e) {
16150             // Fire the drag end event for the item that was dragged
16151             if (this.dragCurrent) {
16152                 if (this.dragThreshMet) {
16153                     this.dragCurrent.b4EndDrag(e);
16154                     this.dragCurrent.endDrag(e);
16155                 }
16156
16157                 this.dragCurrent.onMouseUp(e);
16158             }
16159
16160             this.dragCurrent = null;
16161             this.dragOvers = {};
16162         },
16163
16164         /**
16165          * Internal function to handle the mousemove event.  Will be invoked
16166          * from the context of the html element.
16167          *
16168          * @TODO figure out what we can do about mouse events lost when the
16169          * user drags objects beyond the window boundary.  Currently we can
16170          * detect this in internet explorer by verifying that the mouse is
16171          * down during the mousemove event.  Firefox doesn't give us the
16172          * button state on the mousemove event.
16173          * @method handleMouseMove
16174          * @param {Event} e the event
16175          * @private
16176          * @static
16177          */
16178         handleMouseMove: function(e) {
16179             if (! this.dragCurrent) {
16180                 return true;
16181             }
16182
16183             // var button = e.which || e.button;
16184
16185             // check for IE mouseup outside of page boundary
16186             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16187                 this.stopEvent(e);
16188                 return this.handleMouseUp(e);
16189             }
16190
16191             if (!this.dragThreshMet) {
16192                 var diffX = Math.abs(this.startX - e.getPageX());
16193                 var diffY = Math.abs(this.startY - e.getPageY());
16194                 if (diffX > this.clickPixelThresh ||
16195                             diffY > this.clickPixelThresh) {
16196                     this.startDrag(this.startX, this.startY);
16197                 }
16198             }
16199
16200             if (this.dragThreshMet) {
16201                 this.dragCurrent.b4Drag(e);
16202                 this.dragCurrent.onDrag(e);
16203                 if(!this.dragCurrent.moveOnly){
16204                     this.fireEvents(e, false);
16205                 }
16206             }
16207
16208             this.stopEvent(e);
16209
16210             return true;
16211         },
16212
16213         /**
16214          * Iterates over all of the DragDrop elements to find ones we are
16215          * hovering over or dropping on
16216          * @method fireEvents
16217          * @param {Event} e the event
16218          * @param {boolean} isDrop is this a drop op or a mouseover op?
16219          * @private
16220          * @static
16221          */
16222         fireEvents: function(e, isDrop) {
16223             var dc = this.dragCurrent;
16224
16225             // If the user did the mouse up outside of the window, we could
16226             // get here even though we have ended the drag.
16227             if (!dc || dc.isLocked()) {
16228                 return;
16229             }
16230
16231             var pt = e.getPoint();
16232
16233             // cache the previous dragOver array
16234             var oldOvers = [];
16235
16236             var outEvts   = [];
16237             var overEvts  = [];
16238             var dropEvts  = [];
16239             var enterEvts = [];
16240
16241             // Check to see if the object(s) we were hovering over is no longer
16242             // being hovered over so we can fire the onDragOut event
16243             for (var i in this.dragOvers) {
16244
16245                 var ddo = this.dragOvers[i];
16246
16247                 if (! this.isTypeOfDD(ddo)) {
16248                     continue;
16249                 }
16250
16251                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16252                     outEvts.push( ddo );
16253                 }
16254
16255                 oldOvers[i] = true;
16256                 delete this.dragOvers[i];
16257             }
16258
16259             for (var sGroup in dc.groups) {
16260
16261                 if ("string" != typeof sGroup) {
16262                     continue;
16263                 }
16264
16265                 for (i in this.ids[sGroup]) {
16266                     var oDD = this.ids[sGroup][i];
16267                     if (! this.isTypeOfDD(oDD)) {
16268                         continue;
16269                     }
16270
16271                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16272                         if (this.isOverTarget(pt, oDD, this.mode)) {
16273                             // look for drop interactions
16274                             if (isDrop) {
16275                                 dropEvts.push( oDD );
16276                             // look for drag enter and drag over interactions
16277                             } else {
16278
16279                                 // initial drag over: dragEnter fires
16280                                 if (!oldOvers[oDD.id]) {
16281                                     enterEvts.push( oDD );
16282                                 // subsequent drag overs: dragOver fires
16283                                 } else {
16284                                     overEvts.push( oDD );
16285                                 }
16286
16287                                 this.dragOvers[oDD.id] = oDD;
16288                             }
16289                         }
16290                     }
16291                 }
16292             }
16293
16294             if (this.mode) {
16295                 if (outEvts.length) {
16296                     dc.b4DragOut(e, outEvts);
16297                     dc.onDragOut(e, outEvts);
16298                 }
16299
16300                 if (enterEvts.length) {
16301                     dc.onDragEnter(e, enterEvts);
16302                 }
16303
16304                 if (overEvts.length) {
16305                     dc.b4DragOver(e, overEvts);
16306                     dc.onDragOver(e, overEvts);
16307                 }
16308
16309                 if (dropEvts.length) {
16310                     dc.b4DragDrop(e, dropEvts);
16311                     dc.onDragDrop(e, dropEvts);
16312                 }
16313
16314             } else {
16315                 // fire dragout events
16316                 var len = 0;
16317                 for (i=0, len=outEvts.length; i<len; ++i) {
16318                     dc.b4DragOut(e, outEvts[i].id);
16319                     dc.onDragOut(e, outEvts[i].id);
16320                 }
16321
16322                 // fire enter events
16323                 for (i=0,len=enterEvts.length; i<len; ++i) {
16324                     // dc.b4DragEnter(e, oDD.id);
16325                     dc.onDragEnter(e, enterEvts[i].id);
16326                 }
16327
16328                 // fire over events
16329                 for (i=0,len=overEvts.length; i<len; ++i) {
16330                     dc.b4DragOver(e, overEvts[i].id);
16331                     dc.onDragOver(e, overEvts[i].id);
16332                 }
16333
16334                 // fire drop events
16335                 for (i=0, len=dropEvts.length; i<len; ++i) {
16336                     dc.b4DragDrop(e, dropEvts[i].id);
16337                     dc.onDragDrop(e, dropEvts[i].id);
16338                 }
16339
16340             }
16341
16342             // notify about a drop that did not find a target
16343             if (isDrop && !dropEvts.length) {
16344                 dc.onInvalidDrop(e);
16345             }
16346
16347         },
16348
16349         /**
16350          * Helper function for getting the best match from the list of drag
16351          * and drop objects returned by the drag and drop events when we are
16352          * in INTERSECT mode.  It returns either the first object that the
16353          * cursor is over, or the object that has the greatest overlap with
16354          * the dragged element.
16355          * @method getBestMatch
16356          * @param  {DragDrop[]} dds The array of drag and drop objects
16357          * targeted
16358          * @return {DragDrop}       The best single match
16359          * @static
16360          */
16361         getBestMatch: function(dds) {
16362             var winner = null;
16363             // Return null if the input is not what we expect
16364             //if (!dds || !dds.length || dds.length == 0) {
16365                // winner = null;
16366             // If there is only one item, it wins
16367             //} else if (dds.length == 1) {
16368
16369             var len = dds.length;
16370
16371             if (len == 1) {
16372                 winner = dds[0];
16373             } else {
16374                 // Loop through the targeted items
16375                 for (var i=0; i<len; ++i) {
16376                     var dd = dds[i];
16377                     // If the cursor is over the object, it wins.  If the
16378                     // cursor is over multiple matches, the first one we come
16379                     // to wins.
16380                     if (dd.cursorIsOver) {
16381                         winner = dd;
16382                         break;
16383                     // Otherwise the object with the most overlap wins
16384                     } else {
16385                         if (!winner ||
16386                             winner.overlap.getArea() < dd.overlap.getArea()) {
16387                             winner = dd;
16388                         }
16389                     }
16390                 }
16391             }
16392
16393             return winner;
16394         },
16395
16396         /**
16397          * Refreshes the cache of the top-left and bottom-right points of the
16398          * drag and drop objects in the specified group(s).  This is in the
16399          * format that is stored in the drag and drop instance, so typical
16400          * usage is:
16401          * <code>
16402          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16403          * </code>
16404          * Alternatively:
16405          * <code>
16406          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16407          * </code>
16408          * @TODO this really should be an indexed array.  Alternatively this
16409          * method could accept both.
16410          * @method refreshCache
16411          * @param {Object} groups an associative array of groups to refresh
16412          * @static
16413          */
16414         refreshCache: function(groups) {
16415             for (var sGroup in groups) {
16416                 if ("string" != typeof sGroup) {
16417                     continue;
16418                 }
16419                 for (var i in this.ids[sGroup]) {
16420                     var oDD = this.ids[sGroup][i];
16421
16422                     if (this.isTypeOfDD(oDD)) {
16423                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16424                         var loc = this.getLocation(oDD);
16425                         if (loc) {
16426                             this.locationCache[oDD.id] = loc;
16427                         } else {
16428                             delete this.locationCache[oDD.id];
16429                             // this will unregister the drag and drop object if
16430                             // the element is not in a usable state
16431                             // oDD.unreg();
16432                         }
16433                     }
16434                 }
16435             }
16436         },
16437
16438         /**
16439          * This checks to make sure an element exists and is in the DOM.  The
16440          * main purpose is to handle cases where innerHTML is used to remove
16441          * drag and drop objects from the DOM.  IE provides an 'unspecified
16442          * error' when trying to access the offsetParent of such an element
16443          * @method verifyEl
16444          * @param {HTMLElement} el the element to check
16445          * @return {boolean} true if the element looks usable
16446          * @static
16447          */
16448         verifyEl: function(el) {
16449             if (el) {
16450                 var parent;
16451                 if(Roo.isIE){
16452                     try{
16453                         parent = el.offsetParent;
16454                     }catch(e){}
16455                 }else{
16456                     parent = el.offsetParent;
16457                 }
16458                 if (parent) {
16459                     return true;
16460                 }
16461             }
16462
16463             return false;
16464         },
16465
16466         /**
16467          * Returns a Region object containing the drag and drop element's position
16468          * and size, including the padding configured for it
16469          * @method getLocation
16470          * @param {DragDrop} oDD the drag and drop object to get the
16471          *                       location for
16472          * @return {Roo.lib.Region} a Region object representing the total area
16473          *                             the element occupies, including any padding
16474          *                             the instance is configured for.
16475          * @static
16476          */
16477         getLocation: function(oDD) {
16478             if (! this.isTypeOfDD(oDD)) {
16479                 return null;
16480             }
16481
16482             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16483
16484             try {
16485                 pos= Roo.lib.Dom.getXY(el);
16486             } catch (e) { }
16487
16488             if (!pos) {
16489                 return null;
16490             }
16491
16492             x1 = pos[0];
16493             x2 = x1 + el.offsetWidth;
16494             y1 = pos[1];
16495             y2 = y1 + el.offsetHeight;
16496
16497             t = y1 - oDD.padding[0];
16498             r = x2 + oDD.padding[1];
16499             b = y2 + oDD.padding[2];
16500             l = x1 - oDD.padding[3];
16501
16502             return new Roo.lib.Region( t, r, b, l );
16503         },
16504
16505         /**
16506          * Checks the cursor location to see if it over the target
16507          * @method isOverTarget
16508          * @param {Roo.lib.Point} pt The point to evaluate
16509          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16510          * @return {boolean} true if the mouse is over the target
16511          * @private
16512          * @static
16513          */
16514         isOverTarget: function(pt, oTarget, intersect) {
16515             // use cache if available
16516             var loc = this.locationCache[oTarget.id];
16517             if (!loc || !this.useCache) {
16518                 loc = this.getLocation(oTarget);
16519                 this.locationCache[oTarget.id] = loc;
16520
16521             }
16522
16523             if (!loc) {
16524                 return false;
16525             }
16526
16527             oTarget.cursorIsOver = loc.contains( pt );
16528
16529             // DragDrop is using this as a sanity check for the initial mousedown
16530             // in this case we are done.  In POINT mode, if the drag obj has no
16531             // contraints, we are also done. Otherwise we need to evaluate the
16532             // location of the target as related to the actual location of the
16533             // dragged element.
16534             var dc = this.dragCurrent;
16535             if (!dc || !dc.getTargetCoord ||
16536                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16537                 return oTarget.cursorIsOver;
16538             }
16539
16540             oTarget.overlap = null;
16541
16542             // Get the current location of the drag element, this is the
16543             // location of the mouse event less the delta that represents
16544             // where the original mousedown happened on the element.  We
16545             // need to consider constraints and ticks as well.
16546             var pos = dc.getTargetCoord(pt.x, pt.y);
16547
16548             var el = dc.getDragEl();
16549             var curRegion = new Roo.lib.Region( pos.y,
16550                                                    pos.x + el.offsetWidth,
16551                                                    pos.y + el.offsetHeight,
16552                                                    pos.x );
16553
16554             var overlap = curRegion.intersect(loc);
16555
16556             if (overlap) {
16557                 oTarget.overlap = overlap;
16558                 return (intersect) ? true : oTarget.cursorIsOver;
16559             } else {
16560                 return false;
16561             }
16562         },
16563
16564         /**
16565          * unload event handler
16566          * @method _onUnload
16567          * @private
16568          * @static
16569          */
16570         _onUnload: function(e, me) {
16571             Roo.dd.DragDropMgr.unregAll();
16572         },
16573
16574         /**
16575          * Cleans up the drag and drop events and objects.
16576          * @method unregAll
16577          * @private
16578          * @static
16579          */
16580         unregAll: function() {
16581
16582             if (this.dragCurrent) {
16583                 this.stopDrag();
16584                 this.dragCurrent = null;
16585             }
16586
16587             this._execOnAll("unreg", []);
16588
16589             for (i in this.elementCache) {
16590                 delete this.elementCache[i];
16591             }
16592
16593             this.elementCache = {};
16594             this.ids = {};
16595         },
16596
16597         /**
16598          * A cache of DOM elements
16599          * @property elementCache
16600          * @private
16601          * @static
16602          */
16603         elementCache: {},
16604
16605         /**
16606          * Get the wrapper for the DOM element specified
16607          * @method getElWrapper
16608          * @param {String} id the id of the element to get
16609          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16610          * @private
16611          * @deprecated This wrapper isn't that useful
16612          * @static
16613          */
16614         getElWrapper: function(id) {
16615             var oWrapper = this.elementCache[id];
16616             if (!oWrapper || !oWrapper.el) {
16617                 oWrapper = this.elementCache[id] =
16618                     new this.ElementWrapper(Roo.getDom(id));
16619             }
16620             return oWrapper;
16621         },
16622
16623         /**
16624          * Returns the actual DOM element
16625          * @method getElement
16626          * @param {String} id the id of the elment to get
16627          * @return {Object} The element
16628          * @deprecated use Roo.getDom instead
16629          * @static
16630          */
16631         getElement: function(id) {
16632             return Roo.getDom(id);
16633         },
16634
16635         /**
16636          * Returns the style property for the DOM element (i.e.,
16637          * document.getElById(id).style)
16638          * @method getCss
16639          * @param {String} id the id of the elment to get
16640          * @return {Object} The style property of the element
16641          * @deprecated use Roo.getDom instead
16642          * @static
16643          */
16644         getCss: function(id) {
16645             var el = Roo.getDom(id);
16646             return (el) ? el.style : null;
16647         },
16648
16649         /**
16650          * Inner class for cached elements
16651          * @class DragDropMgr.ElementWrapper
16652          * @for DragDropMgr
16653          * @private
16654          * @deprecated
16655          */
16656         ElementWrapper: function(el) {
16657                 /**
16658                  * The element
16659                  * @property el
16660                  */
16661                 this.el = el || null;
16662                 /**
16663                  * The element id
16664                  * @property id
16665                  */
16666                 this.id = this.el && el.id;
16667                 /**
16668                  * A reference to the style property
16669                  * @property css
16670                  */
16671                 this.css = this.el && el.style;
16672             },
16673
16674         /**
16675          * Returns the X position of an html element
16676          * @method getPosX
16677          * @param el the element for which to get the position
16678          * @return {int} the X coordinate
16679          * @for DragDropMgr
16680          * @deprecated use Roo.lib.Dom.getX instead
16681          * @static
16682          */
16683         getPosX: function(el) {
16684             return Roo.lib.Dom.getX(el);
16685         },
16686
16687         /**
16688          * Returns the Y position of an html element
16689          * @method getPosY
16690          * @param el the element for which to get the position
16691          * @return {int} the Y coordinate
16692          * @deprecated use Roo.lib.Dom.getY instead
16693          * @static
16694          */
16695         getPosY: function(el) {
16696             return Roo.lib.Dom.getY(el);
16697         },
16698
16699         /**
16700          * Swap two nodes.  In IE, we use the native method, for others we
16701          * emulate the IE behavior
16702          * @method swapNode
16703          * @param n1 the first node to swap
16704          * @param n2 the other node to swap
16705          * @static
16706          */
16707         swapNode: function(n1, n2) {
16708             if (n1.swapNode) {
16709                 n1.swapNode(n2);
16710             } else {
16711                 var p = n2.parentNode;
16712                 var s = n2.nextSibling;
16713
16714                 if (s == n1) {
16715                     p.insertBefore(n1, n2);
16716                 } else if (n2 == n1.nextSibling) {
16717                     p.insertBefore(n2, n1);
16718                 } else {
16719                     n1.parentNode.replaceChild(n2, n1);
16720                     p.insertBefore(n1, s);
16721                 }
16722             }
16723         },
16724
16725         /**
16726          * Returns the current scroll position
16727          * @method getScroll
16728          * @private
16729          * @static
16730          */
16731         getScroll: function () {
16732             var t, l, dde=document.documentElement, db=document.body;
16733             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16734                 t = dde.scrollTop;
16735                 l = dde.scrollLeft;
16736             } else if (db) {
16737                 t = db.scrollTop;
16738                 l = db.scrollLeft;
16739             } else {
16740
16741             }
16742             return { top: t, left: l };
16743         },
16744
16745         /**
16746          * Returns the specified element style property
16747          * @method getStyle
16748          * @param {HTMLElement} el          the element
16749          * @param {string}      styleProp   the style property
16750          * @return {string} The value of the style property
16751          * @deprecated use Roo.lib.Dom.getStyle
16752          * @static
16753          */
16754         getStyle: function(el, styleProp) {
16755             return Roo.fly(el).getStyle(styleProp);
16756         },
16757
16758         /**
16759          * Gets the scrollTop
16760          * @method getScrollTop
16761          * @return {int} the document's scrollTop
16762          * @static
16763          */
16764         getScrollTop: function () { return this.getScroll().top; },
16765
16766         /**
16767          * Gets the scrollLeft
16768          * @method getScrollLeft
16769          * @return {int} the document's scrollTop
16770          * @static
16771          */
16772         getScrollLeft: function () { return this.getScroll().left; },
16773
16774         /**
16775          * Sets the x/y position of an element to the location of the
16776          * target element.
16777          * @method moveToEl
16778          * @param {HTMLElement} moveEl      The element to move
16779          * @param {HTMLElement} targetEl    The position reference element
16780          * @static
16781          */
16782         moveToEl: function (moveEl, targetEl) {
16783             var aCoord = Roo.lib.Dom.getXY(targetEl);
16784             Roo.lib.Dom.setXY(moveEl, aCoord);
16785         },
16786
16787         /**
16788          * Numeric array sort function
16789          * @method numericSort
16790          * @static
16791          */
16792         numericSort: function(a, b) { return (a - b); },
16793
16794         /**
16795          * Internal counter
16796          * @property _timeoutCount
16797          * @private
16798          * @static
16799          */
16800         _timeoutCount: 0,
16801
16802         /**
16803          * Trying to make the load order less important.  Without this we get
16804          * an error if this file is loaded before the Event Utility.
16805          * @method _addListeners
16806          * @private
16807          * @static
16808          */
16809         _addListeners: function() {
16810             var DDM = Roo.dd.DDM;
16811             if ( Roo.lib.Event && document ) {
16812                 DDM._onLoad();
16813             } else {
16814                 if (DDM._timeoutCount > 2000) {
16815                 } else {
16816                     setTimeout(DDM._addListeners, 10);
16817                     if (document && document.body) {
16818                         DDM._timeoutCount += 1;
16819                     }
16820                 }
16821             }
16822         },
16823
16824         /**
16825          * Recursively searches the immediate parent and all child nodes for
16826          * the handle element in order to determine wheter or not it was
16827          * clicked.
16828          * @method handleWasClicked
16829          * @param node the html element to inspect
16830          * @static
16831          */
16832         handleWasClicked: function(node, id) {
16833             if (this.isHandle(id, node.id)) {
16834                 return true;
16835             } else {
16836                 // check to see if this is a text node child of the one we want
16837                 var p = node.parentNode;
16838
16839                 while (p) {
16840                     if (this.isHandle(id, p.id)) {
16841                         return true;
16842                     } else {
16843                         p = p.parentNode;
16844                     }
16845                 }
16846             }
16847
16848             return false;
16849         }
16850
16851     };
16852
16853 }();
16854
16855 // shorter alias, save a few bytes
16856 Roo.dd.DDM = Roo.dd.DragDropMgr;
16857 Roo.dd.DDM._addListeners();
16858
16859 }/*
16860  * Based on:
16861  * Ext JS Library 1.1.1
16862  * Copyright(c) 2006-2007, Ext JS, LLC.
16863  *
16864  * Originally Released Under LGPL - original licence link has changed is not relivant.
16865  *
16866  * Fork - LGPL
16867  * <script type="text/javascript">
16868  */
16869
16870 /**
16871  * @class Roo.dd.DD
16872  * A DragDrop implementation where the linked element follows the
16873  * mouse cursor during a drag.
16874  * @extends Roo.dd.DragDrop
16875  * @constructor
16876  * @param {String} id the id of the linked element
16877  * @param {String} sGroup the group of related DragDrop items
16878  * @param {object} config an object containing configurable attributes
16879  *                Valid properties for DD:
16880  *                    scroll
16881  */
16882 Roo.dd.DD = function(id, sGroup, config) {
16883     if (id) {
16884         this.init(id, sGroup, config);
16885     }
16886 };
16887
16888 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16889
16890     /**
16891      * When set to true, the utility automatically tries to scroll the browser
16892      * window wehn a drag and drop element is dragged near the viewport boundary.
16893      * Defaults to true.
16894      * @property scroll
16895      * @type boolean
16896      */
16897     scroll: true,
16898
16899     /**
16900      * Sets the pointer offset to the distance between the linked element's top
16901      * left corner and the location the element was clicked
16902      * @method autoOffset
16903      * @param {int} iPageX the X coordinate of the click
16904      * @param {int} iPageY the Y coordinate of the click
16905      */
16906     autoOffset: function(iPageX, iPageY) {
16907         var x = iPageX - this.startPageX;
16908         var y = iPageY - this.startPageY;
16909         this.setDelta(x, y);
16910     },
16911
16912     /**
16913      * Sets the pointer offset.  You can call this directly to force the
16914      * offset to be in a particular location (e.g., pass in 0,0 to set it
16915      * to the center of the object)
16916      * @method setDelta
16917      * @param {int} iDeltaX the distance from the left
16918      * @param {int} iDeltaY the distance from the top
16919      */
16920     setDelta: function(iDeltaX, iDeltaY) {
16921         this.deltaX = iDeltaX;
16922         this.deltaY = iDeltaY;
16923     },
16924
16925     /**
16926      * Sets the drag element to the location of the mousedown or click event,
16927      * maintaining the cursor location relative to the location on the element
16928      * that was clicked.  Override this if you want to place the element in a
16929      * location other than where the cursor is.
16930      * @method setDragElPos
16931      * @param {int} iPageX the X coordinate of the mousedown or drag event
16932      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16933      */
16934     setDragElPos: function(iPageX, iPageY) {
16935         // the first time we do this, we are going to check to make sure
16936         // the element has css positioning
16937
16938         var el = this.getDragEl();
16939         this.alignElWithMouse(el, iPageX, iPageY);
16940     },
16941
16942     /**
16943      * Sets the element to the location of the mousedown or click event,
16944      * maintaining the cursor location relative to the location on the element
16945      * that was clicked.  Override this if you want to place the element in a
16946      * location other than where the cursor is.
16947      * @method alignElWithMouse
16948      * @param {HTMLElement} el the element to move
16949      * @param {int} iPageX the X coordinate of the mousedown or drag event
16950      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16951      */
16952     alignElWithMouse: function(el, iPageX, iPageY) {
16953         var oCoord = this.getTargetCoord(iPageX, iPageY);
16954         var fly = el.dom ? el : Roo.fly(el);
16955         if (!this.deltaSetXY) {
16956             var aCoord = [oCoord.x, oCoord.y];
16957             fly.setXY(aCoord);
16958             var newLeft = fly.getLeft(true);
16959             var newTop  = fly.getTop(true);
16960             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16961         } else {
16962             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16963         }
16964
16965         this.cachePosition(oCoord.x, oCoord.y);
16966         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16967         return oCoord;
16968     },
16969
16970     /**
16971      * Saves the most recent position so that we can reset the constraints and
16972      * tick marks on-demand.  We need to know this so that we can calculate the
16973      * number of pixels the element is offset from its original position.
16974      * @method cachePosition
16975      * @param iPageX the current x position (optional, this just makes it so we
16976      * don't have to look it up again)
16977      * @param iPageY the current y position (optional, this just makes it so we
16978      * don't have to look it up again)
16979      */
16980     cachePosition: function(iPageX, iPageY) {
16981         if (iPageX) {
16982             this.lastPageX = iPageX;
16983             this.lastPageY = iPageY;
16984         } else {
16985             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16986             this.lastPageX = aCoord[0];
16987             this.lastPageY = aCoord[1];
16988         }
16989     },
16990
16991     /**
16992      * Auto-scroll the window if the dragged object has been moved beyond the
16993      * visible window boundary.
16994      * @method autoScroll
16995      * @param {int} x the drag element's x position
16996      * @param {int} y the drag element's y position
16997      * @param {int} h the height of the drag element
16998      * @param {int} w the width of the drag element
16999      * @private
17000      */
17001     autoScroll: function(x, y, h, w) {
17002
17003         if (this.scroll) {
17004             // The client height
17005             var clientH = Roo.lib.Dom.getViewWidth();
17006
17007             // The client width
17008             var clientW = Roo.lib.Dom.getViewHeight();
17009
17010             // The amt scrolled down
17011             var st = this.DDM.getScrollTop();
17012
17013             // The amt scrolled right
17014             var sl = this.DDM.getScrollLeft();
17015
17016             // Location of the bottom of the element
17017             var bot = h + y;
17018
17019             // Location of the right of the element
17020             var right = w + x;
17021
17022             // The distance from the cursor to the bottom of the visible area,
17023             // adjusted so that we don't scroll if the cursor is beyond the
17024             // element drag constraints
17025             var toBot = (clientH + st - y - this.deltaY);
17026
17027             // The distance from the cursor to the right of the visible area
17028             var toRight = (clientW + sl - x - this.deltaX);
17029
17030
17031             // How close to the edge the cursor must be before we scroll
17032             // var thresh = (document.all) ? 100 : 40;
17033             var thresh = 40;
17034
17035             // How many pixels to scroll per autoscroll op.  This helps to reduce
17036             // clunky scrolling. IE is more sensitive about this ... it needs this
17037             // value to be higher.
17038             var scrAmt = (document.all) ? 80 : 30;
17039
17040             // Scroll down if we are near the bottom of the visible page and the
17041             // obj extends below the crease
17042             if ( bot > clientH && toBot < thresh ) {
17043                 window.scrollTo(sl, st + scrAmt);
17044             }
17045
17046             // Scroll up if the window is scrolled down and the top of the object
17047             // goes above the top border
17048             if ( y < st && st > 0 && y - st < thresh ) {
17049                 window.scrollTo(sl, st - scrAmt);
17050             }
17051
17052             // Scroll right if the obj is beyond the right border and the cursor is
17053             // near the border.
17054             if ( right > clientW && toRight < thresh ) {
17055                 window.scrollTo(sl + scrAmt, st);
17056             }
17057
17058             // Scroll left if the window has been scrolled to the right and the obj
17059             // extends past the left border
17060             if ( x < sl && sl > 0 && x - sl < thresh ) {
17061                 window.scrollTo(sl - scrAmt, st);
17062             }
17063         }
17064     },
17065
17066     /**
17067      * Finds the location the element should be placed if we want to move
17068      * it to where the mouse location less the click offset would place us.
17069      * @method getTargetCoord
17070      * @param {int} iPageX the X coordinate of the click
17071      * @param {int} iPageY the Y coordinate of the click
17072      * @return an object that contains the coordinates (Object.x and Object.y)
17073      * @private
17074      */
17075     getTargetCoord: function(iPageX, iPageY) {
17076
17077
17078         var x = iPageX - this.deltaX;
17079         var y = iPageY - this.deltaY;
17080
17081         if (this.constrainX) {
17082             if (x < this.minX) { x = this.minX; }
17083             if (x > this.maxX) { x = this.maxX; }
17084         }
17085
17086         if (this.constrainY) {
17087             if (y < this.minY) { y = this.minY; }
17088             if (y > this.maxY) { y = this.maxY; }
17089         }
17090
17091         x = this.getTick(x, this.xTicks);
17092         y = this.getTick(y, this.yTicks);
17093
17094
17095         return {x:x, y:y};
17096     },
17097
17098     /*
17099      * Sets up config options specific to this class. Overrides
17100      * Roo.dd.DragDrop, but all versions of this method through the
17101      * inheritance chain are called
17102      */
17103     applyConfig: function() {
17104         Roo.dd.DD.superclass.applyConfig.call(this);
17105         this.scroll = (this.config.scroll !== false);
17106     },
17107
17108     /*
17109      * Event that fires prior to the onMouseDown event.  Overrides
17110      * Roo.dd.DragDrop.
17111      */
17112     b4MouseDown: function(e) {
17113         // this.resetConstraints();
17114         this.autoOffset(e.getPageX(),
17115                             e.getPageY());
17116     },
17117
17118     /*
17119      * Event that fires prior to the onDrag event.  Overrides
17120      * Roo.dd.DragDrop.
17121      */
17122     b4Drag: function(e) {
17123         this.setDragElPos(e.getPageX(),
17124                             e.getPageY());
17125     },
17126
17127     toString: function() {
17128         return ("DD " + this.id);
17129     }
17130
17131     //////////////////////////////////////////////////////////////////////////
17132     // Debugging ygDragDrop events that can be overridden
17133     //////////////////////////////////////////////////////////////////////////
17134     /*
17135     startDrag: function(x, y) {
17136     },
17137
17138     onDrag: function(e) {
17139     },
17140
17141     onDragEnter: function(e, id) {
17142     },
17143
17144     onDragOver: function(e, id) {
17145     },
17146
17147     onDragOut: function(e, id) {
17148     },
17149
17150     onDragDrop: function(e, id) {
17151     },
17152
17153     endDrag: function(e) {
17154     }
17155
17156     */
17157
17158 });/*
17159  * Based on:
17160  * Ext JS Library 1.1.1
17161  * Copyright(c) 2006-2007, Ext JS, LLC.
17162  *
17163  * Originally Released Under LGPL - original licence link has changed is not relivant.
17164  *
17165  * Fork - LGPL
17166  * <script type="text/javascript">
17167  */
17168
17169 /**
17170  * @class Roo.dd.DDProxy
17171  * A DragDrop implementation that inserts an empty, bordered div into
17172  * the document that follows the cursor during drag operations.  At the time of
17173  * the click, the frame div is resized to the dimensions of the linked html
17174  * element, and moved to the exact location of the linked element.
17175  *
17176  * References to the "frame" element refer to the single proxy element that
17177  * was created to be dragged in place of all DDProxy elements on the
17178  * page.
17179  *
17180  * @extends Roo.dd.DD
17181  * @constructor
17182  * @param {String} id the id of the linked html element
17183  * @param {String} sGroup the group of related DragDrop objects
17184  * @param {object} config an object containing configurable attributes
17185  *                Valid properties for DDProxy in addition to those in DragDrop:
17186  *                   resizeFrame, centerFrame, dragElId
17187  */
17188 Roo.dd.DDProxy = function(id, sGroup, config) {
17189     if (id) {
17190         this.init(id, sGroup, config);
17191         this.initFrame();
17192     }
17193 };
17194
17195 /**
17196  * The default drag frame div id
17197  * @property Roo.dd.DDProxy.dragElId
17198  * @type String
17199  * @static
17200  */
17201 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17202
17203 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17204
17205     /**
17206      * By default we resize the drag frame to be the same size as the element
17207      * we want to drag (this is to get the frame effect).  We can turn it off
17208      * if we want a different behavior.
17209      * @property resizeFrame
17210      * @type boolean
17211      */
17212     resizeFrame: true,
17213
17214     /**
17215      * By default the frame is positioned exactly where the drag element is, so
17216      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17217      * you do not have constraints on the obj is to have the drag frame centered
17218      * around the cursor.  Set centerFrame to true for this effect.
17219      * @property centerFrame
17220      * @type boolean
17221      */
17222     centerFrame: false,
17223
17224     /**
17225      * Creates the proxy element if it does not yet exist
17226      * @method createFrame
17227      */
17228     createFrame: function() {
17229         var self = this;
17230         var body = document.body;
17231
17232         if (!body || !body.firstChild) {
17233             setTimeout( function() { self.createFrame(); }, 50 );
17234             return;
17235         }
17236
17237         var div = this.getDragEl();
17238
17239         if (!div) {
17240             div    = document.createElement("div");
17241             div.id = this.dragElId;
17242             var s  = div.style;
17243
17244             s.position   = "absolute";
17245             s.visibility = "hidden";
17246             s.cursor     = "move";
17247             s.border     = "2px solid #aaa";
17248             s.zIndex     = 999;
17249
17250             // appendChild can blow up IE if invoked prior to the window load event
17251             // while rendering a table.  It is possible there are other scenarios
17252             // that would cause this to happen as well.
17253             body.insertBefore(div, body.firstChild);
17254         }
17255     },
17256
17257     /**
17258      * Initialization for the drag frame element.  Must be called in the
17259      * constructor of all subclasses
17260      * @method initFrame
17261      */
17262     initFrame: function() {
17263         this.createFrame();
17264     },
17265
17266     applyConfig: function() {
17267         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17268
17269         this.resizeFrame = (this.config.resizeFrame !== false);
17270         this.centerFrame = (this.config.centerFrame);
17271         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17272     },
17273
17274     /**
17275      * Resizes the drag frame to the dimensions of the clicked object, positions
17276      * it over the object, and finally displays it
17277      * @method showFrame
17278      * @param {int} iPageX X click position
17279      * @param {int} iPageY Y click position
17280      * @private
17281      */
17282     showFrame: function(iPageX, iPageY) {
17283         var el = this.getEl();
17284         var dragEl = this.getDragEl();
17285         var s = dragEl.style;
17286
17287         this._resizeProxy();
17288
17289         if (this.centerFrame) {
17290             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17291                            Math.round(parseInt(s.height, 10)/2) );
17292         }
17293
17294         this.setDragElPos(iPageX, iPageY);
17295
17296         Roo.fly(dragEl).show();
17297     },
17298
17299     /**
17300      * The proxy is automatically resized to the dimensions of the linked
17301      * element when a drag is initiated, unless resizeFrame is set to false
17302      * @method _resizeProxy
17303      * @private
17304      */
17305     _resizeProxy: function() {
17306         if (this.resizeFrame) {
17307             var el = this.getEl();
17308             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17309         }
17310     },
17311
17312     // overrides Roo.dd.DragDrop
17313     b4MouseDown: function(e) {
17314         var x = e.getPageX();
17315         var y = e.getPageY();
17316         this.autoOffset(x, y);
17317         this.setDragElPos(x, y);
17318     },
17319
17320     // overrides Roo.dd.DragDrop
17321     b4StartDrag: function(x, y) {
17322         // show the drag frame
17323         this.showFrame(x, y);
17324     },
17325
17326     // overrides Roo.dd.DragDrop
17327     b4EndDrag: function(e) {
17328         Roo.fly(this.getDragEl()).hide();
17329     },
17330
17331     // overrides Roo.dd.DragDrop
17332     // By default we try to move the element to the last location of the frame.
17333     // This is so that the default behavior mirrors that of Roo.dd.DD.
17334     endDrag: function(e) {
17335
17336         var lel = this.getEl();
17337         var del = this.getDragEl();
17338
17339         // Show the drag frame briefly so we can get its position
17340         del.style.visibility = "";
17341
17342         this.beforeMove();
17343         // Hide the linked element before the move to get around a Safari
17344         // rendering bug.
17345         lel.style.visibility = "hidden";
17346         Roo.dd.DDM.moveToEl(lel, del);
17347         del.style.visibility = "hidden";
17348         lel.style.visibility = "";
17349
17350         this.afterDrag();
17351     },
17352
17353     beforeMove : function(){
17354
17355     },
17356
17357     afterDrag : function(){
17358
17359     },
17360
17361     toString: function() {
17362         return ("DDProxy " + this.id);
17363     }
17364
17365 });
17366 /*
17367  * Based on:
17368  * Ext JS Library 1.1.1
17369  * Copyright(c) 2006-2007, Ext JS, LLC.
17370  *
17371  * Originally Released Under LGPL - original licence link has changed is not relivant.
17372  *
17373  * Fork - LGPL
17374  * <script type="text/javascript">
17375  */
17376
17377  /**
17378  * @class Roo.dd.DDTarget
17379  * A DragDrop implementation that does not move, but can be a drop
17380  * target.  You would get the same result by simply omitting implementation
17381  * for the event callbacks, but this way we reduce the processing cost of the
17382  * event listener and the callbacks.
17383  * @extends Roo.dd.DragDrop
17384  * @constructor
17385  * @param {String} id the id of the element that is a drop target
17386  * @param {String} sGroup the group of related DragDrop objects
17387  * @param {object} config an object containing configurable attributes
17388  *                 Valid properties for DDTarget in addition to those in
17389  *                 DragDrop:
17390  *                    none
17391  */
17392 Roo.dd.DDTarget = function(id, sGroup, config) {
17393     if (id) {
17394         this.initTarget(id, sGroup, config);
17395     }
17396     if (config.listeners || config.events) { 
17397        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17398             listeners : config.listeners || {}, 
17399             events : config.events || {} 
17400         });    
17401     }
17402 };
17403
17404 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17405 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17406     toString: function() {
17407         return ("DDTarget " + this.id);
17408     }
17409 });
17410 /*
17411  * Based on:
17412  * Ext JS Library 1.1.1
17413  * Copyright(c) 2006-2007, Ext JS, LLC.
17414  *
17415  * Originally Released Under LGPL - original licence link has changed is not relivant.
17416  *
17417  * Fork - LGPL
17418  * <script type="text/javascript">
17419  */
17420  
17421
17422 /**
17423  * @class Roo.dd.ScrollManager
17424  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17425  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17426  * @singleton
17427  */
17428 Roo.dd.ScrollManager = function(){
17429     var ddm = Roo.dd.DragDropMgr;
17430     var els = {};
17431     var dragEl = null;
17432     var proc = {};
17433     
17434     var onStop = function(e){
17435         dragEl = null;
17436         clearProc();
17437     };
17438     
17439     var triggerRefresh = function(){
17440         if(ddm.dragCurrent){
17441              ddm.refreshCache(ddm.dragCurrent.groups);
17442         }
17443     };
17444     
17445     var doScroll = function(){
17446         if(ddm.dragCurrent){
17447             var dds = Roo.dd.ScrollManager;
17448             if(!dds.animate){
17449                 if(proc.el.scroll(proc.dir, dds.increment)){
17450                     triggerRefresh();
17451                 }
17452             }else{
17453                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17454             }
17455         }
17456     };
17457     
17458     var clearProc = function(){
17459         if(proc.id){
17460             clearInterval(proc.id);
17461         }
17462         proc.id = 0;
17463         proc.el = null;
17464         proc.dir = "";
17465     };
17466     
17467     var startProc = function(el, dir){
17468         clearProc();
17469         proc.el = el;
17470         proc.dir = dir;
17471         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17472     };
17473     
17474     var onFire = function(e, isDrop){
17475         if(isDrop || !ddm.dragCurrent){ return; }
17476         var dds = Roo.dd.ScrollManager;
17477         if(!dragEl || dragEl != ddm.dragCurrent){
17478             dragEl = ddm.dragCurrent;
17479             // refresh regions on drag start
17480             dds.refreshCache();
17481         }
17482         
17483         var xy = Roo.lib.Event.getXY(e);
17484         var pt = new Roo.lib.Point(xy[0], xy[1]);
17485         for(var id in els){
17486             var el = els[id], r = el._region;
17487             if(r && r.contains(pt) && el.isScrollable()){
17488                 if(r.bottom - pt.y <= dds.thresh){
17489                     if(proc.el != el){
17490                         startProc(el, "down");
17491                     }
17492                     return;
17493                 }else if(r.right - pt.x <= dds.thresh){
17494                     if(proc.el != el){
17495                         startProc(el, "left");
17496                     }
17497                     return;
17498                 }else if(pt.y - r.top <= dds.thresh){
17499                     if(proc.el != el){
17500                         startProc(el, "up");
17501                     }
17502                     return;
17503                 }else if(pt.x - r.left <= dds.thresh){
17504                     if(proc.el != el){
17505                         startProc(el, "right");
17506                     }
17507                     return;
17508                 }
17509             }
17510         }
17511         clearProc();
17512     };
17513     
17514     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17515     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17516     
17517     return {
17518         /**
17519          * Registers new overflow element(s) to auto scroll
17520          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17521          */
17522         register : function(el){
17523             if(el instanceof Array){
17524                 for(var i = 0, len = el.length; i < len; i++) {
17525                         this.register(el[i]);
17526                 }
17527             }else{
17528                 el = Roo.get(el);
17529                 els[el.id] = el;
17530             }
17531         },
17532         
17533         /**
17534          * Unregisters overflow element(s) so they are no longer scrolled
17535          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17536          */
17537         unregister : function(el){
17538             if(el instanceof Array){
17539                 for(var i = 0, len = el.length; i < len; i++) {
17540                         this.unregister(el[i]);
17541                 }
17542             }else{
17543                 el = Roo.get(el);
17544                 delete els[el.id];
17545             }
17546         },
17547         
17548         /**
17549          * The number of pixels from the edge of a container the pointer needs to be to 
17550          * trigger scrolling (defaults to 25)
17551          * @type Number
17552          */
17553         thresh : 25,
17554         
17555         /**
17556          * The number of pixels to scroll in each scroll increment (defaults to 50)
17557          * @type Number
17558          */
17559         increment : 100,
17560         
17561         /**
17562          * The frequency of scrolls in milliseconds (defaults to 500)
17563          * @type Number
17564          */
17565         frequency : 500,
17566         
17567         /**
17568          * True to animate the scroll (defaults to true)
17569          * @type Boolean
17570          */
17571         animate: true,
17572         
17573         /**
17574          * The animation duration in seconds - 
17575          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17576          * @type Number
17577          */
17578         animDuration: .4,
17579         
17580         /**
17581          * Manually trigger a cache refresh.
17582          */
17583         refreshCache : function(){
17584             for(var id in els){
17585                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17586                     els[id]._region = els[id].getRegion();
17587                 }
17588             }
17589         }
17590     };
17591 }();/*
17592  * Based on:
17593  * Ext JS Library 1.1.1
17594  * Copyright(c) 2006-2007, Ext JS, LLC.
17595  *
17596  * Originally Released Under LGPL - original licence link has changed is not relivant.
17597  *
17598  * Fork - LGPL
17599  * <script type="text/javascript">
17600  */
17601  
17602
17603 /**
17604  * @class Roo.dd.Registry
17605  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17606  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17607  * @singleton
17608  */
17609 Roo.dd.Registry = function(){
17610     var elements = {}; 
17611     var handles = {}; 
17612     var autoIdSeed = 0;
17613
17614     var getId = function(el, autogen){
17615         if(typeof el == "string"){
17616             return el;
17617         }
17618         var id = el.id;
17619         if(!id && autogen !== false){
17620             id = "roodd-" + (++autoIdSeed);
17621             el.id = id;
17622         }
17623         return id;
17624     };
17625     
17626     return {
17627     /**
17628      * Register a drag drop element
17629      * @param {String|HTMLElement} element The id or DOM node to register
17630      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17631      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17632      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17633      * populated in the data object (if applicable):
17634      * <pre>
17635 Value      Description<br />
17636 ---------  ------------------------------------------<br />
17637 handles    Array of DOM nodes that trigger dragging<br />
17638            for the element being registered<br />
17639 isHandle   True if the element passed in triggers<br />
17640            dragging itself, else false
17641 </pre>
17642      */
17643         register : function(el, data){
17644             data = data || {};
17645             if(typeof el == "string"){
17646                 el = document.getElementById(el);
17647             }
17648             data.ddel = el;
17649             elements[getId(el)] = data;
17650             if(data.isHandle !== false){
17651                 handles[data.ddel.id] = data;
17652             }
17653             if(data.handles){
17654                 var hs = data.handles;
17655                 for(var i = 0, len = hs.length; i < len; i++){
17656                         handles[getId(hs[i])] = data;
17657                 }
17658             }
17659         },
17660
17661     /**
17662      * Unregister a drag drop element
17663      * @param {String|HTMLElement}  element The id or DOM node to unregister
17664      */
17665         unregister : function(el){
17666             var id = getId(el, false);
17667             var data = elements[id];
17668             if(data){
17669                 delete elements[id];
17670                 if(data.handles){
17671                     var hs = data.handles;
17672                     for(var i = 0, len = hs.length; i < len; i++){
17673                         delete handles[getId(hs[i], false)];
17674                     }
17675                 }
17676             }
17677         },
17678
17679     /**
17680      * Returns the handle registered for a DOM Node by id
17681      * @param {String|HTMLElement} id The DOM node or id to look up
17682      * @return {Object} handle The custom handle data
17683      */
17684         getHandle : function(id){
17685             if(typeof id != "string"){ // must be element?
17686                 id = id.id;
17687             }
17688             return handles[id];
17689         },
17690
17691     /**
17692      * Returns the handle that is registered for the DOM node that is the target of the event
17693      * @param {Event} e The event
17694      * @return {Object} handle The custom handle data
17695      */
17696         getHandleFromEvent : function(e){
17697             var t = Roo.lib.Event.getTarget(e);
17698             return t ? handles[t.id] : null;
17699         },
17700
17701     /**
17702      * Returns a custom data object that is registered for a DOM node by id
17703      * @param {String|HTMLElement} id The DOM node or id to look up
17704      * @return {Object} data The custom data
17705      */
17706         getTarget : function(id){
17707             if(typeof id != "string"){ // must be element?
17708                 id = id.id;
17709             }
17710             return elements[id];
17711         },
17712
17713     /**
17714      * Returns a custom data object that is registered for the DOM node that is the target of the event
17715      * @param {Event} e The event
17716      * @return {Object} data The custom data
17717      */
17718         getTargetFromEvent : function(e){
17719             var t = Roo.lib.Event.getTarget(e);
17720             return t ? elements[t.id] || handles[t.id] : null;
17721         }
17722     };
17723 }();/*
17724  * Based on:
17725  * Ext JS Library 1.1.1
17726  * Copyright(c) 2006-2007, Ext JS, LLC.
17727  *
17728  * Originally Released Under LGPL - original licence link has changed is not relivant.
17729  *
17730  * Fork - LGPL
17731  * <script type="text/javascript">
17732  */
17733  
17734
17735 /**
17736  * @class Roo.dd.StatusProxy
17737  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17738  * default drag proxy used by all Roo.dd components.
17739  * @constructor
17740  * @param {Object} config
17741  */
17742 Roo.dd.StatusProxy = function(config){
17743     Roo.apply(this, config);
17744     this.id = this.id || Roo.id();
17745     this.el = new Roo.Layer({
17746         dh: {
17747             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17748                 {tag: "div", cls: "x-dd-drop-icon"},
17749                 {tag: "div", cls: "x-dd-drag-ghost"}
17750             ]
17751         }, 
17752         shadow: !config || config.shadow !== false
17753     });
17754     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17755     this.dropStatus = this.dropNotAllowed;
17756 };
17757
17758 Roo.dd.StatusProxy.prototype = {
17759     /**
17760      * @cfg {String} dropAllowed
17761      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17762      */
17763     dropAllowed : "x-dd-drop-ok",
17764     /**
17765      * @cfg {String} dropNotAllowed
17766      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17767      */
17768     dropNotAllowed : "x-dd-drop-nodrop",
17769
17770     /**
17771      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17772      * over the current target element.
17773      * @param {String} cssClass The css class for the new drop status indicator image
17774      */
17775     setStatus : function(cssClass){
17776         cssClass = cssClass || this.dropNotAllowed;
17777         if(this.dropStatus != cssClass){
17778             this.el.replaceClass(this.dropStatus, cssClass);
17779             this.dropStatus = cssClass;
17780         }
17781     },
17782
17783     /**
17784      * Resets the status indicator to the default dropNotAllowed value
17785      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17786      */
17787     reset : function(clearGhost){
17788         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17789         this.dropStatus = this.dropNotAllowed;
17790         if(clearGhost){
17791             this.ghost.update("");
17792         }
17793     },
17794
17795     /**
17796      * Updates the contents of the ghost element
17797      * @param {String} html The html that will replace the current innerHTML of the ghost element
17798      */
17799     update : function(html){
17800         if(typeof html == "string"){
17801             this.ghost.update(html);
17802         }else{
17803             this.ghost.update("");
17804             html.style.margin = "0";
17805             this.ghost.dom.appendChild(html);
17806         }
17807         // ensure float = none set?? cant remember why though.
17808         var el = this.ghost.dom.firstChild;
17809                 if(el){
17810                         Roo.fly(el).setStyle('float', 'none');
17811                 }
17812     },
17813     
17814     /**
17815      * Returns the underlying proxy {@link Roo.Layer}
17816      * @return {Roo.Layer} el
17817     */
17818     getEl : function(){
17819         return this.el;
17820     },
17821
17822     /**
17823      * Returns the ghost element
17824      * @return {Roo.Element} el
17825      */
17826     getGhost : function(){
17827         return this.ghost;
17828     },
17829
17830     /**
17831      * Hides the proxy
17832      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17833      */
17834     hide : function(clear){
17835         this.el.hide();
17836         if(clear){
17837             this.reset(true);
17838         }
17839     },
17840
17841     /**
17842      * Stops the repair animation if it's currently running
17843      */
17844     stop : function(){
17845         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17846             this.anim.stop();
17847         }
17848     },
17849
17850     /**
17851      * Displays this proxy
17852      */
17853     show : function(){
17854         this.el.show();
17855     },
17856
17857     /**
17858      * Force the Layer to sync its shadow and shim positions to the element
17859      */
17860     sync : function(){
17861         this.el.sync();
17862     },
17863
17864     /**
17865      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17866      * invalid drop operation by the item being dragged.
17867      * @param {Array} xy The XY position of the element ([x, y])
17868      * @param {Function} callback The function to call after the repair is complete
17869      * @param {Object} scope The scope in which to execute the callback
17870      */
17871     repair : function(xy, callback, scope){
17872         this.callback = callback;
17873         this.scope = scope;
17874         if(xy && this.animRepair !== false){
17875             this.el.addClass("x-dd-drag-repair");
17876             this.el.hideUnders(true);
17877             this.anim = this.el.shift({
17878                 duration: this.repairDuration || .5,
17879                 easing: 'easeOut',
17880                 xy: xy,
17881                 stopFx: true,
17882                 callback: this.afterRepair,
17883                 scope: this
17884             });
17885         }else{
17886             this.afterRepair();
17887         }
17888     },
17889
17890     // private
17891     afterRepair : function(){
17892         this.hide(true);
17893         if(typeof this.callback == "function"){
17894             this.callback.call(this.scope || this);
17895         }
17896         this.callback = null;
17897         this.scope = null;
17898     }
17899 };/*
17900  * Based on:
17901  * Ext JS Library 1.1.1
17902  * Copyright(c) 2006-2007, Ext JS, LLC.
17903  *
17904  * Originally Released Under LGPL - original licence link has changed is not relivant.
17905  *
17906  * Fork - LGPL
17907  * <script type="text/javascript">
17908  */
17909
17910 /**
17911  * @class Roo.dd.DragSource
17912  * @extends Roo.dd.DDProxy
17913  * A simple class that provides the basic implementation needed to make any element draggable.
17914  * @constructor
17915  * @param {String/HTMLElement/Element} el The container element
17916  * @param {Object} config
17917  */
17918 Roo.dd.DragSource = function(el, config){
17919     this.el = Roo.get(el);
17920     this.dragData = {};
17921     
17922     Roo.apply(this, config);
17923     
17924     if(!this.proxy){
17925         this.proxy = new Roo.dd.StatusProxy();
17926     }
17927
17928     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17929           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17930     
17931     this.dragging = false;
17932 };
17933
17934 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17935     /**
17936      * @cfg {String} dropAllowed
17937      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17938      */
17939     dropAllowed : "x-dd-drop-ok",
17940     /**
17941      * @cfg {String} dropNotAllowed
17942      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17943      */
17944     dropNotAllowed : "x-dd-drop-nodrop",
17945
17946     /**
17947      * Returns the data object associated with this drag source
17948      * @return {Object} data An object containing arbitrary data
17949      */
17950     getDragData : function(e){
17951         return this.dragData;
17952     },
17953
17954     // private
17955     onDragEnter : function(e, id){
17956         var target = Roo.dd.DragDropMgr.getDDById(id);
17957         this.cachedTarget = target;
17958         if(this.beforeDragEnter(target, e, id) !== false){
17959             if(target.isNotifyTarget){
17960                 var status = target.notifyEnter(this, e, this.dragData);
17961                 this.proxy.setStatus(status);
17962             }else{
17963                 this.proxy.setStatus(this.dropAllowed);
17964             }
17965             
17966             if(this.afterDragEnter){
17967                 /**
17968                  * An empty function by default, but provided so that you can perform a custom action
17969                  * when the dragged item enters the drop target by providing an implementation.
17970                  * @param {Roo.dd.DragDrop} target The drop target
17971                  * @param {Event} e The event object
17972                  * @param {String} id The id of the dragged element
17973                  * @method afterDragEnter
17974                  */
17975                 this.afterDragEnter(target, e, id);
17976             }
17977         }
17978     },
17979
17980     /**
17981      * An empty function by default, but provided so that you can perform a custom action
17982      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17983      * @param {Roo.dd.DragDrop} target The drop target
17984      * @param {Event} e The event object
17985      * @param {String} id The id of the dragged element
17986      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17987      */
17988     beforeDragEnter : function(target, e, id){
17989         return true;
17990     },
17991
17992     // private
17993     alignElWithMouse: function() {
17994         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17995         this.proxy.sync();
17996     },
17997
17998     // private
17999     onDragOver : function(e, id){
18000         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18001         if(this.beforeDragOver(target, e, id) !== false){
18002             if(target.isNotifyTarget){
18003                 var status = target.notifyOver(this, e, this.dragData);
18004                 this.proxy.setStatus(status);
18005             }
18006
18007             if(this.afterDragOver){
18008                 /**
18009                  * An empty function by default, but provided so that you can perform a custom action
18010                  * while the dragged item is over the drop target by providing an implementation.
18011                  * @param {Roo.dd.DragDrop} target The drop target
18012                  * @param {Event} e The event object
18013                  * @param {String} id The id of the dragged element
18014                  * @method afterDragOver
18015                  */
18016                 this.afterDragOver(target, e, id);
18017             }
18018         }
18019     },
18020
18021     /**
18022      * An empty function by default, but provided so that you can perform a custom action
18023      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18024      * @param {Roo.dd.DragDrop} target The drop target
18025      * @param {Event} e The event object
18026      * @param {String} id The id of the dragged element
18027      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18028      */
18029     beforeDragOver : function(target, e, id){
18030         return true;
18031     },
18032
18033     // private
18034     onDragOut : function(e, id){
18035         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18036         if(this.beforeDragOut(target, e, id) !== false){
18037             if(target.isNotifyTarget){
18038                 target.notifyOut(this, e, this.dragData);
18039             }
18040             this.proxy.reset();
18041             if(this.afterDragOut){
18042                 /**
18043                  * An empty function by default, but provided so that you can perform a custom action
18044                  * after the dragged item is dragged out of the target without dropping.
18045                  * @param {Roo.dd.DragDrop} target The drop target
18046                  * @param {Event} e The event object
18047                  * @param {String} id The id of the dragged element
18048                  * @method afterDragOut
18049                  */
18050                 this.afterDragOut(target, e, id);
18051             }
18052         }
18053         this.cachedTarget = null;
18054     },
18055
18056     /**
18057      * An empty function by default, but provided so that you can perform a custom action before the dragged
18058      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18059      * @param {Roo.dd.DragDrop} target The drop target
18060      * @param {Event} e The event object
18061      * @param {String} id The id of the dragged element
18062      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18063      */
18064     beforeDragOut : function(target, e, id){
18065         return true;
18066     },
18067     
18068     // private
18069     onDragDrop : function(e, id){
18070         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18071         if(this.beforeDragDrop(target, e, id) !== false){
18072             if(target.isNotifyTarget){
18073                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18074                     this.onValidDrop(target, e, id);
18075                 }else{
18076                     this.onInvalidDrop(target, e, id);
18077                 }
18078             }else{
18079                 this.onValidDrop(target, e, id);
18080             }
18081             
18082             if(this.afterDragDrop){
18083                 /**
18084                  * An empty function by default, but provided so that you can perform a custom action
18085                  * after a valid drag drop has occurred by providing an implementation.
18086                  * @param {Roo.dd.DragDrop} target The drop target
18087                  * @param {Event} e The event object
18088                  * @param {String} id The id of the dropped element
18089                  * @method afterDragDrop
18090                  */
18091                 this.afterDragDrop(target, e, id);
18092             }
18093         }
18094         delete this.cachedTarget;
18095     },
18096
18097     /**
18098      * An empty function by default, but provided so that you can perform a custom action before the dragged
18099      * item is dropped onto the target and optionally cancel the onDragDrop.
18100      * @param {Roo.dd.DragDrop} target The drop target
18101      * @param {Event} e The event object
18102      * @param {String} id The id of the dragged element
18103      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18104      */
18105     beforeDragDrop : function(target, e, id){
18106         return true;
18107     },
18108
18109     // private
18110     onValidDrop : function(target, e, id){
18111         this.hideProxy();
18112         if(this.afterValidDrop){
18113             /**
18114              * An empty function by default, but provided so that you can perform a custom action
18115              * after a valid drop has occurred by providing an implementation.
18116              * @param {Object} target The target DD 
18117              * @param {Event} e The event object
18118              * @param {String} id The id of the dropped element
18119              * @method afterInvalidDrop
18120              */
18121             this.afterValidDrop(target, e, id);
18122         }
18123     },
18124
18125     // private
18126     getRepairXY : function(e, data){
18127         return this.el.getXY();  
18128     },
18129
18130     // private
18131     onInvalidDrop : function(target, e, id){
18132         this.beforeInvalidDrop(target, e, id);
18133         if(this.cachedTarget){
18134             if(this.cachedTarget.isNotifyTarget){
18135                 this.cachedTarget.notifyOut(this, e, this.dragData);
18136             }
18137             this.cacheTarget = null;
18138         }
18139         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18140
18141         if(this.afterInvalidDrop){
18142             /**
18143              * An empty function by default, but provided so that you can perform a custom action
18144              * after an invalid drop has occurred by providing an implementation.
18145              * @param {Event} e The event object
18146              * @param {String} id The id of the dropped element
18147              * @method afterInvalidDrop
18148              */
18149             this.afterInvalidDrop(e, id);
18150         }
18151     },
18152
18153     // private
18154     afterRepair : function(){
18155         if(Roo.enableFx){
18156             this.el.highlight(this.hlColor || "c3daf9");
18157         }
18158         this.dragging = false;
18159     },
18160
18161     /**
18162      * An empty function by default, but provided so that you can perform a custom action after an invalid
18163      * drop has occurred.
18164      * @param {Roo.dd.DragDrop} target The drop target
18165      * @param {Event} e The event object
18166      * @param {String} id The id of the dragged element
18167      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18168      */
18169     beforeInvalidDrop : function(target, e, id){
18170         return true;
18171     },
18172
18173     // private
18174     handleMouseDown : function(e){
18175         if(this.dragging) {
18176             return;
18177         }
18178         var data = this.getDragData(e);
18179         if(data && this.onBeforeDrag(data, e) !== false){
18180             this.dragData = data;
18181             this.proxy.stop();
18182             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18183         } 
18184     },
18185
18186     /**
18187      * An empty function by default, but provided so that you can perform a custom action before the initial
18188      * drag event begins and optionally cancel it.
18189      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18190      * @param {Event} e The event object
18191      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18192      */
18193     onBeforeDrag : function(data, e){
18194         return true;
18195     },
18196
18197     /**
18198      * An empty function by default, but provided so that you can perform a custom action once the initial
18199      * drag event has begun.  The drag cannot be canceled from this function.
18200      * @param {Number} x The x position of the click on the dragged object
18201      * @param {Number} y The y position of the click on the dragged object
18202      */
18203     onStartDrag : Roo.emptyFn,
18204
18205     // private - YUI override
18206     startDrag : function(x, y){
18207         this.proxy.reset();
18208         this.dragging = true;
18209         this.proxy.update("");
18210         this.onInitDrag(x, y);
18211         this.proxy.show();
18212     },
18213
18214     // private
18215     onInitDrag : function(x, y){
18216         var clone = this.el.dom.cloneNode(true);
18217         clone.id = Roo.id(); // prevent duplicate ids
18218         this.proxy.update(clone);
18219         this.onStartDrag(x, y);
18220         return true;
18221     },
18222
18223     /**
18224      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18225      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18226      */
18227     getProxy : function(){
18228         return this.proxy;  
18229     },
18230
18231     /**
18232      * Hides the drag source's {@link Roo.dd.StatusProxy}
18233      */
18234     hideProxy : function(){
18235         this.proxy.hide();  
18236         this.proxy.reset(true);
18237         this.dragging = false;
18238     },
18239
18240     // private
18241     triggerCacheRefresh : function(){
18242         Roo.dd.DDM.refreshCache(this.groups);
18243     },
18244
18245     // private - override to prevent hiding
18246     b4EndDrag: function(e) {
18247     },
18248
18249     // private - override to prevent moving
18250     endDrag : function(e){
18251         this.onEndDrag(this.dragData, e);
18252     },
18253
18254     // private
18255     onEndDrag : function(data, e){
18256     },
18257     
18258     // private - pin to cursor
18259     autoOffset : function(x, y) {
18260         this.setDelta(-12, -20);
18261     }    
18262 });/*
18263  * Based on:
18264  * Ext JS Library 1.1.1
18265  * Copyright(c) 2006-2007, Ext JS, LLC.
18266  *
18267  * Originally Released Under LGPL - original licence link has changed is not relivant.
18268  *
18269  * Fork - LGPL
18270  * <script type="text/javascript">
18271  */
18272
18273
18274 /**
18275  * @class Roo.dd.DropTarget
18276  * @extends Roo.dd.DDTarget
18277  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18278  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18279  * @constructor
18280  * @param {String/HTMLElement/Element} el The container element
18281  * @param {Object} config
18282  */
18283 Roo.dd.DropTarget = function(el, config){
18284     this.el = Roo.get(el);
18285     
18286     var listeners = false; ;
18287     if (config && config.listeners) {
18288         listeners= config.listeners;
18289         delete config.listeners;
18290     }
18291     Roo.apply(this, config);
18292     
18293     if(this.containerScroll){
18294         Roo.dd.ScrollManager.register(this.el);
18295     }
18296     this.addEvents( {
18297          /**
18298          * @scope Roo.dd.DropTarget
18299          */
18300          
18301          /**
18302          * @event enter
18303          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18304          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18305          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18306          * 
18307          * IMPORTANT : it should set this.overClass and this.dropAllowed
18308          * 
18309          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18310          * @param {Event} e The event
18311          * @param {Object} data An object containing arbitrary data supplied by the drag source
18312          */
18313         "enter" : true,
18314         
18315          /**
18316          * @event over
18317          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18318          * This method will be called on every mouse movement while the drag source is over the drop target.
18319          * This default implementation simply returns the dropAllowed config value.
18320          * 
18321          * IMPORTANT : it should set this.dropAllowed
18322          * 
18323          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18324          * @param {Event} e The event
18325          * @param {Object} data An object containing arbitrary data supplied by the drag source
18326          
18327          */
18328         "over" : true,
18329         /**
18330          * @event out
18331          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18332          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18333          * overClass (if any) from the drop element.
18334          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18335          * @param {Event} e The event
18336          * @param {Object} data An object containing arbitrary data supplied by the drag source
18337          */
18338          "out" : true,
18339          
18340         /**
18341          * @event drop
18342          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18343          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18344          * implementation that does something to process the drop event and returns true so that the drag source's
18345          * repair action does not run.
18346          * 
18347          * IMPORTANT : it should set this.success
18348          * 
18349          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18350          * @param {Event} e The event
18351          * @param {Object} data An object containing arbitrary data supplied by the drag source
18352         */
18353          "drop" : true
18354     });
18355             
18356      
18357     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18358         this.el.dom, 
18359         this.ddGroup || this.group,
18360         {
18361             isTarget: true,
18362             listeners : listeners || {} 
18363            
18364         
18365         }
18366     );
18367
18368 };
18369
18370 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18371     /**
18372      * @cfg {String} overClass
18373      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18374      */
18375      /**
18376      * @cfg {String} ddGroup
18377      * The drag drop group to handle drop events for
18378      */
18379      
18380     /**
18381      * @cfg {String} dropAllowed
18382      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18383      */
18384     dropAllowed : "x-dd-drop-ok",
18385     /**
18386      * @cfg {String} dropNotAllowed
18387      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18388      */
18389     dropNotAllowed : "x-dd-drop-nodrop",
18390     /**
18391      * @cfg {boolean} success
18392      * set this after drop listener.. 
18393      */
18394     success : false,
18395     /**
18396      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18397      * if the drop point is valid for over/enter..
18398      */
18399     valid : false,
18400     // private
18401     isTarget : true,
18402
18403     // private
18404     isNotifyTarget : true,
18405     
18406     /**
18407      * @hide
18408      */
18409     notifyEnter : function(dd, e, data)
18410     {
18411         this.valid = true;
18412         this.fireEvent('enter', dd, e, data);
18413         if(this.overClass){
18414             this.el.addClass(this.overClass);
18415         }
18416         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18417             this.valid ? this.dropAllowed : this.dropNotAllowed
18418         );
18419     },
18420
18421     /**
18422      * @hide
18423      */
18424     notifyOver : function(dd, e, data)
18425     {
18426         this.valid = true;
18427         this.fireEvent('over', dd, e, data);
18428         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18429             this.valid ? this.dropAllowed : this.dropNotAllowed
18430         );
18431     },
18432
18433     /**
18434      * @hide
18435      */
18436     notifyOut : function(dd, e, data)
18437     {
18438         this.fireEvent('out', dd, e, data);
18439         if(this.overClass){
18440             this.el.removeClass(this.overClass);
18441         }
18442     },
18443
18444     /**
18445      * @hide
18446      */
18447     notifyDrop : function(dd, e, data)
18448     {
18449         this.success = false;
18450         this.fireEvent('drop', dd, e, data);
18451         return this.success;
18452     }
18453 });/*
18454  * Based on:
18455  * Ext JS Library 1.1.1
18456  * Copyright(c) 2006-2007, Ext JS, LLC.
18457  *
18458  * Originally Released Under LGPL - original licence link has changed is not relivant.
18459  *
18460  * Fork - LGPL
18461  * <script type="text/javascript">
18462  */
18463
18464
18465 /**
18466  * @class Roo.dd.DragZone
18467  * @extends Roo.dd.DragSource
18468  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18469  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18470  * @constructor
18471  * @param {String/HTMLElement/Element} el The container element
18472  * @param {Object} config
18473  */
18474 Roo.dd.DragZone = function(el, config){
18475     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18476     if(this.containerScroll){
18477         Roo.dd.ScrollManager.register(this.el);
18478     }
18479 };
18480
18481 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18482     /**
18483      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18484      * for auto scrolling during drag operations.
18485      */
18486     /**
18487      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18488      * method after a failed drop (defaults to "c3daf9" - light blue)
18489      */
18490
18491     /**
18492      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18493      * for a valid target to drag based on the mouse down. Override this method
18494      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18495      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18496      * @param {EventObject} e The mouse down event
18497      * @return {Object} The dragData
18498      */
18499     getDragData : function(e){
18500         return Roo.dd.Registry.getHandleFromEvent(e);
18501     },
18502     
18503     /**
18504      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18505      * this.dragData.ddel
18506      * @param {Number} x The x position of the click on the dragged object
18507      * @param {Number} y The y position of the click on the dragged object
18508      * @return {Boolean} true to continue the drag, false to cancel
18509      */
18510     onInitDrag : function(x, y){
18511         this.proxy.update(this.dragData.ddel.cloneNode(true));
18512         this.onStartDrag(x, y);
18513         return true;
18514     },
18515     
18516     /**
18517      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18518      */
18519     afterRepair : function(){
18520         if(Roo.enableFx){
18521             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18522         }
18523         this.dragging = false;
18524     },
18525
18526     /**
18527      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18528      * the XY of this.dragData.ddel
18529      * @param {EventObject} e The mouse up event
18530      * @return {Array} The xy location (e.g. [100, 200])
18531      */
18532     getRepairXY : function(e){
18533         return Roo.Element.fly(this.dragData.ddel).getXY();  
18534     }
18535 });/*
18536  * Based on:
18537  * Ext JS Library 1.1.1
18538  * Copyright(c) 2006-2007, Ext JS, LLC.
18539  *
18540  * Originally Released Under LGPL - original licence link has changed is not relivant.
18541  *
18542  * Fork - LGPL
18543  * <script type="text/javascript">
18544  */
18545 /**
18546  * @class Roo.dd.DropZone
18547  * @extends Roo.dd.DropTarget
18548  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18549  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18550  * @constructor
18551  * @param {String/HTMLElement/Element} el The container element
18552  * @param {Object} config
18553  */
18554 Roo.dd.DropZone = function(el, config){
18555     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18556 };
18557
18558 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18559     /**
18560      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18561      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18562      * provide your own custom lookup.
18563      * @param {Event} e The event
18564      * @return {Object} data The custom data
18565      */
18566     getTargetFromEvent : function(e){
18567         return Roo.dd.Registry.getTargetFromEvent(e);
18568     },
18569
18570     /**
18571      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18572      * that it has registered.  This method has no default implementation and should be overridden to provide
18573      * node-specific processing if necessary.
18574      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18575      * {@link #getTargetFromEvent} for this node)
18576      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18577      * @param {Event} e The event
18578      * @param {Object} data An object containing arbitrary data supplied by the drag source
18579      */
18580     onNodeEnter : function(n, dd, e, data){
18581         
18582     },
18583
18584     /**
18585      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18586      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18587      * overridden to provide the proper feedback.
18588      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18589      * {@link #getTargetFromEvent} for this node)
18590      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18591      * @param {Event} e The event
18592      * @param {Object} data An object containing arbitrary data supplied by the drag source
18593      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18594      * underlying {@link Roo.dd.StatusProxy} can be updated
18595      */
18596     onNodeOver : function(n, dd, e, data){
18597         return this.dropAllowed;
18598     },
18599
18600     /**
18601      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18602      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18603      * node-specific processing if necessary.
18604      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18605      * {@link #getTargetFromEvent} for this node)
18606      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18607      * @param {Event} e The event
18608      * @param {Object} data An object containing arbitrary data supplied by the drag source
18609      */
18610     onNodeOut : function(n, dd, e, data){
18611         
18612     },
18613
18614     /**
18615      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18616      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18617      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18618      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18619      * {@link #getTargetFromEvent} for this node)
18620      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18621      * @param {Event} e The event
18622      * @param {Object} data An object containing arbitrary data supplied by the drag source
18623      * @return {Boolean} True if the drop was valid, else false
18624      */
18625     onNodeDrop : function(n, dd, e, data){
18626         return false;
18627     },
18628
18629     /**
18630      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18631      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18632      * it should be overridden to provide the proper feedback if necessary.
18633      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18634      * @param {Event} e The event
18635      * @param {Object} data An object containing arbitrary data supplied by the drag source
18636      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18637      * underlying {@link Roo.dd.StatusProxy} can be updated
18638      */
18639     onContainerOver : function(dd, e, data){
18640         return this.dropNotAllowed;
18641     },
18642
18643     /**
18644      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18645      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18646      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18647      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18649      * @param {Event} e The event
18650      * @param {Object} data An object containing arbitrary data supplied by the drag source
18651      * @return {Boolean} True if the drop was valid, else false
18652      */
18653     onContainerDrop : function(dd, e, data){
18654         return false;
18655     },
18656
18657     /**
18658      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18659      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18660      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18661      * you should override this method and provide a custom implementation.
18662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18663      * @param {Event} e The event
18664      * @param {Object} data An object containing arbitrary data supplied by the drag source
18665      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18666      * underlying {@link Roo.dd.StatusProxy} can be updated
18667      */
18668     notifyEnter : function(dd, e, data){
18669         return this.dropNotAllowed;
18670     },
18671
18672     /**
18673      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18674      * This method will be called on every mouse movement while the drag source is over the drop zone.
18675      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18676      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18677      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18678      * registered node, it will call {@link #onContainerOver}.
18679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18680      * @param {Event} e The event
18681      * @param {Object} data An object containing arbitrary data supplied by the drag source
18682      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18683      * underlying {@link Roo.dd.StatusProxy} can be updated
18684      */
18685     notifyOver : function(dd, e, data){
18686         var n = this.getTargetFromEvent(e);
18687         if(!n){ // not over valid drop target
18688             if(this.lastOverNode){
18689                 this.onNodeOut(this.lastOverNode, dd, e, data);
18690                 this.lastOverNode = null;
18691             }
18692             return this.onContainerOver(dd, e, data);
18693         }
18694         if(this.lastOverNode != n){
18695             if(this.lastOverNode){
18696                 this.onNodeOut(this.lastOverNode, dd, e, data);
18697             }
18698             this.onNodeEnter(n, dd, e, data);
18699             this.lastOverNode = n;
18700         }
18701         return this.onNodeOver(n, dd, e, data);
18702     },
18703
18704     /**
18705      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18706      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18707      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18711      */
18712     notifyOut : function(dd, e, data){
18713         if(this.lastOverNode){
18714             this.onNodeOut(this.lastOverNode, dd, e, data);
18715             this.lastOverNode = null;
18716         }
18717     },
18718
18719     /**
18720      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18721      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18722      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18723      * otherwise it will call {@link #onContainerDrop}.
18724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18725      * @param {Event} e The event
18726      * @param {Object} data An object containing arbitrary data supplied by the drag source
18727      * @return {Boolean} True if the drop was valid, else false
18728      */
18729     notifyDrop : function(dd, e, data){
18730         if(this.lastOverNode){
18731             this.onNodeOut(this.lastOverNode, dd, e, data);
18732             this.lastOverNode = null;
18733         }
18734         var n = this.getTargetFromEvent(e);
18735         return n ?
18736             this.onNodeDrop(n, dd, e, data) :
18737             this.onContainerDrop(dd, e, data);
18738     },
18739
18740     // private
18741     triggerCacheRefresh : function(){
18742         Roo.dd.DDM.refreshCache(this.groups);
18743     }  
18744 });/*
18745  * Based on:
18746  * Ext JS Library 1.1.1
18747  * Copyright(c) 2006-2007, Ext JS, LLC.
18748  *
18749  * Originally Released Under LGPL - original licence link has changed is not relivant.
18750  *
18751  * Fork - LGPL
18752  * <script type="text/javascript">
18753  */
18754
18755
18756 /**
18757  * @class Roo.data.SortTypes
18758  * @singleton
18759  * Defines the default sorting (casting?) comparison functions used when sorting data.
18760  */
18761 Roo.data.SortTypes = {
18762     /**
18763      * Default sort that does nothing
18764      * @param {Mixed} s The value being converted
18765      * @return {Mixed} The comparison value
18766      */
18767     none : function(s){
18768         return s;
18769     },
18770     
18771     /**
18772      * The regular expression used to strip tags
18773      * @type {RegExp}
18774      * @property
18775      */
18776     stripTagsRE : /<\/?[^>]+>/gi,
18777     
18778     /**
18779      * Strips all HTML tags to sort on text only
18780      * @param {Mixed} s The value being converted
18781      * @return {String} The comparison value
18782      */
18783     asText : function(s){
18784         return String(s).replace(this.stripTagsRE, "");
18785     },
18786     
18787     /**
18788      * Strips all HTML tags to sort on text only - Case insensitive
18789      * @param {Mixed} s The value being converted
18790      * @return {String} The comparison value
18791      */
18792     asUCText : function(s){
18793         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18794     },
18795     
18796     /**
18797      * Case insensitive string
18798      * @param {Mixed} s The value being converted
18799      * @return {String} The comparison value
18800      */
18801     asUCString : function(s) {
18802         return String(s).toUpperCase();
18803     },
18804     
18805     /**
18806      * Date sorting
18807      * @param {Mixed} s The value being converted
18808      * @return {Number} The comparison value
18809      */
18810     asDate : function(s) {
18811         if(!s){
18812             return 0;
18813         }
18814         if(s instanceof Date){
18815             return s.getTime();
18816         }
18817         return Date.parse(String(s));
18818     },
18819     
18820     /**
18821      * Float sorting
18822      * @param {Mixed} s The value being converted
18823      * @return {Float} The comparison value
18824      */
18825     asFloat : function(s) {
18826         var val = parseFloat(String(s).replace(/,/g, ""));
18827         if(isNaN(val)) val = 0;
18828         return val;
18829     },
18830     
18831     /**
18832      * Integer sorting
18833      * @param {Mixed} s The value being converted
18834      * @return {Number} The comparison value
18835      */
18836     asInt : function(s) {
18837         var val = parseInt(String(s).replace(/,/g, ""));
18838         if(isNaN(val)) val = 0;
18839         return val;
18840     }
18841 };/*
18842  * Based on:
18843  * Ext JS Library 1.1.1
18844  * Copyright(c) 2006-2007, Ext JS, LLC.
18845  *
18846  * Originally Released Under LGPL - original licence link has changed is not relivant.
18847  *
18848  * Fork - LGPL
18849  * <script type="text/javascript">
18850  */
18851
18852 /**
18853 * @class Roo.data.Record
18854  * Instances of this class encapsulate both record <em>definition</em> information, and record
18855  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18856  * to access Records cached in an {@link Roo.data.Store} object.<br>
18857  * <p>
18858  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18859  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18860  * objects.<br>
18861  * <p>
18862  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18863  * @constructor
18864  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18865  * {@link #create}. The parameters are the same.
18866  * @param {Array} data An associative Array of data values keyed by the field name.
18867  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18868  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18869  * not specified an integer id is generated.
18870  */
18871 Roo.data.Record = function(data, id){
18872     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18873     this.data = data;
18874 };
18875
18876 /**
18877  * Generate a constructor for a specific record layout.
18878  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18879  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18880  * Each field definition object may contain the following properties: <ul>
18881  * <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,
18882  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18883  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18884  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18885  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18886  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18887  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18888  * this may be omitted.</p></li>
18889  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18890  * <ul><li>auto (Default, implies no conversion)</li>
18891  * <li>string</li>
18892  * <li>int</li>
18893  * <li>float</li>
18894  * <li>boolean</li>
18895  * <li>date</li></ul></p></li>
18896  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18897  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18898  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18899  * by the Reader into an object that will be stored in the Record. It is passed the
18900  * following parameters:<ul>
18901  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18902  * </ul></p></li>
18903  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18904  * </ul>
18905  * <br>usage:<br><pre><code>
18906 var TopicRecord = Roo.data.Record.create(
18907     {name: 'title', mapping: 'topic_title'},
18908     {name: 'author', mapping: 'username'},
18909     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18910     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18911     {name: 'lastPoster', mapping: 'user2'},
18912     {name: 'excerpt', mapping: 'post_text'}
18913 );
18914
18915 var myNewRecord = new TopicRecord({
18916     title: 'Do my job please',
18917     author: 'noobie',
18918     totalPosts: 1,
18919     lastPost: new Date(),
18920     lastPoster: 'Animal',
18921     excerpt: 'No way dude!'
18922 });
18923 myStore.add(myNewRecord);
18924 </code></pre>
18925  * @method create
18926  * @static
18927  */
18928 Roo.data.Record.create = function(o){
18929     var f = function(){
18930         f.superclass.constructor.apply(this, arguments);
18931     };
18932     Roo.extend(f, Roo.data.Record);
18933     var p = f.prototype;
18934     p.fields = new Roo.util.MixedCollection(false, function(field){
18935         return field.name;
18936     });
18937     for(var i = 0, len = o.length; i < len; i++){
18938         p.fields.add(new Roo.data.Field(o[i]));
18939     }
18940     f.getField = function(name){
18941         return p.fields.get(name);  
18942     };
18943     return f;
18944 };
18945
18946 Roo.data.Record.AUTO_ID = 1000;
18947 Roo.data.Record.EDIT = 'edit';
18948 Roo.data.Record.REJECT = 'reject';
18949 Roo.data.Record.COMMIT = 'commit';
18950
18951 Roo.data.Record.prototype = {
18952     /**
18953      * Readonly flag - true if this record has been modified.
18954      * @type Boolean
18955      */
18956     dirty : false,
18957     editing : false,
18958     error: null,
18959     modified: null,
18960
18961     // private
18962     join : function(store){
18963         this.store = store;
18964     },
18965
18966     /**
18967      * Set the named field to the specified value.
18968      * @param {String} name The name of the field to set.
18969      * @param {Object} value The value to set the field to.
18970      */
18971     set : function(name, value){
18972         if(this.data[name] == value){
18973             return;
18974         }
18975         this.dirty = true;
18976         if(!this.modified){
18977             this.modified = {};
18978         }
18979         if(typeof this.modified[name] == 'undefined'){
18980             this.modified[name] = this.data[name];
18981         }
18982         this.data[name] = value;
18983         if(!this.editing){
18984             this.store.afterEdit(this);
18985         }       
18986     },
18987
18988     /**
18989      * Get the value of the named field.
18990      * @param {String} name The name of the field to get the value of.
18991      * @return {Object} The value of the field.
18992      */
18993     get : function(name){
18994         return this.data[name]; 
18995     },
18996
18997     // private
18998     beginEdit : function(){
18999         this.editing = true;
19000         this.modified = {}; 
19001     },
19002
19003     // private
19004     cancelEdit : function(){
19005         this.editing = false;
19006         delete this.modified;
19007     },
19008
19009     // private
19010     endEdit : function(){
19011         this.editing = false;
19012         if(this.dirty && this.store){
19013             this.store.afterEdit(this);
19014         }
19015     },
19016
19017     /**
19018      * Usually called by the {@link Roo.data.Store} which owns the Record.
19019      * Rejects all changes made to the Record since either creation, or the last commit operation.
19020      * Modified fields are reverted to their original values.
19021      * <p>
19022      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19023      * of reject operations.
19024      */
19025     reject : function(){
19026         var m = this.modified;
19027         for(var n in m){
19028             if(typeof m[n] != "function"){
19029                 this.data[n] = m[n];
19030             }
19031         }
19032         this.dirty = false;
19033         delete this.modified;
19034         this.editing = false;
19035         if(this.store){
19036             this.store.afterReject(this);
19037         }
19038     },
19039
19040     /**
19041      * Usually called by the {@link Roo.data.Store} which owns the Record.
19042      * Commits all changes made to the Record since either creation, or the last commit operation.
19043      * <p>
19044      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19045      * of commit operations.
19046      */
19047     commit : function(){
19048         this.dirty = false;
19049         delete this.modified;
19050         this.editing = false;
19051         if(this.store){
19052             this.store.afterCommit(this);
19053         }
19054     },
19055
19056     // private
19057     hasError : function(){
19058         return this.error != null;
19059     },
19060
19061     // private
19062     clearError : function(){
19063         this.error = null;
19064     },
19065
19066     /**
19067      * Creates a copy of this record.
19068      * @param {String} id (optional) A new record id if you don't want to use this record's id
19069      * @return {Record}
19070      */
19071     copy : function(newId) {
19072         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19073     }
19074 };/*
19075  * Based on:
19076  * Ext JS Library 1.1.1
19077  * Copyright(c) 2006-2007, Ext JS, LLC.
19078  *
19079  * Originally Released Under LGPL - original licence link has changed is not relivant.
19080  *
19081  * Fork - LGPL
19082  * <script type="text/javascript">
19083  */
19084
19085
19086
19087 /**
19088  * @class Roo.data.Store
19089  * @extends Roo.util.Observable
19090  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19091  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19092  * <p>
19093  * 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
19094  * has no knowledge of the format of the data returned by the Proxy.<br>
19095  * <p>
19096  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19097  * instances from the data object. These records are cached and made available through accessor functions.
19098  * @constructor
19099  * Creates a new Store.
19100  * @param {Object} config A config object containing the objects needed for the Store to access data,
19101  * and read the data into Records.
19102  */
19103 Roo.data.Store = function(config){
19104     this.data = new Roo.util.MixedCollection(false);
19105     this.data.getKey = function(o){
19106         return o.id;
19107     };
19108     this.baseParams = {};
19109     // private
19110     this.paramNames = {
19111         "start" : "start",
19112         "limit" : "limit",
19113         "sort" : "sort",
19114         "dir" : "dir"
19115     };
19116
19117     if(config && config.data){
19118         this.inlineData = config.data;
19119         delete config.data;
19120     }
19121
19122     Roo.apply(this, config);
19123     
19124     if(this.reader){ // reader passed
19125         this.reader = Roo.factory(this.reader, Roo.data);
19126         this.reader.xmodule = this.xmodule || false;
19127         if(!this.recordType){
19128             this.recordType = this.reader.recordType;
19129         }
19130         if(this.reader.onMetaChange){
19131             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19132         }
19133     }
19134
19135     if(this.recordType){
19136         this.fields = this.recordType.prototype.fields;
19137     }
19138     this.modified = [];
19139
19140     this.addEvents({
19141         /**
19142          * @event datachanged
19143          * Fires when the data cache has changed, and a widget which is using this Store
19144          * as a Record cache should refresh its view.
19145          * @param {Store} this
19146          */
19147         datachanged : true,
19148         /**
19149          * @event metachange
19150          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19151          * @param {Store} this
19152          * @param {Object} meta The JSON metadata
19153          */
19154         metachange : true,
19155         /**
19156          * @event add
19157          * Fires when Records have been added to the Store
19158          * @param {Store} this
19159          * @param {Roo.data.Record[]} records The array of Records added
19160          * @param {Number} index The index at which the record(s) were added
19161          */
19162         add : true,
19163         /**
19164          * @event remove
19165          * Fires when a Record has been removed from the Store
19166          * @param {Store} this
19167          * @param {Roo.data.Record} record The Record that was removed
19168          * @param {Number} index The index at which the record was removed
19169          */
19170         remove : true,
19171         /**
19172          * @event update
19173          * Fires when a Record has been updated
19174          * @param {Store} this
19175          * @param {Roo.data.Record} record The Record that was updated
19176          * @param {String} operation The update operation being performed.  Value may be one of:
19177          * <pre><code>
19178  Roo.data.Record.EDIT
19179  Roo.data.Record.REJECT
19180  Roo.data.Record.COMMIT
19181          * </code></pre>
19182          */
19183         update : true,
19184         /**
19185          * @event clear
19186          * Fires when the data cache has been cleared.
19187          * @param {Store} this
19188          */
19189         clear : true,
19190         /**
19191          * @event beforeload
19192          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19193          * the load action will be canceled.
19194          * @param {Store} this
19195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19196          */
19197         beforeload : true,
19198         /**
19199          * @event load
19200          * Fires after a new set of Records has been loaded.
19201          * @param {Store} this
19202          * @param {Roo.data.Record[]} records The Records that were loaded
19203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19204          */
19205         load : true,
19206         /**
19207          * @event loadexception
19208          * Fires if an exception occurs in the Proxy during loading.
19209          * Called with the signature of the Proxy's "loadexception" event.
19210          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19211          * 
19212          * @param {Proxy} 
19213          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19214          * @param {Object} load options 
19215          * @param {Object} jsonData from your request (normally this contains the Exception)
19216          */
19217         loadexception : true
19218     });
19219     
19220     if(this.proxy){
19221         this.proxy = Roo.factory(this.proxy, Roo.data);
19222         this.proxy.xmodule = this.xmodule || false;
19223         this.relayEvents(this.proxy,  ["loadexception"]);
19224     }
19225     this.sortToggle = {};
19226
19227     Roo.data.Store.superclass.constructor.call(this);
19228
19229     if(this.inlineData){
19230         this.loadData(this.inlineData);
19231         delete this.inlineData;
19232     }
19233 };
19234 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19235      /**
19236     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19237     * without a remote query - used by combo/forms at present.
19238     */
19239     
19240     /**
19241     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19242     */
19243     /**
19244     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19245     */
19246     /**
19247     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19248     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19249     */
19250     /**
19251     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19252     * on any HTTP request
19253     */
19254     /**
19255     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19256     */
19257     /**
19258     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19259     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19260     */
19261     remoteSort : false,
19262
19263     /**
19264     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19265      * loaded or when a record is removed. (defaults to false).
19266     */
19267     pruneModifiedRecords : false,
19268
19269     // private
19270     lastOptions : null,
19271
19272     /**
19273      * Add Records to the Store and fires the add event.
19274      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19275      */
19276     add : function(records){
19277         records = [].concat(records);
19278         for(var i = 0, len = records.length; i < len; i++){
19279             records[i].join(this);
19280         }
19281         var index = this.data.length;
19282         this.data.addAll(records);
19283         this.fireEvent("add", this, records, index);
19284     },
19285
19286     /**
19287      * Remove a Record from the Store and fires the remove event.
19288      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19289      */
19290     remove : function(record){
19291         var index = this.data.indexOf(record);
19292         this.data.removeAt(index);
19293         if(this.pruneModifiedRecords){
19294             this.modified.remove(record);
19295         }
19296         this.fireEvent("remove", this, record, index);
19297     },
19298
19299     /**
19300      * Remove all Records from the Store and fires the clear event.
19301      */
19302     removeAll : function(){
19303         this.data.clear();
19304         if(this.pruneModifiedRecords){
19305             this.modified = [];
19306         }
19307         this.fireEvent("clear", this);
19308     },
19309
19310     /**
19311      * Inserts Records to the Store at the given index and fires the add event.
19312      * @param {Number} index The start index at which to insert the passed Records.
19313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19314      */
19315     insert : function(index, records){
19316         records = [].concat(records);
19317         for(var i = 0, len = records.length; i < len; i++){
19318             this.data.insert(index, records[i]);
19319             records[i].join(this);
19320         }
19321         this.fireEvent("add", this, records, index);
19322     },
19323
19324     /**
19325      * Get the index within the cache of the passed Record.
19326      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19327      * @return {Number} The index of the passed Record. Returns -1 if not found.
19328      */
19329     indexOf : function(record){
19330         return this.data.indexOf(record);
19331     },
19332
19333     /**
19334      * Get the index within the cache of the Record with the passed id.
19335      * @param {String} id The id of the Record to find.
19336      * @return {Number} The index of the Record. Returns -1 if not found.
19337      */
19338     indexOfId : function(id){
19339         return this.data.indexOfKey(id);
19340     },
19341
19342     /**
19343      * Get the Record with the specified id.
19344      * @param {String} id The id of the Record to find.
19345      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19346      */
19347     getById : function(id){
19348         return this.data.key(id);
19349     },
19350
19351     /**
19352      * Get the Record at the specified index.
19353      * @param {Number} index The index of the Record to find.
19354      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19355      */
19356     getAt : function(index){
19357         return this.data.itemAt(index);
19358     },
19359
19360     /**
19361      * Returns a range of Records between specified indices.
19362      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19363      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19364      * @return {Roo.data.Record[]} An array of Records
19365      */
19366     getRange : function(start, end){
19367         return this.data.getRange(start, end);
19368     },
19369
19370     // private
19371     storeOptions : function(o){
19372         o = Roo.apply({}, o);
19373         delete o.callback;
19374         delete o.scope;
19375         this.lastOptions = o;
19376     },
19377
19378     /**
19379      * Loads the Record cache from the configured Proxy using the configured Reader.
19380      * <p>
19381      * If using remote paging, then the first load call must specify the <em>start</em>
19382      * and <em>limit</em> properties in the options.params property to establish the initial
19383      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19384      * <p>
19385      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19386      * and this call will return before the new data has been loaded. Perform any post-processing
19387      * in a callback function, or in a "load" event handler.</strong>
19388      * <p>
19389      * @param {Object} options An object containing properties which control loading options:<ul>
19390      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19391      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19392      * passed the following arguments:<ul>
19393      * <li>r : Roo.data.Record[]</li>
19394      * <li>options: Options object from the load call</li>
19395      * <li>success: Boolean success indicator</li></ul></li>
19396      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19397      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19398      * </ul>
19399      */
19400     load : function(options){
19401         options = options || {};
19402         if(this.fireEvent("beforeload", this, options) !== false){
19403             this.storeOptions(options);
19404             var p = Roo.apply(options.params || {}, this.baseParams);
19405             // if meta was not loaded from remote source.. try requesting it.
19406             if (!this.reader.metaFromRemote) {
19407                 p._requestMeta = 1;
19408             }
19409             if(this.sortInfo && this.remoteSort){
19410                 var pn = this.paramNames;
19411                 p[pn["sort"]] = this.sortInfo.field;
19412                 p[pn["dir"]] = this.sortInfo.direction;
19413             }
19414             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19415         }
19416     },
19417
19418     /**
19419      * Reloads the Record cache from the configured Proxy using the configured Reader and
19420      * the options from the last load operation performed.
19421      * @param {Object} options (optional) An object containing properties which may override the options
19422      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19423      * the most recently used options are reused).
19424      */
19425     reload : function(options){
19426         this.load(Roo.applyIf(options||{}, this.lastOptions));
19427     },
19428
19429     // private
19430     // Called as a callback by the Reader during a load operation.
19431     loadRecords : function(o, options, success){
19432         if(!o || success === false){
19433             if(success !== false){
19434                 this.fireEvent("load", this, [], options);
19435             }
19436             if(options.callback){
19437                 options.callback.call(options.scope || this, [], options, false);
19438             }
19439             return;
19440         }
19441         // if data returned failure - throw an exception.
19442         if (o.success === false) {
19443             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19444             return;
19445         }
19446         var r = o.records, t = o.totalRecords || r.length;
19447         if(!options || options.add !== true){
19448             if(this.pruneModifiedRecords){
19449                 this.modified = [];
19450             }
19451             for(var i = 0, len = r.length; i < len; i++){
19452                 r[i].join(this);
19453             }
19454             if(this.snapshot){
19455                 this.data = this.snapshot;
19456                 delete this.snapshot;
19457             }
19458             this.data.clear();
19459             this.data.addAll(r);
19460             this.totalLength = t;
19461             this.applySort();
19462             this.fireEvent("datachanged", this);
19463         }else{
19464             this.totalLength = Math.max(t, this.data.length+r.length);
19465             this.add(r);
19466         }
19467         this.fireEvent("load", this, r, options);
19468         if(options.callback){
19469             options.callback.call(options.scope || this, r, options, true);
19470         }
19471     },
19472
19473     /**
19474      * Loads data from a passed data block. A Reader which understands the format of the data
19475      * must have been configured in the constructor.
19476      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19477      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19478      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19479      */
19480     loadData : function(o, append){
19481         var r = this.reader.readRecords(o);
19482         this.loadRecords(r, {add: append}, true);
19483     },
19484
19485     /**
19486      * Gets the number of cached records.
19487      * <p>
19488      * <em>If using paging, this may not be the total size of the dataset. If the data object
19489      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19490      * the data set size</em>
19491      */
19492     getCount : function(){
19493         return this.data.length || 0;
19494     },
19495
19496     /**
19497      * Gets the total number of records in the dataset as returned by the server.
19498      * <p>
19499      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19500      * the dataset size</em>
19501      */
19502     getTotalCount : function(){
19503         return this.totalLength || 0;
19504     },
19505
19506     /**
19507      * Returns the sort state of the Store as an object with two properties:
19508      * <pre><code>
19509  field {String} The name of the field by which the Records are sorted
19510  direction {String} The sort order, "ASC" or "DESC"
19511      * </code></pre>
19512      */
19513     getSortState : function(){
19514         return this.sortInfo;
19515     },
19516
19517     // private
19518     applySort : function(){
19519         if(this.sortInfo && !this.remoteSort){
19520             var s = this.sortInfo, f = s.field;
19521             var st = this.fields.get(f).sortType;
19522             var fn = function(r1, r2){
19523                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19524                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19525             };
19526             this.data.sort(s.direction, fn);
19527             if(this.snapshot && this.snapshot != this.data){
19528                 this.snapshot.sort(s.direction, fn);
19529             }
19530         }
19531     },
19532
19533     /**
19534      * Sets the default sort column and order to be used by the next load operation.
19535      * @param {String} fieldName The name of the field to sort by.
19536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19537      */
19538     setDefaultSort : function(field, dir){
19539         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19540     },
19541
19542     /**
19543      * Sort the Records.
19544      * If remote sorting is used, the sort is performed on the server, and the cache is
19545      * reloaded. If local sorting is used, the cache is sorted internally.
19546      * @param {String} fieldName The name of the field to sort by.
19547      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19548      */
19549     sort : function(fieldName, dir){
19550         var f = this.fields.get(fieldName);
19551         if(!dir){
19552             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19553                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19554             }else{
19555                 dir = f.sortDir;
19556             }
19557         }
19558         this.sortToggle[f.name] = dir;
19559         this.sortInfo = {field: f.name, direction: dir};
19560         if(!this.remoteSort){
19561             this.applySort();
19562             this.fireEvent("datachanged", this);
19563         }else{
19564             this.load(this.lastOptions);
19565         }
19566     },
19567
19568     /**
19569      * Calls the specified function for each of the Records in the cache.
19570      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19571      * Returning <em>false</em> aborts and exits the iteration.
19572      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19573      */
19574     each : function(fn, scope){
19575         this.data.each(fn, scope);
19576     },
19577
19578     /**
19579      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19580      * (e.g., during paging).
19581      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19582      */
19583     getModifiedRecords : function(){
19584         return this.modified;
19585     },
19586
19587     // private
19588     createFilterFn : function(property, value, anyMatch){
19589         if(!value.exec){ // not a regex
19590             value = String(value);
19591             if(value.length == 0){
19592                 return false;
19593             }
19594             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19595         }
19596         return function(r){
19597             return value.test(r.data[property]);
19598         };
19599     },
19600
19601     /**
19602      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19603      * @param {String} property A field on your records
19604      * @param {Number} start The record index to start at (defaults to 0)
19605      * @param {Number} end The last record index to include (defaults to length - 1)
19606      * @return {Number} The sum
19607      */
19608     sum : function(property, start, end){
19609         var rs = this.data.items, v = 0;
19610         start = start || 0;
19611         end = (end || end === 0) ? end : rs.length-1;
19612
19613         for(var i = start; i <= end; i++){
19614             v += (rs[i].data[property] || 0);
19615         }
19616         return v;
19617     },
19618
19619     /**
19620      * Filter the records by a specified property.
19621      * @param {String} field A field on your records
19622      * @param {String/RegExp} value Either a string that the field
19623      * should start with or a RegExp to test against the field
19624      * @param {Boolean} anyMatch True to match any part not just the beginning
19625      */
19626     filter : function(property, value, anyMatch){
19627         var fn = this.createFilterFn(property, value, anyMatch);
19628         return fn ? this.filterBy(fn) : this.clearFilter();
19629     },
19630
19631     /**
19632      * Filter by a function. The specified function will be called with each
19633      * record in this data source. If the function returns true the record is included,
19634      * otherwise it is filtered.
19635      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19636      * @param {Object} scope (optional) The scope of the function (defaults to this)
19637      */
19638     filterBy : function(fn, scope){
19639         this.snapshot = this.snapshot || this.data;
19640         this.data = this.queryBy(fn, scope||this);
19641         this.fireEvent("datachanged", this);
19642     },
19643
19644     /**
19645      * Query the records by a specified property.
19646      * @param {String} field A field on your records
19647      * @param {String/RegExp} value Either a string that the field
19648      * should start with or a RegExp to test against the field
19649      * @param {Boolean} anyMatch True to match any part not just the beginning
19650      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19651      */
19652     query : function(property, value, anyMatch){
19653         var fn = this.createFilterFn(property, value, anyMatch);
19654         return fn ? this.queryBy(fn) : this.data.clone();
19655     },
19656
19657     /**
19658      * Query by a function. The specified function will be called with each
19659      * record in this data source. If the function returns true the record is included
19660      * in the results.
19661      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19662      * @param {Object} scope (optional) The scope of the function (defaults to this)
19663       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19664      **/
19665     queryBy : function(fn, scope){
19666         var data = this.snapshot || this.data;
19667         return data.filterBy(fn, scope||this);
19668     },
19669
19670     /**
19671      * Collects unique values for a particular dataIndex from this store.
19672      * @param {String} dataIndex The property to collect
19673      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19674      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19675      * @return {Array} An array of the unique values
19676      **/
19677     collect : function(dataIndex, allowNull, bypassFilter){
19678         var d = (bypassFilter === true && this.snapshot) ?
19679                 this.snapshot.items : this.data.items;
19680         var v, sv, r = [], l = {};
19681         for(var i = 0, len = d.length; i < len; i++){
19682             v = d[i].data[dataIndex];
19683             sv = String(v);
19684             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19685                 l[sv] = true;
19686                 r[r.length] = v;
19687             }
19688         }
19689         return r;
19690     },
19691
19692     /**
19693      * Revert to a view of the Record cache with no filtering applied.
19694      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19695      */
19696     clearFilter : function(suppressEvent){
19697         if(this.snapshot && this.snapshot != this.data){
19698             this.data = this.snapshot;
19699             delete this.snapshot;
19700             if(suppressEvent !== true){
19701                 this.fireEvent("datachanged", this);
19702             }
19703         }
19704     },
19705
19706     // private
19707     afterEdit : function(record){
19708         if(this.modified.indexOf(record) == -1){
19709             this.modified.push(record);
19710         }
19711         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19712     },
19713
19714     // private
19715     afterReject : function(record){
19716         this.modified.remove(record);
19717         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19718     },
19719
19720     // private
19721     afterCommit : function(record){
19722         this.modified.remove(record);
19723         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19724     },
19725
19726     /**
19727      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19728      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19729      */
19730     commitChanges : function(){
19731         var m = this.modified.slice(0);
19732         this.modified = [];
19733         for(var i = 0, len = m.length; i < len; i++){
19734             m[i].commit();
19735         }
19736     },
19737
19738     /**
19739      * Cancel outstanding changes on all changed records.
19740      */
19741     rejectChanges : function(){
19742         var m = this.modified.slice(0);
19743         this.modified = [];
19744         for(var i = 0, len = m.length; i < len; i++){
19745             m[i].reject();
19746         }
19747     },
19748
19749     onMetaChange : function(meta, rtype, o){
19750         this.recordType = rtype;
19751         this.fields = rtype.prototype.fields;
19752         delete this.snapshot;
19753         this.sortInfo = meta.sortInfo || this.sortInfo;
19754         this.modified = [];
19755         this.fireEvent('metachange', this, this.reader.meta);
19756     }
19757 });/*
19758  * Based on:
19759  * Ext JS Library 1.1.1
19760  * Copyright(c) 2006-2007, Ext JS, LLC.
19761  *
19762  * Originally Released Under LGPL - original licence link has changed is not relivant.
19763  *
19764  * Fork - LGPL
19765  * <script type="text/javascript">
19766  */
19767
19768 /**
19769  * @class Roo.data.SimpleStore
19770  * @extends Roo.data.Store
19771  * Small helper class to make creating Stores from Array data easier.
19772  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19773  * @cfg {Array} fields An array of field definition objects, or field name strings.
19774  * @cfg {Array} data The multi-dimensional array of data
19775  * @constructor
19776  * @param {Object} config
19777  */
19778 Roo.data.SimpleStore = function(config){
19779     Roo.data.SimpleStore.superclass.constructor.call(this, {
19780         isLocal : true,
19781         reader: new Roo.data.ArrayReader({
19782                 id: config.id
19783             },
19784             Roo.data.Record.create(config.fields)
19785         ),
19786         proxy : new Roo.data.MemoryProxy(config.data)
19787     });
19788     this.load();
19789 };
19790 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800
19801 /**
19802 /**
19803  * @extends Roo.data.Store
19804  * @class Roo.data.JsonStore
19805  * Small helper class to make creating Stores for JSON data easier. <br/>
19806 <pre><code>
19807 var store = new Roo.data.JsonStore({
19808     url: 'get-images.php',
19809     root: 'images',
19810     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19811 });
19812 </code></pre>
19813  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19814  * JsonReader and HttpProxy (unless inline data is provided).</b>
19815  * @cfg {Array} fields An array of field definition objects, or field name strings.
19816  * @constructor
19817  * @param {Object} config
19818  */
19819 Roo.data.JsonStore = function(c){
19820     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19821         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19822         reader: new Roo.data.JsonReader(c, c.fields)
19823     }));
19824 };
19825 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19826  * Based on:
19827  * Ext JS Library 1.1.1
19828  * Copyright(c) 2006-2007, Ext JS, LLC.
19829  *
19830  * Originally Released Under LGPL - original licence link has changed is not relivant.
19831  *
19832  * Fork - LGPL
19833  * <script type="text/javascript">
19834  */
19835
19836  
19837 Roo.data.Field = function(config){
19838     if(typeof config == "string"){
19839         config = {name: config};
19840     }
19841     Roo.apply(this, config);
19842     
19843     if(!this.type){
19844         this.type = "auto";
19845     }
19846     
19847     var st = Roo.data.SortTypes;
19848     // named sortTypes are supported, here we look them up
19849     if(typeof this.sortType == "string"){
19850         this.sortType = st[this.sortType];
19851     }
19852     
19853     // set default sortType for strings and dates
19854     if(!this.sortType){
19855         switch(this.type){
19856             case "string":
19857                 this.sortType = st.asUCString;
19858                 break;
19859             case "date":
19860                 this.sortType = st.asDate;
19861                 break;
19862             default:
19863                 this.sortType = st.none;
19864         }
19865     }
19866
19867     // define once
19868     var stripRe = /[\$,%]/g;
19869
19870     // prebuilt conversion function for this field, instead of
19871     // switching every time we're reading a value
19872     if(!this.convert){
19873         var cv, dateFormat = this.dateFormat;
19874         switch(this.type){
19875             case "":
19876             case "auto":
19877             case undefined:
19878                 cv = function(v){ return v; };
19879                 break;
19880             case "string":
19881                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19882                 break;
19883             case "int":
19884                 cv = function(v){
19885                     return v !== undefined && v !== null && v !== '' ?
19886                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19887                     };
19888                 break;
19889             case "float":
19890                 cv = function(v){
19891                     return v !== undefined && v !== null && v !== '' ?
19892                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19893                     };
19894                 break;
19895             case "bool":
19896             case "boolean":
19897                 cv = function(v){ return v === true || v === "true" || v == 1; };
19898                 break;
19899             case "date":
19900                 cv = function(v){
19901                     if(!v){
19902                         return '';
19903                     }
19904                     if(v instanceof Date){
19905                         return v;
19906                     }
19907                     if(dateFormat){
19908                         if(dateFormat == "timestamp"){
19909                             return new Date(v*1000);
19910                         }
19911                         return Date.parseDate(v, dateFormat);
19912                     }
19913                     var parsed = Date.parse(v);
19914                     return parsed ? new Date(parsed) : null;
19915                 };
19916              break;
19917             
19918         }
19919         this.convert = cv;
19920     }
19921 };
19922
19923 Roo.data.Field.prototype = {
19924     dateFormat: null,
19925     defaultValue: "",
19926     mapping: null,
19927     sortType : null,
19928     sortDir : "ASC"
19929 };/*
19930  * Based on:
19931  * Ext JS Library 1.1.1
19932  * Copyright(c) 2006-2007, Ext JS, LLC.
19933  *
19934  * Originally Released Under LGPL - original licence link has changed is not relivant.
19935  *
19936  * Fork - LGPL
19937  * <script type="text/javascript">
19938  */
19939  
19940 // Base class for reading structured data from a data source.  This class is intended to be
19941 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19942
19943 /**
19944  * @class Roo.data.DataReader
19945  * Base class for reading structured data from a data source.  This class is intended to be
19946  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19947  */
19948
19949 Roo.data.DataReader = function(meta, recordType){
19950     
19951     this.meta = meta;
19952     
19953     this.recordType = recordType instanceof Array ? 
19954         Roo.data.Record.create(recordType) : recordType;
19955 };
19956
19957 Roo.data.DataReader.prototype = {
19958      /**
19959      * Create an empty record
19960      * @param {Object} data (optional) - overlay some values
19961      * @return {Roo.data.Record} record created.
19962      */
19963     newRow :  function(d) {
19964         var da =  {};
19965         this.recordType.prototype.fields.each(function(c) {
19966             switch( c.type) {
19967                 case 'int' : da[c.name] = 0; break;
19968                 case 'date' : da[c.name] = new Date(); break;
19969                 case 'float' : da[c.name] = 0.0; break;
19970                 case 'boolean' : da[c.name] = false; break;
19971                 default : da[c.name] = ""; break;
19972             }
19973             
19974         });
19975         return new this.recordType(Roo.apply(da, d));
19976     }
19977     
19978 };/*
19979  * Based on:
19980  * Ext JS Library 1.1.1
19981  * Copyright(c) 2006-2007, Ext JS, LLC.
19982  *
19983  * Originally Released Under LGPL - original licence link has changed is not relivant.
19984  *
19985  * Fork - LGPL
19986  * <script type="text/javascript">
19987  */
19988
19989 /**
19990  * @class Roo.data.DataProxy
19991  * @extends Roo.data.Observable
19992  * This class is an abstract base class for implementations which provide retrieval of
19993  * unformatted data objects.<br>
19994  * <p>
19995  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19996  * (of the appropriate type which knows how to parse the data object) to provide a block of
19997  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19998  * <p>
19999  * Custom implementations must implement the load method as described in
20000  * {@link Roo.data.HttpProxy#load}.
20001  */
20002 Roo.data.DataProxy = function(){
20003     this.addEvents({
20004         /**
20005          * @event beforeload
20006          * Fires before a network request is made to retrieve a data object.
20007          * @param {Object} This DataProxy object.
20008          * @param {Object} params The params parameter to the load function.
20009          */
20010         beforeload : true,
20011         /**
20012          * @event load
20013          * Fires before the load method's callback is called.
20014          * @param {Object} This DataProxy object.
20015          * @param {Object} o The data object.
20016          * @param {Object} arg The callback argument object passed to the load function.
20017          */
20018         load : true,
20019         /**
20020          * @event loadexception
20021          * Fires if an Exception occurs during data retrieval.
20022          * @param {Object} This DataProxy object.
20023          * @param {Object} o The data object.
20024          * @param {Object} arg The callback argument object passed to the load function.
20025          * @param {Object} e The Exception.
20026          */
20027         loadexception : true
20028     });
20029     Roo.data.DataProxy.superclass.constructor.call(this);
20030 };
20031
20032 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20033
20034     /**
20035      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20036      */
20037 /*
20038  * Based on:
20039  * Ext JS Library 1.1.1
20040  * Copyright(c) 2006-2007, Ext JS, LLC.
20041  *
20042  * Originally Released Under LGPL - original licence link has changed is not relivant.
20043  *
20044  * Fork - LGPL
20045  * <script type="text/javascript">
20046  */
20047 /**
20048  * @class Roo.data.MemoryProxy
20049  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20050  * to the Reader when its load method is called.
20051  * @constructor
20052  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20053  */
20054 Roo.data.MemoryProxy = function(data){
20055     if (data.data) {
20056         data = data.data;
20057     }
20058     Roo.data.MemoryProxy.superclass.constructor.call(this);
20059     this.data = data;
20060 };
20061
20062 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20063     /**
20064      * Load data from the requested source (in this case an in-memory
20065      * data object passed to the constructor), read the data object into
20066      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20067      * process that block using the passed callback.
20068      * @param {Object} params This parameter is not used by the MemoryProxy class.
20069      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20070      * object into a block of Roo.data.Records.
20071      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20072      * The function must be passed <ul>
20073      * <li>The Record block object</li>
20074      * <li>The "arg" argument from the load function</li>
20075      * <li>A boolean success indicator</li>
20076      * </ul>
20077      * @param {Object} scope The scope in which to call the callback
20078      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20079      */
20080     load : function(params, reader, callback, scope, arg){
20081         params = params || {};
20082         var result;
20083         try {
20084             result = reader.readRecords(this.data);
20085         }catch(e){
20086             this.fireEvent("loadexception", this, arg, null, e);
20087             callback.call(scope, null, arg, false);
20088             return;
20089         }
20090         callback.call(scope, result, arg, true);
20091     },
20092     
20093     // private
20094     update : function(params, records){
20095         
20096     }
20097 });/*
20098  * Based on:
20099  * Ext JS Library 1.1.1
20100  * Copyright(c) 2006-2007, Ext JS, LLC.
20101  *
20102  * Originally Released Under LGPL - original licence link has changed is not relivant.
20103  *
20104  * Fork - LGPL
20105  * <script type="text/javascript">
20106  */
20107 /**
20108  * @class Roo.data.HttpProxy
20109  * @extends Roo.data.DataProxy
20110  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20111  * configured to reference a certain URL.<br><br>
20112  * <p>
20113  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20114  * from which the running page was served.<br><br>
20115  * <p>
20116  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20117  * <p>
20118  * Be aware that to enable the browser to parse an XML document, the server must set
20119  * the Content-Type header in the HTTP response to "text/xml".
20120  * @constructor
20121  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20122  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20123  * will be used to make the request.
20124  */
20125 Roo.data.HttpProxy = function(conn){
20126     Roo.data.HttpProxy.superclass.constructor.call(this);
20127     // is conn a conn config or a real conn?
20128     this.conn = conn;
20129     this.useAjax = !conn || !conn.events;
20130   
20131 };
20132
20133 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20134     // thse are take from connection...
20135     
20136     /**
20137      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20138      */
20139     /**
20140      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20141      * extra parameters to each request made by this object. (defaults to undefined)
20142      */
20143     /**
20144      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20145      *  to each request made by this object. (defaults to undefined)
20146      */
20147     /**
20148      * @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)
20149      */
20150     /**
20151      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20152      */
20153      /**
20154      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20155      * @type Boolean
20156      */
20157   
20158
20159     /**
20160      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20161      * @type Boolean
20162      */
20163     /**
20164      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20165      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20166      * a finer-grained basis than the DataProxy events.
20167      */
20168     getConnection : function(){
20169         return this.useAjax ? Roo.Ajax : this.conn;
20170     },
20171
20172     /**
20173      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20174      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20175      * process that block using the passed callback.
20176      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20177      * for the request to the remote server.
20178      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20179      * object into a block of Roo.data.Records.
20180      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20181      * The function must be passed <ul>
20182      * <li>The Record block object</li>
20183      * <li>The "arg" argument from the load function</li>
20184      * <li>A boolean success indicator</li>
20185      * </ul>
20186      * @param {Object} scope The scope in which to call the callback
20187      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20188      */
20189     load : function(params, reader, callback, scope, arg){
20190         if(this.fireEvent("beforeload", this, params) !== false){
20191             var  o = {
20192                 params : params || {},
20193                 request: {
20194                     callback : callback,
20195                     scope : scope,
20196                     arg : arg
20197                 },
20198                 reader: reader,
20199                 callback : this.loadResponse,
20200                 scope: this
20201             };
20202             if(this.useAjax){
20203                 Roo.applyIf(o, this.conn);
20204                 if(this.activeRequest){
20205                     Roo.Ajax.abort(this.activeRequest);
20206                 }
20207                 this.activeRequest = Roo.Ajax.request(o);
20208             }else{
20209                 this.conn.request(o);
20210             }
20211         }else{
20212             callback.call(scope||this, null, arg, false);
20213         }
20214     },
20215
20216     // private
20217     loadResponse : function(o, success, response){
20218         delete this.activeRequest;
20219         if(!success){
20220             this.fireEvent("loadexception", this, o, response);
20221             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20222             return;
20223         }
20224         var result;
20225         try {
20226             result = o.reader.read(response);
20227         }catch(e){
20228             this.fireEvent("loadexception", this, o, response, e);
20229             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20230             return;
20231         }
20232         
20233         this.fireEvent("load", this, o, o.request.arg);
20234         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20235     },
20236
20237     // private
20238     update : function(dataSet){
20239
20240     },
20241
20242     // private
20243     updateResponse : function(dataSet){
20244
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256
20257 /**
20258  * @class Roo.data.ScriptTagProxy
20259  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20260  * other than the originating domain of the running page.<br><br>
20261  * <p>
20262  * <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
20263  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20264  * <p>
20265  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20266  * source code that is used as the source inside a &lt;script> tag.<br><br>
20267  * <p>
20268  * In order for the browser to process the returned data, the server must wrap the data object
20269  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20270  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20271  * depending on whether the callback name was passed:
20272  * <p>
20273  * <pre><code>
20274 boolean scriptTag = false;
20275 String cb = request.getParameter("callback");
20276 if (cb != null) {
20277     scriptTag = true;
20278     response.setContentType("text/javascript");
20279 } else {
20280     response.setContentType("application/x-json");
20281 }
20282 Writer out = response.getWriter();
20283 if (scriptTag) {
20284     out.write(cb + "(");
20285 }
20286 out.print(dataBlock.toJsonString());
20287 if (scriptTag) {
20288     out.write(");");
20289 }
20290 </pre></code>
20291  *
20292  * @constructor
20293  * @param {Object} config A configuration object.
20294  */
20295 Roo.data.ScriptTagProxy = function(config){
20296     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20297     Roo.apply(this, config);
20298     this.head = document.getElementsByTagName("head")[0];
20299 };
20300
20301 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20302
20303 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20304     /**
20305      * @cfg {String} url The URL from which to request the data object.
20306      */
20307     /**
20308      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20309      */
20310     timeout : 30000,
20311     /**
20312      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20313      * the server the name of the callback function set up by the load call to process the returned data object.
20314      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20315      * javascript output which calls this named function passing the data object as its only parameter.
20316      */
20317     callbackParam : "callback",
20318     /**
20319      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20320      * name to the request.
20321      */
20322     nocache : true,
20323
20324     /**
20325      * Load data from the configured URL, read the data object into
20326      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20327      * process that block using the passed callback.
20328      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20329      * for the request to the remote server.
20330      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20331      * object into a block of Roo.data.Records.
20332      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20333      * The function must be passed <ul>
20334      * <li>The Record block object</li>
20335      * <li>The "arg" argument from the load function</li>
20336      * <li>A boolean success indicator</li>
20337      * </ul>
20338      * @param {Object} scope The scope in which to call the callback
20339      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20340      */
20341     load : function(params, reader, callback, scope, arg){
20342         if(this.fireEvent("beforeload", this, params) !== false){
20343
20344             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20345
20346             var url = this.url;
20347             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20348             if(this.nocache){
20349                 url += "&_dc=" + (new Date().getTime());
20350             }
20351             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20352             var trans = {
20353                 id : transId,
20354                 cb : "stcCallback"+transId,
20355                 scriptId : "stcScript"+transId,
20356                 params : params,
20357                 arg : arg,
20358                 url : url,
20359                 callback : callback,
20360                 scope : scope,
20361                 reader : reader
20362             };
20363             var conn = this;
20364
20365             window[trans.cb] = function(o){
20366                 conn.handleResponse(o, trans);
20367             };
20368
20369             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20370
20371             if(this.autoAbort !== false){
20372                 this.abort();
20373             }
20374
20375             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20376
20377             var script = document.createElement("script");
20378             script.setAttribute("src", url);
20379             script.setAttribute("type", "text/javascript");
20380             script.setAttribute("id", trans.scriptId);
20381             this.head.appendChild(script);
20382
20383             this.trans = trans;
20384         }else{
20385             callback.call(scope||this, null, arg, false);
20386         }
20387     },
20388
20389     // private
20390     isLoading : function(){
20391         return this.trans ? true : false;
20392     },
20393
20394     /**
20395      * Abort the current server request.
20396      */
20397     abort : function(){
20398         if(this.isLoading()){
20399             this.destroyTrans(this.trans);
20400         }
20401     },
20402
20403     // private
20404     destroyTrans : function(trans, isLoaded){
20405         this.head.removeChild(document.getElementById(trans.scriptId));
20406         clearTimeout(trans.timeoutId);
20407         if(isLoaded){
20408             window[trans.cb] = undefined;
20409             try{
20410                 delete window[trans.cb];
20411             }catch(e){}
20412         }else{
20413             // if hasn't been loaded, wait for load to remove it to prevent script error
20414             window[trans.cb] = function(){
20415                 window[trans.cb] = undefined;
20416                 try{
20417                     delete window[trans.cb];
20418                 }catch(e){}
20419             };
20420         }
20421     },
20422
20423     // private
20424     handleResponse : function(o, trans){
20425         this.trans = false;
20426         this.destroyTrans(trans, true);
20427         var result;
20428         try {
20429             result = trans.reader.readRecords(o);
20430         }catch(e){
20431             this.fireEvent("loadexception", this, o, trans.arg, e);
20432             trans.callback.call(trans.scope||window, null, trans.arg, false);
20433             return;
20434         }
20435         this.fireEvent("load", this, o, trans.arg);
20436         trans.callback.call(trans.scope||window, result, trans.arg, true);
20437     },
20438
20439     // private
20440     handleFailure : function(trans){
20441         this.trans = false;
20442         this.destroyTrans(trans, false);
20443         this.fireEvent("loadexception", this, null, trans.arg);
20444         trans.callback.call(trans.scope||window, null, trans.arg, false);
20445     }
20446 });/*
20447  * Based on:
20448  * Ext JS Library 1.1.1
20449  * Copyright(c) 2006-2007, Ext JS, LLC.
20450  *
20451  * Originally Released Under LGPL - original licence link has changed is not relivant.
20452  *
20453  * Fork - LGPL
20454  * <script type="text/javascript">
20455  */
20456
20457 /**
20458  * @class Roo.data.JsonReader
20459  * @extends Roo.data.DataReader
20460  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20461  * based on mappings in a provided Roo.data.Record constructor.
20462  * 
20463  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20464  * in the reply previously. 
20465  * 
20466  * <p>
20467  * Example code:
20468  * <pre><code>
20469 var RecordDef = Roo.data.Record.create([
20470     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20471     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20472 ]);
20473 var myReader = new Roo.data.JsonReader({
20474     totalProperty: "results",    // The property which contains the total dataset size (optional)
20475     root: "rows",                // The property which contains an Array of row objects
20476     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20477 }, RecordDef);
20478 </code></pre>
20479  * <p>
20480  * This would consume a JSON file like this:
20481  * <pre><code>
20482 { 'results': 2, 'rows': [
20483     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20484     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20485 }
20486 </code></pre>
20487  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20488  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20489  * paged from the remote server.
20490  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20491  * @cfg {String} root name of the property which contains the Array of row objects.
20492  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20493  * @constructor
20494  * Create a new JsonReader
20495  * @param {Object} meta Metadata configuration options
20496  * @param {Object} recordType Either an Array of field definition objects,
20497  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20498  */
20499 Roo.data.JsonReader = function(meta, recordType){
20500     
20501     meta = meta || {};
20502     // set some defaults:
20503     Roo.applyIf(meta, {
20504         totalProperty: 'total',
20505         successProperty : 'success',
20506         root : 'data',
20507         id : 'id'
20508     });
20509     
20510     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20511 };
20512 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20513     
20514     /**
20515      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20516      * Used by Store query builder to append _requestMeta to params.
20517      * 
20518      */
20519     metaFromRemote : false,
20520     /**
20521      * This method is only used by a DataProxy which has retrieved data from a remote server.
20522      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20523      * @return {Object} data A data block which is used by an Roo.data.Store object as
20524      * a cache of Roo.data.Records.
20525      */
20526     read : function(response){
20527         var json = response.responseText;
20528        
20529         var o = /* eval:var:o */ eval("("+json+")");
20530         if(!o) {
20531             throw {message: "JsonReader.read: Json object not found"};
20532         }
20533         
20534         if(o.metaData){
20535             
20536             delete this.ef;
20537             this.metaFromRemote = true;
20538             this.meta = o.metaData;
20539             this.recordType = Roo.data.Record.create(o.metaData.fields);
20540             this.onMetaChange(this.meta, this.recordType, o);
20541         }
20542         return this.readRecords(o);
20543     },
20544
20545     // private function a store will implement
20546     onMetaChange : function(meta, recordType, o){
20547
20548     },
20549
20550     /**
20551          * @ignore
20552          */
20553     simpleAccess: function(obj, subsc) {
20554         return obj[subsc];
20555     },
20556
20557         /**
20558          * @ignore
20559          */
20560     getJsonAccessor: function(){
20561         var re = /[\[\.]/;
20562         return function(expr) {
20563             try {
20564                 return(re.test(expr))
20565                     ? new Function("obj", "return obj." + expr)
20566                     : function(obj){
20567                         return obj[expr];
20568                     };
20569             } catch(e){}
20570             return Roo.emptyFn;
20571         };
20572     }(),
20573
20574     /**
20575      * Create a data block containing Roo.data.Records from an XML document.
20576      * @param {Object} o An object which contains an Array of row objects in the property specified
20577      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20578      * which contains the total size of the dataset.
20579      * @return {Object} data A data block which is used by an Roo.data.Store object as
20580      * a cache of Roo.data.Records.
20581      */
20582     readRecords : function(o){
20583         /**
20584          * After any data loads, the raw JSON data is available for further custom processing.
20585          * @type Object
20586          */
20587         this.jsonData = o;
20588         var s = this.meta, Record = this.recordType,
20589             f = Record.prototype.fields, fi = f.items, fl = f.length;
20590
20591 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20592         if (!this.ef) {
20593             if(s.totalProperty) {
20594                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20595                 }
20596                 if(s.successProperty) {
20597                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20598                 }
20599                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20600                 if (s.id) {
20601                         var g = this.getJsonAccessor(s.id);
20602                         this.getId = function(rec) {
20603                                 var r = g(rec);
20604                                 return (r === undefined || r === "") ? null : r;
20605                         };
20606                 } else {
20607                         this.getId = function(){return null;};
20608                 }
20609             this.ef = [];
20610             for(var jj = 0; jj < fl; jj++){
20611                 f = fi[jj];
20612                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20613                 this.ef[jj] = this.getJsonAccessor(map);
20614             }
20615         }
20616
20617         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20618         if(s.totalProperty){
20619             var vt = parseInt(this.getTotal(o), 10);
20620             if(!isNaN(vt)){
20621                 totalRecords = vt;
20622             }
20623         }
20624         if(s.successProperty){
20625             var vs = this.getSuccess(o);
20626             if(vs === false || vs === 'false'){
20627                 success = false;
20628             }
20629         }
20630         var records = [];
20631             for(var i = 0; i < c; i++){
20632                     var n = root[i];
20633                 var values = {};
20634                 var id = this.getId(n);
20635                 for(var j = 0; j < fl; j++){
20636                     f = fi[j];
20637                 var v = this.ef[j](n);
20638                 if (!f.convert) {
20639                     Roo.log('missing convert for ' + f.name);
20640                     Roo.log(f);
20641                     continue;
20642                 }
20643                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20644                 }
20645                 var record = new Record(values, id);
20646                 record.json = n;
20647                 records[i] = record;
20648             }
20649             return {
20650                 success : success,
20651                 records : records,
20652                 totalRecords : totalRecords
20653             };
20654     }
20655 });/*
20656  * Based on:
20657  * Ext JS Library 1.1.1
20658  * Copyright(c) 2006-2007, Ext JS, LLC.
20659  *
20660  * Originally Released Under LGPL - original licence link has changed is not relivant.
20661  *
20662  * Fork - LGPL
20663  * <script type="text/javascript">
20664  */
20665
20666 /**
20667  * @class Roo.data.XmlReader
20668  * @extends Roo.data.DataReader
20669  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20670  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20671  * <p>
20672  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20673  * header in the HTTP response must be set to "text/xml".</em>
20674  * <p>
20675  * Example code:
20676  * <pre><code>
20677 var RecordDef = Roo.data.Record.create([
20678    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20679    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20680 ]);
20681 var myReader = new Roo.data.XmlReader({
20682    totalRecords: "results", // The element which contains the total dataset size (optional)
20683    record: "row",           // The repeated element which contains row information
20684    id: "id"                 // The element within the row that provides an ID for the record (optional)
20685 }, RecordDef);
20686 </code></pre>
20687  * <p>
20688  * This would consume an XML file like this:
20689  * <pre><code>
20690 &lt;?xml?>
20691 &lt;dataset>
20692  &lt;results>2&lt;/results>
20693  &lt;row>
20694    &lt;id>1&lt;/id>
20695    &lt;name>Bill&lt;/name>
20696    &lt;occupation>Gardener&lt;/occupation>
20697  &lt;/row>
20698  &lt;row>
20699    &lt;id>2&lt;/id>
20700    &lt;name>Ben&lt;/name>
20701    &lt;occupation>Horticulturalist&lt;/occupation>
20702  &lt;/row>
20703 &lt;/dataset>
20704 </code></pre>
20705  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20706  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20707  * paged from the remote server.
20708  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20709  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20710  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20711  * a record identifier value.
20712  * @constructor
20713  * Create a new XmlReader
20714  * @param {Object} meta Metadata configuration options
20715  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20716  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20717  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20718  */
20719 Roo.data.XmlReader = function(meta, recordType){
20720     meta = meta || {};
20721     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20722 };
20723 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20724     /**
20725      * This method is only used by a DataProxy which has retrieved data from a remote server.
20726          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20727          * to contain a method called 'responseXML' that returns an XML document object.
20728      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20729      * a cache of Roo.data.Records.
20730      */
20731     read : function(response){
20732         var doc = response.responseXML;
20733         if(!doc) {
20734             throw {message: "XmlReader.read: XML Document not available"};
20735         }
20736         return this.readRecords(doc);
20737     },
20738
20739     /**
20740      * Create a data block containing Roo.data.Records from an XML document.
20741          * @param {Object} doc A parsed XML document.
20742      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20743      * a cache of Roo.data.Records.
20744      */
20745     readRecords : function(doc){
20746         /**
20747          * After any data loads/reads, the raw XML Document is available for further custom processing.
20748          * @type XMLDocument
20749          */
20750         this.xmlData = doc;
20751         var root = doc.documentElement || doc;
20752         var q = Roo.DomQuery;
20753         var recordType = this.recordType, fields = recordType.prototype.fields;
20754         var sid = this.meta.id;
20755         var totalRecords = 0, success = true;
20756         if(this.meta.totalRecords){
20757             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20758         }
20759         
20760         if(this.meta.success){
20761             var sv = q.selectValue(this.meta.success, root, true);
20762             success = sv !== false && sv !== 'false';
20763         }
20764         var records = [];
20765         var ns = q.select(this.meta.record, root);
20766         for(var i = 0, len = ns.length; i < len; i++) {
20767                 var n = ns[i];
20768                 var values = {};
20769                 var id = sid ? q.selectValue(sid, n) : undefined;
20770                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20771                     var f = fields.items[j];
20772                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20773                     v = f.convert(v);
20774                     values[f.name] = v;
20775                 }
20776                 var record = new recordType(values, id);
20777                 record.node = n;
20778                 records[records.length] = record;
20779             }
20780
20781             return {
20782                 success : success,
20783                 records : records,
20784                 totalRecords : totalRecords || records.length
20785             };
20786     }
20787 });/*
20788  * Based on:
20789  * Ext JS Library 1.1.1
20790  * Copyright(c) 2006-2007, Ext JS, LLC.
20791  *
20792  * Originally Released Under LGPL - original licence link has changed is not relivant.
20793  *
20794  * Fork - LGPL
20795  * <script type="text/javascript">
20796  */
20797
20798 /**
20799  * @class Roo.data.ArrayReader
20800  * @extends Roo.data.DataReader
20801  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20802  * Each element of that Array represents a row of data fields. The
20803  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20804  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20805  * <p>
20806  * Example code:.
20807  * <pre><code>
20808 var RecordDef = Roo.data.Record.create([
20809     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20810     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20811 ]);
20812 var myReader = new Roo.data.ArrayReader({
20813     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20814 }, RecordDef);
20815 </code></pre>
20816  * <p>
20817  * This would consume an Array like this:
20818  * <pre><code>
20819 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20820   </code></pre>
20821  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20822  * @constructor
20823  * Create a new JsonReader
20824  * @param {Object} meta Metadata configuration options.
20825  * @param {Object} recordType Either an Array of field definition objects
20826  * as specified to {@link Roo.data.Record#create},
20827  * or an {@link Roo.data.Record} object
20828  * created using {@link Roo.data.Record#create}.
20829  */
20830 Roo.data.ArrayReader = function(meta, recordType){
20831     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20832 };
20833
20834 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20835     /**
20836      * Create a data block containing Roo.data.Records from an XML document.
20837      * @param {Object} o An Array of row objects which represents the dataset.
20838      * @return {Object} data A data block which is used by an Roo.data.Store object as
20839      * a cache of Roo.data.Records.
20840      */
20841     readRecords : function(o){
20842         var sid = this.meta ? this.meta.id : null;
20843         var recordType = this.recordType, fields = recordType.prototype.fields;
20844         var records = [];
20845         var root = o;
20846             for(var i = 0; i < root.length; i++){
20847                     var n = root[i];
20848                 var values = {};
20849                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20850                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20851                 var f = fields.items[j];
20852                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20853                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20854                 v = f.convert(v);
20855                 values[f.name] = v;
20856             }
20857                 var record = new recordType(values, id);
20858                 record.json = n;
20859                 records[records.length] = record;
20860             }
20861             return {
20862                 records : records,
20863                 totalRecords : records.length
20864             };
20865     }
20866 });/*
20867  * Based on:
20868  * Ext JS Library 1.1.1
20869  * Copyright(c) 2006-2007, Ext JS, LLC.
20870  *
20871  * Originally Released Under LGPL - original licence link has changed is not relivant.
20872  *
20873  * Fork - LGPL
20874  * <script type="text/javascript">
20875  */
20876
20877
20878 /**
20879  * @class Roo.data.Tree
20880  * @extends Roo.util.Observable
20881  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20882  * in the tree have most standard DOM functionality.
20883  * @constructor
20884  * @param {Node} root (optional) The root node
20885  */
20886 Roo.data.Tree = function(root){
20887    this.nodeHash = {};
20888    /**
20889     * The root node for this tree
20890     * @type Node
20891     */
20892    this.root = null;
20893    if(root){
20894        this.setRootNode(root);
20895    }
20896    this.addEvents({
20897        /**
20898         * @event append
20899         * Fires when a new child node is appended to a node in this tree.
20900         * @param {Tree} tree The owner tree
20901         * @param {Node} parent The parent node
20902         * @param {Node} node The newly appended node
20903         * @param {Number} index The index of the newly appended node
20904         */
20905        "append" : true,
20906        /**
20907         * @event remove
20908         * Fires when a child node is removed from a node in this tree.
20909         * @param {Tree} tree The owner tree
20910         * @param {Node} parent The parent node
20911         * @param {Node} node The child node removed
20912         */
20913        "remove" : true,
20914        /**
20915         * @event move
20916         * Fires when a node is moved to a new location in the tree
20917         * @param {Tree} tree The owner tree
20918         * @param {Node} node The node moved
20919         * @param {Node} oldParent The old parent of this node
20920         * @param {Node} newParent The new parent of this node
20921         * @param {Number} index The index it was moved to
20922         */
20923        "move" : true,
20924        /**
20925         * @event insert
20926         * Fires when a new child node is inserted in a node in this tree.
20927         * @param {Tree} tree The owner tree
20928         * @param {Node} parent The parent node
20929         * @param {Node} node The child node inserted
20930         * @param {Node} refNode The child node the node was inserted before
20931         */
20932        "insert" : true,
20933        /**
20934         * @event beforeappend
20935         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20936         * @param {Tree} tree The owner tree
20937         * @param {Node} parent The parent node
20938         * @param {Node} node The child node to be appended
20939         */
20940        "beforeappend" : true,
20941        /**
20942         * @event beforeremove
20943         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20944         * @param {Tree} tree The owner tree
20945         * @param {Node} parent The parent node
20946         * @param {Node} node The child node to be removed
20947         */
20948        "beforeremove" : true,
20949        /**
20950         * @event beforemove
20951         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20952         * @param {Tree} tree The owner tree
20953         * @param {Node} node The node being moved
20954         * @param {Node} oldParent The parent of the node
20955         * @param {Node} newParent The new parent the node is moving to
20956         * @param {Number} index The index it is being moved to
20957         */
20958        "beforemove" : true,
20959        /**
20960         * @event beforeinsert
20961         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20962         * @param {Tree} tree The owner tree
20963         * @param {Node} parent The parent node
20964         * @param {Node} node The child node to be inserted
20965         * @param {Node} refNode The child node the node is being inserted before
20966         */
20967        "beforeinsert" : true
20968    });
20969
20970     Roo.data.Tree.superclass.constructor.call(this);
20971 };
20972
20973 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20974     pathSeparator: "/",
20975
20976     proxyNodeEvent : function(){
20977         return this.fireEvent.apply(this, arguments);
20978     },
20979
20980     /**
20981      * Returns the root node for this tree.
20982      * @return {Node}
20983      */
20984     getRootNode : function(){
20985         return this.root;
20986     },
20987
20988     /**
20989      * Sets the root node for this tree.
20990      * @param {Node} node
20991      * @return {Node}
20992      */
20993     setRootNode : function(node){
20994         this.root = node;
20995         node.ownerTree = this;
20996         node.isRoot = true;
20997         this.registerNode(node);
20998         return node;
20999     },
21000
21001     /**
21002      * Gets a node in this tree by its id.
21003      * @param {String} id
21004      * @return {Node}
21005      */
21006     getNodeById : function(id){
21007         return this.nodeHash[id];
21008     },
21009
21010     registerNode : function(node){
21011         this.nodeHash[node.id] = node;
21012     },
21013
21014     unregisterNode : function(node){
21015         delete this.nodeHash[node.id];
21016     },
21017
21018     toString : function(){
21019         return "[Tree"+(this.id?" "+this.id:"")+"]";
21020     }
21021 });
21022
21023 /**
21024  * @class Roo.data.Node
21025  * @extends Roo.util.Observable
21026  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21027  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21028  * @constructor
21029  * @param {Object} attributes The attributes/config for the node
21030  */
21031 Roo.data.Node = function(attributes){
21032     /**
21033      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21034      * @type {Object}
21035      */
21036     this.attributes = attributes || {};
21037     this.leaf = this.attributes.leaf;
21038     /**
21039      * The node id. @type String
21040      */
21041     this.id = this.attributes.id;
21042     if(!this.id){
21043         this.id = Roo.id(null, "ynode-");
21044         this.attributes.id = this.id;
21045     }
21046     /**
21047      * All child nodes of this node. @type Array
21048      */
21049     this.childNodes = [];
21050     if(!this.childNodes.indexOf){ // indexOf is a must
21051         this.childNodes.indexOf = function(o){
21052             for(var i = 0, len = this.length; i < len; i++){
21053                 if(this[i] == o) {
21054                     return i;
21055                 }
21056             }
21057             return -1;
21058         };
21059     }
21060     /**
21061      * The parent node for this node. @type Node
21062      */
21063     this.parentNode = null;
21064     /**
21065      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21066      */
21067     this.firstChild = null;
21068     /**
21069      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21070      */
21071     this.lastChild = null;
21072     /**
21073      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21074      */
21075     this.previousSibling = null;
21076     /**
21077      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21078      */
21079     this.nextSibling = null;
21080
21081     this.addEvents({
21082        /**
21083         * @event append
21084         * Fires when a new child node is appended
21085         * @param {Tree} tree The owner tree
21086         * @param {Node} this This node
21087         * @param {Node} node The newly appended node
21088         * @param {Number} index The index of the newly appended node
21089         */
21090        "append" : true,
21091        /**
21092         * @event remove
21093         * Fires when a child node is removed
21094         * @param {Tree} tree The owner tree
21095         * @param {Node} this This node
21096         * @param {Node} node The removed node
21097         */
21098        "remove" : true,
21099        /**
21100         * @event move
21101         * Fires when this node is moved to a new location in the tree
21102         * @param {Tree} tree The owner tree
21103         * @param {Node} this This node
21104         * @param {Node} oldParent The old parent of this node
21105         * @param {Node} newParent The new parent of this node
21106         * @param {Number} index The index it was moved to
21107         */
21108        "move" : true,
21109        /**
21110         * @event insert
21111         * Fires when a new child node is inserted.
21112         * @param {Tree} tree The owner tree
21113         * @param {Node} this This node
21114         * @param {Node} node The child node inserted
21115         * @param {Node} refNode The child node the node was inserted before
21116         */
21117        "insert" : true,
21118        /**
21119         * @event beforeappend
21120         * Fires before a new child is appended, return false to cancel the append.
21121         * @param {Tree} tree The owner tree
21122         * @param {Node} this This node
21123         * @param {Node} node The child node to be appended
21124         */
21125        "beforeappend" : true,
21126        /**
21127         * @event beforeremove
21128         * Fires before a child is removed, return false to cancel the remove.
21129         * @param {Tree} tree The owner tree
21130         * @param {Node} this This node
21131         * @param {Node} node The child node to be removed
21132         */
21133        "beforeremove" : true,
21134        /**
21135         * @event beforemove
21136         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21137         * @param {Tree} tree The owner tree
21138         * @param {Node} this This node
21139         * @param {Node} oldParent The parent of this node
21140         * @param {Node} newParent The new parent this node is moving to
21141         * @param {Number} index The index it is being moved to
21142         */
21143        "beforemove" : true,
21144        /**
21145         * @event beforeinsert
21146         * Fires before a new child is inserted, return false to cancel the insert.
21147         * @param {Tree} tree The owner tree
21148         * @param {Node} this This node
21149         * @param {Node} node The child node to be inserted
21150         * @param {Node} refNode The child node the node is being inserted before
21151         */
21152        "beforeinsert" : true
21153    });
21154     this.listeners = this.attributes.listeners;
21155     Roo.data.Node.superclass.constructor.call(this);
21156 };
21157
21158 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21159     fireEvent : function(evtName){
21160         // first do standard event for this node
21161         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21162             return false;
21163         }
21164         // then bubble it up to the tree if the event wasn't cancelled
21165         var ot = this.getOwnerTree();
21166         if(ot){
21167             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21168                 return false;
21169             }
21170         }
21171         return true;
21172     },
21173
21174     /**
21175      * Returns true if this node is a leaf
21176      * @return {Boolean}
21177      */
21178     isLeaf : function(){
21179         return this.leaf === true;
21180     },
21181
21182     // private
21183     setFirstChild : function(node){
21184         this.firstChild = node;
21185     },
21186
21187     //private
21188     setLastChild : function(node){
21189         this.lastChild = node;
21190     },
21191
21192
21193     /**
21194      * Returns true if this node is the last child of its parent
21195      * @return {Boolean}
21196      */
21197     isLast : function(){
21198        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21199     },
21200
21201     /**
21202      * Returns true if this node is the first child of its parent
21203      * @return {Boolean}
21204      */
21205     isFirst : function(){
21206        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21207     },
21208
21209     hasChildNodes : function(){
21210         return !this.isLeaf() && this.childNodes.length > 0;
21211     },
21212
21213     /**
21214      * Insert node(s) as the last child node of this node.
21215      * @param {Node/Array} node The node or Array of nodes to append
21216      * @return {Node} The appended node if single append, or null if an array was passed
21217      */
21218     appendChild : function(node){
21219         var multi = false;
21220         if(node instanceof Array){
21221             multi = node;
21222         }else if(arguments.length > 1){
21223             multi = arguments;
21224         }
21225         // if passed an array or multiple args do them one by one
21226         if(multi){
21227             for(var i = 0, len = multi.length; i < len; i++) {
21228                 this.appendChild(multi[i]);
21229             }
21230         }else{
21231             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21232                 return false;
21233             }
21234             var index = this.childNodes.length;
21235             var oldParent = node.parentNode;
21236             // it's a move, make sure we move it cleanly
21237             if(oldParent){
21238                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21239                     return false;
21240                 }
21241                 oldParent.removeChild(node);
21242             }
21243             index = this.childNodes.length;
21244             if(index == 0){
21245                 this.setFirstChild(node);
21246             }
21247             this.childNodes.push(node);
21248             node.parentNode = this;
21249             var ps = this.childNodes[index-1];
21250             if(ps){
21251                 node.previousSibling = ps;
21252                 ps.nextSibling = node;
21253             }else{
21254                 node.previousSibling = null;
21255             }
21256             node.nextSibling = null;
21257             this.setLastChild(node);
21258             node.setOwnerTree(this.getOwnerTree());
21259             this.fireEvent("append", this.ownerTree, this, node, index);
21260             if(oldParent){
21261                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21262             }
21263             return node;
21264         }
21265     },
21266
21267     /**
21268      * Removes a child node from this node.
21269      * @param {Node} node The node to remove
21270      * @return {Node} The removed node
21271      */
21272     removeChild : function(node){
21273         var index = this.childNodes.indexOf(node);
21274         if(index == -1){
21275             return false;
21276         }
21277         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21278             return false;
21279         }
21280
21281         // remove it from childNodes collection
21282         this.childNodes.splice(index, 1);
21283
21284         // update siblings
21285         if(node.previousSibling){
21286             node.previousSibling.nextSibling = node.nextSibling;
21287         }
21288         if(node.nextSibling){
21289             node.nextSibling.previousSibling = node.previousSibling;
21290         }
21291
21292         // update child refs
21293         if(this.firstChild == node){
21294             this.setFirstChild(node.nextSibling);
21295         }
21296         if(this.lastChild == node){
21297             this.setLastChild(node.previousSibling);
21298         }
21299
21300         node.setOwnerTree(null);
21301         // clear any references from the node
21302         node.parentNode = null;
21303         node.previousSibling = null;
21304         node.nextSibling = null;
21305         this.fireEvent("remove", this.ownerTree, this, node);
21306         return node;
21307     },
21308
21309     /**
21310      * Inserts the first node before the second node in this nodes childNodes collection.
21311      * @param {Node} node The node to insert
21312      * @param {Node} refNode The node to insert before (if null the node is appended)
21313      * @return {Node} The inserted node
21314      */
21315     insertBefore : function(node, refNode){
21316         if(!refNode){ // like standard Dom, refNode can be null for append
21317             return this.appendChild(node);
21318         }
21319         // nothing to do
21320         if(node == refNode){
21321             return false;
21322         }
21323
21324         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21325             return false;
21326         }
21327         var index = this.childNodes.indexOf(refNode);
21328         var oldParent = node.parentNode;
21329         var refIndex = index;
21330
21331         // when moving internally, indexes will change after remove
21332         if(oldParent == this && this.childNodes.indexOf(node) < index){
21333             refIndex--;
21334         }
21335
21336         // it's a move, make sure we move it cleanly
21337         if(oldParent){
21338             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21339                 return false;
21340             }
21341             oldParent.removeChild(node);
21342         }
21343         if(refIndex == 0){
21344             this.setFirstChild(node);
21345         }
21346         this.childNodes.splice(refIndex, 0, node);
21347         node.parentNode = this;
21348         var ps = this.childNodes[refIndex-1];
21349         if(ps){
21350             node.previousSibling = ps;
21351             ps.nextSibling = node;
21352         }else{
21353             node.previousSibling = null;
21354         }
21355         node.nextSibling = refNode;
21356         refNode.previousSibling = node;
21357         node.setOwnerTree(this.getOwnerTree());
21358         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21359         if(oldParent){
21360             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21361         }
21362         return node;
21363     },
21364
21365     /**
21366      * Returns the child node at the specified index.
21367      * @param {Number} index
21368      * @return {Node}
21369      */
21370     item : function(index){
21371         return this.childNodes[index];
21372     },
21373
21374     /**
21375      * Replaces one child node in this node with another.
21376      * @param {Node} newChild The replacement node
21377      * @param {Node} oldChild The node to replace
21378      * @return {Node} The replaced node
21379      */
21380     replaceChild : function(newChild, oldChild){
21381         this.insertBefore(newChild, oldChild);
21382         this.removeChild(oldChild);
21383         return oldChild;
21384     },
21385
21386     /**
21387      * Returns the index of a child node
21388      * @param {Node} node
21389      * @return {Number} The index of the node or -1 if it was not found
21390      */
21391     indexOf : function(child){
21392         return this.childNodes.indexOf(child);
21393     },
21394
21395     /**
21396      * Returns the tree this node is in.
21397      * @return {Tree}
21398      */
21399     getOwnerTree : function(){
21400         // if it doesn't have one, look for one
21401         if(!this.ownerTree){
21402             var p = this;
21403             while(p){
21404                 if(p.ownerTree){
21405                     this.ownerTree = p.ownerTree;
21406                     break;
21407                 }
21408                 p = p.parentNode;
21409             }
21410         }
21411         return this.ownerTree;
21412     },
21413
21414     /**
21415      * Returns depth of this node (the root node has a depth of 0)
21416      * @return {Number}
21417      */
21418     getDepth : function(){
21419         var depth = 0;
21420         var p = this;
21421         while(p.parentNode){
21422             ++depth;
21423             p = p.parentNode;
21424         }
21425         return depth;
21426     },
21427
21428     // private
21429     setOwnerTree : function(tree){
21430         // if it's move, we need to update everyone
21431         if(tree != this.ownerTree){
21432             if(this.ownerTree){
21433                 this.ownerTree.unregisterNode(this);
21434             }
21435             this.ownerTree = tree;
21436             var cs = this.childNodes;
21437             for(var i = 0, len = cs.length; i < len; i++) {
21438                 cs[i].setOwnerTree(tree);
21439             }
21440             if(tree){
21441                 tree.registerNode(this);
21442             }
21443         }
21444     },
21445
21446     /**
21447      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21448      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21449      * @return {String} The path
21450      */
21451     getPath : function(attr){
21452         attr = attr || "id";
21453         var p = this.parentNode;
21454         var b = [this.attributes[attr]];
21455         while(p){
21456             b.unshift(p.attributes[attr]);
21457             p = p.parentNode;
21458         }
21459         var sep = this.getOwnerTree().pathSeparator;
21460         return sep + b.join(sep);
21461     },
21462
21463     /**
21464      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21465      * function call will be the scope provided or the current node. The arguments to the function
21466      * will be the args provided or the current node. If the function returns false at any point,
21467      * the bubble is stopped.
21468      * @param {Function} fn The function to call
21469      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21470      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21471      */
21472     bubble : function(fn, scope, args){
21473         var p = this;
21474         while(p){
21475             if(fn.call(scope || p, args || p) === false){
21476                 break;
21477             }
21478             p = p.parentNode;
21479         }
21480     },
21481
21482     /**
21483      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21484      * function call will be the scope provided or the current node. The arguments to the function
21485      * will be the args provided or the current node. If the function returns false at any point,
21486      * the cascade is stopped on that branch.
21487      * @param {Function} fn The function to call
21488      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21489      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21490      */
21491     cascade : function(fn, scope, args){
21492         if(fn.call(scope || this, args || this) !== false){
21493             var cs = this.childNodes;
21494             for(var i = 0, len = cs.length; i < len; i++) {
21495                 cs[i].cascade(fn, scope, args);
21496             }
21497         }
21498     },
21499
21500     /**
21501      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21502      * function call will be the scope provided or the current node. The arguments to the function
21503      * will be the args provided or the current node. If the function returns false at any point,
21504      * the iteration stops.
21505      * @param {Function} fn The function to call
21506      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21507      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21508      */
21509     eachChild : function(fn, scope, args){
21510         var cs = this.childNodes;
21511         for(var i = 0, len = cs.length; i < len; i++) {
21512                 if(fn.call(scope || this, args || cs[i]) === false){
21513                     break;
21514                 }
21515         }
21516     },
21517
21518     /**
21519      * Finds the first child that has the attribute with the specified value.
21520      * @param {String} attribute The attribute name
21521      * @param {Mixed} value The value to search for
21522      * @return {Node} The found child or null if none was found
21523      */
21524     findChild : function(attribute, value){
21525         var cs = this.childNodes;
21526         for(var i = 0, len = cs.length; i < len; i++) {
21527                 if(cs[i].attributes[attribute] == value){
21528                     return cs[i];
21529                 }
21530         }
21531         return null;
21532     },
21533
21534     /**
21535      * Finds the first child by a custom function. The child matches if the function passed
21536      * returns true.
21537      * @param {Function} fn
21538      * @param {Object} scope (optional)
21539      * @return {Node} The found child or null if none was found
21540      */
21541     findChildBy : function(fn, scope){
21542         var cs = this.childNodes;
21543         for(var i = 0, len = cs.length; i < len; i++) {
21544                 if(fn.call(scope||cs[i], cs[i]) === true){
21545                     return cs[i];
21546                 }
21547         }
21548         return null;
21549     },
21550
21551     /**
21552      * Sorts this nodes children using the supplied sort function
21553      * @param {Function} fn
21554      * @param {Object} scope (optional)
21555      */
21556     sort : function(fn, scope){
21557         var cs = this.childNodes;
21558         var len = cs.length;
21559         if(len > 0){
21560             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21561             cs.sort(sortFn);
21562             for(var i = 0; i < len; i++){
21563                 var n = cs[i];
21564                 n.previousSibling = cs[i-1];
21565                 n.nextSibling = cs[i+1];
21566                 if(i == 0){
21567                     this.setFirstChild(n);
21568                 }
21569                 if(i == len-1){
21570                     this.setLastChild(n);
21571                 }
21572             }
21573         }
21574     },
21575
21576     /**
21577      * Returns true if this node is an ancestor (at any point) of the passed node.
21578      * @param {Node} node
21579      * @return {Boolean}
21580      */
21581     contains : function(node){
21582         return node.isAncestor(this);
21583     },
21584
21585     /**
21586      * Returns true if the passed node is an ancestor (at any point) of this node.
21587      * @param {Node} node
21588      * @return {Boolean}
21589      */
21590     isAncestor : function(node){
21591         var p = this.parentNode;
21592         while(p){
21593             if(p == node){
21594                 return true;
21595             }
21596             p = p.parentNode;
21597         }
21598         return false;
21599     },
21600
21601     toString : function(){
21602         return "[Node"+(this.id?" "+this.id:"")+"]";
21603     }
21604 });/*
21605  * Based on:
21606  * Ext JS Library 1.1.1
21607  * Copyright(c) 2006-2007, Ext JS, LLC.
21608  *
21609  * Originally Released Under LGPL - original licence link has changed is not relivant.
21610  *
21611  * Fork - LGPL
21612  * <script type="text/javascript">
21613  */
21614  
21615
21616 /**
21617  * @class Roo.ComponentMgr
21618  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21619  * @singleton
21620  */
21621 Roo.ComponentMgr = function(){
21622     var all = new Roo.util.MixedCollection();
21623
21624     return {
21625         /**
21626          * Registers a component.
21627          * @param {Roo.Component} c The component
21628          */
21629         register : function(c){
21630             all.add(c);
21631         },
21632
21633         /**
21634          * Unregisters a component.
21635          * @param {Roo.Component} c The component
21636          */
21637         unregister : function(c){
21638             all.remove(c);
21639         },
21640
21641         /**
21642          * Returns a component by id
21643          * @param {String} id The component id
21644          */
21645         get : function(id){
21646             return all.get(id);
21647         },
21648
21649         /**
21650          * Registers a function that will be called when a specified component is added to ComponentMgr
21651          * @param {String} id The component id
21652          * @param {Funtction} fn The callback function
21653          * @param {Object} scope The scope of the callback
21654          */
21655         onAvailable : function(id, fn, scope){
21656             all.on("add", function(index, o){
21657                 if(o.id == id){
21658                     fn.call(scope || o, o);
21659                     all.un("add", fn, scope);
21660                 }
21661             });
21662         }
21663     };
21664 }();/*
21665  * Based on:
21666  * Ext JS Library 1.1.1
21667  * Copyright(c) 2006-2007, Ext JS, LLC.
21668  *
21669  * Originally Released Under LGPL - original licence link has changed is not relivant.
21670  *
21671  * Fork - LGPL
21672  * <script type="text/javascript">
21673  */
21674  
21675 /**
21676  * @class Roo.Component
21677  * @extends Roo.util.Observable
21678  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21679  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21680  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21681  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21682  * All visual components (widgets) that require rendering into a layout should subclass Component.
21683  * @constructor
21684  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21685  * 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
21686  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21687  */
21688 Roo.Component = function(config){
21689     config = config || {};
21690     if(config.tagName || config.dom || typeof config == "string"){ // element object
21691         config = {el: config, id: config.id || config};
21692     }
21693     this.initialConfig = config;
21694
21695     Roo.apply(this, config);
21696     this.addEvents({
21697         /**
21698          * @event disable
21699          * Fires after the component is disabled.
21700              * @param {Roo.Component} this
21701              */
21702         disable : true,
21703         /**
21704          * @event enable
21705          * Fires after the component is enabled.
21706              * @param {Roo.Component} this
21707              */
21708         enable : true,
21709         /**
21710          * @event beforeshow
21711          * Fires before the component is shown.  Return false to stop the show.
21712              * @param {Roo.Component} this
21713              */
21714         beforeshow : true,
21715         /**
21716          * @event show
21717          * Fires after the component is shown.
21718              * @param {Roo.Component} this
21719              */
21720         show : true,
21721         /**
21722          * @event beforehide
21723          * Fires before the component is hidden. Return false to stop the hide.
21724              * @param {Roo.Component} this
21725              */
21726         beforehide : true,
21727         /**
21728          * @event hide
21729          * Fires after the component is hidden.
21730              * @param {Roo.Component} this
21731              */
21732         hide : true,
21733         /**
21734          * @event beforerender
21735          * Fires before the component is rendered. Return false to stop the render.
21736              * @param {Roo.Component} this
21737              */
21738         beforerender : true,
21739         /**
21740          * @event render
21741          * Fires after the component is rendered.
21742              * @param {Roo.Component} this
21743              */
21744         render : true,
21745         /**
21746          * @event beforedestroy
21747          * Fires before the component is destroyed. Return false to stop the destroy.
21748              * @param {Roo.Component} this
21749              */
21750         beforedestroy : true,
21751         /**
21752          * @event destroy
21753          * Fires after the component is destroyed.
21754              * @param {Roo.Component} this
21755              */
21756         destroy : true
21757     });
21758     if(!this.id){
21759         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21760     }
21761     Roo.ComponentMgr.register(this);
21762     Roo.Component.superclass.constructor.call(this);
21763     this.initComponent();
21764     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21765         this.render(this.renderTo);
21766         delete this.renderTo;
21767     }
21768 };
21769
21770 // private
21771 Roo.Component.AUTO_ID = 1000;
21772
21773 Roo.extend(Roo.Component, Roo.util.Observable, {
21774     /**
21775      * @property {Boolean} hidden
21776      * true if this component is hidden. Read-only.
21777      */
21778     hidden : false,
21779     /**
21780      * true if this component is disabled. Read-only.
21781      */
21782     disabled : false,
21783     /**
21784      * true if this component has been rendered. Read-only.
21785      */
21786     rendered : false,
21787     
21788     /** @cfg {String} disableClass
21789      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21790      */
21791     disabledClass : "x-item-disabled",
21792         /** @cfg {Boolean} allowDomMove
21793          * Whether the component can move the Dom node when rendering (defaults to true).
21794          */
21795     allowDomMove : true,
21796     /** @cfg {String} hideMode
21797      * How this component should hidden. Supported values are
21798      * "visibility" (css visibility), "offsets" (negative offset position) and
21799      * "display" (css display) - defaults to "display".
21800      */
21801     hideMode: 'display',
21802
21803     // private
21804     ctype : "Roo.Component",
21805
21806     /** @cfg {String} actionMode 
21807      * which property holds the element that used for  hide() / show() / disable() / enable()
21808      * default is 'el' 
21809      */
21810     actionMode : "el",
21811
21812     // private
21813     getActionEl : function(){
21814         return this[this.actionMode];
21815     },
21816
21817     initComponent : Roo.emptyFn,
21818     /**
21819      * If this is a lazy rendering component, render it to its container element.
21820      * @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.
21821      */
21822     render : function(container, position){
21823         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21824             if(!container && this.el){
21825                 this.el = Roo.get(this.el);
21826                 container = this.el.dom.parentNode;
21827                 this.allowDomMove = false;
21828             }
21829             this.container = Roo.get(container);
21830             this.rendered = true;
21831             if(position !== undefined){
21832                 if(typeof position == 'number'){
21833                     position = this.container.dom.childNodes[position];
21834                 }else{
21835                     position = Roo.getDom(position);
21836                 }
21837             }
21838             this.onRender(this.container, position || null);
21839             if(this.cls){
21840                 this.el.addClass(this.cls);
21841                 delete this.cls;
21842             }
21843             if(this.style){
21844                 this.el.applyStyles(this.style);
21845                 delete this.style;
21846             }
21847             this.fireEvent("render", this);
21848             this.afterRender(this.container);
21849             if(this.hidden){
21850                 this.hide();
21851             }
21852             if(this.disabled){
21853                 this.disable();
21854             }
21855         }
21856         return this;
21857     },
21858
21859     // private
21860     // default function is not really useful
21861     onRender : function(ct, position){
21862         if(this.el){
21863             this.el = Roo.get(this.el);
21864             if(this.allowDomMove !== false){
21865                 ct.dom.insertBefore(this.el.dom, position);
21866             }
21867         }
21868     },
21869
21870     // private
21871     getAutoCreate : function(){
21872         var cfg = typeof this.autoCreate == "object" ?
21873                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21874         if(this.id && !cfg.id){
21875             cfg.id = this.id;
21876         }
21877         return cfg;
21878     },
21879
21880     // private
21881     afterRender : Roo.emptyFn,
21882
21883     /**
21884      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21885      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21886      */
21887     destroy : function(){
21888         if(this.fireEvent("beforedestroy", this) !== false){
21889             this.purgeListeners();
21890             this.beforeDestroy();
21891             if(this.rendered){
21892                 this.el.removeAllListeners();
21893                 this.el.remove();
21894                 if(this.actionMode == "container"){
21895                     this.container.remove();
21896                 }
21897             }
21898             this.onDestroy();
21899             Roo.ComponentMgr.unregister(this);
21900             this.fireEvent("destroy", this);
21901         }
21902     },
21903
21904         // private
21905     beforeDestroy : function(){
21906
21907     },
21908
21909         // private
21910         onDestroy : function(){
21911
21912     },
21913
21914     /**
21915      * Returns the underlying {@link Roo.Element}.
21916      * @return {Roo.Element} The element
21917      */
21918     getEl : function(){
21919         return this.el;
21920     },
21921
21922     /**
21923      * Returns the id of this component.
21924      * @return {String}
21925      */
21926     getId : function(){
21927         return this.id;
21928     },
21929
21930     /**
21931      * Try to focus this component.
21932      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21933      * @return {Roo.Component} this
21934      */
21935     focus : function(selectText){
21936         if(this.rendered){
21937             this.el.focus();
21938             if(selectText === true){
21939                 this.el.dom.select();
21940             }
21941         }
21942         return this;
21943     },
21944
21945     // private
21946     blur : function(){
21947         if(this.rendered){
21948             this.el.blur();
21949         }
21950         return this;
21951     },
21952
21953     /**
21954      * Disable this component.
21955      * @return {Roo.Component} this
21956      */
21957     disable : function(){
21958         if(this.rendered){
21959             this.onDisable();
21960         }
21961         this.disabled = true;
21962         this.fireEvent("disable", this);
21963         return this;
21964     },
21965
21966         // private
21967     onDisable : function(){
21968         this.getActionEl().addClass(this.disabledClass);
21969         this.el.dom.disabled = true;
21970     },
21971
21972     /**
21973      * Enable this component.
21974      * @return {Roo.Component} this
21975      */
21976     enable : function(){
21977         if(this.rendered){
21978             this.onEnable();
21979         }
21980         this.disabled = false;
21981         this.fireEvent("enable", this);
21982         return this;
21983     },
21984
21985         // private
21986     onEnable : function(){
21987         this.getActionEl().removeClass(this.disabledClass);
21988         this.el.dom.disabled = false;
21989     },
21990
21991     /**
21992      * Convenience function for setting disabled/enabled by boolean.
21993      * @param {Boolean} disabled
21994      */
21995     setDisabled : function(disabled){
21996         this[disabled ? "disable" : "enable"]();
21997     },
21998
21999     /**
22000      * Show this component.
22001      * @return {Roo.Component} this
22002      */
22003     show: function(){
22004         if(this.fireEvent("beforeshow", this) !== false){
22005             this.hidden = false;
22006             if(this.rendered){
22007                 this.onShow();
22008             }
22009             this.fireEvent("show", this);
22010         }
22011         return this;
22012     },
22013
22014     // private
22015     onShow : function(){
22016         var ae = this.getActionEl();
22017         if(this.hideMode == 'visibility'){
22018             ae.dom.style.visibility = "visible";
22019         }else if(this.hideMode == 'offsets'){
22020             ae.removeClass('x-hidden');
22021         }else{
22022             ae.dom.style.display = "";
22023         }
22024     },
22025
22026     /**
22027      * Hide this component.
22028      * @return {Roo.Component} this
22029      */
22030     hide: function(){
22031         if(this.fireEvent("beforehide", this) !== false){
22032             this.hidden = true;
22033             if(this.rendered){
22034                 this.onHide();
22035             }
22036             this.fireEvent("hide", this);
22037         }
22038         return this;
22039     },
22040
22041     // private
22042     onHide : function(){
22043         var ae = this.getActionEl();
22044         if(this.hideMode == 'visibility'){
22045             ae.dom.style.visibility = "hidden";
22046         }else if(this.hideMode == 'offsets'){
22047             ae.addClass('x-hidden');
22048         }else{
22049             ae.dom.style.display = "none";
22050         }
22051     },
22052
22053     /**
22054      * Convenience function to hide or show this component by boolean.
22055      * @param {Boolean} visible True to show, false to hide
22056      * @return {Roo.Component} this
22057      */
22058     setVisible: function(visible){
22059         if(visible) {
22060             this.show();
22061         }else{
22062             this.hide();
22063         }
22064         return this;
22065     },
22066
22067     /**
22068      * Returns true if this component is visible.
22069      */
22070     isVisible : function(){
22071         return this.getActionEl().isVisible();
22072     },
22073
22074     cloneConfig : function(overrides){
22075         overrides = overrides || {};
22076         var id = overrides.id || Roo.id();
22077         var cfg = Roo.applyIf(overrides, this.initialConfig);
22078         cfg.id = id; // prevent dup id
22079         return new this.constructor(cfg);
22080     }
22081 });/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091  (function(){ 
22092 /**
22093  * @class Roo.Layer
22094  * @extends Roo.Element
22095  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22096  * automatic maintaining of shadow/shim positions.
22097  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22098  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22099  * you can pass a string with a CSS class name. False turns off the shadow.
22100  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22101  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22102  * @cfg {String} cls CSS class to add to the element
22103  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22104  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22105  * @constructor
22106  * @param {Object} config An object with config options.
22107  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22108  */
22109
22110 Roo.Layer = function(config, existingEl){
22111     config = config || {};
22112     var dh = Roo.DomHelper;
22113     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22114     if(existingEl){
22115         this.dom = Roo.getDom(existingEl);
22116     }
22117     if(!this.dom){
22118         var o = config.dh || {tag: "div", cls: "x-layer"};
22119         this.dom = dh.append(pel, o);
22120     }
22121     if(config.cls){
22122         this.addClass(config.cls);
22123     }
22124     this.constrain = config.constrain !== false;
22125     this.visibilityMode = Roo.Element.VISIBILITY;
22126     if(config.id){
22127         this.id = this.dom.id = config.id;
22128     }else{
22129         this.id = Roo.id(this.dom);
22130     }
22131     this.zindex = config.zindex || this.getZIndex();
22132     this.position("absolute", this.zindex);
22133     if(config.shadow){
22134         this.shadowOffset = config.shadowOffset || 4;
22135         this.shadow = new Roo.Shadow({
22136             offset : this.shadowOffset,
22137             mode : config.shadow
22138         });
22139     }else{
22140         this.shadowOffset = 0;
22141     }
22142     this.useShim = config.shim !== false && Roo.useShims;
22143     this.useDisplay = config.useDisplay;
22144     this.hide();
22145 };
22146
22147 var supr = Roo.Element.prototype;
22148
22149 // shims are shared among layer to keep from having 100 iframes
22150 var shims = [];
22151
22152 Roo.extend(Roo.Layer, Roo.Element, {
22153
22154     getZIndex : function(){
22155         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22156     },
22157
22158     getShim : function(){
22159         if(!this.useShim){
22160             return null;
22161         }
22162         if(this.shim){
22163             return this.shim;
22164         }
22165         var shim = shims.shift();
22166         if(!shim){
22167             shim = this.createShim();
22168             shim.enableDisplayMode('block');
22169             shim.dom.style.display = 'none';
22170             shim.dom.style.visibility = 'visible';
22171         }
22172         var pn = this.dom.parentNode;
22173         if(shim.dom.parentNode != pn){
22174             pn.insertBefore(shim.dom, this.dom);
22175         }
22176         shim.setStyle('z-index', this.getZIndex()-2);
22177         this.shim = shim;
22178         return shim;
22179     },
22180
22181     hideShim : function(){
22182         if(this.shim){
22183             this.shim.setDisplayed(false);
22184             shims.push(this.shim);
22185             delete this.shim;
22186         }
22187     },
22188
22189     disableShadow : function(){
22190         if(this.shadow){
22191             this.shadowDisabled = true;
22192             this.shadow.hide();
22193             this.lastShadowOffset = this.shadowOffset;
22194             this.shadowOffset = 0;
22195         }
22196     },
22197
22198     enableShadow : function(show){
22199         if(this.shadow){
22200             this.shadowDisabled = false;
22201             this.shadowOffset = this.lastShadowOffset;
22202             delete this.lastShadowOffset;
22203             if(show){
22204                 this.sync(true);
22205             }
22206         }
22207     },
22208
22209     // private
22210     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22211     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22212     sync : function(doShow){
22213         var sw = this.shadow;
22214         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22215             var sh = this.getShim();
22216
22217             var w = this.getWidth(),
22218                 h = this.getHeight();
22219
22220             var l = this.getLeft(true),
22221                 t = this.getTop(true);
22222
22223             if(sw && !this.shadowDisabled){
22224                 if(doShow && !sw.isVisible()){
22225                     sw.show(this);
22226                 }else{
22227                     sw.realign(l, t, w, h);
22228                 }
22229                 if(sh){
22230                     if(doShow){
22231                        sh.show();
22232                     }
22233                     // fit the shim behind the shadow, so it is shimmed too
22234                     var a = sw.adjusts, s = sh.dom.style;
22235                     s.left = (Math.min(l, l+a.l))+"px";
22236                     s.top = (Math.min(t, t+a.t))+"px";
22237                     s.width = (w+a.w)+"px";
22238                     s.height = (h+a.h)+"px";
22239                 }
22240             }else if(sh){
22241                 if(doShow){
22242                    sh.show();
22243                 }
22244                 sh.setSize(w, h);
22245                 sh.setLeftTop(l, t);
22246             }
22247             
22248         }
22249     },
22250
22251     // private
22252     destroy : function(){
22253         this.hideShim();
22254         if(this.shadow){
22255             this.shadow.hide();
22256         }
22257         this.removeAllListeners();
22258         var pn = this.dom.parentNode;
22259         if(pn){
22260             pn.removeChild(this.dom);
22261         }
22262         Roo.Element.uncache(this.id);
22263     },
22264
22265     remove : function(){
22266         this.destroy();
22267     },
22268
22269     // private
22270     beginUpdate : function(){
22271         this.updating = true;
22272     },
22273
22274     // private
22275     endUpdate : function(){
22276         this.updating = false;
22277         this.sync(true);
22278     },
22279
22280     // private
22281     hideUnders : function(negOffset){
22282         if(this.shadow){
22283             this.shadow.hide();
22284         }
22285         this.hideShim();
22286     },
22287
22288     // private
22289     constrainXY : function(){
22290         if(this.constrain){
22291             var vw = Roo.lib.Dom.getViewWidth(),
22292                 vh = Roo.lib.Dom.getViewHeight();
22293             var s = Roo.get(document).getScroll();
22294
22295             var xy = this.getXY();
22296             var x = xy[0], y = xy[1];   
22297             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22298             // only move it if it needs it
22299             var moved = false;
22300             // first validate right/bottom
22301             if((x + w) > vw+s.left){
22302                 x = vw - w - this.shadowOffset;
22303                 moved = true;
22304             }
22305             if((y + h) > vh+s.top){
22306                 y = vh - h - this.shadowOffset;
22307                 moved = true;
22308             }
22309             // then make sure top/left isn't negative
22310             if(x < s.left){
22311                 x = s.left;
22312                 moved = true;
22313             }
22314             if(y < s.top){
22315                 y = s.top;
22316                 moved = true;
22317             }
22318             if(moved){
22319                 if(this.avoidY){
22320                     var ay = this.avoidY;
22321                     if(y <= ay && (y+h) >= ay){
22322                         y = ay-h-5;   
22323                     }
22324                 }
22325                 xy = [x, y];
22326                 this.storeXY(xy);
22327                 supr.setXY.call(this, xy);
22328                 this.sync();
22329             }
22330         }
22331     },
22332
22333     isVisible : function(){
22334         return this.visible;    
22335     },
22336
22337     // private
22338     showAction : function(){
22339         this.visible = true; // track visibility to prevent getStyle calls
22340         if(this.useDisplay === true){
22341             this.setDisplayed("");
22342         }else if(this.lastXY){
22343             supr.setXY.call(this, this.lastXY);
22344         }else if(this.lastLT){
22345             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22346         }
22347     },
22348
22349     // private
22350     hideAction : function(){
22351         this.visible = false;
22352         if(this.useDisplay === true){
22353             this.setDisplayed(false);
22354         }else{
22355             this.setLeftTop(-10000,-10000);
22356         }
22357     },
22358
22359     // overridden Element method
22360     setVisible : function(v, a, d, c, e){
22361         if(v){
22362             this.showAction();
22363         }
22364         if(a && v){
22365             var cb = function(){
22366                 this.sync(true);
22367                 if(c){
22368                     c();
22369                 }
22370             }.createDelegate(this);
22371             supr.setVisible.call(this, true, true, d, cb, e);
22372         }else{
22373             if(!v){
22374                 this.hideUnders(true);
22375             }
22376             var cb = c;
22377             if(a){
22378                 cb = function(){
22379                     this.hideAction();
22380                     if(c){
22381                         c();
22382                     }
22383                 }.createDelegate(this);
22384             }
22385             supr.setVisible.call(this, v, a, d, cb, e);
22386             if(v){
22387                 this.sync(true);
22388             }else if(!a){
22389                 this.hideAction();
22390             }
22391         }
22392     },
22393
22394     storeXY : function(xy){
22395         delete this.lastLT;
22396         this.lastXY = xy;
22397     },
22398
22399     storeLeftTop : function(left, top){
22400         delete this.lastXY;
22401         this.lastLT = [left, top];
22402     },
22403
22404     // private
22405     beforeFx : function(){
22406         this.beforeAction();
22407         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22408     },
22409
22410     // private
22411     afterFx : function(){
22412         Roo.Layer.superclass.afterFx.apply(this, arguments);
22413         this.sync(this.isVisible());
22414     },
22415
22416     // private
22417     beforeAction : function(){
22418         if(!this.updating && this.shadow){
22419             this.shadow.hide();
22420         }
22421     },
22422
22423     // overridden Element method
22424     setLeft : function(left){
22425         this.storeLeftTop(left, this.getTop(true));
22426         supr.setLeft.apply(this, arguments);
22427         this.sync();
22428     },
22429
22430     setTop : function(top){
22431         this.storeLeftTop(this.getLeft(true), top);
22432         supr.setTop.apply(this, arguments);
22433         this.sync();
22434     },
22435
22436     setLeftTop : function(left, top){
22437         this.storeLeftTop(left, top);
22438         supr.setLeftTop.apply(this, arguments);
22439         this.sync();
22440     },
22441
22442     setXY : function(xy, a, d, c, e){
22443         this.fixDisplay();
22444         this.beforeAction();
22445         this.storeXY(xy);
22446         var cb = this.createCB(c);
22447         supr.setXY.call(this, xy, a, d, cb, e);
22448         if(!a){
22449             cb();
22450         }
22451     },
22452
22453     // private
22454     createCB : function(c){
22455         var el = this;
22456         return function(){
22457             el.constrainXY();
22458             el.sync(true);
22459             if(c){
22460                 c();
22461             }
22462         };
22463     },
22464
22465     // overridden Element method
22466     setX : function(x, a, d, c, e){
22467         this.setXY([x, this.getY()], a, d, c, e);
22468     },
22469
22470     // overridden Element method
22471     setY : function(y, a, d, c, e){
22472         this.setXY([this.getX(), y], a, d, c, e);
22473     },
22474
22475     // overridden Element method
22476     setSize : function(w, h, a, d, c, e){
22477         this.beforeAction();
22478         var cb = this.createCB(c);
22479         supr.setSize.call(this, w, h, a, d, cb, e);
22480         if(!a){
22481             cb();
22482         }
22483     },
22484
22485     // overridden Element method
22486     setWidth : function(w, a, d, c, e){
22487         this.beforeAction();
22488         var cb = this.createCB(c);
22489         supr.setWidth.call(this, w, a, d, cb, e);
22490         if(!a){
22491             cb();
22492         }
22493     },
22494
22495     // overridden Element method
22496     setHeight : function(h, a, d, c, e){
22497         this.beforeAction();
22498         var cb = this.createCB(c);
22499         supr.setHeight.call(this, h, a, d, cb, e);
22500         if(!a){
22501             cb();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setBounds : function(x, y, w, h, a, d, c, e){
22507         this.beforeAction();
22508         var cb = this.createCB(c);
22509         if(!a){
22510             this.storeXY([x, y]);
22511             supr.setXY.call(this, [x, y]);
22512             supr.setSize.call(this, w, h, a, d, cb, e);
22513             cb();
22514         }else{
22515             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22516         }
22517         return this;
22518     },
22519     
22520     /**
22521      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22522      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22523      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22524      * @param {Number} zindex The new z-index to set
22525      * @return {this} The Layer
22526      */
22527     setZIndex : function(zindex){
22528         this.zindex = zindex;
22529         this.setStyle("z-index", zindex + 2);
22530         if(this.shadow){
22531             this.shadow.setZIndex(zindex + 1);
22532         }
22533         if(this.shim){
22534             this.shim.setStyle("z-index", zindex);
22535         }
22536     }
22537 });
22538 })();/*
22539  * Based on:
22540  * Ext JS Library 1.1.1
22541  * Copyright(c) 2006-2007, Ext JS, LLC.
22542  *
22543  * Originally Released Under LGPL - original licence link has changed is not relivant.
22544  *
22545  * Fork - LGPL
22546  * <script type="text/javascript">
22547  */
22548
22549
22550 /**
22551  * @class Roo.Shadow
22552  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22553  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22554  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22555  * @constructor
22556  * Create a new Shadow
22557  * @param {Object} config The config object
22558  */
22559 Roo.Shadow = function(config){
22560     Roo.apply(this, config);
22561     if(typeof this.mode != "string"){
22562         this.mode = this.defaultMode;
22563     }
22564     var o = this.offset, a = {h: 0};
22565     var rad = Math.floor(this.offset/2);
22566     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22567         case "drop":
22568             a.w = 0;
22569             a.l = a.t = o;
22570             a.t -= 1;
22571             if(Roo.isIE){
22572                 a.l -= this.offset + rad;
22573                 a.t -= this.offset + rad;
22574                 a.w -= rad;
22575                 a.h -= rad;
22576                 a.t += 1;
22577             }
22578         break;
22579         case "sides":
22580             a.w = (o*2);
22581             a.l = -o;
22582             a.t = o-1;
22583             if(Roo.isIE){
22584                 a.l -= (this.offset - rad);
22585                 a.t -= this.offset + rad;
22586                 a.l += 1;
22587                 a.w -= (this.offset - rad)*2;
22588                 a.w -= rad + 1;
22589                 a.h -= 1;
22590             }
22591         break;
22592         case "frame":
22593             a.w = a.h = (o*2);
22594             a.l = a.t = -o;
22595             a.t += 1;
22596             a.h -= 2;
22597             if(Roo.isIE){
22598                 a.l -= (this.offset - rad);
22599                 a.t -= (this.offset - rad);
22600                 a.l += 1;
22601                 a.w -= (this.offset + rad + 1);
22602                 a.h -= (this.offset + rad);
22603                 a.h += 1;
22604             }
22605         break;
22606     };
22607
22608     this.adjusts = a;
22609 };
22610
22611 Roo.Shadow.prototype = {
22612     /**
22613      * @cfg {String} mode
22614      * The shadow display mode.  Supports the following options:<br />
22615      * sides: Shadow displays on both sides and bottom only<br />
22616      * frame: Shadow displays equally on all four sides<br />
22617      * drop: Traditional bottom-right drop shadow (default)
22618      */
22619     /**
22620      * @cfg {String} offset
22621      * The number of pixels to offset the shadow from the element (defaults to 4)
22622      */
22623     offset: 4,
22624
22625     // private
22626     defaultMode: "drop",
22627
22628     /**
22629      * Displays the shadow under the target element
22630      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22631      */
22632     show : function(target){
22633         target = Roo.get(target);
22634         if(!this.el){
22635             this.el = Roo.Shadow.Pool.pull();
22636             if(this.el.dom.nextSibling != target.dom){
22637                 this.el.insertBefore(target);
22638             }
22639         }
22640         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22641         if(Roo.isIE){
22642             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22643         }
22644         this.realign(
22645             target.getLeft(true),
22646             target.getTop(true),
22647             target.getWidth(),
22648             target.getHeight()
22649         );
22650         this.el.dom.style.display = "block";
22651     },
22652
22653     /**
22654      * Returns true if the shadow is visible, else false
22655      */
22656     isVisible : function(){
22657         return this.el ? true : false;  
22658     },
22659
22660     /**
22661      * Direct alignment when values are already available. Show must be called at least once before
22662      * calling this method to ensure it is initialized.
22663      * @param {Number} left The target element left position
22664      * @param {Number} top The target element top position
22665      * @param {Number} width The target element width
22666      * @param {Number} height The target element height
22667      */
22668     realign : function(l, t, w, h){
22669         if(!this.el){
22670             return;
22671         }
22672         var a = this.adjusts, d = this.el.dom, s = d.style;
22673         var iea = 0;
22674         s.left = (l+a.l)+"px";
22675         s.top = (t+a.t)+"px";
22676         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22677  
22678         if(s.width != sws || s.height != shs){
22679             s.width = sws;
22680             s.height = shs;
22681             if(!Roo.isIE){
22682                 var cn = d.childNodes;
22683                 var sww = Math.max(0, (sw-12))+"px";
22684                 cn[0].childNodes[1].style.width = sww;
22685                 cn[1].childNodes[1].style.width = sww;
22686                 cn[2].childNodes[1].style.width = sww;
22687                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22688             }
22689         }
22690     },
22691
22692     /**
22693      * Hides this shadow
22694      */
22695     hide : function(){
22696         if(this.el){
22697             this.el.dom.style.display = "none";
22698             Roo.Shadow.Pool.push(this.el);
22699             delete this.el;
22700         }
22701     },
22702
22703     /**
22704      * Adjust the z-index of this shadow
22705      * @param {Number} zindex The new z-index
22706      */
22707     setZIndex : function(z){
22708         this.zIndex = z;
22709         if(this.el){
22710             this.el.setStyle("z-index", z);
22711         }
22712     }
22713 };
22714
22715 // Private utility class that manages the internal Shadow cache
22716 Roo.Shadow.Pool = function(){
22717     var p = [];
22718     var markup = Roo.isIE ?
22719                  '<div class="x-ie-shadow"></div>' :
22720                  '<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>';
22721     return {
22722         pull : function(){
22723             var sh = p.shift();
22724             if(!sh){
22725                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22726                 sh.autoBoxAdjust = false;
22727             }
22728             return sh;
22729         },
22730
22731         push : function(sh){
22732             p.push(sh);
22733         }
22734     };
22735 }();/*
22736  * Based on:
22737  * Ext JS Library 1.1.1
22738  * Copyright(c) 2006-2007, Ext JS, LLC.
22739  *
22740  * Originally Released Under LGPL - original licence link has changed is not relivant.
22741  *
22742  * Fork - LGPL
22743  * <script type="text/javascript">
22744  */
22745
22746 /**
22747  * @class Roo.BoxComponent
22748  * @extends Roo.Component
22749  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22750  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22751  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22752  * layout containers.
22753  * @constructor
22754  * @param {Roo.Element/String/Object} config The configuration options.
22755  */
22756 Roo.BoxComponent = function(config){
22757     Roo.Component.call(this, config);
22758     this.addEvents({
22759         /**
22760          * @event resize
22761          * Fires after the component is resized.
22762              * @param {Roo.Component} this
22763              * @param {Number} adjWidth The box-adjusted width that was set
22764              * @param {Number} adjHeight The box-adjusted height that was set
22765              * @param {Number} rawWidth The width that was originally specified
22766              * @param {Number} rawHeight The height that was originally specified
22767              */
22768         resize : true,
22769         /**
22770          * @event move
22771          * Fires after the component is moved.
22772              * @param {Roo.Component} this
22773              * @param {Number} x The new x position
22774              * @param {Number} y The new y position
22775              */
22776         move : true
22777     });
22778 };
22779
22780 Roo.extend(Roo.BoxComponent, Roo.Component, {
22781     // private, set in afterRender to signify that the component has been rendered
22782     boxReady : false,
22783     // private, used to defer height settings to subclasses
22784     deferHeight: false,
22785     /** @cfg {Number} width
22786      * width (optional) size of component
22787      */
22788      /** @cfg {Number} height
22789      * height (optional) size of component
22790      */
22791      
22792     /**
22793      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22794      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22795      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22796      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22797      * @return {Roo.BoxComponent} this
22798      */
22799     setSize : function(w, h){
22800         // support for standard size objects
22801         if(typeof w == 'object'){
22802             h = w.height;
22803             w = w.width;
22804         }
22805         // not rendered
22806         if(!this.boxReady){
22807             this.width = w;
22808             this.height = h;
22809             return this;
22810         }
22811
22812         // prevent recalcs when not needed
22813         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22814             return this;
22815         }
22816         this.lastSize = {width: w, height: h};
22817
22818         var adj = this.adjustSize(w, h);
22819         var aw = adj.width, ah = adj.height;
22820         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22821             var rz = this.getResizeEl();
22822             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22823                 rz.setSize(aw, ah);
22824             }else if(!this.deferHeight && ah !== undefined){
22825                 rz.setHeight(ah);
22826             }else if(aw !== undefined){
22827                 rz.setWidth(aw);
22828             }
22829             this.onResize(aw, ah, w, h);
22830             this.fireEvent('resize', this, aw, ah, w, h);
22831         }
22832         return this;
22833     },
22834
22835     /**
22836      * Gets the current size of the component's underlying element.
22837      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22838      */
22839     getSize : function(){
22840         return this.el.getSize();
22841     },
22842
22843     /**
22844      * Gets the current XY position of the component's underlying element.
22845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22846      * @return {Array} The XY position of the element (e.g., [100, 200])
22847      */
22848     getPosition : function(local){
22849         if(local === true){
22850             return [this.el.getLeft(true), this.el.getTop(true)];
22851         }
22852         return this.xy || this.el.getXY();
22853     },
22854
22855     /**
22856      * Gets the current box measurements of the component's underlying element.
22857      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22858      * @returns {Object} box An object in the format {x, y, width, height}
22859      */
22860     getBox : function(local){
22861         var s = this.el.getSize();
22862         if(local){
22863             s.x = this.el.getLeft(true);
22864             s.y = this.el.getTop(true);
22865         }else{
22866             var xy = this.xy || this.el.getXY();
22867             s.x = xy[0];
22868             s.y = xy[1];
22869         }
22870         return s;
22871     },
22872
22873     /**
22874      * Sets the current box measurements of the component's underlying element.
22875      * @param {Object} box An object in the format {x, y, width, height}
22876      * @returns {Roo.BoxComponent} this
22877      */
22878     updateBox : function(box){
22879         this.setSize(box.width, box.height);
22880         this.setPagePosition(box.x, box.y);
22881         return this;
22882     },
22883
22884     // protected
22885     getResizeEl : function(){
22886         return this.resizeEl || this.el;
22887     },
22888
22889     // protected
22890     getPositionEl : function(){
22891         return this.positionEl || this.el;
22892     },
22893
22894     /**
22895      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22896      * This method fires the move event.
22897      * @param {Number} left The new left
22898      * @param {Number} top The new top
22899      * @returns {Roo.BoxComponent} this
22900      */
22901     setPosition : function(x, y){
22902         this.x = x;
22903         this.y = y;
22904         if(!this.boxReady){
22905             return this;
22906         }
22907         var adj = this.adjustPosition(x, y);
22908         var ax = adj.x, ay = adj.y;
22909
22910         var el = this.getPositionEl();
22911         if(ax !== undefined || ay !== undefined){
22912             if(ax !== undefined && ay !== undefined){
22913                 el.setLeftTop(ax, ay);
22914             }else if(ax !== undefined){
22915                 el.setLeft(ax);
22916             }else if(ay !== undefined){
22917                 el.setTop(ay);
22918             }
22919             this.onPosition(ax, ay);
22920             this.fireEvent('move', this, ax, ay);
22921         }
22922         return this;
22923     },
22924
22925     /**
22926      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22927      * This method fires the move event.
22928      * @param {Number} x The new x position
22929      * @param {Number} y The new y position
22930      * @returns {Roo.BoxComponent} this
22931      */
22932     setPagePosition : function(x, y){
22933         this.pageX = x;
22934         this.pageY = y;
22935         if(!this.boxReady){
22936             return;
22937         }
22938         if(x === undefined || y === undefined){ // cannot translate undefined points
22939             return;
22940         }
22941         var p = this.el.translatePoints(x, y);
22942         this.setPosition(p.left, p.top);
22943         return this;
22944     },
22945
22946     // private
22947     onRender : function(ct, position){
22948         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22949         if(this.resizeEl){
22950             this.resizeEl = Roo.get(this.resizeEl);
22951         }
22952         if(this.positionEl){
22953             this.positionEl = Roo.get(this.positionEl);
22954         }
22955     },
22956
22957     // private
22958     afterRender : function(){
22959         Roo.BoxComponent.superclass.afterRender.call(this);
22960         this.boxReady = true;
22961         this.setSize(this.width, this.height);
22962         if(this.x || this.y){
22963             this.setPosition(this.x, this.y);
22964         }
22965         if(this.pageX || this.pageY){
22966             this.setPagePosition(this.pageX, this.pageY);
22967         }
22968     },
22969
22970     /**
22971      * Force the component's size to recalculate based on the underlying element's current height and width.
22972      * @returns {Roo.BoxComponent} this
22973      */
22974     syncSize : function(){
22975         delete this.lastSize;
22976         this.setSize(this.el.getWidth(), this.el.getHeight());
22977         return this;
22978     },
22979
22980     /**
22981      * Called after the component is resized, this method is empty by default but can be implemented by any
22982      * subclass that needs to perform custom logic after a resize occurs.
22983      * @param {Number} adjWidth The box-adjusted width that was set
22984      * @param {Number} adjHeight The box-adjusted height that was set
22985      * @param {Number} rawWidth The width that was originally specified
22986      * @param {Number} rawHeight The height that was originally specified
22987      */
22988     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22989
22990     },
22991
22992     /**
22993      * Called after the component is moved, this method is empty by default but can be implemented by any
22994      * subclass that needs to perform custom logic after a move occurs.
22995      * @param {Number} x The new x position
22996      * @param {Number} y The new y position
22997      */
22998     onPosition : function(x, y){
22999
23000     },
23001
23002     // private
23003     adjustSize : function(w, h){
23004         if(this.autoWidth){
23005             w = 'auto';
23006         }
23007         if(this.autoHeight){
23008             h = 'auto';
23009         }
23010         return {width : w, height: h};
23011     },
23012
23013     // private
23014     adjustPosition : function(x, y){
23015         return {x : x, y: y};
23016     }
23017 });/*
23018  * Based on:
23019  * Ext JS Library 1.1.1
23020  * Copyright(c) 2006-2007, Ext JS, LLC.
23021  *
23022  * Originally Released Under LGPL - original licence link has changed is not relivant.
23023  *
23024  * Fork - LGPL
23025  * <script type="text/javascript">
23026  */
23027
23028
23029 /**
23030  * @class Roo.SplitBar
23031  * @extends Roo.util.Observable
23032  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23033  * <br><br>
23034  * Usage:
23035  * <pre><code>
23036 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23037                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23038 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23039 split.minSize = 100;
23040 split.maxSize = 600;
23041 split.animate = true;
23042 split.on('moved', splitterMoved);
23043 </code></pre>
23044  * @constructor
23045  * Create a new SplitBar
23046  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23047  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23048  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23049  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23050                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23051                         position of the SplitBar).
23052  */
23053 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23054     
23055     /** @private */
23056     this.el = Roo.get(dragElement, true);
23057     this.el.dom.unselectable = "on";
23058     /** @private */
23059     this.resizingEl = Roo.get(resizingElement, true);
23060
23061     /**
23062      * @private
23063      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23064      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23065      * @type Number
23066      */
23067     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23068     
23069     /**
23070      * The minimum size of the resizing element. (Defaults to 0)
23071      * @type Number
23072      */
23073     this.minSize = 0;
23074     
23075     /**
23076      * The maximum size of the resizing element. (Defaults to 2000)
23077      * @type Number
23078      */
23079     this.maxSize = 2000;
23080     
23081     /**
23082      * Whether to animate the transition to the new size
23083      * @type Boolean
23084      */
23085     this.animate = false;
23086     
23087     /**
23088      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23089      * @type Boolean
23090      */
23091     this.useShim = false;
23092     
23093     /** @private */
23094     this.shim = null;
23095     
23096     if(!existingProxy){
23097         /** @private */
23098         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23099     }else{
23100         this.proxy = Roo.get(existingProxy).dom;
23101     }
23102     /** @private */
23103     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23104     
23105     /** @private */
23106     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23107     
23108     /** @private */
23109     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23110     
23111     /** @private */
23112     this.dragSpecs = {};
23113     
23114     /**
23115      * @private The adapter to use to positon and resize elements
23116      */
23117     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23118     this.adapter.init(this);
23119     
23120     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23121         /** @private */
23122         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23123         this.el.addClass("x-splitbar-h");
23124     }else{
23125         /** @private */
23126         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23127         this.el.addClass("x-splitbar-v");
23128     }
23129     
23130     this.addEvents({
23131         /**
23132          * @event resize
23133          * Fires when the splitter is moved (alias for {@link #event-moved})
23134          * @param {Roo.SplitBar} this
23135          * @param {Number} newSize the new width or height
23136          */
23137         "resize" : true,
23138         /**
23139          * @event moved
23140          * Fires when the splitter is moved
23141          * @param {Roo.SplitBar} this
23142          * @param {Number} newSize the new width or height
23143          */
23144         "moved" : true,
23145         /**
23146          * @event beforeresize
23147          * Fires before the splitter is dragged
23148          * @param {Roo.SplitBar} this
23149          */
23150         "beforeresize" : true,
23151
23152         "beforeapply" : true
23153     });
23154
23155     Roo.util.Observable.call(this);
23156 };
23157
23158 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23159     onStartProxyDrag : function(x, y){
23160         this.fireEvent("beforeresize", this);
23161         if(!this.overlay){
23162             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23163             o.unselectable();
23164             o.enableDisplayMode("block");
23165             // all splitbars share the same overlay
23166             Roo.SplitBar.prototype.overlay = o;
23167         }
23168         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23169         this.overlay.show();
23170         Roo.get(this.proxy).setDisplayed("block");
23171         var size = this.adapter.getElementSize(this);
23172         this.activeMinSize = this.getMinimumSize();;
23173         this.activeMaxSize = this.getMaximumSize();;
23174         var c1 = size - this.activeMinSize;
23175         var c2 = Math.max(this.activeMaxSize - size, 0);
23176         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23177             this.dd.resetConstraints();
23178             this.dd.setXConstraint(
23179                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23180                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23181             );
23182             this.dd.setYConstraint(0, 0);
23183         }else{
23184             this.dd.resetConstraints();
23185             this.dd.setXConstraint(0, 0);
23186             this.dd.setYConstraint(
23187                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23188                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23189             );
23190          }
23191         this.dragSpecs.startSize = size;
23192         this.dragSpecs.startPoint = [x, y];
23193         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23194     },
23195     
23196     /** 
23197      * @private Called after the drag operation by the DDProxy
23198      */
23199     onEndProxyDrag : function(e){
23200         Roo.get(this.proxy).setDisplayed(false);
23201         var endPoint = Roo.lib.Event.getXY(e);
23202         if(this.overlay){
23203             this.overlay.hide();
23204         }
23205         var newSize;
23206         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23207             newSize = this.dragSpecs.startSize + 
23208                 (this.placement == Roo.SplitBar.LEFT ?
23209                     endPoint[0] - this.dragSpecs.startPoint[0] :
23210                     this.dragSpecs.startPoint[0] - endPoint[0]
23211                 );
23212         }else{
23213             newSize = this.dragSpecs.startSize + 
23214                 (this.placement == Roo.SplitBar.TOP ?
23215                     endPoint[1] - this.dragSpecs.startPoint[1] :
23216                     this.dragSpecs.startPoint[1] - endPoint[1]
23217                 );
23218         }
23219         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23220         if(newSize != this.dragSpecs.startSize){
23221             if(this.fireEvent('beforeapply', this, newSize) !== false){
23222                 this.adapter.setElementSize(this, newSize);
23223                 this.fireEvent("moved", this, newSize);
23224                 this.fireEvent("resize", this, newSize);
23225             }
23226         }
23227     },
23228     
23229     /**
23230      * Get the adapter this SplitBar uses
23231      * @return The adapter object
23232      */
23233     getAdapter : function(){
23234         return this.adapter;
23235     },
23236     
23237     /**
23238      * Set the adapter this SplitBar uses
23239      * @param {Object} adapter A SplitBar adapter object
23240      */
23241     setAdapter : function(adapter){
23242         this.adapter = adapter;
23243         this.adapter.init(this);
23244     },
23245     
23246     /**
23247      * Gets the minimum size for the resizing element
23248      * @return {Number} The minimum size
23249      */
23250     getMinimumSize : function(){
23251         return this.minSize;
23252     },
23253     
23254     /**
23255      * Sets the minimum size for the resizing element
23256      * @param {Number} minSize The minimum size
23257      */
23258     setMinimumSize : function(minSize){
23259         this.minSize = minSize;
23260     },
23261     
23262     /**
23263      * Gets the maximum size for the resizing element
23264      * @return {Number} The maximum size
23265      */
23266     getMaximumSize : function(){
23267         return this.maxSize;
23268     },
23269     
23270     /**
23271      * Sets the maximum size for the resizing element
23272      * @param {Number} maxSize The maximum size
23273      */
23274     setMaximumSize : function(maxSize){
23275         this.maxSize = maxSize;
23276     },
23277     
23278     /**
23279      * Sets the initialize size for the resizing element
23280      * @param {Number} size The initial size
23281      */
23282     setCurrentSize : function(size){
23283         var oldAnimate = this.animate;
23284         this.animate = false;
23285         this.adapter.setElementSize(this, size);
23286         this.animate = oldAnimate;
23287     },
23288     
23289     /**
23290      * Destroy this splitbar. 
23291      * @param {Boolean} removeEl True to remove the element
23292      */
23293     destroy : function(removeEl){
23294         if(this.shim){
23295             this.shim.remove();
23296         }
23297         this.dd.unreg();
23298         this.proxy.parentNode.removeChild(this.proxy);
23299         if(removeEl){
23300             this.el.remove();
23301         }
23302     }
23303 });
23304
23305 /**
23306  * @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.
23307  */
23308 Roo.SplitBar.createProxy = function(dir){
23309     var proxy = new Roo.Element(document.createElement("div"));
23310     proxy.unselectable();
23311     var cls = 'x-splitbar-proxy';
23312     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23313     document.body.appendChild(proxy.dom);
23314     return proxy.dom;
23315 };
23316
23317 /** 
23318  * @class Roo.SplitBar.BasicLayoutAdapter
23319  * Default Adapter. It assumes the splitter and resizing element are not positioned
23320  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23321  */
23322 Roo.SplitBar.BasicLayoutAdapter = function(){
23323 };
23324
23325 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23326     // do nothing for now
23327     init : function(s){
23328     
23329     },
23330     /**
23331      * Called before drag operations to get the current size of the resizing element. 
23332      * @param {Roo.SplitBar} s The SplitBar using this adapter
23333      */
23334      getElementSize : function(s){
23335         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23336             return s.resizingEl.getWidth();
23337         }else{
23338             return s.resizingEl.getHeight();
23339         }
23340     },
23341     
23342     /**
23343      * Called after drag operations to set the size of the resizing element.
23344      * @param {Roo.SplitBar} s The SplitBar using this adapter
23345      * @param {Number} newSize The new size to set
23346      * @param {Function} onComplete A function to be invoked when resizing is complete
23347      */
23348     setElementSize : function(s, newSize, onComplete){
23349         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23350             if(!s.animate){
23351                 s.resizingEl.setWidth(newSize);
23352                 if(onComplete){
23353                     onComplete(s, newSize);
23354                 }
23355             }else{
23356                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23357             }
23358         }else{
23359             
23360             if(!s.animate){
23361                 s.resizingEl.setHeight(newSize);
23362                 if(onComplete){
23363                     onComplete(s, newSize);
23364                 }
23365             }else{
23366                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23367             }
23368         }
23369     }
23370 };
23371
23372 /** 
23373  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23374  * @extends Roo.SplitBar.BasicLayoutAdapter
23375  * Adapter that  moves the splitter element to align with the resized sizing element. 
23376  * Used with an absolute positioned SplitBar.
23377  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23378  * document.body, make sure you assign an id to the body element.
23379  */
23380 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23381     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23382     this.container = Roo.get(container);
23383 };
23384
23385 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23386     init : function(s){
23387         this.basic.init(s);
23388     },
23389     
23390     getElementSize : function(s){
23391         return this.basic.getElementSize(s);
23392     },
23393     
23394     setElementSize : function(s, newSize, onComplete){
23395         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23396     },
23397     
23398     moveSplitter : function(s){
23399         var yes = Roo.SplitBar;
23400         switch(s.placement){
23401             case yes.LEFT:
23402                 s.el.setX(s.resizingEl.getRight());
23403                 break;
23404             case yes.RIGHT:
23405                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23406                 break;
23407             case yes.TOP:
23408                 s.el.setY(s.resizingEl.getBottom());
23409                 break;
23410             case yes.BOTTOM:
23411                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23412                 break;
23413         }
23414     }
23415 };
23416
23417 /**
23418  * Orientation constant - Create a vertical SplitBar
23419  * @static
23420  * @type Number
23421  */
23422 Roo.SplitBar.VERTICAL = 1;
23423
23424 /**
23425  * Orientation constant - Create a horizontal SplitBar
23426  * @static
23427  * @type Number
23428  */
23429 Roo.SplitBar.HORIZONTAL = 2;
23430
23431 /**
23432  * Placement constant - The resizing element is to the left of the splitter element
23433  * @static
23434  * @type Number
23435  */
23436 Roo.SplitBar.LEFT = 1;
23437
23438 /**
23439  * Placement constant - The resizing element is to the right of the splitter element
23440  * @static
23441  * @type Number
23442  */
23443 Roo.SplitBar.RIGHT = 2;
23444
23445 /**
23446  * Placement constant - The resizing element is positioned above the splitter element
23447  * @static
23448  * @type Number
23449  */
23450 Roo.SplitBar.TOP = 3;
23451
23452 /**
23453  * Placement constant - The resizing element is positioned under splitter element
23454  * @static
23455  * @type Number
23456  */
23457 Roo.SplitBar.BOTTOM = 4;
23458 /*
23459  * Based on:
23460  * Ext JS Library 1.1.1
23461  * Copyright(c) 2006-2007, Ext JS, LLC.
23462  *
23463  * Originally Released Under LGPL - original licence link has changed is not relivant.
23464  *
23465  * Fork - LGPL
23466  * <script type="text/javascript">
23467  */
23468
23469 /**
23470  * @class Roo.View
23471  * @extends Roo.util.Observable
23472  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23473  * This class also supports single and multi selection modes. <br>
23474  * Create a data model bound view:
23475  <pre><code>
23476  var store = new Roo.data.Store(...);
23477
23478  var view = new Roo.View({
23479     el : "my-element",
23480     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23481  
23482     singleSelect: true,
23483     selectedClass: "ydataview-selected",
23484     store: store
23485  });
23486
23487  // listen for node click?
23488  view.on("click", function(vw, index, node, e){
23489  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23490  });
23491
23492  // load XML data
23493  dataModel.load("foobar.xml");
23494  </code></pre>
23495  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23496  * <br><br>
23497  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23498  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23499  * 
23500  * Note: old style constructor is still suported (container, template, config)
23501  * 
23502  * @constructor
23503  * Create a new View
23504  * @param {Object} config The config object
23505  * 
23506  */
23507 Roo.View = function(config, depreciated_tpl, depreciated_config){
23508     
23509     if (typeof(depreciated_tpl) == 'undefined') {
23510         // new way.. - universal constructor.
23511         Roo.apply(this, config);
23512         this.el  = Roo.get(this.el);
23513     } else {
23514         // old format..
23515         this.el  = Roo.get(config);
23516         this.tpl = depreciated_tpl;
23517         Roo.apply(this, depreciated_config);
23518     }
23519      
23520     
23521     if(typeof(this.tpl) == "string"){
23522         this.tpl = new Roo.Template(this.tpl);
23523     } else {
23524         // support xtype ctors..
23525         this.tpl = new Roo.factory(this.tpl, Roo);
23526     }
23527     
23528     
23529     this.tpl.compile();
23530    
23531
23532      
23533     /** @private */
23534     this.addEvents({
23535     /**
23536      * @event beforeclick
23537      * Fires before a click is processed. Returns false to cancel the default action.
23538      * @param {Roo.View} this
23539      * @param {Number} index The index of the target node
23540      * @param {HTMLElement} node The target node
23541      * @param {Roo.EventObject} e The raw event object
23542      */
23543         "beforeclick" : true,
23544     /**
23545      * @event click
23546      * Fires when a template node is clicked.
23547      * @param {Roo.View} this
23548      * @param {Number} index The index of the target node
23549      * @param {HTMLElement} node The target node
23550      * @param {Roo.EventObject} e The raw event object
23551      */
23552         "click" : true,
23553     /**
23554      * @event dblclick
23555      * Fires when a template node is double clicked.
23556      * @param {Roo.View} this
23557      * @param {Number} index The index of the target node
23558      * @param {HTMLElement} node The target node
23559      * @param {Roo.EventObject} e The raw event object
23560      */
23561         "dblclick" : true,
23562     /**
23563      * @event contextmenu
23564      * Fires when a template node is right clicked.
23565      * @param {Roo.View} this
23566      * @param {Number} index The index of the target node
23567      * @param {HTMLElement} node The target node
23568      * @param {Roo.EventObject} e The raw event object
23569      */
23570         "contextmenu" : true,
23571     /**
23572      * @event selectionchange
23573      * Fires when the selected nodes change.
23574      * @param {Roo.View} this
23575      * @param {Array} selections Array of the selected nodes
23576      */
23577         "selectionchange" : true,
23578
23579     /**
23580      * @event beforeselect
23581      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23582      * @param {Roo.View} this
23583      * @param {HTMLElement} node The node to be selected
23584      * @param {Array} selections Array of currently selected nodes
23585      */
23586         "beforeselect" : true
23587     });
23588
23589     this.el.on({
23590         "click": this.onClick,
23591         "dblclick": this.onDblClick,
23592         "contextmenu": this.onContextMenu,
23593         scope:this
23594     });
23595
23596     this.selections = [];
23597     this.nodes = [];
23598     this.cmp = new Roo.CompositeElementLite([]);
23599     if(this.store){
23600         this.store = Roo.factory(this.store, Roo.data);
23601         this.setStore(this.store, true);
23602     }
23603     Roo.View.superclass.constructor.call(this);
23604 };
23605
23606 Roo.extend(Roo.View, Roo.util.Observable, {
23607     
23608      /**
23609      * @cfg {Roo.data.Store} store Data store to load data from.
23610      */
23611     store : false,
23612     
23613     /**
23614      * @cfg {String|Roo.Element} el The container element.
23615      */
23616     el : '',
23617     
23618     /**
23619      * @cfg {String|Roo.Template} tpl The template used by this View 
23620      */
23621     tpl : false,
23622     
23623     /**
23624      * @cfg {String} selectedClass The css class to add to selected nodes
23625      */
23626     selectedClass : "x-view-selected",
23627      /**
23628      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23629      */
23630     emptyText : "",
23631     /**
23632      * @cfg {Boolean} multiSelect Allow multiple selection
23633      */
23634     
23635     multiSelect : false,
23636     /**
23637      * @cfg {Boolean} singleSelect Allow single selection
23638      */
23639     singleSelect:  false,
23640     
23641     /**
23642      * Returns the element this view is bound to.
23643      * @return {Roo.Element}
23644      */
23645     getEl : function(){
23646         return this.el;
23647     },
23648
23649     /**
23650      * Refreshes the view.
23651      */
23652     refresh : function(){
23653         var t = this.tpl;
23654         this.clearSelections();
23655         this.el.update("");
23656         var html = [];
23657         var records = this.store.getRange();
23658         if(records.length < 1){
23659             this.el.update(this.emptyText);
23660             return;
23661         }
23662         for(var i = 0, len = records.length; i < len; i++){
23663             var data = this.prepareData(records[i].data, i, records[i]);
23664             html[html.length] = t.apply(data);
23665         }
23666         this.el.update(html.join(""));
23667         this.nodes = this.el.dom.childNodes;
23668         this.updateIndexes(0);
23669     },
23670
23671     /**
23672      * Function to override to reformat the data that is sent to
23673      * the template for each node.
23674      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23675      * a JSON object for an UpdateManager bound view).
23676      */
23677     prepareData : function(data){
23678         return data;
23679     },
23680
23681     onUpdate : function(ds, record){
23682         this.clearSelections();
23683         var index = this.store.indexOf(record);
23684         var n = this.nodes[index];
23685         this.tpl.insertBefore(n, this.prepareData(record.data));
23686         n.parentNode.removeChild(n);
23687         this.updateIndexes(index, index);
23688     },
23689
23690     onAdd : function(ds, records, index){
23691         this.clearSelections();
23692         if(this.nodes.length == 0){
23693             this.refresh();
23694             return;
23695         }
23696         var n = this.nodes[index];
23697         for(var i = 0, len = records.length; i < len; i++){
23698             var d = this.prepareData(records[i].data);
23699             if(n){
23700                 this.tpl.insertBefore(n, d);
23701             }else{
23702                 this.tpl.append(this.el, d);
23703             }
23704         }
23705         this.updateIndexes(index);
23706     },
23707
23708     onRemove : function(ds, record, index){
23709         this.clearSelections();
23710         this.el.dom.removeChild(this.nodes[index]);
23711         this.updateIndexes(index);
23712     },
23713
23714     /**
23715      * Refresh an individual node.
23716      * @param {Number} index
23717      */
23718     refreshNode : function(index){
23719         this.onUpdate(this.store, this.store.getAt(index));
23720     },
23721
23722     updateIndexes : function(startIndex, endIndex){
23723         var ns = this.nodes;
23724         startIndex = startIndex || 0;
23725         endIndex = endIndex || ns.length - 1;
23726         for(var i = startIndex; i <= endIndex; i++){
23727             ns[i].nodeIndex = i;
23728         }
23729     },
23730
23731     /**
23732      * Changes the data store this view uses and refresh the view.
23733      * @param {Store} store
23734      */
23735     setStore : function(store, initial){
23736         if(!initial && this.store){
23737             this.store.un("datachanged", this.refresh);
23738             this.store.un("add", this.onAdd);
23739             this.store.un("remove", this.onRemove);
23740             this.store.un("update", this.onUpdate);
23741             this.store.un("clear", this.refresh);
23742         }
23743         if(store){
23744           
23745             store.on("datachanged", this.refresh, this);
23746             store.on("add", this.onAdd, this);
23747             store.on("remove", this.onRemove, this);
23748             store.on("update", this.onUpdate, this);
23749             store.on("clear", this.refresh, this);
23750         }
23751         
23752         if(store){
23753             this.refresh();
23754         }
23755     },
23756
23757     /**
23758      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23759      * @param {HTMLElement} node
23760      * @return {HTMLElement} The template node
23761      */
23762     findItemFromChild : function(node){
23763         var el = this.el.dom;
23764         if(!node || node.parentNode == el){
23765                     return node;
23766             }
23767             var p = node.parentNode;
23768             while(p && p != el){
23769             if(p.parentNode == el){
23770                 return p;
23771             }
23772             p = p.parentNode;
23773         }
23774             return null;
23775     },
23776
23777     /** @ignore */
23778     onClick : function(e){
23779         var item = this.findItemFromChild(e.getTarget());
23780         if(item){
23781             var index = this.indexOf(item);
23782             if(this.onItemClick(item, index, e) !== false){
23783                 this.fireEvent("click", this, index, item, e);
23784             }
23785         }else{
23786             this.clearSelections();
23787         }
23788     },
23789
23790     /** @ignore */
23791     onContextMenu : function(e){
23792         var item = this.findItemFromChild(e.getTarget());
23793         if(item){
23794             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23795         }
23796     },
23797
23798     /** @ignore */
23799     onDblClick : function(e){
23800         var item = this.findItemFromChild(e.getTarget());
23801         if(item){
23802             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23803         }
23804     },
23805
23806     onItemClick : function(item, index, e){
23807         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23808             return false;
23809         }
23810         if(this.multiSelect || this.singleSelect){
23811             if(this.multiSelect && e.shiftKey && this.lastSelection){
23812                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23813             }else{
23814                 this.select(item, this.multiSelect && e.ctrlKey);
23815                 this.lastSelection = item;
23816             }
23817             e.preventDefault();
23818         }
23819         return true;
23820     },
23821
23822     /**
23823      * Get the number of selected nodes.
23824      * @return {Number}
23825      */
23826     getSelectionCount : function(){
23827         return this.selections.length;
23828     },
23829
23830     /**
23831      * Get the currently selected nodes.
23832      * @return {Array} An array of HTMLElements
23833      */
23834     getSelectedNodes : function(){
23835         return this.selections;
23836     },
23837
23838     /**
23839      * Get the indexes of the selected nodes.
23840      * @return {Array}
23841      */
23842     getSelectedIndexes : function(){
23843         var indexes = [], s = this.selections;
23844         for(var i = 0, len = s.length; i < len; i++){
23845             indexes.push(s[i].nodeIndex);
23846         }
23847         return indexes;
23848     },
23849
23850     /**
23851      * Clear all selections
23852      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23853      */
23854     clearSelections : function(suppressEvent){
23855         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23856             this.cmp.elements = this.selections;
23857             this.cmp.removeClass(this.selectedClass);
23858             this.selections = [];
23859             if(!suppressEvent){
23860                 this.fireEvent("selectionchange", this, this.selections);
23861             }
23862         }
23863     },
23864
23865     /**
23866      * Returns true if the passed node is selected
23867      * @param {HTMLElement/Number} node The node or node index
23868      * @return {Boolean}
23869      */
23870     isSelected : function(node){
23871         var s = this.selections;
23872         if(s.length < 1){
23873             return false;
23874         }
23875         node = this.getNode(node);
23876         return s.indexOf(node) !== -1;
23877     },
23878
23879     /**
23880      * Selects nodes.
23881      * @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
23882      * @param {Boolean} keepExisting (optional) true to keep existing selections
23883      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23884      */
23885     select : function(nodeInfo, keepExisting, suppressEvent){
23886         if(nodeInfo instanceof Array){
23887             if(!keepExisting){
23888                 this.clearSelections(true);
23889             }
23890             for(var i = 0, len = nodeInfo.length; i < len; i++){
23891                 this.select(nodeInfo[i], true, true);
23892             }
23893         } else{
23894             var node = this.getNode(nodeInfo);
23895             if(node && !this.isSelected(node)){
23896                 if(!keepExisting){
23897                     this.clearSelections(true);
23898                 }
23899                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23900                     Roo.fly(node).addClass(this.selectedClass);
23901                     this.selections.push(node);
23902                     if(!suppressEvent){
23903                         this.fireEvent("selectionchange", this, this.selections);
23904                     }
23905                 }
23906             }
23907         }
23908     },
23909
23910     /**
23911      * Gets a template node.
23912      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23913      * @return {HTMLElement} The node or null if it wasn't found
23914      */
23915     getNode : function(nodeInfo){
23916         if(typeof nodeInfo == "string"){
23917             return document.getElementById(nodeInfo);
23918         }else if(typeof nodeInfo == "number"){
23919             return this.nodes[nodeInfo];
23920         }
23921         return nodeInfo;
23922     },
23923
23924     /**
23925      * Gets a range template nodes.
23926      * @param {Number} startIndex
23927      * @param {Number} endIndex
23928      * @return {Array} An array of nodes
23929      */
23930     getNodes : function(start, end){
23931         var ns = this.nodes;
23932         start = start || 0;
23933         end = typeof end == "undefined" ? ns.length - 1 : end;
23934         var nodes = [];
23935         if(start <= end){
23936             for(var i = start; i <= end; i++){
23937                 nodes.push(ns[i]);
23938             }
23939         } else{
23940             for(var i = start; i >= end; i--){
23941                 nodes.push(ns[i]);
23942             }
23943         }
23944         return nodes;
23945     },
23946
23947     /**
23948      * Finds the index of the passed node
23949      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23950      * @return {Number} The index of the node or -1
23951      */
23952     indexOf : function(node){
23953         node = this.getNode(node);
23954         if(typeof node.nodeIndex == "number"){
23955             return node.nodeIndex;
23956         }
23957         var ns = this.nodes;
23958         for(var i = 0, len = ns.length; i < len; i++){
23959             if(ns[i] == node){
23960                 return i;
23961             }
23962         }
23963         return -1;
23964     }
23965 });
23966 /*
23967  * Based on:
23968  * Ext JS Library 1.1.1
23969  * Copyright(c) 2006-2007, Ext JS, LLC.
23970  *
23971  * Originally Released Under LGPL - original licence link has changed is not relivant.
23972  *
23973  * Fork - LGPL
23974  * <script type="text/javascript">
23975  */
23976
23977 /**
23978  * @class Roo.JsonView
23979  * @extends Roo.View
23980  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23981 <pre><code>
23982 var view = new Roo.JsonView({
23983     container: "my-element",
23984     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23985     multiSelect: true, 
23986     jsonRoot: "data" 
23987 });
23988
23989 // listen for node click?
23990 view.on("click", function(vw, index, node, e){
23991     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23992 });
23993
23994 // direct load of JSON data
23995 view.load("foobar.php");
23996
23997 // Example from my blog list
23998 var tpl = new Roo.Template(
23999     '&lt;div class="entry"&gt;' +
24000     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24001     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24002     "&lt;/div&gt;&lt;hr /&gt;"
24003 );
24004
24005 var moreView = new Roo.JsonView({
24006     container :  "entry-list", 
24007     template : tpl,
24008     jsonRoot: "posts"
24009 });
24010 moreView.on("beforerender", this.sortEntries, this);
24011 moreView.load({
24012     url: "/blog/get-posts.php",
24013     params: "allposts=true",
24014     text: "Loading Blog Entries..."
24015 });
24016 </code></pre>
24017
24018 * Note: old code is supported with arguments : (container, template, config)
24019
24020
24021  * @constructor
24022  * Create a new JsonView
24023  * 
24024  * @param {Object} config The config object
24025  * 
24026  */
24027 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24028     
24029     
24030     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24031
24032     var um = this.el.getUpdateManager();
24033     um.setRenderer(this);
24034     um.on("update", this.onLoad, this);
24035     um.on("failure", this.onLoadException, this);
24036
24037     /**
24038      * @event beforerender
24039      * Fires before rendering of the downloaded JSON data.
24040      * @param {Roo.JsonView} this
24041      * @param {Object} data The JSON data loaded
24042      */
24043     /**
24044      * @event load
24045      * Fires when data is loaded.
24046      * @param {Roo.JsonView} this
24047      * @param {Object} data The JSON data loaded
24048      * @param {Object} response The raw Connect response object
24049      */
24050     /**
24051      * @event loadexception
24052      * Fires when loading fails.
24053      * @param {Roo.JsonView} this
24054      * @param {Object} response The raw Connect response object
24055      */
24056     this.addEvents({
24057         'beforerender' : true,
24058         'load' : true,
24059         'loadexception' : true
24060     });
24061 };
24062 Roo.extend(Roo.JsonView, Roo.View, {
24063     /**
24064      * @type {String} The root property in the loaded JSON object that contains the data
24065      */
24066     jsonRoot : "",
24067
24068     /**
24069      * Refreshes the view.
24070      */
24071     refresh : function(){
24072         this.clearSelections();
24073         this.el.update("");
24074         var html = [];
24075         var o = this.jsonData;
24076         if(o && o.length > 0){
24077             for(var i = 0, len = o.length; i < len; i++){
24078                 var data = this.prepareData(o[i], i, o);
24079                 html[html.length] = this.tpl.apply(data);
24080             }
24081         }else{
24082             html.push(this.emptyText);
24083         }
24084         this.el.update(html.join(""));
24085         this.nodes = this.el.dom.childNodes;
24086         this.updateIndexes(0);
24087     },
24088
24089     /**
24090      * 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.
24091      * @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:
24092      <pre><code>
24093      view.load({
24094          url: "your-url.php",
24095          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24096          callback: yourFunction,
24097          scope: yourObject, //(optional scope)
24098          discardUrl: false,
24099          nocache: false,
24100          text: "Loading...",
24101          timeout: 30,
24102          scripts: false
24103      });
24104      </code></pre>
24105      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24106      * 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.
24107      * @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}
24108      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24109      * @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.
24110      */
24111     load : function(){
24112         var um = this.el.getUpdateManager();
24113         um.update.apply(um, arguments);
24114     },
24115
24116     render : function(el, response){
24117         this.clearSelections();
24118         this.el.update("");
24119         var o;
24120         try{
24121             o = Roo.util.JSON.decode(response.responseText);
24122             if(this.jsonRoot){
24123                 
24124                 o = o[this.jsonRoot];
24125             }
24126         } catch(e){
24127         }
24128         /**
24129          * The current JSON data or null
24130          */
24131         this.jsonData = o;
24132         this.beforeRender();
24133         this.refresh();
24134     },
24135
24136 /**
24137  * Get the number of records in the current JSON dataset
24138  * @return {Number}
24139  */
24140     getCount : function(){
24141         return this.jsonData ? this.jsonData.length : 0;
24142     },
24143
24144 /**
24145  * Returns the JSON object for the specified node(s)
24146  * @param {HTMLElement/Array} node The node or an array of nodes
24147  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24148  * you get the JSON object for the node
24149  */
24150     getNodeData : function(node){
24151         if(node instanceof Array){
24152             var data = [];
24153             for(var i = 0, len = node.length; i < len; i++){
24154                 data.push(this.getNodeData(node[i]));
24155             }
24156             return data;
24157         }
24158         return this.jsonData[this.indexOf(node)] || null;
24159     },
24160
24161     beforeRender : function(){
24162         this.snapshot = this.jsonData;
24163         if(this.sortInfo){
24164             this.sort.apply(this, this.sortInfo);
24165         }
24166         this.fireEvent("beforerender", this, this.jsonData);
24167     },
24168
24169     onLoad : function(el, o){
24170         this.fireEvent("load", this, this.jsonData, o);
24171     },
24172
24173     onLoadException : function(el, o){
24174         this.fireEvent("loadexception", this, o);
24175     },
24176
24177 /**
24178  * Filter the data by a specific property.
24179  * @param {String} property A property on your JSON objects
24180  * @param {String/RegExp} value Either string that the property values
24181  * should start with, or a RegExp to test against the property
24182  */
24183     filter : function(property, value){
24184         if(this.jsonData){
24185             var data = [];
24186             var ss = this.snapshot;
24187             if(typeof value == "string"){
24188                 var vlen = value.length;
24189                 if(vlen == 0){
24190                     this.clearFilter();
24191                     return;
24192                 }
24193                 value = value.toLowerCase();
24194                 for(var i = 0, len = ss.length; i < len; i++){
24195                     var o = ss[i];
24196                     if(o[property].substr(0, vlen).toLowerCase() == value){
24197                         data.push(o);
24198                     }
24199                 }
24200             } else if(value.exec){ // regex?
24201                 for(var i = 0, len = ss.length; i < len; i++){
24202                     var o = ss[i];
24203                     if(value.test(o[property])){
24204                         data.push(o);
24205                     }
24206                 }
24207             } else{
24208                 return;
24209             }
24210             this.jsonData = data;
24211             this.refresh();
24212         }
24213     },
24214
24215 /**
24216  * Filter by a function. The passed function will be called with each
24217  * object in the current dataset. If the function returns true the value is kept,
24218  * otherwise it is filtered.
24219  * @param {Function} fn
24220  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24221  */
24222     filterBy : function(fn, scope){
24223         if(this.jsonData){
24224             var data = [];
24225             var ss = this.snapshot;
24226             for(var i = 0, len = ss.length; i < len; i++){
24227                 var o = ss[i];
24228                 if(fn.call(scope || this, o)){
24229                     data.push(o);
24230                 }
24231             }
24232             this.jsonData = data;
24233             this.refresh();
24234         }
24235     },
24236
24237 /**
24238  * Clears the current filter.
24239  */
24240     clearFilter : function(){
24241         if(this.snapshot && this.jsonData != this.snapshot){
24242             this.jsonData = this.snapshot;
24243             this.refresh();
24244         }
24245     },
24246
24247
24248 /**
24249  * Sorts the data for this view and refreshes it.
24250  * @param {String} property A property on your JSON objects to sort on
24251  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24252  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24253  */
24254     sort : function(property, dir, sortType){
24255         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24256         if(this.jsonData){
24257             var p = property;
24258             var dsc = dir && dir.toLowerCase() == "desc";
24259             var f = function(o1, o2){
24260                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24261                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24262                 ;
24263                 if(v1 < v2){
24264                     return dsc ? +1 : -1;
24265                 } else if(v1 > v2){
24266                     return dsc ? -1 : +1;
24267                 } else{
24268                     return 0;
24269                 }
24270             };
24271             this.jsonData.sort(f);
24272             this.refresh();
24273             if(this.jsonData != this.snapshot){
24274                 this.snapshot.sort(f);
24275             }
24276         }
24277     }
24278 });/*
24279  * Based on:
24280  * Ext JS Library 1.1.1
24281  * Copyright(c) 2006-2007, Ext JS, LLC.
24282  *
24283  * Originally Released Under LGPL - original licence link has changed is not relivant.
24284  *
24285  * Fork - LGPL
24286  * <script type="text/javascript">
24287  */
24288  
24289
24290 /**
24291  * @class Roo.ColorPalette
24292  * @extends Roo.Component
24293  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24294  * Here's an example of typical usage:
24295  * <pre><code>
24296 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24297 cp.render('my-div');
24298
24299 cp.on('select', function(palette, selColor){
24300     // do something with selColor
24301 });
24302 </code></pre>
24303  * @constructor
24304  * Create a new ColorPalette
24305  * @param {Object} config The config object
24306  */
24307 Roo.ColorPalette = function(config){
24308     Roo.ColorPalette.superclass.constructor.call(this, config);
24309     this.addEvents({
24310         /**
24311              * @event select
24312              * Fires when a color is selected
24313              * @param {ColorPalette} this
24314              * @param {String} color The 6-digit color hex code (without the # symbol)
24315              */
24316         select: true
24317     });
24318
24319     if(this.handler){
24320         this.on("select", this.handler, this.scope, true);
24321     }
24322 };
24323 Roo.extend(Roo.ColorPalette, Roo.Component, {
24324     /**
24325      * @cfg {String} itemCls
24326      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24327      */
24328     itemCls : "x-color-palette",
24329     /**
24330      * @cfg {String} value
24331      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24332      * the hex codes are case-sensitive.
24333      */
24334     value : null,
24335     clickEvent:'click',
24336     // private
24337     ctype: "Roo.ColorPalette",
24338
24339     /**
24340      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24341      */
24342     allowReselect : false,
24343
24344     /**
24345      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24346      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24347      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24348      * of colors with the width setting until the box is symmetrical.</p>
24349      * <p>You can override individual colors if needed:</p>
24350      * <pre><code>
24351 var cp = new Roo.ColorPalette();
24352 cp.colors[0] = "FF0000";  // change the first box to red
24353 </code></pre>
24354
24355 Or you can provide a custom array of your own for complete control:
24356 <pre><code>
24357 var cp = new Roo.ColorPalette();
24358 cp.colors = ["000000", "993300", "333300"];
24359 </code></pre>
24360      * @type Array
24361      */
24362     colors : [
24363         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24364         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24365         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24366         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24367         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24368     ],
24369
24370     // private
24371     onRender : function(container, position){
24372         var t = new Roo.MasterTemplate(
24373             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24374         );
24375         var c = this.colors;
24376         for(var i = 0, len = c.length; i < len; i++){
24377             t.add([c[i]]);
24378         }
24379         var el = document.createElement("div");
24380         el.className = this.itemCls;
24381         t.overwrite(el);
24382         container.dom.insertBefore(el, position);
24383         this.el = Roo.get(el);
24384         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24385         if(this.clickEvent != 'click'){
24386             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24387         }
24388     },
24389
24390     // private
24391     afterRender : function(){
24392         Roo.ColorPalette.superclass.afterRender.call(this);
24393         if(this.value){
24394             var s = this.value;
24395             this.value = null;
24396             this.select(s);
24397         }
24398     },
24399
24400     // private
24401     handleClick : function(e, t){
24402         e.preventDefault();
24403         if(!this.disabled){
24404             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24405             this.select(c.toUpperCase());
24406         }
24407     },
24408
24409     /**
24410      * Selects the specified color in the palette (fires the select event)
24411      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24412      */
24413     select : function(color){
24414         color = color.replace("#", "");
24415         if(color != this.value || this.allowReselect){
24416             var el = this.el;
24417             if(this.value){
24418                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24419             }
24420             el.child("a.color-"+color).addClass("x-color-palette-sel");
24421             this.value = color;
24422             this.fireEvent("select", this, color);
24423         }
24424     }
24425 });/*
24426  * Based on:
24427  * Ext JS Library 1.1.1
24428  * Copyright(c) 2006-2007, Ext JS, LLC.
24429  *
24430  * Originally Released Under LGPL - original licence link has changed is not relivant.
24431  *
24432  * Fork - LGPL
24433  * <script type="text/javascript">
24434  */
24435  
24436 /**
24437  * @class Roo.DatePicker
24438  * @extends Roo.Component
24439  * Simple date picker class.
24440  * @constructor
24441  * Create a new DatePicker
24442  * @param {Object} config The config object
24443  */
24444 Roo.DatePicker = function(config){
24445     Roo.DatePicker.superclass.constructor.call(this, config);
24446
24447     this.value = config && config.value ?
24448                  config.value.clearTime() : new Date().clearTime();
24449
24450     this.addEvents({
24451         /**
24452              * @event select
24453              * Fires when a date is selected
24454              * @param {DatePicker} this
24455              * @param {Date} date The selected date
24456              */
24457         select: true
24458     });
24459
24460     if(this.handler){
24461         this.on("select", this.handler,  this.scope || this);
24462     }
24463     // build the disabledDatesRE
24464     if(!this.disabledDatesRE && this.disabledDates){
24465         var dd = this.disabledDates;
24466         var re = "(?:";
24467         for(var i = 0; i < dd.length; i++){
24468             re += dd[i];
24469             if(i != dd.length-1) re += "|";
24470         }
24471         this.disabledDatesRE = new RegExp(re + ")");
24472     }
24473 };
24474
24475 Roo.extend(Roo.DatePicker, Roo.Component, {
24476     /**
24477      * @cfg {String} todayText
24478      * The text to display on the button that selects the current date (defaults to "Today")
24479      */
24480     todayText : "Today",
24481     /**
24482      * @cfg {String} okText
24483      * The text to display on the ok button
24484      */
24485     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24486     /**
24487      * @cfg {String} cancelText
24488      * The text to display on the cancel button
24489      */
24490     cancelText : "Cancel",
24491     /**
24492      * @cfg {String} todayTip
24493      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24494      */
24495     todayTip : "{0} (Spacebar)",
24496     /**
24497      * @cfg {Date} minDate
24498      * Minimum allowable date (JavaScript date object, defaults to null)
24499      */
24500     minDate : null,
24501     /**
24502      * @cfg {Date} maxDate
24503      * Maximum allowable date (JavaScript date object, defaults to null)
24504      */
24505     maxDate : null,
24506     /**
24507      * @cfg {String} minText
24508      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24509      */
24510     minText : "This date is before the minimum date",
24511     /**
24512      * @cfg {String} maxText
24513      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24514      */
24515     maxText : "This date is after the maximum date",
24516     /**
24517      * @cfg {String} format
24518      * The default date format string which can be overriden for localization support.  The format must be
24519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24520      */
24521     format : "m/d/y",
24522     /**
24523      * @cfg {Array} disabledDays
24524      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24525      */
24526     disabledDays : null,
24527     /**
24528      * @cfg {String} disabledDaysText
24529      * The tooltip to display when the date falls on a disabled day (defaults to "")
24530      */
24531     disabledDaysText : "",
24532     /**
24533      * @cfg {RegExp} disabledDatesRE
24534      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24535      */
24536     disabledDatesRE : null,
24537     /**
24538      * @cfg {String} disabledDatesText
24539      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24540      */
24541     disabledDatesText : "",
24542     /**
24543      * @cfg {Boolean} constrainToViewport
24544      * True to constrain the date picker to the viewport (defaults to true)
24545      */
24546     constrainToViewport : true,
24547     /**
24548      * @cfg {Array} monthNames
24549      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24550      */
24551     monthNames : Date.monthNames,
24552     /**
24553      * @cfg {Array} dayNames
24554      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24555      */
24556     dayNames : Date.dayNames,
24557     /**
24558      * @cfg {String} nextText
24559      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24560      */
24561     nextText: 'Next Month (Control+Right)',
24562     /**
24563      * @cfg {String} prevText
24564      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24565      */
24566     prevText: 'Previous Month (Control+Left)',
24567     /**
24568      * @cfg {String} monthYearText
24569      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24570      */
24571     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24572     /**
24573      * @cfg {Number} startDay
24574      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24575      */
24576     startDay : 0,
24577     /**
24578      * @cfg {Bool} showClear
24579      * Show a clear button (usefull for date form elements that can be blank.)
24580      */
24581     
24582     showClear: false,
24583     
24584     /**
24585      * Sets the value of the date field
24586      * @param {Date} value The date to set
24587      */
24588     setValue : function(value){
24589         var old = this.value;
24590         this.value = value.clearTime(true);
24591         if(this.el){
24592             this.update(this.value);
24593         }
24594     },
24595
24596     /**
24597      * Gets the current selected value of the date field
24598      * @return {Date} The selected date
24599      */
24600     getValue : function(){
24601         return this.value;
24602     },
24603
24604     // private
24605     focus : function(){
24606         if(this.el){
24607             this.update(this.activeDate);
24608         }
24609     },
24610
24611     // private
24612     onRender : function(container, position){
24613         var m = [
24614              '<table cellspacing="0">',
24615                 '<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>',
24616                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24617         var dn = this.dayNames;
24618         for(var i = 0; i < 7; i++){
24619             var d = this.startDay+i;
24620             if(d > 6){
24621                 d = d-7;
24622             }
24623             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24624         }
24625         m[m.length] = "</tr></thead><tbody><tr>";
24626         for(var i = 0; i < 42; i++) {
24627             if(i % 7 == 0 && i != 0){
24628                 m[m.length] = "</tr><tr>";
24629             }
24630             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24631         }
24632         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24633             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24634
24635         var el = document.createElement("div");
24636         el.className = "x-date-picker";
24637         el.innerHTML = m.join("");
24638
24639         container.dom.insertBefore(el, position);
24640
24641         this.el = Roo.get(el);
24642         this.eventEl = Roo.get(el.firstChild);
24643
24644         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24645             handler: this.showPrevMonth,
24646             scope: this,
24647             preventDefault:true,
24648             stopDefault:true
24649         });
24650
24651         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24652             handler: this.showNextMonth,
24653             scope: this,
24654             preventDefault:true,
24655             stopDefault:true
24656         });
24657
24658         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24659
24660         this.monthPicker = this.el.down('div.x-date-mp');
24661         this.monthPicker.enableDisplayMode('block');
24662         
24663         var kn = new Roo.KeyNav(this.eventEl, {
24664             "left" : function(e){
24665                 e.ctrlKey ?
24666                     this.showPrevMonth() :
24667                     this.update(this.activeDate.add("d", -1));
24668             },
24669
24670             "right" : function(e){
24671                 e.ctrlKey ?
24672                     this.showNextMonth() :
24673                     this.update(this.activeDate.add("d", 1));
24674             },
24675
24676             "up" : function(e){
24677                 e.ctrlKey ?
24678                     this.showNextYear() :
24679                     this.update(this.activeDate.add("d", -7));
24680             },
24681
24682             "down" : function(e){
24683                 e.ctrlKey ?
24684                     this.showPrevYear() :
24685                     this.update(this.activeDate.add("d", 7));
24686             },
24687
24688             "pageUp" : function(e){
24689                 this.showNextMonth();
24690             },
24691
24692             "pageDown" : function(e){
24693                 this.showPrevMonth();
24694             },
24695
24696             "enter" : function(e){
24697                 e.stopPropagation();
24698                 return true;
24699             },
24700
24701             scope : this
24702         });
24703
24704         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24705
24706         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24707
24708         this.el.unselectable();
24709         
24710         this.cells = this.el.select("table.x-date-inner tbody td");
24711         this.textNodes = this.el.query("table.x-date-inner tbody span");
24712
24713         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24714             text: "&#160;",
24715             tooltip: this.monthYearText
24716         });
24717
24718         this.mbtn.on('click', this.showMonthPicker, this);
24719         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24720
24721
24722         var today = (new Date()).dateFormat(this.format);
24723         
24724         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24725         if (this.showClear) {
24726             baseTb.add( new Roo.Toolbar.Fill());
24727         }
24728         baseTb.add({
24729             text: String.format(this.todayText, today),
24730             tooltip: String.format(this.todayTip, today),
24731             handler: this.selectToday,
24732             scope: this
24733         });
24734         
24735         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24736             
24737         //});
24738         if (this.showClear) {
24739             
24740             baseTb.add( new Roo.Toolbar.Fill());
24741             baseTb.add({
24742                 text: '&#160;',
24743                 cls: 'x-btn-icon x-btn-clear',
24744                 handler: function() {
24745                     //this.value = '';
24746                     this.fireEvent("select", this, '');
24747                 },
24748                 scope: this
24749             });
24750         }
24751         
24752         
24753         if(Roo.isIE){
24754             this.el.repaint();
24755         }
24756         this.update(this.value);
24757     },
24758
24759     createMonthPicker : function(){
24760         if(!this.monthPicker.dom.firstChild){
24761             var buf = ['<table border="0" cellspacing="0">'];
24762             for(var i = 0; i < 6; i++){
24763                 buf.push(
24764                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24765                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24766                     i == 0 ?
24767                     '<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>' :
24768                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24769                 );
24770             }
24771             buf.push(
24772                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24773                     this.okText,
24774                     '</button><button type="button" class="x-date-mp-cancel">',
24775                     this.cancelText,
24776                     '</button></td></tr>',
24777                 '</table>'
24778             );
24779             this.monthPicker.update(buf.join(''));
24780             this.monthPicker.on('click', this.onMonthClick, this);
24781             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24782
24783             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24784             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24785
24786             this.mpMonths.each(function(m, a, i){
24787                 i += 1;
24788                 if((i%2) == 0){
24789                     m.dom.xmonth = 5 + Math.round(i * .5);
24790                 }else{
24791                     m.dom.xmonth = Math.round((i-1) * .5);
24792                 }
24793             });
24794         }
24795     },
24796
24797     showMonthPicker : function(){
24798         this.createMonthPicker();
24799         var size = this.el.getSize();
24800         this.monthPicker.setSize(size);
24801         this.monthPicker.child('table').setSize(size);
24802
24803         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24804         this.updateMPMonth(this.mpSelMonth);
24805         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24806         this.updateMPYear(this.mpSelYear);
24807
24808         this.monthPicker.slideIn('t', {duration:.2});
24809     },
24810
24811     updateMPYear : function(y){
24812         this.mpyear = y;
24813         var ys = this.mpYears.elements;
24814         for(var i = 1; i <= 10; i++){
24815             var td = ys[i-1], y2;
24816             if((i%2) == 0){
24817                 y2 = y + Math.round(i * .5);
24818                 td.firstChild.innerHTML = y2;
24819                 td.xyear = y2;
24820             }else{
24821                 y2 = y - (5-Math.round(i * .5));
24822                 td.firstChild.innerHTML = y2;
24823                 td.xyear = y2;
24824             }
24825             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24826         }
24827     },
24828
24829     updateMPMonth : function(sm){
24830         this.mpMonths.each(function(m, a, i){
24831             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24832         });
24833     },
24834
24835     selectMPMonth: function(m){
24836         
24837     },
24838
24839     onMonthClick : function(e, t){
24840         e.stopEvent();
24841         var el = new Roo.Element(t), pn;
24842         if(el.is('button.x-date-mp-cancel')){
24843             this.hideMonthPicker();
24844         }
24845         else if(el.is('button.x-date-mp-ok')){
24846             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24847             this.hideMonthPicker();
24848         }
24849         else if(pn = el.up('td.x-date-mp-month', 2)){
24850             this.mpMonths.removeClass('x-date-mp-sel');
24851             pn.addClass('x-date-mp-sel');
24852             this.mpSelMonth = pn.dom.xmonth;
24853         }
24854         else if(pn = el.up('td.x-date-mp-year', 2)){
24855             this.mpYears.removeClass('x-date-mp-sel');
24856             pn.addClass('x-date-mp-sel');
24857             this.mpSelYear = pn.dom.xyear;
24858         }
24859         else if(el.is('a.x-date-mp-prev')){
24860             this.updateMPYear(this.mpyear-10);
24861         }
24862         else if(el.is('a.x-date-mp-next')){
24863             this.updateMPYear(this.mpyear+10);
24864         }
24865     },
24866
24867     onMonthDblClick : function(e, t){
24868         e.stopEvent();
24869         var el = new Roo.Element(t), pn;
24870         if(pn = el.up('td.x-date-mp-month', 2)){
24871             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24872             this.hideMonthPicker();
24873         }
24874         else if(pn = el.up('td.x-date-mp-year', 2)){
24875             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24876             this.hideMonthPicker();
24877         }
24878     },
24879
24880     hideMonthPicker : function(disableAnim){
24881         if(this.monthPicker){
24882             if(disableAnim === true){
24883                 this.monthPicker.hide();
24884             }else{
24885                 this.monthPicker.slideOut('t', {duration:.2});
24886             }
24887         }
24888     },
24889
24890     // private
24891     showPrevMonth : function(e){
24892         this.update(this.activeDate.add("mo", -1));
24893     },
24894
24895     // private
24896     showNextMonth : function(e){
24897         this.update(this.activeDate.add("mo", 1));
24898     },
24899
24900     // private
24901     showPrevYear : function(){
24902         this.update(this.activeDate.add("y", -1));
24903     },
24904
24905     // private
24906     showNextYear : function(){
24907         this.update(this.activeDate.add("y", 1));
24908     },
24909
24910     // private
24911     handleMouseWheel : function(e){
24912         var delta = e.getWheelDelta();
24913         if(delta > 0){
24914             this.showPrevMonth();
24915             e.stopEvent();
24916         } else if(delta < 0){
24917             this.showNextMonth();
24918             e.stopEvent();
24919         }
24920     },
24921
24922     // private
24923     handleDateClick : function(e, t){
24924         e.stopEvent();
24925         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24926             this.setValue(new Date(t.dateValue));
24927             this.fireEvent("select", this, this.value);
24928         }
24929     },
24930
24931     // private
24932     selectToday : function(){
24933         this.setValue(new Date().clearTime());
24934         this.fireEvent("select", this, this.value);
24935     },
24936
24937     // private
24938     update : function(date){
24939         var vd = this.activeDate;
24940         this.activeDate = date;
24941         if(vd && this.el){
24942             var t = date.getTime();
24943             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24944                 this.cells.removeClass("x-date-selected");
24945                 this.cells.each(function(c){
24946                    if(c.dom.firstChild.dateValue == t){
24947                        c.addClass("x-date-selected");
24948                        setTimeout(function(){
24949                             try{c.dom.firstChild.focus();}catch(e){}
24950                        }, 50);
24951                        return false;
24952                    }
24953                 });
24954                 return;
24955             }
24956         }
24957         var days = date.getDaysInMonth();
24958         var firstOfMonth = date.getFirstDateOfMonth();
24959         var startingPos = firstOfMonth.getDay()-this.startDay;
24960
24961         if(startingPos <= this.startDay){
24962             startingPos += 7;
24963         }
24964
24965         var pm = date.add("mo", -1);
24966         var prevStart = pm.getDaysInMonth()-startingPos;
24967
24968         var cells = this.cells.elements;
24969         var textEls = this.textNodes;
24970         days += startingPos;
24971
24972         // convert everything to numbers so it's fast
24973         var day = 86400000;
24974         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24975         var today = new Date().clearTime().getTime();
24976         var sel = date.clearTime().getTime();
24977         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24978         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24979         var ddMatch = this.disabledDatesRE;
24980         var ddText = this.disabledDatesText;
24981         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24982         var ddaysText = this.disabledDaysText;
24983         var format = this.format;
24984
24985         var setCellClass = function(cal, cell){
24986             cell.title = "";
24987             var t = d.getTime();
24988             cell.firstChild.dateValue = t;
24989             if(t == today){
24990                 cell.className += " x-date-today";
24991                 cell.title = cal.todayText;
24992             }
24993             if(t == sel){
24994                 cell.className += " x-date-selected";
24995                 setTimeout(function(){
24996                     try{cell.firstChild.focus();}catch(e){}
24997                 }, 50);
24998             }
24999             // disabling
25000             if(t < min) {
25001                 cell.className = " x-date-disabled";
25002                 cell.title = cal.minText;
25003                 return;
25004             }
25005             if(t > max) {
25006                 cell.className = " x-date-disabled";
25007                 cell.title = cal.maxText;
25008                 return;
25009             }
25010             if(ddays){
25011                 if(ddays.indexOf(d.getDay()) != -1){
25012                     cell.title = ddaysText;
25013                     cell.className = " x-date-disabled";
25014                 }
25015             }
25016             if(ddMatch && format){
25017                 var fvalue = d.dateFormat(format);
25018                 if(ddMatch.test(fvalue)){
25019                     cell.title = ddText.replace("%0", fvalue);
25020                     cell.className = " x-date-disabled";
25021                 }
25022             }
25023         };
25024
25025         var i = 0;
25026         for(; i < startingPos; i++) {
25027             textEls[i].innerHTML = (++prevStart);
25028             d.setDate(d.getDate()+1);
25029             cells[i].className = "x-date-prevday";
25030             setCellClass(this, cells[i]);
25031         }
25032         for(; i < days; i++){
25033             intDay = i - startingPos + 1;
25034             textEls[i].innerHTML = (intDay);
25035             d.setDate(d.getDate()+1);
25036             cells[i].className = "x-date-active";
25037             setCellClass(this, cells[i]);
25038         }
25039         var extraDays = 0;
25040         for(; i < 42; i++) {
25041              textEls[i].innerHTML = (++extraDays);
25042              d.setDate(d.getDate()+1);
25043              cells[i].className = "x-date-nextday";
25044              setCellClass(this, cells[i]);
25045         }
25046
25047         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25048
25049         if(!this.internalRender){
25050             var main = this.el.dom.firstChild;
25051             var w = main.offsetWidth;
25052             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25053             Roo.fly(main).setWidth(w);
25054             this.internalRender = true;
25055             // opera does not respect the auto grow header center column
25056             // then, after it gets a width opera refuses to recalculate
25057             // without a second pass
25058             if(Roo.isOpera && !this.secondPass){
25059                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25060                 this.secondPass = true;
25061                 this.update.defer(10, this, [date]);
25062             }
25063         }
25064     }
25065 });/*
25066  * Based on:
25067  * Ext JS Library 1.1.1
25068  * Copyright(c) 2006-2007, Ext JS, LLC.
25069  *
25070  * Originally Released Under LGPL - original licence link has changed is not relivant.
25071  *
25072  * Fork - LGPL
25073  * <script type="text/javascript">
25074  */
25075 /**
25076  * @class Roo.TabPanel
25077  * @extends Roo.util.Observable
25078  * A lightweight tab container.
25079  * <br><br>
25080  * Usage:
25081  * <pre><code>
25082 // basic tabs 1, built from existing content
25083 var tabs = new Roo.TabPanel("tabs1");
25084 tabs.addTab("script", "View Script");
25085 tabs.addTab("markup", "View Markup");
25086 tabs.activate("script");
25087
25088 // more advanced tabs, built from javascript
25089 var jtabs = new Roo.TabPanel("jtabs");
25090 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25091
25092 // set up the UpdateManager
25093 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25094 var updater = tab2.getUpdateManager();
25095 updater.setDefaultUrl("ajax1.htm");
25096 tab2.on('activate', updater.refresh, updater, true);
25097
25098 // Use setUrl for Ajax loading
25099 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25100 tab3.setUrl("ajax2.htm", null, true);
25101
25102 // Disabled tab
25103 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25104 tab4.disable();
25105
25106 jtabs.activate("jtabs-1");
25107  * </code></pre>
25108  * @constructor
25109  * Create a new TabPanel.
25110  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25111  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25112  */
25113 Roo.TabPanel = function(container, config){
25114     /**
25115     * The container element for this TabPanel.
25116     * @type Roo.Element
25117     */
25118     this.el = Roo.get(container, true);
25119     if(config){
25120         if(typeof config == "boolean"){
25121             this.tabPosition = config ? "bottom" : "top";
25122         }else{
25123             Roo.apply(this, config);
25124         }
25125     }
25126     if(this.tabPosition == "bottom"){
25127         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25128         this.el.addClass("x-tabs-bottom");
25129     }
25130     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25131     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25132     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25133     if(Roo.isIE){
25134         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25135     }
25136     if(this.tabPosition != "bottom"){
25137     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25138      * @type Roo.Element
25139      */
25140       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25141       this.el.addClass("x-tabs-top");
25142     }
25143     this.items = [];
25144
25145     this.bodyEl.setStyle("position", "relative");
25146
25147     this.active = null;
25148     this.activateDelegate = this.activate.createDelegate(this);
25149
25150     this.addEvents({
25151         /**
25152          * @event tabchange
25153          * Fires when the active tab changes
25154          * @param {Roo.TabPanel} this
25155          * @param {Roo.TabPanelItem} activePanel The new active tab
25156          */
25157         "tabchange": true,
25158         /**
25159          * @event beforetabchange
25160          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25161          * @param {Roo.TabPanel} this
25162          * @param {Object} e Set cancel to true on this object to cancel the tab change
25163          * @param {Roo.TabPanelItem} tab The tab being changed to
25164          */
25165         "beforetabchange" : true
25166     });
25167
25168     Roo.EventManager.onWindowResize(this.onResize, this);
25169     this.cpad = this.el.getPadding("lr");
25170     this.hiddenCount = 0;
25171
25172     Roo.TabPanel.superclass.constructor.call(this);
25173 };
25174
25175 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25176         /*
25177          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25178          */
25179     tabPosition : "top",
25180         /*
25181          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25182          */
25183     currentTabWidth : 0,
25184         /*
25185          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25186          */
25187     minTabWidth : 40,
25188         /*
25189          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25190          */
25191     maxTabWidth : 250,
25192         /*
25193          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25194          */
25195     preferredTabWidth : 175,
25196         /*
25197          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25198          */
25199     resizeTabs : false,
25200         /*
25201          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25202          */
25203     monitorResize : true,
25204
25205     /**
25206      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25207      * @param {String} id The id of the div to use <b>or create</b>
25208      * @param {String} text The text for the tab
25209      * @param {String} content (optional) Content to put in the TabPanelItem body
25210      * @param {Boolean} closable (optional) True to create a close icon on the tab
25211      * @return {Roo.TabPanelItem} The created TabPanelItem
25212      */
25213     addTab : function(id, text, content, closable){
25214         var item = new Roo.TabPanelItem(this, id, text, closable);
25215         this.addTabItem(item);
25216         if(content){
25217             item.setContent(content);
25218         }
25219         return item;
25220     },
25221
25222     /**
25223      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25224      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25225      * @return {Roo.TabPanelItem}
25226      */
25227     getTab : function(id){
25228         return this.items[id];
25229     },
25230
25231     /**
25232      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25233      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25234      */
25235     hideTab : function(id){
25236         var t = this.items[id];
25237         if(!t.isHidden()){
25238            t.setHidden(true);
25239            this.hiddenCount++;
25240            this.autoSizeTabs();
25241         }
25242     },
25243
25244     /**
25245      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25246      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25247      */
25248     unhideTab : function(id){
25249         var t = this.items[id];
25250         if(t.isHidden()){
25251            t.setHidden(false);
25252            this.hiddenCount--;
25253            this.autoSizeTabs();
25254         }
25255     },
25256
25257     /**
25258      * Adds an existing {@link Roo.TabPanelItem}.
25259      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25260      */
25261     addTabItem : function(item){
25262         this.items[item.id] = item;
25263         this.items.push(item);
25264         if(this.resizeTabs){
25265            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25266            this.autoSizeTabs();
25267         }else{
25268             item.autoSize();
25269         }
25270     },
25271
25272     /**
25273      * Removes a {@link Roo.TabPanelItem}.
25274      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25275      */
25276     removeTab : function(id){
25277         var items = this.items;
25278         var tab = items[id];
25279         if(!tab) { return; }
25280         var index = items.indexOf(tab);
25281         if(this.active == tab && items.length > 1){
25282             var newTab = this.getNextAvailable(index);
25283             if(newTab) {
25284                 newTab.activate();
25285             }
25286         }
25287         this.stripEl.dom.removeChild(tab.pnode.dom);
25288         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25289             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25290         }
25291         items.splice(index, 1);
25292         delete this.items[tab.id];
25293         tab.fireEvent("close", tab);
25294         tab.purgeListeners();
25295         this.autoSizeTabs();
25296     },
25297
25298     getNextAvailable : function(start){
25299         var items = this.items;
25300         var index = start;
25301         // look for a next tab that will slide over to
25302         // replace the one being removed
25303         while(index < items.length){
25304             var item = items[++index];
25305             if(item && !item.isHidden()){
25306                 return item;
25307             }
25308         }
25309         // if one isn't found select the previous tab (on the left)
25310         index = start;
25311         while(index >= 0){
25312             var item = items[--index];
25313             if(item && !item.isHidden()){
25314                 return item;
25315             }
25316         }
25317         return null;
25318     },
25319
25320     /**
25321      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25322      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25323      */
25324     disableTab : function(id){
25325         var tab = this.items[id];
25326         if(tab && this.active != tab){
25327             tab.disable();
25328         }
25329     },
25330
25331     /**
25332      * Enables a {@link Roo.TabPanelItem} that is disabled.
25333      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25334      */
25335     enableTab : function(id){
25336         var tab = this.items[id];
25337         tab.enable();
25338     },
25339
25340     /**
25341      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25342      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25343      * @return {Roo.TabPanelItem} The TabPanelItem.
25344      */
25345     activate : function(id){
25346         var tab = this.items[id];
25347         if(!tab){
25348             return null;
25349         }
25350         if(tab == this.active || tab.disabled){
25351             return tab;
25352         }
25353         var e = {};
25354         this.fireEvent("beforetabchange", this, e, tab);
25355         if(e.cancel !== true && !tab.disabled){
25356             if(this.active){
25357                 this.active.hide();
25358             }
25359             this.active = this.items[id];
25360             this.active.show();
25361             this.fireEvent("tabchange", this, this.active);
25362         }
25363         return tab;
25364     },
25365
25366     /**
25367      * Gets the active {@link Roo.TabPanelItem}.
25368      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25369      */
25370     getActiveTab : function(){
25371         return this.active;
25372     },
25373
25374     /**
25375      * Updates the tab body element to fit the height of the container element
25376      * for overflow scrolling
25377      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25378      */
25379     syncHeight : function(targetHeight){
25380         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25381         var bm = this.bodyEl.getMargins();
25382         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25383         this.bodyEl.setHeight(newHeight);
25384         return newHeight;
25385     },
25386
25387     onResize : function(){
25388         if(this.monitorResize){
25389             this.autoSizeTabs();
25390         }
25391     },
25392
25393     /**
25394      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25395      */
25396     beginUpdate : function(){
25397         this.updating = true;
25398     },
25399
25400     /**
25401      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25402      */
25403     endUpdate : function(){
25404         this.updating = false;
25405         this.autoSizeTabs();
25406     },
25407
25408     /**
25409      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25410      */
25411     autoSizeTabs : function(){
25412         var count = this.items.length;
25413         var vcount = count - this.hiddenCount;
25414         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25415         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25416         var availWidth = Math.floor(w / vcount);
25417         var b = this.stripBody;
25418         if(b.getWidth() > w){
25419             var tabs = this.items;
25420             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25421             if(availWidth < this.minTabWidth){
25422                 /*if(!this.sleft){    // incomplete scrolling code
25423                     this.createScrollButtons();
25424                 }
25425                 this.showScroll();
25426                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25427             }
25428         }else{
25429             if(this.currentTabWidth < this.preferredTabWidth){
25430                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25431             }
25432         }
25433     },
25434
25435     /**
25436      * Returns the number of tabs in this TabPanel.
25437      * @return {Number}
25438      */
25439      getCount : function(){
25440          return this.items.length;
25441      },
25442
25443     /**
25444      * Resizes all the tabs to the passed width
25445      * @param {Number} The new width
25446      */
25447     setTabWidth : function(width){
25448         this.currentTabWidth = width;
25449         for(var i = 0, len = this.items.length; i < len; i++) {
25450                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25451         }
25452     },
25453
25454     /**
25455      * Destroys this TabPanel
25456      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25457      */
25458     destroy : function(removeEl){
25459         Roo.EventManager.removeResizeListener(this.onResize, this);
25460         for(var i = 0, len = this.items.length; i < len; i++){
25461             this.items[i].purgeListeners();
25462         }
25463         if(removeEl === true){
25464             this.el.update("");
25465             this.el.remove();
25466         }
25467     }
25468 });
25469
25470 /**
25471  * @class Roo.TabPanelItem
25472  * @extends Roo.util.Observable
25473  * Represents an individual item (tab plus body) in a TabPanel.
25474  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25475  * @param {String} id The id of this TabPanelItem
25476  * @param {String} text The text for the tab of this TabPanelItem
25477  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25478  */
25479 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25480     /**
25481      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25482      * @type Roo.TabPanel
25483      */
25484     this.tabPanel = tabPanel;
25485     /**
25486      * The id for this TabPanelItem
25487      * @type String
25488      */
25489     this.id = id;
25490     /** @private */
25491     this.disabled = false;
25492     /** @private */
25493     this.text = text;
25494     /** @private */
25495     this.loaded = false;
25496     this.closable = closable;
25497
25498     /**
25499      * The body element for this TabPanelItem.
25500      * @type Roo.Element
25501      */
25502     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25503     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25504     this.bodyEl.setStyle("display", "block");
25505     this.bodyEl.setStyle("zoom", "1");
25506     this.hideAction();
25507
25508     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25509     /** @private */
25510     this.el = Roo.get(els.el, true);
25511     this.inner = Roo.get(els.inner, true);
25512     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25513     this.pnode = Roo.get(els.el.parentNode, true);
25514     this.el.on("mousedown", this.onTabMouseDown, this);
25515     this.el.on("click", this.onTabClick, this);
25516     /** @private */
25517     if(closable){
25518         var c = Roo.get(els.close, true);
25519         c.dom.title = this.closeText;
25520         c.addClassOnOver("close-over");
25521         c.on("click", this.closeClick, this);
25522      }
25523
25524     this.addEvents({
25525          /**
25526          * @event activate
25527          * Fires when this tab becomes the active tab.
25528          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25529          * @param {Roo.TabPanelItem} this
25530          */
25531         "activate": true,
25532         /**
25533          * @event beforeclose
25534          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25535          * @param {Roo.TabPanelItem} this
25536          * @param {Object} e Set cancel to true on this object to cancel the close.
25537          */
25538         "beforeclose": true,
25539         /**
25540          * @event close
25541          * Fires when this tab is closed.
25542          * @param {Roo.TabPanelItem} this
25543          */
25544          "close": true,
25545         /**
25546          * @event deactivate
25547          * Fires when this tab is no longer the active tab.
25548          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25549          * @param {Roo.TabPanelItem} this
25550          */
25551          "deactivate" : true
25552     });
25553     this.hidden = false;
25554
25555     Roo.TabPanelItem.superclass.constructor.call(this);
25556 };
25557
25558 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25559     purgeListeners : function(){
25560        Roo.util.Observable.prototype.purgeListeners.call(this);
25561        this.el.removeAllListeners();
25562     },
25563     /**
25564      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25565      */
25566     show : function(){
25567         this.pnode.addClass("on");
25568         this.showAction();
25569         if(Roo.isOpera){
25570             this.tabPanel.stripWrap.repaint();
25571         }
25572         this.fireEvent("activate", this.tabPanel, this);
25573     },
25574
25575     /**
25576      * Returns true if this tab is the active tab.
25577      * @return {Boolean}
25578      */
25579     isActive : function(){
25580         return this.tabPanel.getActiveTab() == this;
25581     },
25582
25583     /**
25584      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25585      */
25586     hide : function(){
25587         this.pnode.removeClass("on");
25588         this.hideAction();
25589         this.fireEvent("deactivate", this.tabPanel, this);
25590     },
25591
25592     hideAction : function(){
25593         this.bodyEl.hide();
25594         this.bodyEl.setStyle("position", "absolute");
25595         this.bodyEl.setLeft("-20000px");
25596         this.bodyEl.setTop("-20000px");
25597     },
25598
25599     showAction : function(){
25600         this.bodyEl.setStyle("position", "relative");
25601         this.bodyEl.setTop("");
25602         this.bodyEl.setLeft("");
25603         this.bodyEl.show();
25604     },
25605
25606     /**
25607      * Set the tooltip for the tab.
25608      * @param {String} tooltip The tab's tooltip
25609      */
25610     setTooltip : function(text){
25611         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25612             this.textEl.dom.qtip = text;
25613             this.textEl.dom.removeAttribute('title');
25614         }else{
25615             this.textEl.dom.title = text;
25616         }
25617     },
25618
25619     onTabClick : function(e){
25620         e.preventDefault();
25621         this.tabPanel.activate(this.id);
25622     },
25623
25624     onTabMouseDown : function(e){
25625         e.preventDefault();
25626         this.tabPanel.activate(this.id);
25627     },
25628
25629     getWidth : function(){
25630         return this.inner.getWidth();
25631     },
25632
25633     setWidth : function(width){
25634         var iwidth = width - this.pnode.getPadding("lr");
25635         this.inner.setWidth(iwidth);
25636         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25637         this.pnode.setWidth(width);
25638     },
25639
25640     /**
25641      * Show or hide the tab
25642      * @param {Boolean} hidden True to hide or false to show.
25643      */
25644     setHidden : function(hidden){
25645         this.hidden = hidden;
25646         this.pnode.setStyle("display", hidden ? "none" : "");
25647     },
25648
25649     /**
25650      * Returns true if this tab is "hidden"
25651      * @return {Boolean}
25652      */
25653     isHidden : function(){
25654         return this.hidden;
25655     },
25656
25657     /**
25658      * Returns the text for this tab
25659      * @return {String}
25660      */
25661     getText : function(){
25662         return this.text;
25663     },
25664
25665     autoSize : function(){
25666         //this.el.beginMeasure();
25667         this.textEl.setWidth(1);
25668         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25669         //this.el.endMeasure();
25670     },
25671
25672     /**
25673      * Sets the text for the tab (Note: this also sets the tooltip text)
25674      * @param {String} text The tab's text and tooltip
25675      */
25676     setText : function(text){
25677         this.text = text;
25678         this.textEl.update(text);
25679         this.setTooltip(text);
25680         if(!this.tabPanel.resizeTabs){
25681             this.autoSize();
25682         }
25683     },
25684     /**
25685      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25686      */
25687     activate : function(){
25688         this.tabPanel.activate(this.id);
25689     },
25690
25691     /**
25692      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25693      */
25694     disable : function(){
25695         if(this.tabPanel.active != this){
25696             this.disabled = true;
25697             this.pnode.addClass("disabled");
25698         }
25699     },
25700
25701     /**
25702      * Enables this TabPanelItem if it was previously disabled.
25703      */
25704     enable : function(){
25705         this.disabled = false;
25706         this.pnode.removeClass("disabled");
25707     },
25708
25709     /**
25710      * Sets the content for this TabPanelItem.
25711      * @param {String} content The content
25712      * @param {Boolean} loadScripts true to look for and load scripts
25713      */
25714     setContent : function(content, loadScripts){
25715         this.bodyEl.update(content, loadScripts);
25716     },
25717
25718     /**
25719      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25720      * @return {Roo.UpdateManager} The UpdateManager
25721      */
25722     getUpdateManager : function(){
25723         return this.bodyEl.getUpdateManager();
25724     },
25725
25726     /**
25727      * Set a URL to be used to load the content for this TabPanelItem.
25728      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25729      * @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)
25730      * @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)
25731      * @return {Roo.UpdateManager} The UpdateManager
25732      */
25733     setUrl : function(url, params, loadOnce){
25734         if(this.refreshDelegate){
25735             this.un('activate', this.refreshDelegate);
25736         }
25737         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25738         this.on("activate", this.refreshDelegate);
25739         return this.bodyEl.getUpdateManager();
25740     },
25741
25742     /** @private */
25743     _handleRefresh : function(url, params, loadOnce){
25744         if(!loadOnce || !this.loaded){
25745             var updater = this.bodyEl.getUpdateManager();
25746             updater.update(url, params, this._setLoaded.createDelegate(this));
25747         }
25748     },
25749
25750     /**
25751      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25752      *   Will fail silently if the setUrl method has not been called.
25753      *   This does not activate the panel, just updates its content.
25754      */
25755     refresh : function(){
25756         if(this.refreshDelegate){
25757            this.loaded = false;
25758            this.refreshDelegate();
25759         }
25760     },
25761
25762     /** @private */
25763     _setLoaded : function(){
25764         this.loaded = true;
25765     },
25766
25767     /** @private */
25768     closeClick : function(e){
25769         var o = {};
25770         e.stopEvent();
25771         this.fireEvent("beforeclose", this, o);
25772         if(o.cancel !== true){
25773             this.tabPanel.removeTab(this.id);
25774         }
25775     },
25776     /**
25777      * The text displayed in the tooltip for the close icon.
25778      * @type String
25779      */
25780     closeText : "Close this tab"
25781 });
25782
25783 /** @private */
25784 Roo.TabPanel.prototype.createStrip = function(container){
25785     var strip = document.createElement("div");
25786     strip.className = "x-tabs-wrap";
25787     container.appendChild(strip);
25788     return strip;
25789 };
25790 /** @private */
25791 Roo.TabPanel.prototype.createStripList = function(strip){
25792     // div wrapper for retard IE
25793     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>';
25794     return strip.firstChild.firstChild.firstChild.firstChild;
25795 };
25796 /** @private */
25797 Roo.TabPanel.prototype.createBody = function(container){
25798     var body = document.createElement("div");
25799     Roo.id(body, "tab-body");
25800     Roo.fly(body).addClass("x-tabs-body");
25801     container.appendChild(body);
25802     return body;
25803 };
25804 /** @private */
25805 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25806     var body = Roo.getDom(id);
25807     if(!body){
25808         body = document.createElement("div");
25809         body.id = id;
25810     }
25811     Roo.fly(body).addClass("x-tabs-item-body");
25812     bodyEl.insertBefore(body, bodyEl.firstChild);
25813     return body;
25814 };
25815 /** @private */
25816 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25817     var td = document.createElement("td");
25818     stripEl.appendChild(td);
25819     if(closable){
25820         td.className = "x-tabs-closable";
25821         if(!this.closeTpl){
25822             this.closeTpl = new Roo.Template(
25823                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25824                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25825                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25826             );
25827         }
25828         var el = this.closeTpl.overwrite(td, {"text": text});
25829         var close = el.getElementsByTagName("div")[0];
25830         var inner = el.getElementsByTagName("em")[0];
25831         return {"el": el, "close": close, "inner": inner};
25832     } else {
25833         if(!this.tabTpl){
25834             this.tabTpl = new Roo.Template(
25835                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25836                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25837             );
25838         }
25839         var el = this.tabTpl.overwrite(td, {"text": text});
25840         var inner = el.getElementsByTagName("em")[0];
25841         return {"el": el, "inner": inner};
25842     }
25843 };/*
25844  * Based on:
25845  * Ext JS Library 1.1.1
25846  * Copyright(c) 2006-2007, Ext JS, LLC.
25847  *
25848  * Originally Released Under LGPL - original licence link has changed is not relivant.
25849  *
25850  * Fork - LGPL
25851  * <script type="text/javascript">
25852  */
25853
25854 /**
25855  * @class Roo.Button
25856  * @extends Roo.util.Observable
25857  * Simple Button class
25858  * @cfg {String} text The button text
25859  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25860  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25861  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25862  * @cfg {Object} scope The scope of the handler
25863  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25864  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25865  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25866  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25867  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25868  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25869    applies if enableToggle = true)
25870  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25871  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25872   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25873  * @constructor
25874  * Create a new button
25875  * @param {Object} config The config object
25876  */
25877 Roo.Button = function(renderTo, config)
25878 {
25879     if (!config) {
25880         config = renderTo;
25881         renderTo = config.renderTo || false;
25882     }
25883     
25884     Roo.apply(this, config);
25885     this.addEvents({
25886         /**
25887              * @event click
25888              * Fires when this button is clicked
25889              * @param {Button} this
25890              * @param {EventObject} e The click event
25891              */
25892             "click" : true,
25893         /**
25894              * @event toggle
25895              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25896              * @param {Button} this
25897              * @param {Boolean} pressed
25898              */
25899             "toggle" : true,
25900         /**
25901              * @event mouseover
25902              * Fires when the mouse hovers over the button
25903              * @param {Button} this
25904              * @param {Event} e The event object
25905              */
25906         'mouseover' : true,
25907         /**
25908              * @event mouseout
25909              * Fires when the mouse exits the button
25910              * @param {Button} this
25911              * @param {Event} e The event object
25912              */
25913         'mouseout': true,
25914          /**
25915              * @event render
25916              * Fires when the button is rendered
25917              * @param {Button} this
25918              */
25919         'render': true
25920     });
25921     if(this.menu){
25922         this.menu = Roo.menu.MenuMgr.get(this.menu);
25923     }
25924     // register listeners first!!  - so render can be captured..
25925     Roo.util.Observable.call(this);
25926     if(renderTo){
25927         this.render(renderTo);
25928     }
25929     
25930   
25931 };
25932
25933 Roo.extend(Roo.Button, Roo.util.Observable, {
25934     /**
25935      * 
25936      */
25937     
25938     /**
25939      * Read-only. True if this button is hidden
25940      * @type Boolean
25941      */
25942     hidden : false,
25943     /**
25944      * Read-only. True if this button is disabled
25945      * @type Boolean
25946      */
25947     disabled : false,
25948     /**
25949      * Read-only. True if this button is pressed (only if enableToggle = true)
25950      * @type Boolean
25951      */
25952     pressed : false,
25953
25954     /**
25955      * @cfg {Number} tabIndex 
25956      * The DOM tabIndex for this button (defaults to undefined)
25957      */
25958     tabIndex : undefined,
25959
25960     /**
25961      * @cfg {Boolean} enableToggle
25962      * True to enable pressed/not pressed toggling (defaults to false)
25963      */
25964     enableToggle: false,
25965     /**
25966      * @cfg {Mixed} menu
25967      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25968      */
25969     menu : undefined,
25970     /**
25971      * @cfg {String} menuAlign
25972      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25973      */
25974     menuAlign : "tl-bl?",
25975
25976     /**
25977      * @cfg {String} iconCls
25978      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25979      */
25980     iconCls : undefined,
25981     /**
25982      * @cfg {String} type
25983      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25984      */
25985     type : 'button',
25986
25987     // private
25988     menuClassTarget: 'tr',
25989
25990     /**
25991      * @cfg {String} clickEvent
25992      * The type of event to map to the button's event handler (defaults to 'click')
25993      */
25994     clickEvent : 'click',
25995
25996     /**
25997      * @cfg {Boolean} handleMouseEvents
25998      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25999      */
26000     handleMouseEvents : true,
26001
26002     /**
26003      * @cfg {String} tooltipType
26004      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26005      */
26006     tooltipType : 'qtip',
26007
26008     /**
26009      * @cfg {String} cls
26010      * A CSS class to apply to the button's main element.
26011      */
26012     
26013     /**
26014      * @cfg {Roo.Template} template (Optional)
26015      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26016      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26017      * require code modifications if required elements (e.g. a button) aren't present.
26018      */
26019
26020     // private
26021     render : function(renderTo){
26022         var btn;
26023         if(this.hideParent){
26024             this.parentEl = Roo.get(renderTo);
26025         }
26026         if(!this.dhconfig){
26027             if(!this.template){
26028                 if(!Roo.Button.buttonTemplate){
26029                     // hideous table template
26030                     Roo.Button.buttonTemplate = new Roo.Template(
26031                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26032                         '<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>',
26033                         "</tr></tbody></table>");
26034                 }
26035                 this.template = Roo.Button.buttonTemplate;
26036             }
26037             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26038             var btnEl = btn.child("button:first");
26039             btnEl.on('focus', this.onFocus, this);
26040             btnEl.on('blur', this.onBlur, this);
26041             if(this.cls){
26042                 btn.addClass(this.cls);
26043             }
26044             if(this.icon){
26045                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26046             }
26047             if(this.iconCls){
26048                 btnEl.addClass(this.iconCls);
26049                 if(!this.cls){
26050                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26051                 }
26052             }
26053             if(this.tabIndex !== undefined){
26054                 btnEl.dom.tabIndex = this.tabIndex;
26055             }
26056             if(this.tooltip){
26057                 if(typeof this.tooltip == 'object'){
26058                     Roo.QuickTips.tips(Roo.apply({
26059                           target: btnEl.id
26060                     }, this.tooltip));
26061                 } else {
26062                     btnEl.dom[this.tooltipType] = this.tooltip;
26063                 }
26064             }
26065         }else{
26066             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26067         }
26068         this.el = btn;
26069         if(this.id){
26070             this.el.dom.id = this.el.id = this.id;
26071         }
26072         if(this.menu){
26073             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26074             this.menu.on("show", this.onMenuShow, this);
26075             this.menu.on("hide", this.onMenuHide, this);
26076         }
26077         btn.addClass("x-btn");
26078         if(Roo.isIE && !Roo.isIE7){
26079             this.autoWidth.defer(1, this);
26080         }else{
26081             this.autoWidth();
26082         }
26083         if(this.handleMouseEvents){
26084             btn.on("mouseover", this.onMouseOver, this);
26085             btn.on("mouseout", this.onMouseOut, this);
26086             btn.on("mousedown", this.onMouseDown, this);
26087         }
26088         btn.on(this.clickEvent, this.onClick, this);
26089         //btn.on("mouseup", this.onMouseUp, this);
26090         if(this.hidden){
26091             this.hide();
26092         }
26093         if(this.disabled){
26094             this.disable();
26095         }
26096         Roo.ButtonToggleMgr.register(this);
26097         if(this.pressed){
26098             this.el.addClass("x-btn-pressed");
26099         }
26100         if(this.repeat){
26101             var repeater = new Roo.util.ClickRepeater(btn,
26102                 typeof this.repeat == "object" ? this.repeat : {}
26103             );
26104             repeater.on("click", this.onClick,  this);
26105         }
26106         
26107         this.fireEvent('render', this);
26108         
26109     },
26110     /**
26111      * Returns the button's underlying element
26112      * @return {Roo.Element} The element
26113      */
26114     getEl : function(){
26115         return this.el;  
26116     },
26117     
26118     /**
26119      * Destroys this Button and removes any listeners.
26120      */
26121     destroy : function(){
26122         Roo.ButtonToggleMgr.unregister(this);
26123         this.el.removeAllListeners();
26124         this.purgeListeners();
26125         this.el.remove();
26126     },
26127
26128     // private
26129     autoWidth : function(){
26130         if(this.el){
26131             this.el.setWidth("auto");
26132             if(Roo.isIE7 && Roo.isStrict){
26133                 var ib = this.el.child('button');
26134                 if(ib && ib.getWidth() > 20){
26135                     ib.clip();
26136                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26137                 }
26138             }
26139             if(this.minWidth){
26140                 if(this.hidden){
26141                     this.el.beginMeasure();
26142                 }
26143                 if(this.el.getWidth() < this.minWidth){
26144                     this.el.setWidth(this.minWidth);
26145                 }
26146                 if(this.hidden){
26147                     this.el.endMeasure();
26148                 }
26149             }
26150         }
26151     },
26152
26153     /**
26154      * Assigns this button's click handler
26155      * @param {Function} handler The function to call when the button is clicked
26156      * @param {Object} scope (optional) Scope for the function passed in
26157      */
26158     setHandler : function(handler, scope){
26159         this.handler = handler;
26160         this.scope = scope;  
26161     },
26162     
26163     /**
26164      * Sets this button's text
26165      * @param {String} text The button text
26166      */
26167     setText : function(text){
26168         this.text = text;
26169         if(this.el){
26170             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26171         }
26172         this.autoWidth();
26173     },
26174     
26175     /**
26176      * Gets the text for this button
26177      * @return {String} The button text
26178      */
26179     getText : function(){
26180         return this.text;  
26181     },
26182     
26183     /**
26184      * Show this button
26185      */
26186     show: function(){
26187         this.hidden = false;
26188         if(this.el){
26189             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26190         }
26191     },
26192     
26193     /**
26194      * Hide this button
26195      */
26196     hide: function(){
26197         this.hidden = true;
26198         if(this.el){
26199             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26200         }
26201     },
26202     
26203     /**
26204      * Convenience function for boolean show/hide
26205      * @param {Boolean} visible True to show, false to hide
26206      */
26207     setVisible: function(visible){
26208         if(visible) {
26209             this.show();
26210         }else{
26211             this.hide();
26212         }
26213     },
26214     
26215     /**
26216      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26217      * @param {Boolean} state (optional) Force a particular state
26218      */
26219     toggle : function(state){
26220         state = state === undefined ? !this.pressed : state;
26221         if(state != this.pressed){
26222             if(state){
26223                 this.el.addClass("x-btn-pressed");
26224                 this.pressed = true;
26225                 this.fireEvent("toggle", this, true);
26226             }else{
26227                 this.el.removeClass("x-btn-pressed");
26228                 this.pressed = false;
26229                 this.fireEvent("toggle", this, false);
26230             }
26231             if(this.toggleHandler){
26232                 this.toggleHandler.call(this.scope || this, this, state);
26233             }
26234         }
26235     },
26236     
26237     /**
26238      * Focus the button
26239      */
26240     focus : function(){
26241         this.el.child('button:first').focus();
26242     },
26243     
26244     /**
26245      * Disable this button
26246      */
26247     disable : function(){
26248         if(this.el){
26249             this.el.addClass("x-btn-disabled");
26250         }
26251         this.disabled = true;
26252     },
26253     
26254     /**
26255      * Enable this button
26256      */
26257     enable : function(){
26258         if(this.el){
26259             this.el.removeClass("x-btn-disabled");
26260         }
26261         this.disabled = false;
26262     },
26263
26264     /**
26265      * Convenience function for boolean enable/disable
26266      * @param {Boolean} enabled True to enable, false to disable
26267      */
26268     setDisabled : function(v){
26269         this[v !== true ? "enable" : "disable"]();
26270     },
26271
26272     // private
26273     onClick : function(e){
26274         if(e){
26275             e.preventDefault();
26276         }
26277         if(e.button != 0){
26278             return;
26279         }
26280         if(!this.disabled){
26281             if(this.enableToggle){
26282                 this.toggle();
26283             }
26284             if(this.menu && !this.menu.isVisible()){
26285                 this.menu.show(this.el, this.menuAlign);
26286             }
26287             this.fireEvent("click", this, e);
26288             if(this.handler){
26289                 this.el.removeClass("x-btn-over");
26290                 this.handler.call(this.scope || this, this, e);
26291             }
26292         }
26293     },
26294     // private
26295     onMouseOver : function(e){
26296         if(!this.disabled){
26297             this.el.addClass("x-btn-over");
26298             this.fireEvent('mouseover', this, e);
26299         }
26300     },
26301     // private
26302     onMouseOut : function(e){
26303         if(!e.within(this.el,  true)){
26304             this.el.removeClass("x-btn-over");
26305             this.fireEvent('mouseout', this, e);
26306         }
26307     },
26308     // private
26309     onFocus : function(e){
26310         if(!this.disabled){
26311             this.el.addClass("x-btn-focus");
26312         }
26313     },
26314     // private
26315     onBlur : function(e){
26316         this.el.removeClass("x-btn-focus");
26317     },
26318     // private
26319     onMouseDown : function(e){
26320         if(!this.disabled && e.button == 0){
26321             this.el.addClass("x-btn-click");
26322             Roo.get(document).on('mouseup', this.onMouseUp, this);
26323         }
26324     },
26325     // private
26326     onMouseUp : function(e){
26327         if(e.button == 0){
26328             this.el.removeClass("x-btn-click");
26329             Roo.get(document).un('mouseup', this.onMouseUp, this);
26330         }
26331     },
26332     // private
26333     onMenuShow : function(e){
26334         this.el.addClass("x-btn-menu-active");
26335     },
26336     // private
26337     onMenuHide : function(e){
26338         this.el.removeClass("x-btn-menu-active");
26339     }   
26340 });
26341
26342 // Private utility class used by Button
26343 Roo.ButtonToggleMgr = function(){
26344    var groups = {};
26345    
26346    function toggleGroup(btn, state){
26347        if(state){
26348            var g = groups[btn.toggleGroup];
26349            for(var i = 0, l = g.length; i < l; i++){
26350                if(g[i] != btn){
26351                    g[i].toggle(false);
26352                }
26353            }
26354        }
26355    }
26356    
26357    return {
26358        register : function(btn){
26359            if(!btn.toggleGroup){
26360                return;
26361            }
26362            var g = groups[btn.toggleGroup];
26363            if(!g){
26364                g = groups[btn.toggleGroup] = [];
26365            }
26366            g.push(btn);
26367            btn.on("toggle", toggleGroup);
26368        },
26369        
26370        unregister : function(btn){
26371            if(!btn.toggleGroup){
26372                return;
26373            }
26374            var g = groups[btn.toggleGroup];
26375            if(g){
26376                g.remove(btn);
26377                btn.un("toggle", toggleGroup);
26378            }
26379        }
26380    };
26381 }();/*
26382  * Based on:
26383  * Ext JS Library 1.1.1
26384  * Copyright(c) 2006-2007, Ext JS, LLC.
26385  *
26386  * Originally Released Under LGPL - original licence link has changed is not relivant.
26387  *
26388  * Fork - LGPL
26389  * <script type="text/javascript">
26390  */
26391  
26392 /**
26393  * @class Roo.SplitButton
26394  * @extends Roo.Button
26395  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26396  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26397  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26398  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26399  * @cfg {String} arrowTooltip The title attribute of the arrow
26400  * @constructor
26401  * Create a new menu button
26402  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26403  * @param {Object} config The config object
26404  */
26405 Roo.SplitButton = function(renderTo, config){
26406     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26407     /**
26408      * @event arrowclick
26409      * Fires when this button's arrow is clicked
26410      * @param {SplitButton} this
26411      * @param {EventObject} e The click event
26412      */
26413     this.addEvents({"arrowclick":true});
26414 };
26415
26416 Roo.extend(Roo.SplitButton, Roo.Button, {
26417     render : function(renderTo){
26418         // this is one sweet looking template!
26419         var tpl = new Roo.Template(
26420             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26421             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26422             '<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>',
26423             "</tbody></table></td><td>",
26424             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26425             '<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>',
26426             "</tbody></table></td></tr></table>"
26427         );
26428         var btn = tpl.append(renderTo, [this.text, this.type], true);
26429         var btnEl = btn.child("button");
26430         if(this.cls){
26431             btn.addClass(this.cls);
26432         }
26433         if(this.icon){
26434             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26435         }
26436         if(this.iconCls){
26437             btnEl.addClass(this.iconCls);
26438             if(!this.cls){
26439                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26440             }
26441         }
26442         this.el = btn;
26443         if(this.handleMouseEvents){
26444             btn.on("mouseover", this.onMouseOver, this);
26445             btn.on("mouseout", this.onMouseOut, this);
26446             btn.on("mousedown", this.onMouseDown, this);
26447             btn.on("mouseup", this.onMouseUp, this);
26448         }
26449         btn.on(this.clickEvent, this.onClick, this);
26450         if(this.tooltip){
26451             if(typeof this.tooltip == 'object'){
26452                 Roo.QuickTips.tips(Roo.apply({
26453                       target: btnEl.id
26454                 }, this.tooltip));
26455             } else {
26456                 btnEl.dom[this.tooltipType] = this.tooltip;
26457             }
26458         }
26459         if(this.arrowTooltip){
26460             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26461         }
26462         if(this.hidden){
26463             this.hide();
26464         }
26465         if(this.disabled){
26466             this.disable();
26467         }
26468         if(this.pressed){
26469             this.el.addClass("x-btn-pressed");
26470         }
26471         if(Roo.isIE && !Roo.isIE7){
26472             this.autoWidth.defer(1, this);
26473         }else{
26474             this.autoWidth();
26475         }
26476         if(this.menu){
26477             this.menu.on("show", this.onMenuShow, this);
26478             this.menu.on("hide", this.onMenuHide, this);
26479         }
26480         this.fireEvent('render', this);
26481     },
26482
26483     // private
26484     autoWidth : function(){
26485         if(this.el){
26486             var tbl = this.el.child("table:first");
26487             var tbl2 = this.el.child("table:last");
26488             this.el.setWidth("auto");
26489             tbl.setWidth("auto");
26490             if(Roo.isIE7 && Roo.isStrict){
26491                 var ib = this.el.child('button:first');
26492                 if(ib && ib.getWidth() > 20){
26493                     ib.clip();
26494                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26495                 }
26496             }
26497             if(this.minWidth){
26498                 if(this.hidden){
26499                     this.el.beginMeasure();
26500                 }
26501                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26502                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26503                 }
26504                 if(this.hidden){
26505                     this.el.endMeasure();
26506                 }
26507             }
26508             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26509         } 
26510     },
26511     /**
26512      * Sets this button's click handler
26513      * @param {Function} handler The function to call when the button is clicked
26514      * @param {Object} scope (optional) Scope for the function passed above
26515      */
26516     setHandler : function(handler, scope){
26517         this.handler = handler;
26518         this.scope = scope;  
26519     },
26520     
26521     /**
26522      * Sets this button's arrow click handler
26523      * @param {Function} handler The function to call when the arrow is clicked
26524      * @param {Object} scope (optional) Scope for the function passed above
26525      */
26526     setArrowHandler : function(handler, scope){
26527         this.arrowHandler = handler;
26528         this.scope = scope;  
26529     },
26530     
26531     /**
26532      * Focus the button
26533      */
26534     focus : function(){
26535         if(this.el){
26536             this.el.child("button:first").focus();
26537         }
26538     },
26539
26540     // private
26541     onClick : function(e){
26542         e.preventDefault();
26543         if(!this.disabled){
26544             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26545                 if(this.menu && !this.menu.isVisible()){
26546                     this.menu.show(this.el, this.menuAlign);
26547                 }
26548                 this.fireEvent("arrowclick", this, e);
26549                 if(this.arrowHandler){
26550                     this.arrowHandler.call(this.scope || this, this, e);
26551                 }
26552             }else{
26553                 this.fireEvent("click", this, e);
26554                 if(this.handler){
26555                     this.handler.call(this.scope || this, this, e);
26556                 }
26557             }
26558         }
26559     },
26560     // private
26561     onMouseDown : function(e){
26562         if(!this.disabled){
26563             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26564         }
26565     },
26566     // private
26567     onMouseUp : function(e){
26568         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26569     }   
26570 });
26571
26572
26573 // backwards compat
26574 Roo.MenuButton = Roo.SplitButton;/*
26575  * Based on:
26576  * Ext JS Library 1.1.1
26577  * Copyright(c) 2006-2007, Ext JS, LLC.
26578  *
26579  * Originally Released Under LGPL - original licence link has changed is not relivant.
26580  *
26581  * Fork - LGPL
26582  * <script type="text/javascript">
26583  */
26584
26585 /**
26586  * @class Roo.Toolbar
26587  * Basic Toolbar class.
26588  * @constructor
26589  * Creates a new Toolbar
26590  * @param {Object} config The config object
26591  */ 
26592 Roo.Toolbar = function(container, buttons, config)
26593 {
26594     /// old consturctor format still supported..
26595     if(container instanceof Array){ // omit the container for later rendering
26596         buttons = container;
26597         config = buttons;
26598         container = null;
26599     }
26600     if (typeof(container) == 'object' && container.xtype) {
26601         config = container;
26602         container = config.container;
26603         buttons = config.buttons; // not really - use items!!
26604     }
26605     var xitems = [];
26606     if (config && config.items) {
26607         xitems = config.items;
26608         delete config.items;
26609     }
26610     Roo.apply(this, config);
26611     this.buttons = buttons;
26612     
26613     if(container){
26614         this.render(container);
26615     }
26616     Roo.each(xitems, function(b) {
26617         this.add(b);
26618     }, this);
26619     
26620 };
26621
26622 Roo.Toolbar.prototype = {
26623     /**
26624      * @cfg {Roo.data.Store} items
26625      * array of button configs or elements to add
26626      */
26627     
26628     /**
26629      * @cfg {String/HTMLElement/Element} container
26630      * The id or element that will contain the toolbar
26631      */
26632     // private
26633     render : function(ct){
26634         this.el = Roo.get(ct);
26635         if(this.cls){
26636             this.el.addClass(this.cls);
26637         }
26638         // using a table allows for vertical alignment
26639         // 100% width is needed by Safari...
26640         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26641         this.tr = this.el.child("tr", true);
26642         var autoId = 0;
26643         this.items = new Roo.util.MixedCollection(false, function(o){
26644             return o.id || ("item" + (++autoId));
26645         });
26646         if(this.buttons){
26647             this.add.apply(this, this.buttons);
26648             delete this.buttons;
26649         }
26650     },
26651
26652     /**
26653      * Adds element(s) to the toolbar -- this function takes a variable number of 
26654      * arguments of mixed type and adds them to the toolbar.
26655      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26656      * <ul>
26657      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26658      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26659      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26660      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26661      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26662      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26663      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26664      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26665      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26666      * </ul>
26667      * @param {Mixed} arg2
26668      * @param {Mixed} etc.
26669      */
26670     add : function(){
26671         var a = arguments, l = a.length;
26672         for(var i = 0; i < l; i++){
26673             this._add(a[i]);
26674         }
26675     },
26676     // private..
26677     _add : function(el) {
26678         
26679         if (el.xtype) {
26680             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26681         }
26682         
26683         if (el.applyTo){ // some kind of form field
26684             return this.addField(el);
26685         } 
26686         if (el.render){ // some kind of Toolbar.Item
26687             return this.addItem(el);
26688         }
26689         if (typeof el == "string"){ // string
26690             if(el == "separator" || el == "-"){
26691                 return this.addSeparator();
26692             }
26693             if (el == " "){
26694                 return this.addSpacer();
26695             }
26696             if(el == "->"){
26697                 return this.addFill();
26698             }
26699             return this.addText(el);
26700             
26701         }
26702         if(el.tagName){ // element
26703             return this.addElement(el);
26704         }
26705         if(typeof el == "object"){ // must be button config?
26706             return this.addButton(el);
26707         }
26708         // and now what?!?!
26709         return false;
26710         
26711     },
26712     
26713     /**
26714      * Add an Xtype element
26715      * @param {Object} xtype Xtype Object
26716      * @return {Object} created Object
26717      */
26718     addxtype : function(e){
26719         return this.add(e);  
26720     },
26721     
26722     /**
26723      * Returns the Element for this toolbar.
26724      * @return {Roo.Element}
26725      */
26726     getEl : function(){
26727         return this.el;  
26728     },
26729     
26730     /**
26731      * Adds a separator
26732      * @return {Roo.Toolbar.Item} The separator item
26733      */
26734     addSeparator : function(){
26735         return this.addItem(new Roo.Toolbar.Separator());
26736     },
26737
26738     /**
26739      * Adds a spacer element
26740      * @return {Roo.Toolbar.Spacer} The spacer item
26741      */
26742     addSpacer : function(){
26743         return this.addItem(new Roo.Toolbar.Spacer());
26744     },
26745
26746     /**
26747      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26748      * @return {Roo.Toolbar.Fill} The fill item
26749      */
26750     addFill : function(){
26751         return this.addItem(new Roo.Toolbar.Fill());
26752     },
26753
26754     /**
26755      * Adds any standard HTML element to the toolbar
26756      * @param {String/HTMLElement/Element} el The element or id of the element to add
26757      * @return {Roo.Toolbar.Item} The element's item
26758      */
26759     addElement : function(el){
26760         return this.addItem(new Roo.Toolbar.Item(el));
26761     },
26762     /**
26763      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26764      * @type Roo.util.MixedCollection  
26765      */
26766     items : false,
26767      
26768     /**
26769      * Adds any Toolbar.Item or subclass
26770      * @param {Roo.Toolbar.Item} item
26771      * @return {Roo.Toolbar.Item} The item
26772      */
26773     addItem : function(item){
26774         var td = this.nextBlock();
26775         item.render(td);
26776         this.items.add(item);
26777         return item;
26778     },
26779     
26780     /**
26781      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26782      * @param {Object/Array} config A button config or array of configs
26783      * @return {Roo.Toolbar.Button/Array}
26784      */
26785     addButton : function(config){
26786         if(config instanceof Array){
26787             var buttons = [];
26788             for(var i = 0, len = config.length; i < len; i++) {
26789                 buttons.push(this.addButton(config[i]));
26790             }
26791             return buttons;
26792         }
26793         var b = config;
26794         if(!(config instanceof Roo.Toolbar.Button)){
26795             b = config.split ?
26796                 new Roo.Toolbar.SplitButton(config) :
26797                 new Roo.Toolbar.Button(config);
26798         }
26799         var td = this.nextBlock();
26800         b.render(td);
26801         this.items.add(b);
26802         return b;
26803     },
26804     
26805     /**
26806      * Adds text to the toolbar
26807      * @param {String} text The text to add
26808      * @return {Roo.Toolbar.Item} The element's item
26809      */
26810     addText : function(text){
26811         return this.addItem(new Roo.Toolbar.TextItem(text));
26812     },
26813     
26814     /**
26815      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26816      * @param {Number} index The index where the item is to be inserted
26817      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26818      * @return {Roo.Toolbar.Button/Item}
26819      */
26820     insertButton : function(index, item){
26821         if(item instanceof Array){
26822             var buttons = [];
26823             for(var i = 0, len = item.length; i < len; i++) {
26824                buttons.push(this.insertButton(index + i, item[i]));
26825             }
26826             return buttons;
26827         }
26828         if (!(item instanceof Roo.Toolbar.Button)){
26829            item = new Roo.Toolbar.Button(item);
26830         }
26831         var td = document.createElement("td");
26832         this.tr.insertBefore(td, this.tr.childNodes[index]);
26833         item.render(td);
26834         this.items.insert(index, item);
26835         return item;
26836     },
26837     
26838     /**
26839      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26840      * @param {Object} config
26841      * @return {Roo.Toolbar.Item} The element's item
26842      */
26843     addDom : function(config, returnEl){
26844         var td = this.nextBlock();
26845         Roo.DomHelper.overwrite(td, config);
26846         var ti = new Roo.Toolbar.Item(td.firstChild);
26847         ti.render(td);
26848         this.items.add(ti);
26849         return ti;
26850     },
26851
26852     /**
26853      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26854      * @type Roo.util.MixedCollection  
26855      */
26856     fields : false,
26857     
26858     /**
26859      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26860      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26861      * @param {Roo.form.Field} field
26862      * @return {Roo.ToolbarItem}
26863      */
26864      
26865       
26866     addField : function(field) {
26867         if (!this.fields) {
26868             var autoId = 0;
26869             this.fields = new Roo.util.MixedCollection(false, function(o){
26870                 return o.id || ("item" + (++autoId));
26871             });
26872
26873         }
26874         
26875         var td = this.nextBlock();
26876         field.render(td);
26877         var ti = new Roo.Toolbar.Item(td.firstChild);
26878         ti.render(td);
26879         this.items.add(ti);
26880         this.fields.add(field);
26881         return ti;
26882     },
26883     /**
26884      * Hide the toolbar
26885      * @method hide
26886      */
26887      
26888       
26889     hide : function()
26890     {
26891         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26892         this.el.child('div').hide();
26893     },
26894     /**
26895      * Show the toolbar
26896      * @method show
26897      */
26898     show : function()
26899     {
26900         this.el.child('div').show();
26901     },
26902       
26903     // private
26904     nextBlock : function(){
26905         var td = document.createElement("td");
26906         this.tr.appendChild(td);
26907         return td;
26908     },
26909
26910     // private
26911     destroy : function(){
26912         if(this.items){ // rendered?
26913             Roo.destroy.apply(Roo, this.items.items);
26914         }
26915         if(this.fields){ // rendered?
26916             Roo.destroy.apply(Roo, this.fields.items);
26917         }
26918         Roo.Element.uncache(this.el, this.tr);
26919     }
26920 };
26921
26922 /**
26923  * @class Roo.Toolbar.Item
26924  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26925  * @constructor
26926  * Creates a new Item
26927  * @param {HTMLElement} el 
26928  */
26929 Roo.Toolbar.Item = function(el){
26930     this.el = Roo.getDom(el);
26931     this.id = Roo.id(this.el);
26932     this.hidden = false;
26933 };
26934
26935 Roo.Toolbar.Item.prototype = {
26936     
26937     /**
26938      * Get this item's HTML Element
26939      * @return {HTMLElement}
26940      */
26941     getEl : function(){
26942        return this.el;  
26943     },
26944
26945     // private
26946     render : function(td){
26947         this.td = td;
26948         td.appendChild(this.el);
26949     },
26950     
26951     /**
26952      * Removes and destroys this item.
26953      */
26954     destroy : function(){
26955         this.td.parentNode.removeChild(this.td);
26956     },
26957     
26958     /**
26959      * Shows this item.
26960      */
26961     show: function(){
26962         this.hidden = false;
26963         this.td.style.display = "";
26964     },
26965     
26966     /**
26967      * Hides this item.
26968      */
26969     hide: function(){
26970         this.hidden = true;
26971         this.td.style.display = "none";
26972     },
26973     
26974     /**
26975      * Convenience function for boolean show/hide.
26976      * @param {Boolean} visible true to show/false to hide
26977      */
26978     setVisible: function(visible){
26979         if(visible) {
26980             this.show();
26981         }else{
26982             this.hide();
26983         }
26984     },
26985     
26986     /**
26987      * Try to focus this item.
26988      */
26989     focus : function(){
26990         Roo.fly(this.el).focus();
26991     },
26992     
26993     /**
26994      * Disables this item.
26995      */
26996     disable : function(){
26997         Roo.fly(this.td).addClass("x-item-disabled");
26998         this.disabled = true;
26999         this.el.disabled = true;
27000     },
27001     
27002     /**
27003      * Enables this item.
27004      */
27005     enable : function(){
27006         Roo.fly(this.td).removeClass("x-item-disabled");
27007         this.disabled = false;
27008         this.el.disabled = false;
27009     }
27010 };
27011
27012
27013 /**
27014  * @class Roo.Toolbar.Separator
27015  * @extends Roo.Toolbar.Item
27016  * A simple toolbar separator class
27017  * @constructor
27018  * Creates a new Separator
27019  */
27020 Roo.Toolbar.Separator = function(){
27021     var s = document.createElement("span");
27022     s.className = "ytb-sep";
27023     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27024 };
27025 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27026     enable:Roo.emptyFn,
27027     disable:Roo.emptyFn,
27028     focus:Roo.emptyFn
27029 });
27030
27031 /**
27032  * @class Roo.Toolbar.Spacer
27033  * @extends Roo.Toolbar.Item
27034  * A simple element that adds extra horizontal space to a toolbar.
27035  * @constructor
27036  * Creates a new Spacer
27037  */
27038 Roo.Toolbar.Spacer = function(){
27039     var s = document.createElement("div");
27040     s.className = "ytb-spacer";
27041     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27042 };
27043 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27044     enable:Roo.emptyFn,
27045     disable:Roo.emptyFn,
27046     focus:Roo.emptyFn
27047 });
27048
27049 /**
27050  * @class Roo.Toolbar.Fill
27051  * @extends Roo.Toolbar.Spacer
27052  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27053  * @constructor
27054  * Creates a new Spacer
27055  */
27056 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27057     // private
27058     render : function(td){
27059         td.style.width = '100%';
27060         Roo.Toolbar.Fill.superclass.render.call(this, td);
27061     }
27062 });
27063
27064 /**
27065  * @class Roo.Toolbar.TextItem
27066  * @extends Roo.Toolbar.Item
27067  * A simple class that renders text directly into a toolbar.
27068  * @constructor
27069  * Creates a new TextItem
27070  * @param {String} text
27071  */
27072 Roo.Toolbar.TextItem = function(text){
27073     if (typeof(text) == 'object') {
27074         text = text.text;
27075     }
27076     var s = document.createElement("span");
27077     s.className = "ytb-text";
27078     s.innerHTML = text;
27079     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27080 };
27081 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27082     enable:Roo.emptyFn,
27083     disable:Roo.emptyFn,
27084     focus:Roo.emptyFn
27085 });
27086
27087 /**
27088  * @class Roo.Toolbar.Button
27089  * @extends Roo.Button
27090  * A button that renders into a toolbar.
27091  * @constructor
27092  * Creates a new Button
27093  * @param {Object} config A standard {@link Roo.Button} config object
27094  */
27095 Roo.Toolbar.Button = function(config){
27096     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27097 };
27098 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27099     render : function(td){
27100         this.td = td;
27101         Roo.Toolbar.Button.superclass.render.call(this, td);
27102     },
27103     
27104     /**
27105      * Removes and destroys this button
27106      */
27107     destroy : function(){
27108         Roo.Toolbar.Button.superclass.destroy.call(this);
27109         this.td.parentNode.removeChild(this.td);
27110     },
27111     
27112     /**
27113      * Shows this button
27114      */
27115     show: function(){
27116         this.hidden = false;
27117         this.td.style.display = "";
27118     },
27119     
27120     /**
27121      * Hides this button
27122      */
27123     hide: function(){
27124         this.hidden = true;
27125         this.td.style.display = "none";
27126     },
27127
27128     /**
27129      * Disables this item
27130      */
27131     disable : function(){
27132         Roo.fly(this.td).addClass("x-item-disabled");
27133         this.disabled = true;
27134     },
27135
27136     /**
27137      * Enables this item
27138      */
27139     enable : function(){
27140         Roo.fly(this.td).removeClass("x-item-disabled");
27141         this.disabled = false;
27142     }
27143 });
27144 // backwards compat
27145 Roo.ToolbarButton = Roo.Toolbar.Button;
27146
27147 /**
27148  * @class Roo.Toolbar.SplitButton
27149  * @extends Roo.SplitButton
27150  * A menu button that renders into a toolbar.
27151  * @constructor
27152  * Creates a new SplitButton
27153  * @param {Object} config A standard {@link Roo.SplitButton} config object
27154  */
27155 Roo.Toolbar.SplitButton = function(config){
27156     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27157 };
27158 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27159     render : function(td){
27160         this.td = td;
27161         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27162     },
27163     
27164     /**
27165      * Removes and destroys this button
27166      */
27167     destroy : function(){
27168         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27169         this.td.parentNode.removeChild(this.td);
27170     },
27171     
27172     /**
27173      * Shows this button
27174      */
27175     show: function(){
27176         this.hidden = false;
27177         this.td.style.display = "";
27178     },
27179     
27180     /**
27181      * Hides this button
27182      */
27183     hide: function(){
27184         this.hidden = true;
27185         this.td.style.display = "none";
27186     }
27187 });
27188
27189 // backwards compat
27190 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27191  * Based on:
27192  * Ext JS Library 1.1.1
27193  * Copyright(c) 2006-2007, Ext JS, LLC.
27194  *
27195  * Originally Released Under LGPL - original licence link has changed is not relivant.
27196  *
27197  * Fork - LGPL
27198  * <script type="text/javascript">
27199  */
27200  
27201 /**
27202  * @class Roo.PagingToolbar
27203  * @extends Roo.Toolbar
27204  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27205  * @constructor
27206  * Create a new PagingToolbar
27207  * @param {Object} config The config object
27208  */
27209 Roo.PagingToolbar = function(el, ds, config)
27210 {
27211     // old args format still supported... - xtype is prefered..
27212     if (typeof(el) == 'object' && el.xtype) {
27213         // created from xtype...
27214         config = el;
27215         ds = el.dataSource;
27216         el = config.container;
27217     }
27218     var items = [];
27219     if (config.items) {
27220         items = config.items;
27221         config.items = [];
27222     }
27223     
27224     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27225     this.ds = ds;
27226     this.cursor = 0;
27227     this.renderButtons(this.el);
27228     this.bind(ds);
27229     
27230     // supprot items array.
27231    
27232     Roo.each(items, function(e) {
27233         this.add(Roo.factory(e));
27234     },this);
27235     
27236 };
27237
27238 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27239     /**
27240      * @cfg {Roo.data.Store} dataSource
27241      * The underlying data store providing the paged data
27242      */
27243     /**
27244      * @cfg {String/HTMLElement/Element} container
27245      * container The id or element that will contain the toolbar
27246      */
27247     /**
27248      * @cfg {Boolean} displayInfo
27249      * True to display the displayMsg (defaults to false)
27250      */
27251     /**
27252      * @cfg {Number} pageSize
27253      * The number of records to display per page (defaults to 20)
27254      */
27255     pageSize: 20,
27256     /**
27257      * @cfg {String} displayMsg
27258      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27259      */
27260     displayMsg : 'Displaying {0} - {1} of {2}',
27261     /**
27262      * @cfg {String} emptyMsg
27263      * The message to display when no records are found (defaults to "No data to display")
27264      */
27265     emptyMsg : 'No data to display',
27266     /**
27267      * Customizable piece of the default paging text (defaults to "Page")
27268      * @type String
27269      */
27270     beforePageText : "Page",
27271     /**
27272      * Customizable piece of the default paging text (defaults to "of %0")
27273      * @type String
27274      */
27275     afterPageText : "of {0}",
27276     /**
27277      * Customizable piece of the default paging text (defaults to "First Page")
27278      * @type String
27279      */
27280     firstText : "First Page",
27281     /**
27282      * Customizable piece of the default paging text (defaults to "Previous Page")
27283      * @type String
27284      */
27285     prevText : "Previous Page",
27286     /**
27287      * Customizable piece of the default paging text (defaults to "Next Page")
27288      * @type String
27289      */
27290     nextText : "Next Page",
27291     /**
27292      * Customizable piece of the default paging text (defaults to "Last Page")
27293      * @type String
27294      */
27295     lastText : "Last Page",
27296     /**
27297      * Customizable piece of the default paging text (defaults to "Refresh")
27298      * @type String
27299      */
27300     refreshText : "Refresh",
27301
27302     // private
27303     renderButtons : function(el){
27304         Roo.PagingToolbar.superclass.render.call(this, el);
27305         this.first = this.addButton({
27306             tooltip: this.firstText,
27307             cls: "x-btn-icon x-grid-page-first",
27308             disabled: true,
27309             handler: this.onClick.createDelegate(this, ["first"])
27310         });
27311         this.prev = this.addButton({
27312             tooltip: this.prevText,
27313             cls: "x-btn-icon x-grid-page-prev",
27314             disabled: true,
27315             handler: this.onClick.createDelegate(this, ["prev"])
27316         });
27317         //this.addSeparator();
27318         this.add(this.beforePageText);
27319         this.field = Roo.get(this.addDom({
27320            tag: "input",
27321            type: "text",
27322            size: "3",
27323            value: "1",
27324            cls: "x-grid-page-number"
27325         }).el);
27326         this.field.on("keydown", this.onPagingKeydown, this);
27327         this.field.on("focus", function(){this.dom.select();});
27328         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27329         this.field.setHeight(18);
27330         //this.addSeparator();
27331         this.next = this.addButton({
27332             tooltip: this.nextText,
27333             cls: "x-btn-icon x-grid-page-next",
27334             disabled: true,
27335             handler: this.onClick.createDelegate(this, ["next"])
27336         });
27337         this.last = this.addButton({
27338             tooltip: this.lastText,
27339             cls: "x-btn-icon x-grid-page-last",
27340             disabled: true,
27341             handler: this.onClick.createDelegate(this, ["last"])
27342         });
27343         //this.addSeparator();
27344         this.loading = this.addButton({
27345             tooltip: this.refreshText,
27346             cls: "x-btn-icon x-grid-loading",
27347             handler: this.onClick.createDelegate(this, ["refresh"])
27348         });
27349
27350         if(this.displayInfo){
27351             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27352         }
27353     },
27354
27355     // private
27356     updateInfo : function(){
27357         if(this.displayEl){
27358             var count = this.ds.getCount();
27359             var msg = count == 0 ?
27360                 this.emptyMsg :
27361                 String.format(
27362                     this.displayMsg,
27363                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27364                 );
27365             this.displayEl.update(msg);
27366         }
27367     },
27368
27369     // private
27370     onLoad : function(ds, r, o){
27371        this.cursor = o.params ? o.params.start : 0;
27372        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27373
27374        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27375        this.field.dom.value = ap;
27376        this.first.setDisabled(ap == 1);
27377        this.prev.setDisabled(ap == 1);
27378        this.next.setDisabled(ap == ps);
27379        this.last.setDisabled(ap == ps);
27380        this.loading.enable();
27381        this.updateInfo();
27382     },
27383
27384     // private
27385     getPageData : function(){
27386         var total = this.ds.getTotalCount();
27387         return {
27388             total : total,
27389             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27390             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27391         };
27392     },
27393
27394     // private
27395     onLoadError : function(){
27396         this.loading.enable();
27397     },
27398
27399     // private
27400     onPagingKeydown : function(e){
27401         var k = e.getKey();
27402         var d = this.getPageData();
27403         if(k == e.RETURN){
27404             var v = this.field.dom.value, pageNum;
27405             if(!v || isNaN(pageNum = parseInt(v, 10))){
27406                 this.field.dom.value = d.activePage;
27407                 return;
27408             }
27409             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27410             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27411             e.stopEvent();
27412         }
27413         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))
27414         {
27415           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27416           this.field.dom.value = pageNum;
27417           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27418           e.stopEvent();
27419         }
27420         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27421         {
27422           var v = this.field.dom.value, pageNum; 
27423           var increment = (e.shiftKey) ? 10 : 1;
27424           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27425             increment *= -1;
27426           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27427             this.field.dom.value = d.activePage;
27428             return;
27429           }
27430           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27431           {
27432             this.field.dom.value = parseInt(v, 10) + increment;
27433             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27434             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27435           }
27436           e.stopEvent();
27437         }
27438     },
27439
27440     // private
27441     beforeLoad : function(){
27442         if(this.loading){
27443             this.loading.disable();
27444         }
27445     },
27446
27447     // private
27448     onClick : function(which){
27449         var ds = this.ds;
27450         switch(which){
27451             case "first":
27452                 ds.load({params:{start: 0, limit: this.pageSize}});
27453             break;
27454             case "prev":
27455                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27456             break;
27457             case "next":
27458                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27459             break;
27460             case "last":
27461                 var total = ds.getTotalCount();
27462                 var extra = total % this.pageSize;
27463                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27464                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27465             break;
27466             case "refresh":
27467                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27468             break;
27469         }
27470     },
27471
27472     /**
27473      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27474      * @param {Roo.data.Store} store The data store to unbind
27475      */
27476     unbind : function(ds){
27477         ds.un("beforeload", this.beforeLoad, this);
27478         ds.un("load", this.onLoad, this);
27479         ds.un("loadexception", this.onLoadError, this);
27480         ds.un("remove", this.updateInfo, this);
27481         ds.un("add", this.updateInfo, this);
27482         this.ds = undefined;
27483     },
27484
27485     /**
27486      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27487      * @param {Roo.data.Store} store The data store to bind
27488      */
27489     bind : function(ds){
27490         ds.on("beforeload", this.beforeLoad, this);
27491         ds.on("load", this.onLoad, this);
27492         ds.on("loadexception", this.onLoadError, this);
27493         ds.on("remove", this.updateInfo, this);
27494         ds.on("add", this.updateInfo, this);
27495         this.ds = ds;
27496     }
27497 });/*
27498  * Based on:
27499  * Ext JS Library 1.1.1
27500  * Copyright(c) 2006-2007, Ext JS, LLC.
27501  *
27502  * Originally Released Under LGPL - original licence link has changed is not relivant.
27503  *
27504  * Fork - LGPL
27505  * <script type="text/javascript">
27506  */
27507
27508 /**
27509  * @class Roo.Resizable
27510  * @extends Roo.util.Observable
27511  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27512  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27513  * 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
27514  * the element will be wrapped for you automatically.</p>
27515  * <p>Here is the list of valid resize handles:</p>
27516  * <pre>
27517 Value   Description
27518 ------  -------------------
27519  'n'     north
27520  's'     south
27521  'e'     east
27522  'w'     west
27523  'nw'    northwest
27524  'sw'    southwest
27525  'se'    southeast
27526  'ne'    northeast
27527  'hd'    horizontal drag
27528  'all'   all
27529 </pre>
27530  * <p>Here's an example showing the creation of a typical Resizable:</p>
27531  * <pre><code>
27532 var resizer = new Roo.Resizable("element-id", {
27533     handles: 'all',
27534     minWidth: 200,
27535     minHeight: 100,
27536     maxWidth: 500,
27537     maxHeight: 400,
27538     pinned: true
27539 });
27540 resizer.on("resize", myHandler);
27541 </code></pre>
27542  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27543  * resizer.east.setDisplayed(false);</p>
27544  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27545  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27546  * resize operation's new size (defaults to [0, 0])
27547  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27548  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27549  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27550  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27551  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27552  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27553  * @cfg {Number} width The width of the element in pixels (defaults to null)
27554  * @cfg {Number} height The height of the element in pixels (defaults to null)
27555  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27556  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27557  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27558  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27559  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27560  * in favor of the handles config option (defaults to false)
27561  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27562  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27563  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27564  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27565  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27566  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27567  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27568  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27569  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27570  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27571  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27572  * @constructor
27573  * Create a new resizable component
27574  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27575  * @param {Object} config configuration options
27576   */
27577 Roo.Resizable = function(el, config)
27578 {
27579     this.el = Roo.get(el);
27580
27581     if(config && config.wrap){
27582         config.resizeChild = this.el;
27583         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27584         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27585         this.el.setStyle("overflow", "hidden");
27586         this.el.setPositioning(config.resizeChild.getPositioning());
27587         config.resizeChild.clearPositioning();
27588         if(!config.width || !config.height){
27589             var csize = config.resizeChild.getSize();
27590             this.el.setSize(csize.width, csize.height);
27591         }
27592         if(config.pinned && !config.adjustments){
27593             config.adjustments = "auto";
27594         }
27595     }
27596
27597     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27598     this.proxy.unselectable();
27599     this.proxy.enableDisplayMode('block');
27600
27601     Roo.apply(this, config);
27602
27603     if(this.pinned){
27604         this.disableTrackOver = true;
27605         this.el.addClass("x-resizable-pinned");
27606     }
27607     // if the element isn't positioned, make it relative
27608     var position = this.el.getStyle("position");
27609     if(position != "absolute" && position != "fixed"){
27610         this.el.setStyle("position", "relative");
27611     }
27612     if(!this.handles){ // no handles passed, must be legacy style
27613         this.handles = 's,e,se';
27614         if(this.multiDirectional){
27615             this.handles += ',n,w';
27616         }
27617     }
27618     if(this.handles == "all"){
27619         this.handles = "n s e w ne nw se sw";
27620     }
27621     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27622     var ps = Roo.Resizable.positions;
27623     for(var i = 0, len = hs.length; i < len; i++){
27624         if(hs[i] && ps[hs[i]]){
27625             var pos = ps[hs[i]];
27626             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27627         }
27628     }
27629     // legacy
27630     this.corner = this.southeast;
27631     
27632     // updateBox = the box can move..
27633     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27634         this.updateBox = true;
27635     }
27636
27637     this.activeHandle = null;
27638
27639     if(this.resizeChild){
27640         if(typeof this.resizeChild == "boolean"){
27641             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27642         }else{
27643             this.resizeChild = Roo.get(this.resizeChild, true);
27644         }
27645     }
27646     
27647     if(this.adjustments == "auto"){
27648         var rc = this.resizeChild;
27649         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27650         if(rc && (hw || hn)){
27651             rc.position("relative");
27652             rc.setLeft(hw ? hw.el.getWidth() : 0);
27653             rc.setTop(hn ? hn.el.getHeight() : 0);
27654         }
27655         this.adjustments = [
27656             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27657             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27658         ];
27659     }
27660
27661     if(this.draggable){
27662         this.dd = this.dynamic ?
27663             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27664         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27665     }
27666
27667     // public events
27668     this.addEvents({
27669         /**
27670          * @event beforeresize
27671          * Fired before resize is allowed. Set enabled to false to cancel resize.
27672          * @param {Roo.Resizable} this
27673          * @param {Roo.EventObject} e The mousedown event
27674          */
27675         "beforeresize" : true,
27676         /**
27677          * @event resize
27678          * Fired after a resize.
27679          * @param {Roo.Resizable} this
27680          * @param {Number} width The new width
27681          * @param {Number} height The new height
27682          * @param {Roo.EventObject} e The mouseup event
27683          */
27684         "resize" : true
27685     });
27686
27687     if(this.width !== null && this.height !== null){
27688         this.resizeTo(this.width, this.height);
27689     }else{
27690         this.updateChildSize();
27691     }
27692     if(Roo.isIE){
27693         this.el.dom.style.zoom = 1;
27694     }
27695     Roo.Resizable.superclass.constructor.call(this);
27696 };
27697
27698 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27699         resizeChild : false,
27700         adjustments : [0, 0],
27701         minWidth : 5,
27702         minHeight : 5,
27703         maxWidth : 10000,
27704         maxHeight : 10000,
27705         enabled : true,
27706         animate : false,
27707         duration : .35,
27708         dynamic : false,
27709         handles : false,
27710         multiDirectional : false,
27711         disableTrackOver : false,
27712         easing : 'easeOutStrong',
27713         widthIncrement : 0,
27714         heightIncrement : 0,
27715         pinned : false,
27716         width : null,
27717         height : null,
27718         preserveRatio : false,
27719         transparent: false,
27720         minX: 0,
27721         minY: 0,
27722         draggable: false,
27723
27724         /**
27725          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27726          */
27727         constrainTo: undefined,
27728         /**
27729          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27730          */
27731         resizeRegion: undefined,
27732
27733
27734     /**
27735      * Perform a manual resize
27736      * @param {Number} width
27737      * @param {Number} height
27738      */
27739     resizeTo : function(width, height){
27740         this.el.setSize(width, height);
27741         this.updateChildSize();
27742         this.fireEvent("resize", this, width, height, null);
27743     },
27744
27745     // private
27746     startSizing : function(e, handle){
27747         this.fireEvent("beforeresize", this, e);
27748         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27749
27750             if(!this.overlay){
27751                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27752                 this.overlay.unselectable();
27753                 this.overlay.enableDisplayMode("block");
27754                 this.overlay.on("mousemove", this.onMouseMove, this);
27755                 this.overlay.on("mouseup", this.onMouseUp, this);
27756             }
27757             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27758
27759             this.resizing = true;
27760             this.startBox = this.el.getBox();
27761             this.startPoint = e.getXY();
27762             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27763                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27764
27765             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27766             this.overlay.show();
27767
27768             if(this.constrainTo) {
27769                 var ct = Roo.get(this.constrainTo);
27770                 this.resizeRegion = ct.getRegion().adjust(
27771                     ct.getFrameWidth('t'),
27772                     ct.getFrameWidth('l'),
27773                     -ct.getFrameWidth('b'),
27774                     -ct.getFrameWidth('r')
27775                 );
27776             }
27777
27778             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27779             this.proxy.show();
27780             this.proxy.setBox(this.startBox);
27781             if(!this.dynamic){
27782                 this.proxy.setStyle('visibility', 'visible');
27783             }
27784         }
27785     },
27786
27787     // private
27788     onMouseDown : function(handle, e){
27789         if(this.enabled){
27790             e.stopEvent();
27791             this.activeHandle = handle;
27792             this.startSizing(e, handle);
27793         }
27794     },
27795
27796     // private
27797     onMouseUp : function(e){
27798         var size = this.resizeElement();
27799         this.resizing = false;
27800         this.handleOut();
27801         this.overlay.hide();
27802         this.proxy.hide();
27803         this.fireEvent("resize", this, size.width, size.height, e);
27804     },
27805
27806     // private
27807     updateChildSize : function(){
27808         if(this.resizeChild){
27809             var el = this.el;
27810             var child = this.resizeChild;
27811             var adj = this.adjustments;
27812             if(el.dom.offsetWidth){
27813                 var b = el.getSize(true);
27814                 child.setSize(b.width+adj[0], b.height+adj[1]);
27815             }
27816             // Second call here for IE
27817             // The first call enables instant resizing and
27818             // the second call corrects scroll bars if they
27819             // exist
27820             if(Roo.isIE){
27821                 setTimeout(function(){
27822                     if(el.dom.offsetWidth){
27823                         var b = el.getSize(true);
27824                         child.setSize(b.width+adj[0], b.height+adj[1]);
27825                     }
27826                 }, 10);
27827             }
27828         }
27829     },
27830
27831     // private
27832     snap : function(value, inc, min){
27833         if(!inc || !value) return value;
27834         var newValue = value;
27835         var m = value % inc;
27836         if(m > 0){
27837             if(m > (inc/2)){
27838                 newValue = value + (inc-m);
27839             }else{
27840                 newValue = value - m;
27841             }
27842         }
27843         return Math.max(min, newValue);
27844     },
27845
27846     // private
27847     resizeElement : function(){
27848         var box = this.proxy.getBox();
27849         if(this.updateBox){
27850             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27851         }else{
27852             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27853         }
27854         this.updateChildSize();
27855         if(!this.dynamic){
27856             this.proxy.hide();
27857         }
27858         return box;
27859     },
27860
27861     // private
27862     constrain : function(v, diff, m, mx){
27863         if(v - diff < m){
27864             diff = v - m;
27865         }else if(v - diff > mx){
27866             diff = mx - v;
27867         }
27868         return diff;
27869     },
27870
27871     // private
27872     onMouseMove : function(e){
27873         if(this.enabled){
27874             try{// try catch so if something goes wrong the user doesn't get hung
27875
27876             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27877                 return;
27878             }
27879
27880             //var curXY = this.startPoint;
27881             var curSize = this.curSize || this.startBox;
27882             var x = this.startBox.x, y = this.startBox.y;
27883             var ox = x, oy = y;
27884             var w = curSize.width, h = curSize.height;
27885             var ow = w, oh = h;
27886             var mw = this.minWidth, mh = this.minHeight;
27887             var mxw = this.maxWidth, mxh = this.maxHeight;
27888             var wi = this.widthIncrement;
27889             var hi = this.heightIncrement;
27890
27891             var eventXY = e.getXY();
27892             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27893             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27894
27895             var pos = this.activeHandle.position;
27896
27897             switch(pos){
27898                 case "east":
27899                     w += diffX;
27900                     w = Math.min(Math.max(mw, w), mxw);
27901                     break;
27902              
27903                 case "south":
27904                     h += diffY;
27905                     h = Math.min(Math.max(mh, h), mxh);
27906                     break;
27907                 case "southeast":
27908                     w += diffX;
27909                     h += diffY;
27910                     w = Math.min(Math.max(mw, w), mxw);
27911                     h = Math.min(Math.max(mh, h), mxh);
27912                     break;
27913                 case "north":
27914                     diffY = this.constrain(h, diffY, mh, mxh);
27915                     y += diffY;
27916                     h -= diffY;
27917                     break;
27918                 case "hdrag":
27919                     
27920                     if (wi) {
27921                         var adiffX = Math.abs(diffX);
27922                         var sub = (adiffX % wi); // how much 
27923                         if (sub > (wi/2)) { // far enough to snap
27924                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27925                         } else {
27926                             // remove difference.. 
27927                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27928                         }
27929                     }
27930                     x += diffX;
27931                     x = Math.max(this.minX, x);
27932                     break;
27933                 case "west":
27934                     diffX = this.constrain(w, diffX, mw, mxw);
27935                     x += diffX;
27936                     w -= diffX;
27937                     break;
27938                 case "northeast":
27939                     w += diffX;
27940                     w = Math.min(Math.max(mw, w), mxw);
27941                     diffY = this.constrain(h, diffY, mh, mxh);
27942                     y += diffY;
27943                     h -= diffY;
27944                     break;
27945                 case "northwest":
27946                     diffX = this.constrain(w, diffX, mw, mxw);
27947                     diffY = this.constrain(h, diffY, mh, mxh);
27948                     y += diffY;
27949                     h -= diffY;
27950                     x += diffX;
27951                     w -= diffX;
27952                     break;
27953                case "southwest":
27954                     diffX = this.constrain(w, diffX, mw, mxw);
27955                     h += diffY;
27956                     h = Math.min(Math.max(mh, h), mxh);
27957                     x += diffX;
27958                     w -= diffX;
27959                     break;
27960             }
27961
27962             var sw = this.snap(w, wi, mw);
27963             var sh = this.snap(h, hi, mh);
27964             if(sw != w || sh != h){
27965                 switch(pos){
27966                     case "northeast":
27967                         y -= sh - h;
27968                     break;
27969                     case "north":
27970                         y -= sh - h;
27971                         break;
27972                     case "southwest":
27973                         x -= sw - w;
27974                     break;
27975                     case "west":
27976                         x -= sw - w;
27977                         break;
27978                     case "northwest":
27979                         x -= sw - w;
27980                         y -= sh - h;
27981                     break;
27982                 }
27983                 w = sw;
27984                 h = sh;
27985             }
27986
27987             if(this.preserveRatio){
27988                 switch(pos){
27989                     case "southeast":
27990                     case "east":
27991                         h = oh * (w/ow);
27992                         h = Math.min(Math.max(mh, h), mxh);
27993                         w = ow * (h/oh);
27994                        break;
27995                     case "south":
27996                         w = ow * (h/oh);
27997                         w = Math.min(Math.max(mw, w), mxw);
27998                         h = oh * (w/ow);
27999                         break;
28000                     case "northeast":
28001                         w = ow * (h/oh);
28002                         w = Math.min(Math.max(mw, w), mxw);
28003                         h = oh * (w/ow);
28004                     break;
28005                     case "north":
28006                         var tw = w;
28007                         w = ow * (h/oh);
28008                         w = Math.min(Math.max(mw, w), mxw);
28009                         h = oh * (w/ow);
28010                         x += (tw - w) / 2;
28011                         break;
28012                     case "southwest":
28013                         h = oh * (w/ow);
28014                         h = Math.min(Math.max(mh, h), mxh);
28015                         var tw = w;
28016                         w = ow * (h/oh);
28017                         x += tw - w;
28018                         break;
28019                     case "west":
28020                         var th = h;
28021                         h = oh * (w/ow);
28022                         h = Math.min(Math.max(mh, h), mxh);
28023                         y += (th - h) / 2;
28024                         var tw = w;
28025                         w = ow * (h/oh);
28026                         x += tw - w;
28027                        break;
28028                     case "northwest":
28029                         var tw = w;
28030                         var th = h;
28031                         h = oh * (w/ow);
28032                         h = Math.min(Math.max(mh, h), mxh);
28033                         w = ow * (h/oh);
28034                         y += th - h;
28035                         x += tw - w;
28036                        break;
28037
28038                 }
28039             }
28040             if (pos == 'hdrag') {
28041                 w = ow;
28042             }
28043             this.proxy.setBounds(x, y, w, h);
28044             if(this.dynamic){
28045                 this.resizeElement();
28046             }
28047             }catch(e){}
28048         }
28049     },
28050
28051     // private
28052     handleOver : function(){
28053         if(this.enabled){
28054             this.el.addClass("x-resizable-over");
28055         }
28056     },
28057
28058     // private
28059     handleOut : function(){
28060         if(!this.resizing){
28061             this.el.removeClass("x-resizable-over");
28062         }
28063     },
28064
28065     /**
28066      * Returns the element this component is bound to.
28067      * @return {Roo.Element}
28068      */
28069     getEl : function(){
28070         return this.el;
28071     },
28072
28073     /**
28074      * Returns the resizeChild element (or null).
28075      * @return {Roo.Element}
28076      */
28077     getResizeChild : function(){
28078         return this.resizeChild;
28079     },
28080
28081     /**
28082      * Destroys this resizable. If the element was wrapped and
28083      * removeEl is not true then the element remains.
28084      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28085      */
28086     destroy : function(removeEl){
28087         this.proxy.remove();
28088         if(this.overlay){
28089             this.overlay.removeAllListeners();
28090             this.overlay.remove();
28091         }
28092         var ps = Roo.Resizable.positions;
28093         for(var k in ps){
28094             if(typeof ps[k] != "function" && this[ps[k]]){
28095                 var h = this[ps[k]];
28096                 h.el.removeAllListeners();
28097                 h.el.remove();
28098             }
28099         }
28100         if(removeEl){
28101             this.el.update("");
28102             this.el.remove();
28103         }
28104     }
28105 });
28106
28107 // private
28108 // hash to map config positions to true positions
28109 Roo.Resizable.positions = {
28110     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28111     hd: "hdrag"
28112 };
28113
28114 // private
28115 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28116     if(!this.tpl){
28117         // only initialize the template if resizable is used
28118         var tpl = Roo.DomHelper.createTemplate(
28119             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28120         );
28121         tpl.compile();
28122         Roo.Resizable.Handle.prototype.tpl = tpl;
28123     }
28124     this.position = pos;
28125     this.rz = rz;
28126     // show north drag fro topdra
28127     var handlepos = pos == 'hdrag' ? 'north' : pos;
28128     
28129     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28130     if (pos == 'hdrag') {
28131         this.el.setStyle('cursor', 'pointer');
28132     }
28133     this.el.unselectable();
28134     if(transparent){
28135         this.el.setOpacity(0);
28136     }
28137     this.el.on("mousedown", this.onMouseDown, this);
28138     if(!disableTrackOver){
28139         this.el.on("mouseover", this.onMouseOver, this);
28140         this.el.on("mouseout", this.onMouseOut, this);
28141     }
28142 };
28143
28144 // private
28145 Roo.Resizable.Handle.prototype = {
28146     afterResize : function(rz){
28147         // do nothing
28148     },
28149     // private
28150     onMouseDown : function(e){
28151         this.rz.onMouseDown(this, e);
28152     },
28153     // private
28154     onMouseOver : function(e){
28155         this.rz.handleOver(this, e);
28156     },
28157     // private
28158     onMouseOut : function(e){
28159         this.rz.handleOut(this, e);
28160     }
28161 };/*
28162  * Based on:
28163  * Ext JS Library 1.1.1
28164  * Copyright(c) 2006-2007, Ext JS, LLC.
28165  *
28166  * Originally Released Under LGPL - original licence link has changed is not relivant.
28167  *
28168  * Fork - LGPL
28169  * <script type="text/javascript">
28170  */
28171
28172 /**
28173  * @class Roo.Editor
28174  * @extends Roo.Component
28175  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28176  * @constructor
28177  * Create a new Editor
28178  * @param {Roo.form.Field} field The Field object (or descendant)
28179  * @param {Object} config The config object
28180  */
28181 Roo.Editor = function(field, config){
28182     Roo.Editor.superclass.constructor.call(this, config);
28183     this.field = field;
28184     this.addEvents({
28185         /**
28186              * @event beforestartedit
28187              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28188              * false from the handler of this event.
28189              * @param {Editor} this
28190              * @param {Roo.Element} boundEl The underlying element bound to this editor
28191              * @param {Mixed} value The field value being set
28192              */
28193         "beforestartedit" : true,
28194         /**
28195              * @event startedit
28196              * Fires when this editor is displayed
28197              * @param {Roo.Element} boundEl The underlying element bound to this editor
28198              * @param {Mixed} value The starting field value
28199              */
28200         "startedit" : true,
28201         /**
28202              * @event beforecomplete
28203              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28204              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28205              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28206              * event will not fire since no edit actually occurred.
28207              * @param {Editor} this
28208              * @param {Mixed} value The current field value
28209              * @param {Mixed} startValue The original field value
28210              */
28211         "beforecomplete" : true,
28212         /**
28213              * @event complete
28214              * Fires after editing is complete and any changed value has been written to the underlying field.
28215              * @param {Editor} this
28216              * @param {Mixed} value The current field value
28217              * @param {Mixed} startValue The original field value
28218              */
28219         "complete" : true,
28220         /**
28221          * @event specialkey
28222          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28223          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28224          * @param {Roo.form.Field} this
28225          * @param {Roo.EventObject} e The event object
28226          */
28227         "specialkey" : true
28228     });
28229 };
28230
28231 Roo.extend(Roo.Editor, Roo.Component, {
28232     /**
28233      * @cfg {Boolean/String} autosize
28234      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28235      * or "height" to adopt the height only (defaults to false)
28236      */
28237     /**
28238      * @cfg {Boolean} revertInvalid
28239      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28240      * validation fails (defaults to true)
28241      */
28242     /**
28243      * @cfg {Boolean} ignoreNoChange
28244      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28245      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28246      * will never be ignored.
28247      */
28248     /**
28249      * @cfg {Boolean} hideEl
28250      * False to keep the bound element visible while the editor is displayed (defaults to true)
28251      */
28252     /**
28253      * @cfg {Mixed} value
28254      * The data value of the underlying field (defaults to "")
28255      */
28256     value : "",
28257     /**
28258      * @cfg {String} alignment
28259      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28260      */
28261     alignment: "c-c?",
28262     /**
28263      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28264      * for bottom-right shadow (defaults to "frame")
28265      */
28266     shadow : "frame",
28267     /**
28268      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28269      */
28270     constrain : false,
28271     /**
28272      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28273      */
28274     completeOnEnter : false,
28275     /**
28276      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28277      */
28278     cancelOnEsc : false,
28279     /**
28280      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28281      */
28282     updateEl : false,
28283
28284     // private
28285     onRender : function(ct, position){
28286         this.el = new Roo.Layer({
28287             shadow: this.shadow,
28288             cls: "x-editor",
28289             parentEl : ct,
28290             shim : this.shim,
28291             shadowOffset:4,
28292             id: this.id,
28293             constrain: this.constrain
28294         });
28295         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28296         if(this.field.msgTarget != 'title'){
28297             this.field.msgTarget = 'qtip';
28298         }
28299         this.field.render(this.el);
28300         if(Roo.isGecko){
28301             this.field.el.dom.setAttribute('autocomplete', 'off');
28302         }
28303         this.field.on("specialkey", this.onSpecialKey, this);
28304         if(this.swallowKeys){
28305             this.field.el.swallowEvent(['keydown','keypress']);
28306         }
28307         this.field.show();
28308         this.field.on("blur", this.onBlur, this);
28309         if(this.field.grow){
28310             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28311         }
28312     },
28313
28314     onSpecialKey : function(field, e)
28315     {
28316         //Roo.log('editor onSpecialKey');
28317         if(this.completeOnEnter && e.getKey() == e.ENTER){
28318             e.stopEvent();
28319             this.completeEdit();
28320             return;
28321         }
28322         // do not fire special key otherwise it might hide close the editor...
28323         if(e.getKey() == e.ENTER){    
28324             return;
28325         }
28326         if(this.cancelOnEsc && e.getKey() == e.ESC){
28327             this.cancelEdit();
28328             return;
28329         } 
28330         this.fireEvent('specialkey', field, e);
28331     
28332     },
28333
28334     /**
28335      * Starts the editing process and shows the editor.
28336      * @param {String/HTMLElement/Element} el The element to edit
28337      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28338       * to the innerHTML of el.
28339      */
28340     startEdit : function(el, value){
28341         if(this.editing){
28342             this.completeEdit();
28343         }
28344         this.boundEl = Roo.get(el);
28345         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28346         if(!this.rendered){
28347             this.render(this.parentEl || document.body);
28348         }
28349         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28350             return;
28351         }
28352         this.startValue = v;
28353         this.field.setValue(v);
28354         if(this.autoSize){
28355             var sz = this.boundEl.getSize();
28356             switch(this.autoSize){
28357                 case "width":
28358                 this.setSize(sz.width,  "");
28359                 break;
28360                 case "height":
28361                 this.setSize("",  sz.height);
28362                 break;
28363                 default:
28364                 this.setSize(sz.width,  sz.height);
28365             }
28366         }
28367         this.el.alignTo(this.boundEl, this.alignment);
28368         this.editing = true;
28369         if(Roo.QuickTips){
28370             Roo.QuickTips.disable();
28371         }
28372         this.show();
28373     },
28374
28375     /**
28376      * Sets the height and width of this editor.
28377      * @param {Number} width The new width
28378      * @param {Number} height The new height
28379      */
28380     setSize : function(w, h){
28381         this.field.setSize(w, h);
28382         if(this.el){
28383             this.el.sync();
28384         }
28385     },
28386
28387     /**
28388      * Realigns the editor to the bound field based on the current alignment config value.
28389      */
28390     realign : function(){
28391         this.el.alignTo(this.boundEl, this.alignment);
28392     },
28393
28394     /**
28395      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28396      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28397      */
28398     completeEdit : function(remainVisible){
28399         if(!this.editing){
28400             return;
28401         }
28402         var v = this.getValue();
28403         if(this.revertInvalid !== false && !this.field.isValid()){
28404             v = this.startValue;
28405             this.cancelEdit(true);
28406         }
28407         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28408             this.editing = false;
28409             this.hide();
28410             return;
28411         }
28412         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28413             this.editing = false;
28414             if(this.updateEl && this.boundEl){
28415                 this.boundEl.update(v);
28416             }
28417             if(remainVisible !== true){
28418                 this.hide();
28419             }
28420             this.fireEvent("complete", this, v, this.startValue);
28421         }
28422     },
28423
28424     // private
28425     onShow : function(){
28426         this.el.show();
28427         if(this.hideEl !== false){
28428             this.boundEl.hide();
28429         }
28430         this.field.show();
28431         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28432             this.fixIEFocus = true;
28433             this.deferredFocus.defer(50, this);
28434         }else{
28435             this.field.focus();
28436         }
28437         this.fireEvent("startedit", this.boundEl, this.startValue);
28438     },
28439
28440     deferredFocus : function(){
28441         if(this.editing){
28442             this.field.focus();
28443         }
28444     },
28445
28446     /**
28447      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28448      * reverted to the original starting value.
28449      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28450      * cancel (defaults to false)
28451      */
28452     cancelEdit : function(remainVisible){
28453         if(this.editing){
28454             this.setValue(this.startValue);
28455             if(remainVisible !== true){
28456                 this.hide();
28457             }
28458         }
28459     },
28460
28461     // private
28462     onBlur : function(){
28463         if(this.allowBlur !== true && this.editing){
28464             this.completeEdit();
28465         }
28466     },
28467
28468     // private
28469     onHide : function(){
28470         if(this.editing){
28471             this.completeEdit();
28472             return;
28473         }
28474         this.field.blur();
28475         if(this.field.collapse){
28476             this.field.collapse();
28477         }
28478         this.el.hide();
28479         if(this.hideEl !== false){
28480             this.boundEl.show();
28481         }
28482         if(Roo.QuickTips){
28483             Roo.QuickTips.enable();
28484         }
28485     },
28486
28487     /**
28488      * Sets the data value of the editor
28489      * @param {Mixed} value Any valid value supported by the underlying field
28490      */
28491     setValue : function(v){
28492         this.field.setValue(v);
28493     },
28494
28495     /**
28496      * Gets the data value of the editor
28497      * @return {Mixed} The data value
28498      */
28499     getValue : function(){
28500         return this.field.getValue();
28501     }
28502 });/*
28503  * Based on:
28504  * Ext JS Library 1.1.1
28505  * Copyright(c) 2006-2007, Ext JS, LLC.
28506  *
28507  * Originally Released Under LGPL - original licence link has changed is not relivant.
28508  *
28509  * Fork - LGPL
28510  * <script type="text/javascript">
28511  */
28512  
28513 /**
28514  * @class Roo.BasicDialog
28515  * @extends Roo.util.Observable
28516  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28517  * <pre><code>
28518 var dlg = new Roo.BasicDialog("my-dlg", {
28519     height: 200,
28520     width: 300,
28521     minHeight: 100,
28522     minWidth: 150,
28523     modal: true,
28524     proxyDrag: true,
28525     shadow: true
28526 });
28527 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28528 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28529 dlg.addButton('Cancel', dlg.hide, dlg);
28530 dlg.show();
28531 </code></pre>
28532   <b>A Dialog should always be a direct child of the body element.</b>
28533  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28534  * @cfg {String} title Default text to display in the title bar (defaults to null)
28535  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28536  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28537  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28538  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28539  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28540  * (defaults to null with no animation)
28541  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28542  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28543  * property for valid values (defaults to 'all')
28544  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28545  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28546  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28547  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28548  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28549  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28550  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28551  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28552  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28553  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28554  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28555  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28556  * draggable = true (defaults to false)
28557  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28558  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28559  * shadow (defaults to false)
28560  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28561  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28562  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28563  * @cfg {Array} buttons Array of buttons
28564  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28565  * @constructor
28566  * Create a new BasicDialog.
28567  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28568  * @param {Object} config Configuration options
28569  */
28570 Roo.BasicDialog = function(el, config){
28571     this.el = Roo.get(el);
28572     var dh = Roo.DomHelper;
28573     if(!this.el && config && config.autoCreate){
28574         if(typeof config.autoCreate == "object"){
28575             if(!config.autoCreate.id){
28576                 config.autoCreate.id = el;
28577             }
28578             this.el = dh.append(document.body,
28579                         config.autoCreate, true);
28580         }else{
28581             this.el = dh.append(document.body,
28582                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28583         }
28584     }
28585     el = this.el;
28586     el.setDisplayed(true);
28587     el.hide = this.hideAction;
28588     this.id = el.id;
28589     el.addClass("x-dlg");
28590
28591     Roo.apply(this, config);
28592
28593     this.proxy = el.createProxy("x-dlg-proxy");
28594     this.proxy.hide = this.hideAction;
28595     this.proxy.setOpacity(.5);
28596     this.proxy.hide();
28597
28598     if(config.width){
28599         el.setWidth(config.width);
28600     }
28601     if(config.height){
28602         el.setHeight(config.height);
28603     }
28604     this.size = el.getSize();
28605     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28606         this.xy = [config.x,config.y];
28607     }else{
28608         this.xy = el.getCenterXY(true);
28609     }
28610     /** The header element @type Roo.Element */
28611     this.header = el.child("> .x-dlg-hd");
28612     /** The body element @type Roo.Element */
28613     this.body = el.child("> .x-dlg-bd");
28614     /** The footer element @type Roo.Element */
28615     this.footer = el.child("> .x-dlg-ft");
28616
28617     if(!this.header){
28618         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28619     }
28620     if(!this.body){
28621         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28622     }
28623
28624     this.header.unselectable();
28625     if(this.title){
28626         this.header.update(this.title);
28627     }
28628     // this element allows the dialog to be focused for keyboard event
28629     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28630     this.focusEl.swallowEvent("click", true);
28631
28632     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28633
28634     // wrap the body and footer for special rendering
28635     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28636     if(this.footer){
28637         this.bwrap.dom.appendChild(this.footer.dom);
28638     }
28639
28640     this.bg = this.el.createChild({
28641         tag: "div", cls:"x-dlg-bg",
28642         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28643     });
28644     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28645
28646
28647     if(this.autoScroll !== false && !this.autoTabs){
28648         this.body.setStyle("overflow", "auto");
28649     }
28650
28651     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28652
28653     if(this.closable !== false){
28654         this.el.addClass("x-dlg-closable");
28655         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28656         this.close.on("click", this.closeClick, this);
28657         this.close.addClassOnOver("x-dlg-close-over");
28658     }
28659     if(this.collapsible !== false){
28660         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28661         this.collapseBtn.on("click", this.collapseClick, this);
28662         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28663         this.header.on("dblclick", this.collapseClick, this);
28664     }
28665     if(this.resizable !== false){
28666         this.el.addClass("x-dlg-resizable");
28667         this.resizer = new Roo.Resizable(el, {
28668             minWidth: this.minWidth || 80,
28669             minHeight:this.minHeight || 80,
28670             handles: this.resizeHandles || "all",
28671             pinned: true
28672         });
28673         this.resizer.on("beforeresize", this.beforeResize, this);
28674         this.resizer.on("resize", this.onResize, this);
28675     }
28676     if(this.draggable !== false){
28677         el.addClass("x-dlg-draggable");
28678         if (!this.proxyDrag) {
28679             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28680         }
28681         else {
28682             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28683         }
28684         dd.setHandleElId(this.header.id);
28685         dd.endDrag = this.endMove.createDelegate(this);
28686         dd.startDrag = this.startMove.createDelegate(this);
28687         dd.onDrag = this.onDrag.createDelegate(this);
28688         dd.scroll = false;
28689         this.dd = dd;
28690     }
28691     if(this.modal){
28692         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28693         this.mask.enableDisplayMode("block");
28694         this.mask.hide();
28695         this.el.addClass("x-dlg-modal");
28696     }
28697     if(this.shadow){
28698         this.shadow = new Roo.Shadow({
28699             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28700             offset : this.shadowOffset
28701         });
28702     }else{
28703         this.shadowOffset = 0;
28704     }
28705     if(Roo.useShims && this.shim !== false){
28706         this.shim = this.el.createShim();
28707         this.shim.hide = this.hideAction;
28708         this.shim.hide();
28709     }else{
28710         this.shim = false;
28711     }
28712     if(this.autoTabs){
28713         this.initTabs();
28714     }
28715     if (this.buttons) { 
28716         var bts= this.buttons;
28717         this.buttons = [];
28718         Roo.each(bts, function(b) {
28719             this.addButton(b);
28720         }, this);
28721     }
28722     
28723     
28724     this.addEvents({
28725         /**
28726          * @event keydown
28727          * Fires when a key is pressed
28728          * @param {Roo.BasicDialog} this
28729          * @param {Roo.EventObject} e
28730          */
28731         "keydown" : true,
28732         /**
28733          * @event move
28734          * Fires when this dialog is moved by the user.
28735          * @param {Roo.BasicDialog} this
28736          * @param {Number} x The new page X
28737          * @param {Number} y The new page Y
28738          */
28739         "move" : true,
28740         /**
28741          * @event resize
28742          * Fires when this dialog is resized by the user.
28743          * @param {Roo.BasicDialog} this
28744          * @param {Number} width The new width
28745          * @param {Number} height The new height
28746          */
28747         "resize" : true,
28748         /**
28749          * @event beforehide
28750          * Fires before this dialog is hidden.
28751          * @param {Roo.BasicDialog} this
28752          */
28753         "beforehide" : true,
28754         /**
28755          * @event hide
28756          * Fires when this dialog is hidden.
28757          * @param {Roo.BasicDialog} this
28758          */
28759         "hide" : true,
28760         /**
28761          * @event beforeshow
28762          * Fires before this dialog is shown.
28763          * @param {Roo.BasicDialog} this
28764          */
28765         "beforeshow" : true,
28766         /**
28767          * @event show
28768          * Fires when this dialog is shown.
28769          * @param {Roo.BasicDialog} this
28770          */
28771         "show" : true
28772     });
28773     el.on("keydown", this.onKeyDown, this);
28774     el.on("mousedown", this.toFront, this);
28775     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28776     this.el.hide();
28777     Roo.DialogManager.register(this);
28778     Roo.BasicDialog.superclass.constructor.call(this);
28779 };
28780
28781 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28782     shadowOffset: Roo.isIE ? 6 : 5,
28783     minHeight: 80,
28784     minWidth: 200,
28785     minButtonWidth: 75,
28786     defaultButton: null,
28787     buttonAlign: "right",
28788     tabTag: 'div',
28789     firstShow: true,
28790
28791     /**
28792      * Sets the dialog title text
28793      * @param {String} text The title text to display
28794      * @return {Roo.BasicDialog} this
28795      */
28796     setTitle : function(text){
28797         this.header.update(text);
28798         return this;
28799     },
28800
28801     // private
28802     closeClick : function(){
28803         this.hide();
28804     },
28805
28806     // private
28807     collapseClick : function(){
28808         this[this.collapsed ? "expand" : "collapse"]();
28809     },
28810
28811     /**
28812      * Collapses the dialog to its minimized state (only the title bar is visible).
28813      * Equivalent to the user clicking the collapse dialog button.
28814      */
28815     collapse : function(){
28816         if(!this.collapsed){
28817             this.collapsed = true;
28818             this.el.addClass("x-dlg-collapsed");
28819             this.restoreHeight = this.el.getHeight();
28820             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28821         }
28822     },
28823
28824     /**
28825      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28826      * clicking the expand dialog button.
28827      */
28828     expand : function(){
28829         if(this.collapsed){
28830             this.collapsed = false;
28831             this.el.removeClass("x-dlg-collapsed");
28832             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28833         }
28834     },
28835
28836     /**
28837      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28838      * @return {Roo.TabPanel} The tabs component
28839      */
28840     initTabs : function(){
28841         var tabs = this.getTabs();
28842         while(tabs.getTab(0)){
28843             tabs.removeTab(0);
28844         }
28845         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28846             var dom = el.dom;
28847             tabs.addTab(Roo.id(dom), dom.title);
28848             dom.title = "";
28849         });
28850         tabs.activate(0);
28851         return tabs;
28852     },
28853
28854     // private
28855     beforeResize : function(){
28856         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28857     },
28858
28859     // private
28860     onResize : function(){
28861         this.refreshSize();
28862         this.syncBodyHeight();
28863         this.adjustAssets();
28864         this.focus();
28865         this.fireEvent("resize", this, this.size.width, this.size.height);
28866     },
28867
28868     // private
28869     onKeyDown : function(e){
28870         if(this.isVisible()){
28871             this.fireEvent("keydown", this, e);
28872         }
28873     },
28874
28875     /**
28876      * Resizes the dialog.
28877      * @param {Number} width
28878      * @param {Number} height
28879      * @return {Roo.BasicDialog} this
28880      */
28881     resizeTo : function(width, height){
28882         this.el.setSize(width, height);
28883         this.size = {width: width, height: height};
28884         this.syncBodyHeight();
28885         if(this.fixedcenter){
28886             this.center();
28887         }
28888         if(this.isVisible()){
28889             this.constrainXY();
28890             this.adjustAssets();
28891         }
28892         this.fireEvent("resize", this, width, height);
28893         return this;
28894     },
28895
28896
28897     /**
28898      * Resizes the dialog to fit the specified content size.
28899      * @param {Number} width
28900      * @param {Number} height
28901      * @return {Roo.BasicDialog} this
28902      */
28903     setContentSize : function(w, h){
28904         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28905         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28906         //if(!this.el.isBorderBox()){
28907             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28908             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28909         //}
28910         if(this.tabs){
28911             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28912             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28913         }
28914         this.resizeTo(w, h);
28915         return this;
28916     },
28917
28918     /**
28919      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28920      * executed in response to a particular key being pressed while the dialog is active.
28921      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28922      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28923      * @param {Function} fn The function to call
28924      * @param {Object} scope (optional) The scope of the function
28925      * @return {Roo.BasicDialog} this
28926      */
28927     addKeyListener : function(key, fn, scope){
28928         var keyCode, shift, ctrl, alt;
28929         if(typeof key == "object" && !(key instanceof Array)){
28930             keyCode = key["key"];
28931             shift = key["shift"];
28932             ctrl = key["ctrl"];
28933             alt = key["alt"];
28934         }else{
28935             keyCode = key;
28936         }
28937         var handler = function(dlg, e){
28938             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28939                 var k = e.getKey();
28940                 if(keyCode instanceof Array){
28941                     for(var i = 0, len = keyCode.length; i < len; i++){
28942                         if(keyCode[i] == k){
28943                           fn.call(scope || window, dlg, k, e);
28944                           return;
28945                         }
28946                     }
28947                 }else{
28948                     if(k == keyCode){
28949                         fn.call(scope || window, dlg, k, e);
28950                     }
28951                 }
28952             }
28953         };
28954         this.on("keydown", handler);
28955         return this;
28956     },
28957
28958     /**
28959      * Returns the TabPanel component (creates it if it doesn't exist).
28960      * Note: If you wish to simply check for the existence of tabs without creating them,
28961      * check for a null 'tabs' property.
28962      * @return {Roo.TabPanel} The tabs component
28963      */
28964     getTabs : function(){
28965         if(!this.tabs){
28966             this.el.addClass("x-dlg-auto-tabs");
28967             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28968             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28969         }
28970         return this.tabs;
28971     },
28972
28973     /**
28974      * Adds a button to the footer section of the dialog.
28975      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28976      * object or a valid Roo.DomHelper element config
28977      * @param {Function} handler The function called when the button is clicked
28978      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28979      * @return {Roo.Button} The new button
28980      */
28981     addButton : function(config, handler, scope){
28982         var dh = Roo.DomHelper;
28983         if(!this.footer){
28984             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28985         }
28986         if(!this.btnContainer){
28987             var tb = this.footer.createChild({
28988
28989                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28990                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28991             }, null, true);
28992             this.btnContainer = tb.firstChild.firstChild.firstChild;
28993         }
28994         var bconfig = {
28995             handler: handler,
28996             scope: scope,
28997             minWidth: this.minButtonWidth,
28998             hideParent:true
28999         };
29000         if(typeof config == "string"){
29001             bconfig.text = config;
29002         }else{
29003             if(config.tag){
29004                 bconfig.dhconfig = config;
29005             }else{
29006                 Roo.apply(bconfig, config);
29007             }
29008         }
29009         var fc = false;
29010         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29011             bconfig.position = Math.max(0, bconfig.position);
29012             fc = this.btnContainer.childNodes[bconfig.position];
29013         }
29014          
29015         var btn = new Roo.Button(
29016             fc ? 
29017                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29018                 : this.btnContainer.appendChild(document.createElement("td")),
29019             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29020             bconfig
29021         );
29022         this.syncBodyHeight();
29023         if(!this.buttons){
29024             /**
29025              * Array of all the buttons that have been added to this dialog via addButton
29026              * @type Array
29027              */
29028             this.buttons = [];
29029         }
29030         this.buttons.push(btn);
29031         return btn;
29032     },
29033
29034     /**
29035      * Sets the default button to be focused when the dialog is displayed.
29036      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29037      * @return {Roo.BasicDialog} this
29038      */
29039     setDefaultButton : function(btn){
29040         this.defaultButton = btn;
29041         return this;
29042     },
29043
29044     // private
29045     getHeaderFooterHeight : function(safe){
29046         var height = 0;
29047         if(this.header){
29048            height += this.header.getHeight();
29049         }
29050         if(this.footer){
29051            var fm = this.footer.getMargins();
29052             height += (this.footer.getHeight()+fm.top+fm.bottom);
29053         }
29054         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29055         height += this.centerBg.getPadding("tb");
29056         return height;
29057     },
29058
29059     // private
29060     syncBodyHeight : function(){
29061         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29062         var height = this.size.height - this.getHeaderFooterHeight(false);
29063         bd.setHeight(height-bd.getMargins("tb"));
29064         var hh = this.header.getHeight();
29065         var h = this.size.height-hh;
29066         cb.setHeight(h);
29067         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29068         bw.setHeight(h-cb.getPadding("tb"));
29069         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29070         bd.setWidth(bw.getWidth(true));
29071         if(this.tabs){
29072             this.tabs.syncHeight();
29073             if(Roo.isIE){
29074                 this.tabs.el.repaint();
29075             }
29076         }
29077     },
29078
29079     /**
29080      * Restores the previous state of the dialog if Roo.state is configured.
29081      * @return {Roo.BasicDialog} this
29082      */
29083     restoreState : function(){
29084         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29085         if(box && box.width){
29086             this.xy = [box.x, box.y];
29087             this.resizeTo(box.width, box.height);
29088         }
29089         return this;
29090     },
29091
29092     // private
29093     beforeShow : function(){
29094         this.expand();
29095         if(this.fixedcenter){
29096             this.xy = this.el.getCenterXY(true);
29097         }
29098         if(this.modal){
29099             Roo.get(document.body).addClass("x-body-masked");
29100             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29101             this.mask.show();
29102         }
29103         this.constrainXY();
29104     },
29105
29106     // private
29107     animShow : function(){
29108         var b = Roo.get(this.animateTarget).getBox();
29109         this.proxy.setSize(b.width, b.height);
29110         this.proxy.setLocation(b.x, b.y);
29111         this.proxy.show();
29112         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29113                     true, .35, this.showEl.createDelegate(this));
29114     },
29115
29116     /**
29117      * Shows the dialog.
29118      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29119      * @return {Roo.BasicDialog} this
29120      */
29121     show : function(animateTarget){
29122         if (this.fireEvent("beforeshow", this) === false){
29123             return;
29124         }
29125         if(this.syncHeightBeforeShow){
29126             this.syncBodyHeight();
29127         }else if(this.firstShow){
29128             this.firstShow = false;
29129             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29130         }
29131         this.animateTarget = animateTarget || this.animateTarget;
29132         if(!this.el.isVisible()){
29133             this.beforeShow();
29134             if(this.animateTarget && Roo.get(this.animateTarget)){
29135                 this.animShow();
29136             }else{
29137                 this.showEl();
29138             }
29139         }
29140         return this;
29141     },
29142
29143     // private
29144     showEl : function(){
29145         this.proxy.hide();
29146         this.el.setXY(this.xy);
29147         this.el.show();
29148         this.adjustAssets(true);
29149         this.toFront();
29150         this.focus();
29151         // IE peekaboo bug - fix found by Dave Fenwick
29152         if(Roo.isIE){
29153             this.el.repaint();
29154         }
29155         this.fireEvent("show", this);
29156     },
29157
29158     /**
29159      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29160      * dialog itself will receive focus.
29161      */
29162     focus : function(){
29163         if(this.defaultButton){
29164             this.defaultButton.focus();
29165         }else{
29166             this.focusEl.focus();
29167         }
29168     },
29169
29170     // private
29171     constrainXY : function(){
29172         if(this.constraintoviewport !== false){
29173             if(!this.viewSize){
29174                 if(this.container){
29175                     var s = this.container.getSize();
29176                     this.viewSize = [s.width, s.height];
29177                 }else{
29178                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29179                 }
29180             }
29181             var s = Roo.get(this.container||document).getScroll();
29182
29183             var x = this.xy[0], y = this.xy[1];
29184             var w = this.size.width, h = this.size.height;
29185             var vw = this.viewSize[0], vh = this.viewSize[1];
29186             // only move it if it needs it
29187             var moved = false;
29188             // first validate right/bottom
29189             if(x + w > vw+s.left){
29190                 x = vw - w;
29191                 moved = true;
29192             }
29193             if(y + h > vh+s.top){
29194                 y = vh - h;
29195                 moved = true;
29196             }
29197             // then make sure top/left isn't negative
29198             if(x < s.left){
29199                 x = s.left;
29200                 moved = true;
29201             }
29202             if(y < s.top){
29203                 y = s.top;
29204                 moved = true;
29205             }
29206             if(moved){
29207                 // cache xy
29208                 this.xy = [x, y];
29209                 if(this.isVisible()){
29210                     this.el.setLocation(x, y);
29211                     this.adjustAssets();
29212                 }
29213             }
29214         }
29215     },
29216
29217     // private
29218     onDrag : function(){
29219         if(!this.proxyDrag){
29220             this.xy = this.el.getXY();
29221             this.adjustAssets();
29222         }
29223     },
29224
29225     // private
29226     adjustAssets : function(doShow){
29227         var x = this.xy[0], y = this.xy[1];
29228         var w = this.size.width, h = this.size.height;
29229         if(doShow === true){
29230             if(this.shadow){
29231                 this.shadow.show(this.el);
29232             }
29233             if(this.shim){
29234                 this.shim.show();
29235             }
29236         }
29237         if(this.shadow && this.shadow.isVisible()){
29238             this.shadow.show(this.el);
29239         }
29240         if(this.shim && this.shim.isVisible()){
29241             this.shim.setBounds(x, y, w, h);
29242         }
29243     },
29244
29245     // private
29246     adjustViewport : function(w, h){
29247         if(!w || !h){
29248             w = Roo.lib.Dom.getViewWidth();
29249             h = Roo.lib.Dom.getViewHeight();
29250         }
29251         // cache the size
29252         this.viewSize = [w, h];
29253         if(this.modal && this.mask.isVisible()){
29254             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29255             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29256         }
29257         if(this.isVisible()){
29258             this.constrainXY();
29259         }
29260     },
29261
29262     /**
29263      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29264      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29265      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29266      */
29267     destroy : function(removeEl){
29268         if(this.isVisible()){
29269             this.animateTarget = null;
29270             this.hide();
29271         }
29272         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29273         if(this.tabs){
29274             this.tabs.destroy(removeEl);
29275         }
29276         Roo.destroy(
29277              this.shim,
29278              this.proxy,
29279              this.resizer,
29280              this.close,
29281              this.mask
29282         );
29283         if(this.dd){
29284             this.dd.unreg();
29285         }
29286         if(this.buttons){
29287            for(var i = 0, len = this.buttons.length; i < len; i++){
29288                this.buttons[i].destroy();
29289            }
29290         }
29291         this.el.removeAllListeners();
29292         if(removeEl === true){
29293             this.el.update("");
29294             this.el.remove();
29295         }
29296         Roo.DialogManager.unregister(this);
29297     },
29298
29299     // private
29300     startMove : function(){
29301         if(this.proxyDrag){
29302             this.proxy.show();
29303         }
29304         if(this.constraintoviewport !== false){
29305             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29306         }
29307     },
29308
29309     // private
29310     endMove : function(){
29311         if(!this.proxyDrag){
29312             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29313         }else{
29314             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29315             this.proxy.hide();
29316         }
29317         this.refreshSize();
29318         this.adjustAssets();
29319         this.focus();
29320         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29321     },
29322
29323     /**
29324      * Brings this dialog to the front of any other visible dialogs
29325      * @return {Roo.BasicDialog} this
29326      */
29327     toFront : function(){
29328         Roo.DialogManager.bringToFront(this);
29329         return this;
29330     },
29331
29332     /**
29333      * Sends this dialog to the back (under) of any other visible dialogs
29334      * @return {Roo.BasicDialog} this
29335      */
29336     toBack : function(){
29337         Roo.DialogManager.sendToBack(this);
29338         return this;
29339     },
29340
29341     /**
29342      * Centers this dialog in the viewport
29343      * @return {Roo.BasicDialog} this
29344      */
29345     center : function(){
29346         var xy = this.el.getCenterXY(true);
29347         this.moveTo(xy[0], xy[1]);
29348         return this;
29349     },
29350
29351     /**
29352      * Moves the dialog's top-left corner to the specified point
29353      * @param {Number} x
29354      * @param {Number} y
29355      * @return {Roo.BasicDialog} this
29356      */
29357     moveTo : function(x, y){
29358         this.xy = [x,y];
29359         if(this.isVisible()){
29360             this.el.setXY(this.xy);
29361             this.adjustAssets();
29362         }
29363         return this;
29364     },
29365
29366     /**
29367      * Aligns the dialog to the specified element
29368      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29369      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29370      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29371      * @return {Roo.BasicDialog} this
29372      */
29373     alignTo : function(element, position, offsets){
29374         this.xy = this.el.getAlignToXY(element, position, offsets);
29375         if(this.isVisible()){
29376             this.el.setXY(this.xy);
29377             this.adjustAssets();
29378         }
29379         return this;
29380     },
29381
29382     /**
29383      * Anchors an element to another element and realigns it when the window is resized.
29384      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29385      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29386      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29387      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29388      * is a number, it is used as the buffer delay (defaults to 50ms).
29389      * @return {Roo.BasicDialog} this
29390      */
29391     anchorTo : function(el, alignment, offsets, monitorScroll){
29392         var action = function(){
29393             this.alignTo(el, alignment, offsets);
29394         };
29395         Roo.EventManager.onWindowResize(action, this);
29396         var tm = typeof monitorScroll;
29397         if(tm != 'undefined'){
29398             Roo.EventManager.on(window, 'scroll', action, this,
29399                 {buffer: tm == 'number' ? monitorScroll : 50});
29400         }
29401         action.call(this);
29402         return this;
29403     },
29404
29405     /**
29406      * Returns true if the dialog is visible
29407      * @return {Boolean}
29408      */
29409     isVisible : function(){
29410         return this.el.isVisible();
29411     },
29412
29413     // private
29414     animHide : function(callback){
29415         var b = Roo.get(this.animateTarget).getBox();
29416         this.proxy.show();
29417         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29418         this.el.hide();
29419         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29420                     this.hideEl.createDelegate(this, [callback]));
29421     },
29422
29423     /**
29424      * Hides the dialog.
29425      * @param {Function} callback (optional) Function to call when the dialog is hidden
29426      * @return {Roo.BasicDialog} this
29427      */
29428     hide : function(callback){
29429         if (this.fireEvent("beforehide", this) === false){
29430             return;
29431         }
29432         if(this.shadow){
29433             this.shadow.hide();
29434         }
29435         if(this.shim) {
29436           this.shim.hide();
29437         }
29438         // sometimes animateTarget seems to get set.. causing problems...
29439         // this just double checks..
29440         if(this.animateTarget && Roo.get(this.animateTarget)) {
29441            this.animHide(callback);
29442         }else{
29443             this.el.hide();
29444             this.hideEl(callback);
29445         }
29446         return this;
29447     },
29448
29449     // private
29450     hideEl : function(callback){
29451         this.proxy.hide();
29452         if(this.modal){
29453             this.mask.hide();
29454             Roo.get(document.body).removeClass("x-body-masked");
29455         }
29456         this.fireEvent("hide", this);
29457         if(typeof callback == "function"){
29458             callback();
29459         }
29460     },
29461
29462     // private
29463     hideAction : function(){
29464         this.setLeft("-10000px");
29465         this.setTop("-10000px");
29466         this.setStyle("visibility", "hidden");
29467     },
29468
29469     // private
29470     refreshSize : function(){
29471         this.size = this.el.getSize();
29472         this.xy = this.el.getXY();
29473         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29474     },
29475
29476     // private
29477     // z-index is managed by the DialogManager and may be overwritten at any time
29478     setZIndex : function(index){
29479         if(this.modal){
29480             this.mask.setStyle("z-index", index);
29481         }
29482         if(this.shim){
29483             this.shim.setStyle("z-index", ++index);
29484         }
29485         if(this.shadow){
29486             this.shadow.setZIndex(++index);
29487         }
29488         this.el.setStyle("z-index", ++index);
29489         if(this.proxy){
29490             this.proxy.setStyle("z-index", ++index);
29491         }
29492         if(this.resizer){
29493             this.resizer.proxy.setStyle("z-index", ++index);
29494         }
29495
29496         this.lastZIndex = index;
29497     },
29498
29499     /**
29500      * Returns the element for this dialog
29501      * @return {Roo.Element} The underlying dialog Element
29502      */
29503     getEl : function(){
29504         return this.el;
29505     }
29506 });
29507
29508 /**
29509  * @class Roo.DialogManager
29510  * Provides global access to BasicDialogs that have been created and
29511  * support for z-indexing (layering) multiple open dialogs.
29512  */
29513 Roo.DialogManager = function(){
29514     var list = {};
29515     var accessList = [];
29516     var front = null;
29517
29518     // private
29519     var sortDialogs = function(d1, d2){
29520         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29521     };
29522
29523     // private
29524     var orderDialogs = function(){
29525         accessList.sort(sortDialogs);
29526         var seed = Roo.DialogManager.zseed;
29527         for(var i = 0, len = accessList.length; i < len; i++){
29528             var dlg = accessList[i];
29529             if(dlg){
29530                 dlg.setZIndex(seed + (i*10));
29531             }
29532         }
29533     };
29534
29535     return {
29536         /**
29537          * The starting z-index for BasicDialogs (defaults to 9000)
29538          * @type Number The z-index value
29539          */
29540         zseed : 9000,
29541
29542         // private
29543         register : function(dlg){
29544             list[dlg.id] = dlg;
29545             accessList.push(dlg);
29546         },
29547
29548         // private
29549         unregister : function(dlg){
29550             delete list[dlg.id];
29551             var i=0;
29552             var len=0;
29553             if(!accessList.indexOf){
29554                 for(  i = 0, len = accessList.length; i < len; i++){
29555                     if(accessList[i] == dlg){
29556                         accessList.splice(i, 1);
29557                         return;
29558                     }
29559                 }
29560             }else{
29561                  i = accessList.indexOf(dlg);
29562                 if(i != -1){
29563                     accessList.splice(i, 1);
29564                 }
29565             }
29566         },
29567
29568         /**
29569          * Gets a registered dialog by id
29570          * @param {String/Object} id The id of the dialog or a dialog
29571          * @return {Roo.BasicDialog} this
29572          */
29573         get : function(id){
29574             return typeof id == "object" ? id : list[id];
29575         },
29576
29577         /**
29578          * Brings the specified dialog to the front
29579          * @param {String/Object} dlg The id of the dialog or a dialog
29580          * @return {Roo.BasicDialog} this
29581          */
29582         bringToFront : function(dlg){
29583             dlg = this.get(dlg);
29584             if(dlg != front){
29585                 front = dlg;
29586                 dlg._lastAccess = new Date().getTime();
29587                 orderDialogs();
29588             }
29589             return dlg;
29590         },
29591
29592         /**
29593          * Sends the specified dialog to the back
29594          * @param {String/Object} dlg The id of the dialog or a dialog
29595          * @return {Roo.BasicDialog} this
29596          */
29597         sendToBack : function(dlg){
29598             dlg = this.get(dlg);
29599             dlg._lastAccess = -(new Date().getTime());
29600             orderDialogs();
29601             return dlg;
29602         },
29603
29604         /**
29605          * Hides all dialogs
29606          */
29607         hideAll : function(){
29608             for(var id in list){
29609                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29610                     list[id].hide();
29611                 }
29612             }
29613         }
29614     };
29615 }();
29616
29617 /**
29618  * @class Roo.LayoutDialog
29619  * @extends Roo.BasicDialog
29620  * Dialog which provides adjustments for working with a layout in a Dialog.
29621  * Add your necessary layout config options to the dialog's config.<br>
29622  * Example usage (including a nested layout):
29623  * <pre><code>
29624 if(!dialog){
29625     dialog = new Roo.LayoutDialog("download-dlg", {
29626         modal: true,
29627         width:600,
29628         height:450,
29629         shadow:true,
29630         minWidth:500,
29631         minHeight:350,
29632         autoTabs:true,
29633         proxyDrag:true,
29634         // layout config merges with the dialog config
29635         center:{
29636             tabPosition: "top",
29637             alwaysShowTabs: true
29638         }
29639     });
29640     dialog.addKeyListener(27, dialog.hide, dialog);
29641     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29642     dialog.addButton("Build It!", this.getDownload, this);
29643
29644     // we can even add nested layouts
29645     var innerLayout = new Roo.BorderLayout("dl-inner", {
29646         east: {
29647             initialSize: 200,
29648             autoScroll:true,
29649             split:true
29650         },
29651         center: {
29652             autoScroll:true
29653         }
29654     });
29655     innerLayout.beginUpdate();
29656     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29657     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29658     innerLayout.endUpdate(true);
29659
29660     var layout = dialog.getLayout();
29661     layout.beginUpdate();
29662     layout.add("center", new Roo.ContentPanel("standard-panel",
29663                         {title: "Download the Source", fitToFrame:true}));
29664     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29665                {title: "Build your own roo.js"}));
29666     layout.getRegion("center").showPanel(sp);
29667     layout.endUpdate();
29668 }
29669 </code></pre>
29670     * @constructor
29671     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29672     * @param {Object} config configuration options
29673   */
29674 Roo.LayoutDialog = function(el, cfg){
29675     
29676     var config=  cfg;
29677     if (typeof(cfg) == 'undefined') {
29678         config = Roo.apply({}, el);
29679         // not sure why we use documentElement here.. - it should always be body.
29680         // IE7 borks horribly if we use documentElement.
29681         // webkit also does not like documentElement - it creates a body element...
29682         el = Roo.get( document.body || document.documentElement ).createChild();
29683         //config.autoCreate = true;
29684     }
29685     
29686     
29687     config.autoTabs = false;
29688     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29689     this.body.setStyle({overflow:"hidden", position:"relative"});
29690     this.layout = new Roo.BorderLayout(this.body.dom, config);
29691     this.layout.monitorWindowResize = false;
29692     this.el.addClass("x-dlg-auto-layout");
29693     // fix case when center region overwrites center function
29694     this.center = Roo.BasicDialog.prototype.center;
29695     this.on("show", this.layout.layout, this.layout, true);
29696     if (config.items) {
29697         var xitems = config.items;
29698         delete config.items;
29699         Roo.each(xitems, this.addxtype, this);
29700     }
29701     
29702     
29703 };
29704 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29705     /**
29706      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29707      * @deprecated
29708      */
29709     endUpdate : function(){
29710         this.layout.endUpdate();
29711     },
29712
29713     /**
29714      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29715      *  @deprecated
29716      */
29717     beginUpdate : function(){
29718         this.layout.beginUpdate();
29719     },
29720
29721     /**
29722      * Get the BorderLayout for this dialog
29723      * @return {Roo.BorderLayout}
29724      */
29725     getLayout : function(){
29726         return this.layout;
29727     },
29728
29729     showEl : function(){
29730         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29731         if(Roo.isIE7){
29732             this.layout.layout();
29733         }
29734     },
29735
29736     // private
29737     // Use the syncHeightBeforeShow config option to control this automatically
29738     syncBodyHeight : function(){
29739         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29740         if(this.layout){this.layout.layout();}
29741     },
29742     
29743       /**
29744      * Add an xtype element (actually adds to the layout.)
29745      * @return {Object} xdata xtype object data.
29746      */
29747     
29748     addxtype : function(c) {
29749         return this.layout.addxtype(c);
29750     }
29751 });/*
29752  * Based on:
29753  * Ext JS Library 1.1.1
29754  * Copyright(c) 2006-2007, Ext JS, LLC.
29755  *
29756  * Originally Released Under LGPL - original licence link has changed is not relivant.
29757  *
29758  * Fork - LGPL
29759  * <script type="text/javascript">
29760  */
29761  
29762 /**
29763  * @class Roo.MessageBox
29764  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29765  * Example usage:
29766  *<pre><code>
29767 // Basic alert:
29768 Roo.Msg.alert('Status', 'Changes saved successfully.');
29769
29770 // Prompt for user data:
29771 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29772     if (btn == 'ok'){
29773         // process text value...
29774     }
29775 });
29776
29777 // Show a dialog using config options:
29778 Roo.Msg.show({
29779    title:'Save Changes?',
29780    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29781    buttons: Roo.Msg.YESNOCANCEL,
29782    fn: processResult,
29783    animEl: 'elId'
29784 });
29785 </code></pre>
29786  * @singleton
29787  */
29788 Roo.MessageBox = function(){
29789     var dlg, opt, mask, waitTimer;
29790     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29791     var buttons, activeTextEl, bwidth;
29792
29793     // private
29794     var handleButton = function(button){
29795         dlg.hide();
29796         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29797     };
29798
29799     // private
29800     var handleHide = function(){
29801         if(opt && opt.cls){
29802             dlg.el.removeClass(opt.cls);
29803         }
29804         if(waitTimer){
29805             Roo.TaskMgr.stop(waitTimer);
29806             waitTimer = null;
29807         }
29808     };
29809
29810     // private
29811     var updateButtons = function(b){
29812         var width = 0;
29813         if(!b){
29814             buttons["ok"].hide();
29815             buttons["cancel"].hide();
29816             buttons["yes"].hide();
29817             buttons["no"].hide();
29818             dlg.footer.dom.style.display = 'none';
29819             return width;
29820         }
29821         dlg.footer.dom.style.display = '';
29822         for(var k in buttons){
29823             if(typeof buttons[k] != "function"){
29824                 if(b[k]){
29825                     buttons[k].show();
29826                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29827                     width += buttons[k].el.getWidth()+15;
29828                 }else{
29829                     buttons[k].hide();
29830                 }
29831             }
29832         }
29833         return width;
29834     };
29835
29836     // private
29837     var handleEsc = function(d, k, e){
29838         if(opt && opt.closable !== false){
29839             dlg.hide();
29840         }
29841         if(e){
29842             e.stopEvent();
29843         }
29844     };
29845
29846     return {
29847         /**
29848          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29849          * @return {Roo.BasicDialog} The BasicDialog element
29850          */
29851         getDialog : function(){
29852            if(!dlg){
29853                 dlg = new Roo.BasicDialog("x-msg-box", {
29854                     autoCreate : true,
29855                     shadow: true,
29856                     draggable: true,
29857                     resizable:false,
29858                     constraintoviewport:false,
29859                     fixedcenter:true,
29860                     collapsible : false,
29861                     shim:true,
29862                     modal: true,
29863                     width:400, height:100,
29864                     buttonAlign:"center",
29865                     closeClick : function(){
29866                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29867                             handleButton("no");
29868                         }else{
29869                             handleButton("cancel");
29870                         }
29871                     }
29872                 });
29873                 dlg.on("hide", handleHide);
29874                 mask = dlg.mask;
29875                 dlg.addKeyListener(27, handleEsc);
29876                 buttons = {};
29877                 var bt = this.buttonText;
29878                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29879                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29880                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29881                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29882                 bodyEl = dlg.body.createChild({
29883
29884                     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>'
29885                 });
29886                 msgEl = bodyEl.dom.firstChild;
29887                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29888                 textboxEl.enableDisplayMode();
29889                 textboxEl.addKeyListener([10,13], function(){
29890                     if(dlg.isVisible() && opt && opt.buttons){
29891                         if(opt.buttons.ok){
29892                             handleButton("ok");
29893                         }else if(opt.buttons.yes){
29894                             handleButton("yes");
29895                         }
29896                     }
29897                 });
29898                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29899                 textareaEl.enableDisplayMode();
29900                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29901                 progressEl.enableDisplayMode();
29902                 var pf = progressEl.dom.firstChild;
29903                 if (pf) {
29904                     pp = Roo.get(pf.firstChild);
29905                     pp.setHeight(pf.offsetHeight);
29906                 }
29907                 
29908             }
29909             return dlg;
29910         },
29911
29912         /**
29913          * Updates the message box body text
29914          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29915          * the XHTML-compliant non-breaking space character '&amp;#160;')
29916          * @return {Roo.MessageBox} This message box
29917          */
29918         updateText : function(text){
29919             if(!dlg.isVisible() && !opt.width){
29920                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29921             }
29922             msgEl.innerHTML = text || '&#160;';
29923             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29924                         Math.max(opt.minWidth || this.minWidth, bwidth));
29925             if(opt.prompt){
29926                 activeTextEl.setWidth(w);
29927             }
29928             if(dlg.isVisible()){
29929                 dlg.fixedcenter = false;
29930             }
29931             dlg.setContentSize(w, bodyEl.getHeight());
29932             if(dlg.isVisible()){
29933                 dlg.fixedcenter = true;
29934             }
29935             return this;
29936         },
29937
29938         /**
29939          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29940          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29941          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29942          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29943          * @return {Roo.MessageBox} This message box
29944          */
29945         updateProgress : function(value, text){
29946             if(text){
29947                 this.updateText(text);
29948             }
29949             if (pp) { // weird bug on my firefox - for some reason this is not defined
29950                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29951             }
29952             return this;
29953         },        
29954
29955         /**
29956          * Returns true if the message box is currently displayed
29957          * @return {Boolean} True if the message box is visible, else false
29958          */
29959         isVisible : function(){
29960             return dlg && dlg.isVisible();  
29961         },
29962
29963         /**
29964          * Hides the message box if it is displayed
29965          */
29966         hide : function(){
29967             if(this.isVisible()){
29968                 dlg.hide();
29969             }  
29970         },
29971
29972         /**
29973          * Displays a new message box, or reinitializes an existing message box, based on the config options
29974          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29975          * The following config object properties are supported:
29976          * <pre>
29977 Property    Type             Description
29978 ----------  ---------------  ------------------------------------------------------------------------------------
29979 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29980                                    closes (defaults to undefined)
29981 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29982                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29983 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29984                                    progress and wait dialogs will ignore this property and always hide the
29985                                    close button as they can only be closed programmatically.
29986 cls               String           A custom CSS class to apply to the message box element
29987 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29988                                    displayed (defaults to 75)
29989 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29990                                    function will be btn (the name of the button that was clicked, if applicable,
29991                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29992                                    Progress and wait dialogs will ignore this option since they do not respond to
29993                                    user actions and can only be closed programmatically, so any required function
29994                                    should be called by the same code after it closes the dialog.
29995 icon              String           A CSS class that provides a background image to be used as an icon for
29996                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29997 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29998 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29999 modal             Boolean          False to allow user interaction with the page while the message box is
30000                                    displayed (defaults to true)
30001 msg               String           A string that will replace the existing message box body text (defaults
30002                                    to the XHTML-compliant non-breaking space character '&#160;')
30003 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30004 progress          Boolean          True to display a progress bar (defaults to false)
30005 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30006 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30007 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30008 title             String           The title text
30009 value             String           The string value to set into the active textbox element if displayed
30010 wait              Boolean          True to display a progress bar (defaults to false)
30011 width             Number           The width of the dialog in pixels
30012 </pre>
30013          *
30014          * Example usage:
30015          * <pre><code>
30016 Roo.Msg.show({
30017    title: 'Address',
30018    msg: 'Please enter your address:',
30019    width: 300,
30020    buttons: Roo.MessageBox.OKCANCEL,
30021    multiline: true,
30022    fn: saveAddress,
30023    animEl: 'addAddressBtn'
30024 });
30025 </code></pre>
30026          * @param {Object} config Configuration options
30027          * @return {Roo.MessageBox} This message box
30028          */
30029         show : function(options){
30030             if(this.isVisible()){
30031                 this.hide();
30032             }
30033             var d = this.getDialog();
30034             opt = options;
30035             d.setTitle(opt.title || "&#160;");
30036             d.close.setDisplayed(opt.closable !== false);
30037             activeTextEl = textboxEl;
30038             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30039             if(opt.prompt){
30040                 if(opt.multiline){
30041                     textboxEl.hide();
30042                     textareaEl.show();
30043                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30044                         opt.multiline : this.defaultTextHeight);
30045                     activeTextEl = textareaEl;
30046                 }else{
30047                     textboxEl.show();
30048                     textareaEl.hide();
30049                 }
30050             }else{
30051                 textboxEl.hide();
30052                 textareaEl.hide();
30053             }
30054             progressEl.setDisplayed(opt.progress === true);
30055             this.updateProgress(0);
30056             activeTextEl.dom.value = opt.value || "";
30057             if(opt.prompt){
30058                 dlg.setDefaultButton(activeTextEl);
30059             }else{
30060                 var bs = opt.buttons;
30061                 var db = null;
30062                 if(bs && bs.ok){
30063                     db = buttons["ok"];
30064                 }else if(bs && bs.yes){
30065                     db = buttons["yes"];
30066                 }
30067                 dlg.setDefaultButton(db);
30068             }
30069             bwidth = updateButtons(opt.buttons);
30070             this.updateText(opt.msg);
30071             if(opt.cls){
30072                 d.el.addClass(opt.cls);
30073             }
30074             d.proxyDrag = opt.proxyDrag === true;
30075             d.modal = opt.modal !== false;
30076             d.mask = opt.modal !== false ? mask : false;
30077             if(!d.isVisible()){
30078                 // force it to the end of the z-index stack so it gets a cursor in FF
30079                 document.body.appendChild(dlg.el.dom);
30080                 d.animateTarget = null;
30081                 d.show(options.animEl);
30082             }
30083             return this;
30084         },
30085
30086         /**
30087          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30088          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30089          * and closing the message box when the process is complete.
30090          * @param {String} title The title bar text
30091          * @param {String} msg The message box body text
30092          * @return {Roo.MessageBox} This message box
30093          */
30094         progress : function(title, msg){
30095             this.show({
30096                 title : title,
30097                 msg : msg,
30098                 buttons: false,
30099                 progress:true,
30100                 closable:false,
30101                 minWidth: this.minProgressWidth,
30102                 modal : true
30103             });
30104             return this;
30105         },
30106
30107         /**
30108          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30109          * If a callback function is passed it will be called after the user clicks the button, and the
30110          * id of the button that was clicked will be passed as the only parameter to the callback
30111          * (could also be the top-right close button).
30112          * @param {String} title The title bar text
30113          * @param {String} msg The message box body text
30114          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30115          * @param {Object} scope (optional) The scope of the callback function
30116          * @return {Roo.MessageBox} This message box
30117          */
30118         alert : function(title, msg, fn, scope){
30119             this.show({
30120                 title : title,
30121                 msg : msg,
30122                 buttons: this.OK,
30123                 fn: fn,
30124                 scope : scope,
30125                 modal : true
30126             });
30127             return this;
30128         },
30129
30130         /**
30131          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30132          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30133          * You are responsible for closing the message box when the process is complete.
30134          * @param {String} msg The message box body text
30135          * @param {String} title (optional) The title bar text
30136          * @return {Roo.MessageBox} This message box
30137          */
30138         wait : function(msg, title){
30139             this.show({
30140                 title : title,
30141                 msg : msg,
30142                 buttons: false,
30143                 closable:false,
30144                 progress:true,
30145                 modal:true,
30146                 width:300,
30147                 wait:true
30148             });
30149             waitTimer = Roo.TaskMgr.start({
30150                 run: function(i){
30151                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30152                 },
30153                 interval: 1000
30154             });
30155             return this;
30156         },
30157
30158         /**
30159          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30160          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30161          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30162          * @param {String} title The title bar text
30163          * @param {String} msg The message box body text
30164          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30165          * @param {Object} scope (optional) The scope of the callback function
30166          * @return {Roo.MessageBox} This message box
30167          */
30168         confirm : function(title, msg, fn, scope){
30169             this.show({
30170                 title : title,
30171                 msg : msg,
30172                 buttons: this.YESNO,
30173                 fn: fn,
30174                 scope : scope,
30175                 modal : true
30176             });
30177             return this;
30178         },
30179
30180         /**
30181          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30182          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30183          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30184          * (could also be the top-right close button) and the text that was entered will be passed as the two
30185          * parameters to the callback.
30186          * @param {String} title The title bar text
30187          * @param {String} msg The message box body text
30188          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30189          * @param {Object} scope (optional) The scope of the callback function
30190          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30191          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30192          * @return {Roo.MessageBox} This message box
30193          */
30194         prompt : function(title, msg, fn, scope, multiline){
30195             this.show({
30196                 title : title,
30197                 msg : msg,
30198                 buttons: this.OKCANCEL,
30199                 fn: fn,
30200                 minWidth:250,
30201                 scope : scope,
30202                 prompt:true,
30203                 multiline: multiline,
30204                 modal : true
30205             });
30206             return this;
30207         },
30208
30209         /**
30210          * Button config that displays a single OK button
30211          * @type Object
30212          */
30213         OK : {ok:true},
30214         /**
30215          * Button config that displays Yes and No buttons
30216          * @type Object
30217          */
30218         YESNO : {yes:true, no:true},
30219         /**
30220          * Button config that displays OK and Cancel buttons
30221          * @type Object
30222          */
30223         OKCANCEL : {ok:true, cancel:true},
30224         /**
30225          * Button config that displays Yes, No and Cancel buttons
30226          * @type Object
30227          */
30228         YESNOCANCEL : {yes:true, no:true, cancel:true},
30229
30230         /**
30231          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30232          * @type Number
30233          */
30234         defaultTextHeight : 75,
30235         /**
30236          * The maximum width in pixels of the message box (defaults to 600)
30237          * @type Number
30238          */
30239         maxWidth : 600,
30240         /**
30241          * The minimum width in pixels of the message box (defaults to 100)
30242          * @type Number
30243          */
30244         minWidth : 100,
30245         /**
30246          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30247          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30248          * @type Number
30249          */
30250         minProgressWidth : 250,
30251         /**
30252          * An object containing the default button text strings that can be overriden for localized language support.
30253          * Supported properties are: ok, cancel, yes and no.
30254          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30255          * @type Object
30256          */
30257         buttonText : {
30258             ok : "OK",
30259             cancel : "Cancel",
30260             yes : "Yes",
30261             no : "No"
30262         }
30263     };
30264 }();
30265
30266 /**
30267  * Shorthand for {@link Roo.MessageBox}
30268  */
30269 Roo.Msg = Roo.MessageBox;/*
30270  * Based on:
30271  * Ext JS Library 1.1.1
30272  * Copyright(c) 2006-2007, Ext JS, LLC.
30273  *
30274  * Originally Released Under LGPL - original licence link has changed is not relivant.
30275  *
30276  * Fork - LGPL
30277  * <script type="text/javascript">
30278  */
30279 /**
30280  * @class Roo.QuickTips
30281  * Provides attractive and customizable tooltips for any element.
30282  * @singleton
30283  */
30284 Roo.QuickTips = function(){
30285     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30286     var ce, bd, xy, dd;
30287     var visible = false, disabled = true, inited = false;
30288     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30289     
30290     var onOver = function(e){
30291         if(disabled){
30292             return;
30293         }
30294         var t = e.getTarget();
30295         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30296             return;
30297         }
30298         if(ce && t == ce.el){
30299             clearTimeout(hideProc);
30300             return;
30301         }
30302         if(t && tagEls[t.id]){
30303             tagEls[t.id].el = t;
30304             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30305             return;
30306         }
30307         var ttp, et = Roo.fly(t);
30308         var ns = cfg.namespace;
30309         if(tm.interceptTitles && t.title){
30310             ttp = t.title;
30311             t.qtip = ttp;
30312             t.removeAttribute("title");
30313             e.preventDefault();
30314         }else{
30315             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30316         }
30317         if(ttp){
30318             showProc = show.defer(tm.showDelay, tm, [{
30319                 el: t, 
30320                 text: ttp, 
30321                 width: et.getAttributeNS(ns, cfg.width),
30322                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30323                 title: et.getAttributeNS(ns, cfg.title),
30324                     cls: et.getAttributeNS(ns, cfg.cls)
30325             }]);
30326         }
30327     };
30328     
30329     var onOut = function(e){
30330         clearTimeout(showProc);
30331         var t = e.getTarget();
30332         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30333             hideProc = setTimeout(hide, tm.hideDelay);
30334         }
30335     };
30336     
30337     var onMove = function(e){
30338         if(disabled){
30339             return;
30340         }
30341         xy = e.getXY();
30342         xy[1] += 18;
30343         if(tm.trackMouse && ce){
30344             el.setXY(xy);
30345         }
30346     };
30347     
30348     var onDown = function(e){
30349         clearTimeout(showProc);
30350         clearTimeout(hideProc);
30351         if(!e.within(el)){
30352             if(tm.hideOnClick){
30353                 hide();
30354                 tm.disable();
30355                 tm.enable.defer(100, tm);
30356             }
30357         }
30358     };
30359     
30360     var getPad = function(){
30361         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30362     };
30363
30364     var show = function(o){
30365         if(disabled){
30366             return;
30367         }
30368         clearTimeout(dismissProc);
30369         ce = o;
30370         if(removeCls){ // in case manually hidden
30371             el.removeClass(removeCls);
30372             removeCls = null;
30373         }
30374         if(ce.cls){
30375             el.addClass(ce.cls);
30376             removeCls = ce.cls;
30377         }
30378         if(ce.title){
30379             tipTitle.update(ce.title);
30380             tipTitle.show();
30381         }else{
30382             tipTitle.update('');
30383             tipTitle.hide();
30384         }
30385         el.dom.style.width  = tm.maxWidth+'px';
30386         //tipBody.dom.style.width = '';
30387         tipBodyText.update(o.text);
30388         var p = getPad(), w = ce.width;
30389         if(!w){
30390             var td = tipBodyText.dom;
30391             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30392             if(aw > tm.maxWidth){
30393                 w = tm.maxWidth;
30394             }else if(aw < tm.minWidth){
30395                 w = tm.minWidth;
30396             }else{
30397                 w = aw;
30398             }
30399         }
30400         //tipBody.setWidth(w);
30401         el.setWidth(parseInt(w, 10) + p);
30402         if(ce.autoHide === false){
30403             close.setDisplayed(true);
30404             if(dd){
30405                 dd.unlock();
30406             }
30407         }else{
30408             close.setDisplayed(false);
30409             if(dd){
30410                 dd.lock();
30411             }
30412         }
30413         if(xy){
30414             el.avoidY = xy[1]-18;
30415             el.setXY(xy);
30416         }
30417         if(tm.animate){
30418             el.setOpacity(.1);
30419             el.setStyle("visibility", "visible");
30420             el.fadeIn({callback: afterShow});
30421         }else{
30422             afterShow();
30423         }
30424     };
30425     
30426     var afterShow = function(){
30427         if(ce){
30428             el.show();
30429             esc.enable();
30430             if(tm.autoDismiss && ce.autoHide !== false){
30431                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30432             }
30433         }
30434     };
30435     
30436     var hide = function(noanim){
30437         clearTimeout(dismissProc);
30438         clearTimeout(hideProc);
30439         ce = null;
30440         if(el.isVisible()){
30441             esc.disable();
30442             if(noanim !== true && tm.animate){
30443                 el.fadeOut({callback: afterHide});
30444             }else{
30445                 afterHide();
30446             } 
30447         }
30448     };
30449     
30450     var afterHide = function(){
30451         el.hide();
30452         if(removeCls){
30453             el.removeClass(removeCls);
30454             removeCls = null;
30455         }
30456     };
30457     
30458     return {
30459         /**
30460         * @cfg {Number} minWidth
30461         * The minimum width of the quick tip (defaults to 40)
30462         */
30463        minWidth : 40,
30464         /**
30465         * @cfg {Number} maxWidth
30466         * The maximum width of the quick tip (defaults to 300)
30467         */
30468        maxWidth : 300,
30469         /**
30470         * @cfg {Boolean} interceptTitles
30471         * True to automatically use the element's DOM title value if available (defaults to false)
30472         */
30473        interceptTitles : false,
30474         /**
30475         * @cfg {Boolean} trackMouse
30476         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30477         */
30478        trackMouse : false,
30479         /**
30480         * @cfg {Boolean} hideOnClick
30481         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30482         */
30483        hideOnClick : true,
30484         /**
30485         * @cfg {Number} showDelay
30486         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30487         */
30488        showDelay : 500,
30489         /**
30490         * @cfg {Number} hideDelay
30491         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30492         */
30493        hideDelay : 200,
30494         /**
30495         * @cfg {Boolean} autoHide
30496         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30497         * Used in conjunction with hideDelay.
30498         */
30499        autoHide : true,
30500         /**
30501         * @cfg {Boolean}
30502         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30503         * (defaults to true).  Used in conjunction with autoDismissDelay.
30504         */
30505        autoDismiss : true,
30506         /**
30507         * @cfg {Number}
30508         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30509         */
30510        autoDismissDelay : 5000,
30511        /**
30512         * @cfg {Boolean} animate
30513         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30514         */
30515        animate : false,
30516
30517        /**
30518         * @cfg {String} title
30519         * Title text to display (defaults to '').  This can be any valid HTML markup.
30520         */
30521         title: '',
30522        /**
30523         * @cfg {String} text
30524         * Body text to display (defaults to '').  This can be any valid HTML markup.
30525         */
30526         text : '',
30527        /**
30528         * @cfg {String} cls
30529         * A CSS class to apply to the base quick tip element (defaults to '').
30530         */
30531         cls : '',
30532        /**
30533         * @cfg {Number} width
30534         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30535         * minWidth or maxWidth.
30536         */
30537         width : null,
30538
30539     /**
30540      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30541      * or display QuickTips in a page.
30542      */
30543        init : function(){
30544           tm = Roo.QuickTips;
30545           cfg = tm.tagConfig;
30546           if(!inited){
30547               if(!Roo.isReady){ // allow calling of init() before onReady
30548                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30549                   return;
30550               }
30551               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30552               el.fxDefaults = {stopFx: true};
30553               // maximum custom styling
30554               //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>');
30555               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>');              
30556               tipTitle = el.child('h3');
30557               tipTitle.enableDisplayMode("block");
30558               tipBody = el.child('div.x-tip-bd');
30559               tipBodyText = el.child('div.x-tip-bd-inner');
30560               //bdLeft = el.child('div.x-tip-bd-left');
30561               //bdRight = el.child('div.x-tip-bd-right');
30562               close = el.child('div.x-tip-close');
30563               close.enableDisplayMode("block");
30564               close.on("click", hide);
30565               var d = Roo.get(document);
30566               d.on("mousedown", onDown);
30567               d.on("mouseover", onOver);
30568               d.on("mouseout", onOut);
30569               d.on("mousemove", onMove);
30570               esc = d.addKeyListener(27, hide);
30571               esc.disable();
30572               if(Roo.dd.DD){
30573                   dd = el.initDD("default", null, {
30574                       onDrag : function(){
30575                           el.sync();  
30576                       }
30577                   });
30578                   dd.setHandleElId(tipTitle.id);
30579                   dd.lock();
30580               }
30581               inited = true;
30582           }
30583           this.enable(); 
30584        },
30585
30586     /**
30587      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30588      * are supported:
30589      * <pre>
30590 Property    Type                   Description
30591 ----------  ---------------------  ------------------------------------------------------------------------
30592 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30593      * </ul>
30594      * @param {Object} config The config object
30595      */
30596        register : function(config){
30597            var cs = config instanceof Array ? config : arguments;
30598            for(var i = 0, len = cs.length; i < len; i++) {
30599                var c = cs[i];
30600                var target = c.target;
30601                if(target){
30602                    if(target instanceof Array){
30603                        for(var j = 0, jlen = target.length; j < jlen; j++){
30604                            tagEls[target[j]] = c;
30605                        }
30606                    }else{
30607                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30608                    }
30609                }
30610            }
30611        },
30612
30613     /**
30614      * Removes this quick tip from its element and destroys it.
30615      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30616      */
30617        unregister : function(el){
30618            delete tagEls[Roo.id(el)];
30619        },
30620
30621     /**
30622      * Enable this quick tip.
30623      */
30624        enable : function(){
30625            if(inited && disabled){
30626                locks.pop();
30627                if(locks.length < 1){
30628                    disabled = false;
30629                }
30630            }
30631        },
30632
30633     /**
30634      * Disable this quick tip.
30635      */
30636        disable : function(){
30637           disabled = true;
30638           clearTimeout(showProc);
30639           clearTimeout(hideProc);
30640           clearTimeout(dismissProc);
30641           if(ce){
30642               hide(true);
30643           }
30644           locks.push(1);
30645        },
30646
30647     /**
30648      * Returns true if the quick tip is enabled, else false.
30649      */
30650        isEnabled : function(){
30651             return !disabled;
30652        },
30653
30654         // private
30655        tagConfig : {
30656            namespace : "ext",
30657            attribute : "qtip",
30658            width : "width",
30659            target : "target",
30660            title : "qtitle",
30661            hide : "hide",
30662            cls : "qclass"
30663        }
30664    };
30665 }();
30666
30667 // backwards compat
30668 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30669  * Based on:
30670  * Ext JS Library 1.1.1
30671  * Copyright(c) 2006-2007, Ext JS, LLC.
30672  *
30673  * Originally Released Under LGPL - original licence link has changed is not relivant.
30674  *
30675  * Fork - LGPL
30676  * <script type="text/javascript">
30677  */
30678  
30679
30680 /**
30681  * @class Roo.tree.TreePanel
30682  * @extends Roo.data.Tree
30683
30684  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30685  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30686  * @cfg {Boolean} enableDD true to enable drag and drop
30687  * @cfg {Boolean} enableDrag true to enable just drag
30688  * @cfg {Boolean} enableDrop true to enable just drop
30689  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30690  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30691  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30692  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30693  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30694  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30695  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30696  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30697  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30698  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30699  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30700  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30701  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30702  * @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>
30703  * @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>
30704  * 
30705  * @constructor
30706  * @param {String/HTMLElement/Element} el The container element
30707  * @param {Object} config
30708  */
30709 Roo.tree.TreePanel = function(el, config){
30710     var root = false;
30711     var loader = false;
30712     if (config.root) {
30713         root = config.root;
30714         delete config.root;
30715     }
30716     if (config.loader) {
30717         loader = config.loader;
30718         delete config.loader;
30719     }
30720     
30721     Roo.apply(this, config);
30722     Roo.tree.TreePanel.superclass.constructor.call(this);
30723     this.el = Roo.get(el);
30724     this.el.addClass('x-tree');
30725     //console.log(root);
30726     if (root) {
30727         this.setRootNode( Roo.factory(root, Roo.tree));
30728     }
30729     if (loader) {
30730         this.loader = Roo.factory(loader, Roo.tree);
30731     }
30732    /**
30733     * Read-only. The id of the container element becomes this TreePanel's id.
30734     */
30735    this.id = this.el.id;
30736    this.addEvents({
30737         /**
30738         * @event beforeload
30739         * Fires before a node is loaded, return false to cancel
30740         * @param {Node} node The node being loaded
30741         */
30742         "beforeload" : true,
30743         /**
30744         * @event load
30745         * Fires when a node is loaded
30746         * @param {Node} node The node that was loaded
30747         */
30748         "load" : true,
30749         /**
30750         * @event textchange
30751         * Fires when the text for a node is changed
30752         * @param {Node} node The node
30753         * @param {String} text The new text
30754         * @param {String} oldText The old text
30755         */
30756         "textchange" : true,
30757         /**
30758         * @event beforeexpand
30759         * Fires before a node is expanded, return false to cancel.
30760         * @param {Node} node The node
30761         * @param {Boolean} deep
30762         * @param {Boolean} anim
30763         */
30764         "beforeexpand" : true,
30765         /**
30766         * @event beforecollapse
30767         * Fires before a node is collapsed, return false to cancel.
30768         * @param {Node} node The node
30769         * @param {Boolean} deep
30770         * @param {Boolean} anim
30771         */
30772         "beforecollapse" : true,
30773         /**
30774         * @event expand
30775         * Fires when a node is expanded
30776         * @param {Node} node The node
30777         */
30778         "expand" : true,
30779         /**
30780         * @event disabledchange
30781         * Fires when the disabled status of a node changes
30782         * @param {Node} node The node
30783         * @param {Boolean} disabled
30784         */
30785         "disabledchange" : true,
30786         /**
30787         * @event collapse
30788         * Fires when a node is collapsed
30789         * @param {Node} node The node
30790         */
30791         "collapse" : true,
30792         /**
30793         * @event beforeclick
30794         * Fires before click processing on a node. Return false to cancel the default action.
30795         * @param {Node} node The node
30796         * @param {Roo.EventObject} e The event object
30797         */
30798         "beforeclick":true,
30799         /**
30800         * @event checkchange
30801         * Fires when a node with a checkbox's checked property changes
30802         * @param {Node} this This node
30803         * @param {Boolean} checked
30804         */
30805         "checkchange":true,
30806         /**
30807         * @event click
30808         * Fires when a node is clicked
30809         * @param {Node} node The node
30810         * @param {Roo.EventObject} e The event object
30811         */
30812         "click":true,
30813         /**
30814         * @event dblclick
30815         * Fires when a node is double clicked
30816         * @param {Node} node The node
30817         * @param {Roo.EventObject} e The event object
30818         */
30819         "dblclick":true,
30820         /**
30821         * @event contextmenu
30822         * Fires when a node is right clicked
30823         * @param {Node} node The node
30824         * @param {Roo.EventObject} e The event object
30825         */
30826         "contextmenu":true,
30827         /**
30828         * @event beforechildrenrendered
30829         * Fires right before the child nodes for a node are rendered
30830         * @param {Node} node The node
30831         */
30832         "beforechildrenrendered":true,
30833        /**
30834              * @event startdrag
30835              * Fires when a node starts being dragged
30836              * @param {Roo.tree.TreePanel} this
30837              * @param {Roo.tree.TreeNode} node
30838              * @param {event} e The raw browser event
30839              */ 
30840             "startdrag" : true,
30841             /**
30842              * @event enddrag
30843              * Fires when a drag operation is complete
30844              * @param {Roo.tree.TreePanel} this
30845              * @param {Roo.tree.TreeNode} node
30846              * @param {event} e The raw browser event
30847              */
30848             "enddrag" : true,
30849             /**
30850              * @event dragdrop
30851              * Fires when a dragged node is dropped on a valid DD target
30852              * @param {Roo.tree.TreePanel} this
30853              * @param {Roo.tree.TreeNode} node
30854              * @param {DD} dd The dd it was dropped on
30855              * @param {event} e The raw browser event
30856              */
30857             "dragdrop" : true,
30858             /**
30859              * @event beforenodedrop
30860              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30861              * passed to handlers has the following properties:<br />
30862              * <ul style="padding:5px;padding-left:16px;">
30863              * <li>tree - The TreePanel</li>
30864              * <li>target - The node being targeted for the drop</li>
30865              * <li>data - The drag data from the drag source</li>
30866              * <li>point - The point of the drop - append, above or below</li>
30867              * <li>source - The drag source</li>
30868              * <li>rawEvent - Raw mouse event</li>
30869              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30870              * to be inserted by setting them on this object.</li>
30871              * <li>cancel - Set this to true to cancel the drop.</li>
30872              * </ul>
30873              * @param {Object} dropEvent
30874              */
30875             "beforenodedrop" : true,
30876             /**
30877              * @event nodedrop
30878              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30879              * passed to handlers has the following properties:<br />
30880              * <ul style="padding:5px;padding-left:16px;">
30881              * <li>tree - The TreePanel</li>
30882              * <li>target - The node being targeted for the drop</li>
30883              * <li>data - The drag data from the drag source</li>
30884              * <li>point - The point of the drop - append, above or below</li>
30885              * <li>source - The drag source</li>
30886              * <li>rawEvent - Raw mouse event</li>
30887              * <li>dropNode - Dropped node(s).</li>
30888              * </ul>
30889              * @param {Object} dropEvent
30890              */
30891             "nodedrop" : true,
30892              /**
30893              * @event nodedragover
30894              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30895              * passed to handlers has the following properties:<br />
30896              * <ul style="padding:5px;padding-left:16px;">
30897              * <li>tree - The TreePanel</li>
30898              * <li>target - The node being targeted for the drop</li>
30899              * <li>data - The drag data from the drag source</li>
30900              * <li>point - The point of the drop - append, above or below</li>
30901              * <li>source - The drag source</li>
30902              * <li>rawEvent - Raw mouse event</li>
30903              * <li>dropNode - Drop node(s) provided by the source.</li>
30904              * <li>cancel - Set this to true to signal drop not allowed.</li>
30905              * </ul>
30906              * @param {Object} dragOverEvent
30907              */
30908             "nodedragover" : true
30909         
30910    });
30911    if(this.singleExpand){
30912        this.on("beforeexpand", this.restrictExpand, this);
30913    }
30914 };
30915 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30916     rootVisible : true,
30917     animate: Roo.enableFx,
30918     lines : true,
30919     enableDD : false,
30920     hlDrop : Roo.enableFx,
30921   
30922     renderer: false,
30923     
30924     rendererTip: false,
30925     // private
30926     restrictExpand : function(node){
30927         var p = node.parentNode;
30928         if(p){
30929             if(p.expandedChild && p.expandedChild.parentNode == p){
30930                 p.expandedChild.collapse();
30931             }
30932             p.expandedChild = node;
30933         }
30934     },
30935
30936     // private override
30937     setRootNode : function(node){
30938         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30939         if(!this.rootVisible){
30940             node.ui = new Roo.tree.RootTreeNodeUI(node);
30941         }
30942         return node;
30943     },
30944
30945     /**
30946      * Returns the container element for this TreePanel
30947      */
30948     getEl : function(){
30949         return this.el;
30950     },
30951
30952     /**
30953      * Returns the default TreeLoader for this TreePanel
30954      */
30955     getLoader : function(){
30956         return this.loader;
30957     },
30958
30959     /**
30960      * Expand all nodes
30961      */
30962     expandAll : function(){
30963         this.root.expand(true);
30964     },
30965
30966     /**
30967      * Collapse all nodes
30968      */
30969     collapseAll : function(){
30970         this.root.collapse(true);
30971     },
30972
30973     /**
30974      * Returns the selection model used by this TreePanel
30975      */
30976     getSelectionModel : function(){
30977         if(!this.selModel){
30978             this.selModel = new Roo.tree.DefaultSelectionModel();
30979         }
30980         return this.selModel;
30981     },
30982
30983     /**
30984      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30985      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30986      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30987      * @return {Array}
30988      */
30989     getChecked : function(a, startNode){
30990         startNode = startNode || this.root;
30991         var r = [];
30992         var f = function(){
30993             if(this.attributes.checked){
30994                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30995             }
30996         }
30997         startNode.cascade(f);
30998         return r;
30999     },
31000
31001     /**
31002      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31003      * @param {String} path
31004      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31005      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31006      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31007      */
31008     expandPath : function(path, attr, callback){
31009         attr = attr || "id";
31010         var keys = path.split(this.pathSeparator);
31011         var curNode = this.root;
31012         if(curNode.attributes[attr] != keys[1]){ // invalid root
31013             if(callback){
31014                 callback(false, null);
31015             }
31016             return;
31017         }
31018         var index = 1;
31019         var f = function(){
31020             if(++index == keys.length){
31021                 if(callback){
31022                     callback(true, curNode);
31023                 }
31024                 return;
31025             }
31026             var c = curNode.findChild(attr, keys[index]);
31027             if(!c){
31028                 if(callback){
31029                     callback(false, curNode);
31030                 }
31031                 return;
31032             }
31033             curNode = c;
31034             c.expand(false, false, f);
31035         };
31036         curNode.expand(false, false, f);
31037     },
31038
31039     /**
31040      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31041      * @param {String} path
31042      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31043      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31044      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31045      */
31046     selectPath : function(path, attr, callback){
31047         attr = attr || "id";
31048         var keys = path.split(this.pathSeparator);
31049         var v = keys.pop();
31050         if(keys.length > 0){
31051             var f = function(success, node){
31052                 if(success && node){
31053                     var n = node.findChild(attr, v);
31054                     if(n){
31055                         n.select();
31056                         if(callback){
31057                             callback(true, n);
31058                         }
31059                     }else if(callback){
31060                         callback(false, n);
31061                     }
31062                 }else{
31063                     if(callback){
31064                         callback(false, n);
31065                     }
31066                 }
31067             };
31068             this.expandPath(keys.join(this.pathSeparator), attr, f);
31069         }else{
31070             this.root.select();
31071             if(callback){
31072                 callback(true, this.root);
31073             }
31074         }
31075     },
31076
31077     getTreeEl : function(){
31078         return this.el;
31079     },
31080
31081     /**
31082      * Trigger rendering of this TreePanel
31083      */
31084     render : function(){
31085         if (this.innerCt) {
31086             return this; // stop it rendering more than once!!
31087         }
31088         
31089         this.innerCt = this.el.createChild({tag:"ul",
31090                cls:"x-tree-root-ct " +
31091                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31092
31093         if(this.containerScroll){
31094             Roo.dd.ScrollManager.register(this.el);
31095         }
31096         if((this.enableDD || this.enableDrop) && !this.dropZone){
31097            /**
31098             * The dropZone used by this tree if drop is enabled
31099             * @type Roo.tree.TreeDropZone
31100             */
31101              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31102                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31103            });
31104         }
31105         if((this.enableDD || this.enableDrag) && !this.dragZone){
31106            /**
31107             * The dragZone used by this tree if drag is enabled
31108             * @type Roo.tree.TreeDragZone
31109             */
31110             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31111                ddGroup: this.ddGroup || "TreeDD",
31112                scroll: this.ddScroll
31113            });
31114         }
31115         this.getSelectionModel().init(this);
31116         if (!this.root) {
31117             console.log("ROOT not set in tree");
31118             return;
31119         }
31120         this.root.render();
31121         if(!this.rootVisible){
31122             this.root.renderChildren();
31123         }
31124         return this;
31125     }
31126 });/*
31127  * Based on:
31128  * Ext JS Library 1.1.1
31129  * Copyright(c) 2006-2007, Ext JS, LLC.
31130  *
31131  * Originally Released Under LGPL - original licence link has changed is not relivant.
31132  *
31133  * Fork - LGPL
31134  * <script type="text/javascript">
31135  */
31136  
31137
31138 /**
31139  * @class Roo.tree.DefaultSelectionModel
31140  * @extends Roo.util.Observable
31141  * The default single selection for a TreePanel.
31142  */
31143 Roo.tree.DefaultSelectionModel = function(){
31144    this.selNode = null;
31145    
31146    this.addEvents({
31147        /**
31148         * @event selectionchange
31149         * Fires when the selected node changes
31150         * @param {DefaultSelectionModel} this
31151         * @param {TreeNode} node the new selection
31152         */
31153        "selectionchange" : true,
31154
31155        /**
31156         * @event beforeselect
31157         * Fires before the selected node changes, return false to cancel the change
31158         * @param {DefaultSelectionModel} this
31159         * @param {TreeNode} node the new selection
31160         * @param {TreeNode} node the old selection
31161         */
31162        "beforeselect" : true
31163    });
31164 };
31165
31166 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31167     init : function(tree){
31168         this.tree = tree;
31169         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31170         tree.on("click", this.onNodeClick, this);
31171     },
31172     
31173     onNodeClick : function(node, e){
31174         if (e.ctrlKey && this.selNode == node)  {
31175             this.unselect(node);
31176             return;
31177         }
31178         this.select(node);
31179     },
31180     
31181     /**
31182      * Select a node.
31183      * @param {TreeNode} node The node to select
31184      * @return {TreeNode} The selected node
31185      */
31186     select : function(node){
31187         var last = this.selNode;
31188         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31189             if(last){
31190                 last.ui.onSelectedChange(false);
31191             }
31192             this.selNode = node;
31193             node.ui.onSelectedChange(true);
31194             this.fireEvent("selectionchange", this, node, last);
31195         }
31196         return node;
31197     },
31198     
31199     /**
31200      * Deselect a node.
31201      * @param {TreeNode} node The node to unselect
31202      */
31203     unselect : function(node){
31204         if(this.selNode == node){
31205             this.clearSelections();
31206         }    
31207     },
31208     
31209     /**
31210      * Clear all selections
31211      */
31212     clearSelections : function(){
31213         var n = this.selNode;
31214         if(n){
31215             n.ui.onSelectedChange(false);
31216             this.selNode = null;
31217             this.fireEvent("selectionchange", this, null);
31218         }
31219         return n;
31220     },
31221     
31222     /**
31223      * Get the selected node
31224      * @return {TreeNode} The selected node
31225      */
31226     getSelectedNode : function(){
31227         return this.selNode;    
31228     },
31229     
31230     /**
31231      * Returns true if the node is selected
31232      * @param {TreeNode} node The node to check
31233      * @return {Boolean}
31234      */
31235     isSelected : function(node){
31236         return this.selNode == node;  
31237     },
31238
31239     /**
31240      * Selects the node above the selected node in the tree, intelligently walking the nodes
31241      * @return TreeNode The new selection
31242      */
31243     selectPrevious : function(){
31244         var s = this.selNode || this.lastSelNode;
31245         if(!s){
31246             return null;
31247         }
31248         var ps = s.previousSibling;
31249         if(ps){
31250             if(!ps.isExpanded() || ps.childNodes.length < 1){
31251                 return this.select(ps);
31252             } else{
31253                 var lc = ps.lastChild;
31254                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31255                     lc = lc.lastChild;
31256                 }
31257                 return this.select(lc);
31258             }
31259         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31260             return this.select(s.parentNode);
31261         }
31262         return null;
31263     },
31264
31265     /**
31266      * Selects the node above the selected node in the tree, intelligently walking the nodes
31267      * @return TreeNode The new selection
31268      */
31269     selectNext : function(){
31270         var s = this.selNode || this.lastSelNode;
31271         if(!s){
31272             return null;
31273         }
31274         if(s.firstChild && s.isExpanded()){
31275              return this.select(s.firstChild);
31276          }else if(s.nextSibling){
31277              return this.select(s.nextSibling);
31278          }else if(s.parentNode){
31279             var newS = null;
31280             s.parentNode.bubble(function(){
31281                 if(this.nextSibling){
31282                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31283                     return false;
31284                 }
31285             });
31286             return newS;
31287          }
31288         return null;
31289     },
31290
31291     onKeyDown : function(e){
31292         var s = this.selNode || this.lastSelNode;
31293         // undesirable, but required
31294         var sm = this;
31295         if(!s){
31296             return;
31297         }
31298         var k = e.getKey();
31299         switch(k){
31300              case e.DOWN:
31301                  e.stopEvent();
31302                  this.selectNext();
31303              break;
31304              case e.UP:
31305                  e.stopEvent();
31306                  this.selectPrevious();
31307              break;
31308              case e.RIGHT:
31309                  e.preventDefault();
31310                  if(s.hasChildNodes()){
31311                      if(!s.isExpanded()){
31312                          s.expand();
31313                      }else if(s.firstChild){
31314                          this.select(s.firstChild, e);
31315                      }
31316                  }
31317              break;
31318              case e.LEFT:
31319                  e.preventDefault();
31320                  if(s.hasChildNodes() && s.isExpanded()){
31321                      s.collapse();
31322                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31323                      this.select(s.parentNode, e);
31324                  }
31325              break;
31326         };
31327     }
31328 });
31329
31330 /**
31331  * @class Roo.tree.MultiSelectionModel
31332  * @extends Roo.util.Observable
31333  * Multi selection for a TreePanel.
31334  */
31335 Roo.tree.MultiSelectionModel = function(){
31336    this.selNodes = [];
31337    this.selMap = {};
31338    this.addEvents({
31339        /**
31340         * @event selectionchange
31341         * Fires when the selected nodes change
31342         * @param {MultiSelectionModel} this
31343         * @param {Array} nodes Array of the selected nodes
31344         */
31345        "selectionchange" : true
31346    });
31347 };
31348
31349 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31350     init : function(tree){
31351         this.tree = tree;
31352         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31353         tree.on("click", this.onNodeClick, this);
31354     },
31355     
31356     onNodeClick : function(node, e){
31357         this.select(node, e, e.ctrlKey);
31358     },
31359     
31360     /**
31361      * Select a node.
31362      * @param {TreeNode} node The node to select
31363      * @param {EventObject} e (optional) An event associated with the selection
31364      * @param {Boolean} keepExisting True to retain existing selections
31365      * @return {TreeNode} The selected node
31366      */
31367     select : function(node, e, keepExisting){
31368         if(keepExisting !== true){
31369             this.clearSelections(true);
31370         }
31371         if(this.isSelected(node)){
31372             this.lastSelNode = node;
31373             return node;
31374         }
31375         this.selNodes.push(node);
31376         this.selMap[node.id] = node;
31377         this.lastSelNode = node;
31378         node.ui.onSelectedChange(true);
31379         this.fireEvent("selectionchange", this, this.selNodes);
31380         return node;
31381     },
31382     
31383     /**
31384      * Deselect a node.
31385      * @param {TreeNode} node The node to unselect
31386      */
31387     unselect : function(node){
31388         if(this.selMap[node.id]){
31389             node.ui.onSelectedChange(false);
31390             var sn = this.selNodes;
31391             var index = -1;
31392             if(sn.indexOf){
31393                 index = sn.indexOf(node);
31394             }else{
31395                 for(var i = 0, len = sn.length; i < len; i++){
31396                     if(sn[i] == node){
31397                         index = i;
31398                         break;
31399                     }
31400                 }
31401             }
31402             if(index != -1){
31403                 this.selNodes.splice(index, 1);
31404             }
31405             delete this.selMap[node.id];
31406             this.fireEvent("selectionchange", this, this.selNodes);
31407         }
31408     },
31409     
31410     /**
31411      * Clear all selections
31412      */
31413     clearSelections : function(suppressEvent){
31414         var sn = this.selNodes;
31415         if(sn.length > 0){
31416             for(var i = 0, len = sn.length; i < len; i++){
31417                 sn[i].ui.onSelectedChange(false);
31418             }
31419             this.selNodes = [];
31420             this.selMap = {};
31421             if(suppressEvent !== true){
31422                 this.fireEvent("selectionchange", this, this.selNodes);
31423             }
31424         }
31425     },
31426     
31427     /**
31428      * Returns true if the node is selected
31429      * @param {TreeNode} node The node to check
31430      * @return {Boolean}
31431      */
31432     isSelected : function(node){
31433         return this.selMap[node.id] ? true : false;  
31434     },
31435     
31436     /**
31437      * Returns an array of the selected nodes
31438      * @return {Array}
31439      */
31440     getSelectedNodes : function(){
31441         return this.selNodes;    
31442     },
31443
31444     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31445
31446     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31447
31448     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31449 });/*
31450  * Based on:
31451  * Ext JS Library 1.1.1
31452  * Copyright(c) 2006-2007, Ext JS, LLC.
31453  *
31454  * Originally Released Under LGPL - original licence link has changed is not relivant.
31455  *
31456  * Fork - LGPL
31457  * <script type="text/javascript">
31458  */
31459  
31460 /**
31461  * @class Roo.tree.TreeNode
31462  * @extends Roo.data.Node
31463  * @cfg {String} text The text for this node
31464  * @cfg {Boolean} expanded true to start the node expanded
31465  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31466  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31467  * @cfg {Boolean} disabled true to start the node disabled
31468  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31469  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31470  * @cfg {String} cls A css class to be added to the node
31471  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31472  * @cfg {String} href URL of the link used for the node (defaults to #)
31473  * @cfg {String} hrefTarget target frame for the link
31474  * @cfg {String} qtip An Ext QuickTip for the node
31475  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31476  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31477  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31478  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31479  * (defaults to undefined with no checkbox rendered)
31480  * @constructor
31481  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31482  */
31483 Roo.tree.TreeNode = function(attributes){
31484     attributes = attributes || {};
31485     if(typeof attributes == "string"){
31486         attributes = {text: attributes};
31487     }
31488     this.childrenRendered = false;
31489     this.rendered = false;
31490     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31491     this.expanded = attributes.expanded === true;
31492     this.isTarget = attributes.isTarget !== false;
31493     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31494     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31495
31496     /**
31497      * Read-only. The text for this node. To change it use setText().
31498      * @type String
31499      */
31500     this.text = attributes.text;
31501     /**
31502      * True if this node is disabled.
31503      * @type Boolean
31504      */
31505     this.disabled = attributes.disabled === true;
31506
31507     this.addEvents({
31508         /**
31509         * @event textchange
31510         * Fires when the text for this node is changed
31511         * @param {Node} this This node
31512         * @param {String} text The new text
31513         * @param {String} oldText The old text
31514         */
31515         "textchange" : true,
31516         /**
31517         * @event beforeexpand
31518         * Fires before this node is expanded, return false to cancel.
31519         * @param {Node} this This node
31520         * @param {Boolean} deep
31521         * @param {Boolean} anim
31522         */
31523         "beforeexpand" : true,
31524         /**
31525         * @event beforecollapse
31526         * Fires before this node is collapsed, return false to cancel.
31527         * @param {Node} this This node
31528         * @param {Boolean} deep
31529         * @param {Boolean} anim
31530         */
31531         "beforecollapse" : true,
31532         /**
31533         * @event expand
31534         * Fires when this node is expanded
31535         * @param {Node} this This node
31536         */
31537         "expand" : true,
31538         /**
31539         * @event disabledchange
31540         * Fires when the disabled status of this node changes
31541         * @param {Node} this This node
31542         * @param {Boolean} disabled
31543         */
31544         "disabledchange" : true,
31545         /**
31546         * @event collapse
31547         * Fires when this node is collapsed
31548         * @param {Node} this This node
31549         */
31550         "collapse" : true,
31551         /**
31552         * @event beforeclick
31553         * Fires before click processing. Return false to cancel the default action.
31554         * @param {Node} this This node
31555         * @param {Roo.EventObject} e The event object
31556         */
31557         "beforeclick":true,
31558         /**
31559         * @event checkchange
31560         * Fires when a node with a checkbox's checked property changes
31561         * @param {Node} this This node
31562         * @param {Boolean} checked
31563         */
31564         "checkchange":true,
31565         /**
31566         * @event click
31567         * Fires when this node is clicked
31568         * @param {Node} this This node
31569         * @param {Roo.EventObject} e The event object
31570         */
31571         "click":true,
31572         /**
31573         * @event dblclick
31574         * Fires when this node is double clicked
31575         * @param {Node} this This node
31576         * @param {Roo.EventObject} e The event object
31577         */
31578         "dblclick":true,
31579         /**
31580         * @event contextmenu
31581         * Fires when this node is right clicked
31582         * @param {Node} this This node
31583         * @param {Roo.EventObject} e The event object
31584         */
31585         "contextmenu":true,
31586         /**
31587         * @event beforechildrenrendered
31588         * Fires right before the child nodes for this node are rendered
31589         * @param {Node} this This node
31590         */
31591         "beforechildrenrendered":true
31592     });
31593
31594     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31595
31596     /**
31597      * Read-only. The UI for this node
31598      * @type TreeNodeUI
31599      */
31600     this.ui = new uiClass(this);
31601 };
31602 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31603     preventHScroll: true,
31604     /**
31605      * Returns true if this node is expanded
31606      * @return {Boolean}
31607      */
31608     isExpanded : function(){
31609         return this.expanded;
31610     },
31611
31612     /**
31613      * Returns the UI object for this node
31614      * @return {TreeNodeUI}
31615      */
31616     getUI : function(){
31617         return this.ui;
31618     },
31619
31620     // private override
31621     setFirstChild : function(node){
31622         var of = this.firstChild;
31623         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31624         if(this.childrenRendered && of && node != of){
31625             of.renderIndent(true, true);
31626         }
31627         if(this.rendered){
31628             this.renderIndent(true, true);
31629         }
31630     },
31631
31632     // private override
31633     setLastChild : function(node){
31634         var ol = this.lastChild;
31635         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31636         if(this.childrenRendered && ol && node != ol){
31637             ol.renderIndent(true, true);
31638         }
31639         if(this.rendered){
31640             this.renderIndent(true, true);
31641         }
31642     },
31643
31644     // these methods are overridden to provide lazy rendering support
31645     // private override
31646     appendChild : function(){
31647         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31648         if(node && this.childrenRendered){
31649             node.render();
31650         }
31651         this.ui.updateExpandIcon();
31652         return node;
31653     },
31654
31655     // private override
31656     removeChild : function(node){
31657         this.ownerTree.getSelectionModel().unselect(node);
31658         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31659         // if it's been rendered remove dom node
31660         if(this.childrenRendered){
31661             node.ui.remove();
31662         }
31663         if(this.childNodes.length < 1){
31664             this.collapse(false, false);
31665         }else{
31666             this.ui.updateExpandIcon();
31667         }
31668         if(!this.firstChild) {
31669             this.childrenRendered = false;
31670         }
31671         return node;
31672     },
31673
31674     // private override
31675     insertBefore : function(node, refNode){
31676         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31677         if(newNode && refNode && this.childrenRendered){
31678             node.render();
31679         }
31680         this.ui.updateExpandIcon();
31681         return newNode;
31682     },
31683
31684     /**
31685      * Sets the text for this node
31686      * @param {String} text
31687      */
31688     setText : function(text){
31689         var oldText = this.text;
31690         this.text = text;
31691         this.attributes.text = text;
31692         if(this.rendered){ // event without subscribing
31693             this.ui.onTextChange(this, text, oldText);
31694         }
31695         this.fireEvent("textchange", this, text, oldText);
31696     },
31697
31698     /**
31699      * Triggers selection of this node
31700      */
31701     select : function(){
31702         this.getOwnerTree().getSelectionModel().select(this);
31703     },
31704
31705     /**
31706      * Triggers deselection of this node
31707      */
31708     unselect : function(){
31709         this.getOwnerTree().getSelectionModel().unselect(this);
31710     },
31711
31712     /**
31713      * Returns true if this node is selected
31714      * @return {Boolean}
31715      */
31716     isSelected : function(){
31717         return this.getOwnerTree().getSelectionModel().isSelected(this);
31718     },
31719
31720     /**
31721      * Expand this node.
31722      * @param {Boolean} deep (optional) True to expand all children as well
31723      * @param {Boolean} anim (optional) false to cancel the default animation
31724      * @param {Function} callback (optional) A callback to be called when
31725      * expanding this node completes (does not wait for deep expand to complete).
31726      * Called with 1 parameter, this node.
31727      */
31728     expand : function(deep, anim, callback){
31729         if(!this.expanded){
31730             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31731                 return;
31732             }
31733             if(!this.childrenRendered){
31734                 this.renderChildren();
31735             }
31736             this.expanded = true;
31737             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31738                 this.ui.animExpand(function(){
31739                     this.fireEvent("expand", this);
31740                     if(typeof callback == "function"){
31741                         callback(this);
31742                     }
31743                     if(deep === true){
31744                         this.expandChildNodes(true);
31745                     }
31746                 }.createDelegate(this));
31747                 return;
31748             }else{
31749                 this.ui.expand();
31750                 this.fireEvent("expand", this);
31751                 if(typeof callback == "function"){
31752                     callback(this);
31753                 }
31754             }
31755         }else{
31756            if(typeof callback == "function"){
31757                callback(this);
31758            }
31759         }
31760         if(deep === true){
31761             this.expandChildNodes(true);
31762         }
31763     },
31764
31765     isHiddenRoot : function(){
31766         return this.isRoot && !this.getOwnerTree().rootVisible;
31767     },
31768
31769     /**
31770      * Collapse this node.
31771      * @param {Boolean} deep (optional) True to collapse all children as well
31772      * @param {Boolean} anim (optional) false to cancel the default animation
31773      */
31774     collapse : function(deep, anim){
31775         if(this.expanded && !this.isHiddenRoot()){
31776             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31777                 return;
31778             }
31779             this.expanded = false;
31780             if((this.getOwnerTree().animate && anim !== false) || anim){
31781                 this.ui.animCollapse(function(){
31782                     this.fireEvent("collapse", this);
31783                     if(deep === true){
31784                         this.collapseChildNodes(true);
31785                     }
31786                 }.createDelegate(this));
31787                 return;
31788             }else{
31789                 this.ui.collapse();
31790                 this.fireEvent("collapse", this);
31791             }
31792         }
31793         if(deep === true){
31794             var cs = this.childNodes;
31795             for(var i = 0, len = cs.length; i < len; i++) {
31796                 cs[i].collapse(true, false);
31797             }
31798         }
31799     },
31800
31801     // private
31802     delayedExpand : function(delay){
31803         if(!this.expandProcId){
31804             this.expandProcId = this.expand.defer(delay, this);
31805         }
31806     },
31807
31808     // private
31809     cancelExpand : function(){
31810         if(this.expandProcId){
31811             clearTimeout(this.expandProcId);
31812         }
31813         this.expandProcId = false;
31814     },
31815
31816     /**
31817      * Toggles expanded/collapsed state of the node
31818      */
31819     toggle : function(){
31820         if(this.expanded){
31821             this.collapse();
31822         }else{
31823             this.expand();
31824         }
31825     },
31826
31827     /**
31828      * Ensures all parent nodes are expanded
31829      */
31830     ensureVisible : function(callback){
31831         var tree = this.getOwnerTree();
31832         tree.expandPath(this.parentNode.getPath(), false, function(){
31833             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31834             Roo.callback(callback);
31835         }.createDelegate(this));
31836     },
31837
31838     /**
31839      * Expand all child nodes
31840      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31841      */
31842     expandChildNodes : function(deep){
31843         var cs = this.childNodes;
31844         for(var i = 0, len = cs.length; i < len; i++) {
31845                 cs[i].expand(deep);
31846         }
31847     },
31848
31849     /**
31850      * Collapse all child nodes
31851      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31852      */
31853     collapseChildNodes : function(deep){
31854         var cs = this.childNodes;
31855         for(var i = 0, len = cs.length; i < len; i++) {
31856                 cs[i].collapse(deep);
31857         }
31858     },
31859
31860     /**
31861      * Disables this node
31862      */
31863     disable : function(){
31864         this.disabled = true;
31865         this.unselect();
31866         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31867             this.ui.onDisableChange(this, true);
31868         }
31869         this.fireEvent("disabledchange", this, true);
31870     },
31871
31872     /**
31873      * Enables this node
31874      */
31875     enable : function(){
31876         this.disabled = false;
31877         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31878             this.ui.onDisableChange(this, false);
31879         }
31880         this.fireEvent("disabledchange", this, false);
31881     },
31882
31883     // private
31884     renderChildren : function(suppressEvent){
31885         if(suppressEvent !== false){
31886             this.fireEvent("beforechildrenrendered", this);
31887         }
31888         var cs = this.childNodes;
31889         for(var i = 0, len = cs.length; i < len; i++){
31890             cs[i].render(true);
31891         }
31892         this.childrenRendered = true;
31893     },
31894
31895     // private
31896     sort : function(fn, scope){
31897         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31898         if(this.childrenRendered){
31899             var cs = this.childNodes;
31900             for(var i = 0, len = cs.length; i < len; i++){
31901                 cs[i].render(true);
31902             }
31903         }
31904     },
31905
31906     // private
31907     render : function(bulkRender){
31908         this.ui.render(bulkRender);
31909         if(!this.rendered){
31910             this.rendered = true;
31911             if(this.expanded){
31912                 this.expanded = false;
31913                 this.expand(false, false);
31914             }
31915         }
31916     },
31917
31918     // private
31919     renderIndent : function(deep, refresh){
31920         if(refresh){
31921             this.ui.childIndent = null;
31922         }
31923         this.ui.renderIndent();
31924         if(deep === true && this.childrenRendered){
31925             var cs = this.childNodes;
31926             for(var i = 0, len = cs.length; i < len; i++){
31927                 cs[i].renderIndent(true, refresh);
31928             }
31929         }
31930     }
31931 });/*
31932  * Based on:
31933  * Ext JS Library 1.1.1
31934  * Copyright(c) 2006-2007, Ext JS, LLC.
31935  *
31936  * Originally Released Under LGPL - original licence link has changed is not relivant.
31937  *
31938  * Fork - LGPL
31939  * <script type="text/javascript">
31940  */
31941  
31942 /**
31943  * @class Roo.tree.AsyncTreeNode
31944  * @extends Roo.tree.TreeNode
31945  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31946  * @constructor
31947  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31948  */
31949  Roo.tree.AsyncTreeNode = function(config){
31950     this.loaded = false;
31951     this.loading = false;
31952     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31953     /**
31954     * @event beforeload
31955     * Fires before this node is loaded, return false to cancel
31956     * @param {Node} this This node
31957     */
31958     this.addEvents({'beforeload':true, 'load': true});
31959     /**
31960     * @event load
31961     * Fires when this node is loaded
31962     * @param {Node} this This node
31963     */
31964     /**
31965      * The loader used by this node (defaults to using the tree's defined loader)
31966      * @type TreeLoader
31967      * @property loader
31968      */
31969 };
31970 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31971     expand : function(deep, anim, callback){
31972         if(this.loading){ // if an async load is already running, waiting til it's done
31973             var timer;
31974             var f = function(){
31975                 if(!this.loading){ // done loading
31976                     clearInterval(timer);
31977                     this.expand(deep, anim, callback);
31978                 }
31979             }.createDelegate(this);
31980             timer = setInterval(f, 200);
31981             return;
31982         }
31983         if(!this.loaded){
31984             if(this.fireEvent("beforeload", this) === false){
31985                 return;
31986             }
31987             this.loading = true;
31988             this.ui.beforeLoad(this);
31989             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31990             if(loader){
31991                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31992                 return;
31993             }
31994         }
31995         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31996     },
31997     
31998     /**
31999      * Returns true if this node is currently loading
32000      * @return {Boolean}
32001      */
32002     isLoading : function(){
32003         return this.loading;  
32004     },
32005     
32006     loadComplete : function(deep, anim, callback){
32007         this.loading = false;
32008         this.loaded = true;
32009         this.ui.afterLoad(this);
32010         this.fireEvent("load", this);
32011         this.expand(deep, anim, callback);
32012     },
32013     
32014     /**
32015      * Returns true if this node has been loaded
32016      * @return {Boolean}
32017      */
32018     isLoaded : function(){
32019         return this.loaded;
32020     },
32021     
32022     hasChildNodes : function(){
32023         if(!this.isLeaf() && !this.loaded){
32024             return true;
32025         }else{
32026             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32027         }
32028     },
32029
32030     /**
32031      * Trigger a reload for this node
32032      * @param {Function} callback
32033      */
32034     reload : function(callback){
32035         this.collapse(false, false);
32036         while(this.firstChild){
32037             this.removeChild(this.firstChild);
32038         }
32039         this.childrenRendered = false;
32040         this.loaded = false;
32041         if(this.isHiddenRoot()){
32042             this.expanded = false;
32043         }
32044         this.expand(false, false, callback);
32045     }
32046 });/*
32047  * Based on:
32048  * Ext JS Library 1.1.1
32049  * Copyright(c) 2006-2007, Ext JS, LLC.
32050  *
32051  * Originally Released Under LGPL - original licence link has changed is not relivant.
32052  *
32053  * Fork - LGPL
32054  * <script type="text/javascript">
32055  */
32056  
32057 /**
32058  * @class Roo.tree.TreeNodeUI
32059  * @constructor
32060  * @param {Object} node The node to render
32061  * The TreeNode UI implementation is separate from the
32062  * tree implementation. Unless you are customizing the tree UI,
32063  * you should never have to use this directly.
32064  */
32065 Roo.tree.TreeNodeUI = function(node){
32066     this.node = node;
32067     this.rendered = false;
32068     this.animating = false;
32069     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32070 };
32071
32072 Roo.tree.TreeNodeUI.prototype = {
32073     removeChild : function(node){
32074         if(this.rendered){
32075             this.ctNode.removeChild(node.ui.getEl());
32076         }
32077     },
32078
32079     beforeLoad : function(){
32080          this.addClass("x-tree-node-loading");
32081     },
32082
32083     afterLoad : function(){
32084          this.removeClass("x-tree-node-loading");
32085     },
32086
32087     onTextChange : function(node, text, oldText){
32088         if(this.rendered){
32089             this.textNode.innerHTML = text;
32090         }
32091     },
32092
32093     onDisableChange : function(node, state){
32094         this.disabled = state;
32095         if(state){
32096             this.addClass("x-tree-node-disabled");
32097         }else{
32098             this.removeClass("x-tree-node-disabled");
32099         }
32100     },
32101
32102     onSelectedChange : function(state){
32103         if(state){
32104             this.focus();
32105             this.addClass("x-tree-selected");
32106         }else{
32107             //this.blur();
32108             this.removeClass("x-tree-selected");
32109         }
32110     },
32111
32112     onMove : function(tree, node, oldParent, newParent, index, refNode){
32113         this.childIndent = null;
32114         if(this.rendered){
32115             var targetNode = newParent.ui.getContainer();
32116             if(!targetNode){//target not rendered
32117                 this.holder = document.createElement("div");
32118                 this.holder.appendChild(this.wrap);
32119                 return;
32120             }
32121             var insertBefore = refNode ? refNode.ui.getEl() : null;
32122             if(insertBefore){
32123                 targetNode.insertBefore(this.wrap, insertBefore);
32124             }else{
32125                 targetNode.appendChild(this.wrap);
32126             }
32127             this.node.renderIndent(true);
32128         }
32129     },
32130
32131     addClass : function(cls){
32132         if(this.elNode){
32133             Roo.fly(this.elNode).addClass(cls);
32134         }
32135     },
32136
32137     removeClass : function(cls){
32138         if(this.elNode){
32139             Roo.fly(this.elNode).removeClass(cls);
32140         }
32141     },
32142
32143     remove : function(){
32144         if(this.rendered){
32145             this.holder = document.createElement("div");
32146             this.holder.appendChild(this.wrap);
32147         }
32148     },
32149
32150     fireEvent : function(){
32151         return this.node.fireEvent.apply(this.node, arguments);
32152     },
32153
32154     initEvents : function(){
32155         this.node.on("move", this.onMove, this);
32156         var E = Roo.EventManager;
32157         var a = this.anchor;
32158
32159         var el = Roo.fly(a, '_treeui');
32160
32161         if(Roo.isOpera){ // opera render bug ignores the CSS
32162             el.setStyle("text-decoration", "none");
32163         }
32164
32165         el.on("click", this.onClick, this);
32166         el.on("dblclick", this.onDblClick, this);
32167
32168         if(this.checkbox){
32169             Roo.EventManager.on(this.checkbox,
32170                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32171         }
32172
32173         el.on("contextmenu", this.onContextMenu, this);
32174
32175         var icon = Roo.fly(this.iconNode);
32176         icon.on("click", this.onClick, this);
32177         icon.on("dblclick", this.onDblClick, this);
32178         icon.on("contextmenu", this.onContextMenu, this);
32179         E.on(this.ecNode, "click", this.ecClick, this, true);
32180
32181         if(this.node.disabled){
32182             this.addClass("x-tree-node-disabled");
32183         }
32184         if(this.node.hidden){
32185             this.addClass("x-tree-node-disabled");
32186         }
32187         var ot = this.node.getOwnerTree();
32188         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32189         if(dd && (!this.node.isRoot || ot.rootVisible)){
32190             Roo.dd.Registry.register(this.elNode, {
32191                 node: this.node,
32192                 handles: this.getDDHandles(),
32193                 isHandle: false
32194             });
32195         }
32196     },
32197
32198     getDDHandles : function(){
32199         return [this.iconNode, this.textNode];
32200     },
32201
32202     hide : function(){
32203         if(this.rendered){
32204             this.wrap.style.display = "none";
32205         }
32206     },
32207
32208     show : function(){
32209         if(this.rendered){
32210             this.wrap.style.display = "";
32211         }
32212     },
32213
32214     onContextMenu : function(e){
32215         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32216             e.preventDefault();
32217             this.focus();
32218             this.fireEvent("contextmenu", this.node, e);
32219         }
32220     },
32221
32222     onClick : function(e){
32223         if(this.dropping){
32224             e.stopEvent();
32225             return;
32226         }
32227         if(this.fireEvent("beforeclick", this.node, e) !== false){
32228             if(!this.disabled && this.node.attributes.href){
32229                 this.fireEvent("click", this.node, e);
32230                 return;
32231             }
32232             e.preventDefault();
32233             if(this.disabled){
32234                 return;
32235             }
32236
32237             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32238                 this.node.toggle();
32239             }
32240
32241             this.fireEvent("click", this.node, e);
32242         }else{
32243             e.stopEvent();
32244         }
32245     },
32246
32247     onDblClick : function(e){
32248         e.preventDefault();
32249         if(this.disabled){
32250             return;
32251         }
32252         if(this.checkbox){
32253             this.toggleCheck();
32254         }
32255         if(!this.animating && this.node.hasChildNodes()){
32256             this.node.toggle();
32257         }
32258         this.fireEvent("dblclick", this.node, e);
32259     },
32260
32261     onCheckChange : function(){
32262         var checked = this.checkbox.checked;
32263         this.node.attributes.checked = checked;
32264         this.fireEvent('checkchange', this.node, checked);
32265     },
32266
32267     ecClick : function(e){
32268         if(!this.animating && this.node.hasChildNodes()){
32269             this.node.toggle();
32270         }
32271     },
32272
32273     startDrop : function(){
32274         this.dropping = true;
32275     },
32276
32277     // delayed drop so the click event doesn't get fired on a drop
32278     endDrop : function(){
32279        setTimeout(function(){
32280            this.dropping = false;
32281        }.createDelegate(this), 50);
32282     },
32283
32284     expand : function(){
32285         this.updateExpandIcon();
32286         this.ctNode.style.display = "";
32287     },
32288
32289     focus : function(){
32290         if(!this.node.preventHScroll){
32291             try{this.anchor.focus();
32292             }catch(e){}
32293         }else if(!Roo.isIE){
32294             try{
32295                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32296                 var l = noscroll.scrollLeft;
32297                 this.anchor.focus();
32298                 noscroll.scrollLeft = l;
32299             }catch(e){}
32300         }
32301     },
32302
32303     toggleCheck : function(value){
32304         var cb = this.checkbox;
32305         if(cb){
32306             cb.checked = (value === undefined ? !cb.checked : value);
32307         }
32308     },
32309
32310     blur : function(){
32311         try{
32312             this.anchor.blur();
32313         }catch(e){}
32314     },
32315
32316     animExpand : function(callback){
32317         var ct = Roo.get(this.ctNode);
32318         ct.stopFx();
32319         if(!this.node.hasChildNodes()){
32320             this.updateExpandIcon();
32321             this.ctNode.style.display = "";
32322             Roo.callback(callback);
32323             return;
32324         }
32325         this.animating = true;
32326         this.updateExpandIcon();
32327
32328         ct.slideIn('t', {
32329            callback : function(){
32330                this.animating = false;
32331                Roo.callback(callback);
32332             },
32333             scope: this,
32334             duration: this.node.ownerTree.duration || .25
32335         });
32336     },
32337
32338     highlight : function(){
32339         var tree = this.node.getOwnerTree();
32340         Roo.fly(this.wrap).highlight(
32341             tree.hlColor || "C3DAF9",
32342             {endColor: tree.hlBaseColor}
32343         );
32344     },
32345
32346     collapse : function(){
32347         this.updateExpandIcon();
32348         this.ctNode.style.display = "none";
32349     },
32350
32351     animCollapse : function(callback){
32352         var ct = Roo.get(this.ctNode);
32353         ct.enableDisplayMode('block');
32354         ct.stopFx();
32355
32356         this.animating = true;
32357         this.updateExpandIcon();
32358
32359         ct.slideOut('t', {
32360             callback : function(){
32361                this.animating = false;
32362                Roo.callback(callback);
32363             },
32364             scope: this,
32365             duration: this.node.ownerTree.duration || .25
32366         });
32367     },
32368
32369     getContainer : function(){
32370         return this.ctNode;
32371     },
32372
32373     getEl : function(){
32374         return this.wrap;
32375     },
32376
32377     appendDDGhost : function(ghostNode){
32378         ghostNode.appendChild(this.elNode.cloneNode(true));
32379     },
32380
32381     getDDRepairXY : function(){
32382         return Roo.lib.Dom.getXY(this.iconNode);
32383     },
32384
32385     onRender : function(){
32386         this.render();
32387     },
32388
32389     render : function(bulkRender){
32390         var n = this.node, a = n.attributes;
32391         var targetNode = n.parentNode ?
32392               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32393
32394         if(!this.rendered){
32395             this.rendered = true;
32396
32397             this.renderElements(n, a, targetNode, bulkRender);
32398
32399             if(a.qtip){
32400                if(this.textNode.setAttributeNS){
32401                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32402                    if(a.qtipTitle){
32403                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32404                    }
32405                }else{
32406                    this.textNode.setAttribute("ext:qtip", a.qtip);
32407                    if(a.qtipTitle){
32408                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32409                    }
32410                }
32411             }else if(a.qtipCfg){
32412                 a.qtipCfg.target = Roo.id(this.textNode);
32413                 Roo.QuickTips.register(a.qtipCfg);
32414             }
32415             this.initEvents();
32416             if(!this.node.expanded){
32417                 this.updateExpandIcon();
32418             }
32419         }else{
32420             if(bulkRender === true) {
32421                 targetNode.appendChild(this.wrap);
32422             }
32423         }
32424     },
32425
32426     renderElements : function(n, a, targetNode, bulkRender){
32427         // add some indent caching, this helps performance when rendering a large tree
32428         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32429         var t = n.getOwnerTree();
32430         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32431         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32432         var cb = typeof a.checked == 'boolean';
32433         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32434         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32435             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32436             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32437             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32438             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32439             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32440              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32441                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32442             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32443             "</li>"];
32444
32445         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32446             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32447                                 n.nextSibling.ui.getEl(), buf.join(""));
32448         }else{
32449             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32450         }
32451
32452         this.elNode = this.wrap.childNodes[0];
32453         this.ctNode = this.wrap.childNodes[1];
32454         var cs = this.elNode.childNodes;
32455         this.indentNode = cs[0];
32456         this.ecNode = cs[1];
32457         this.iconNode = cs[2];
32458         var index = 3;
32459         if(cb){
32460             this.checkbox = cs[3];
32461             index++;
32462         }
32463         this.anchor = cs[index];
32464         this.textNode = cs[index].firstChild;
32465     },
32466
32467     getAnchor : function(){
32468         return this.anchor;
32469     },
32470
32471     getTextEl : function(){
32472         return this.textNode;
32473     },
32474
32475     getIconEl : function(){
32476         return this.iconNode;
32477     },
32478
32479     isChecked : function(){
32480         return this.checkbox ? this.checkbox.checked : false;
32481     },
32482
32483     updateExpandIcon : function(){
32484         if(this.rendered){
32485             var n = this.node, c1, c2;
32486             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32487             var hasChild = n.hasChildNodes();
32488             if(hasChild){
32489                 if(n.expanded){
32490                     cls += "-minus";
32491                     c1 = "x-tree-node-collapsed";
32492                     c2 = "x-tree-node-expanded";
32493                 }else{
32494                     cls += "-plus";
32495                     c1 = "x-tree-node-expanded";
32496                     c2 = "x-tree-node-collapsed";
32497                 }
32498                 if(this.wasLeaf){
32499                     this.removeClass("x-tree-node-leaf");
32500                     this.wasLeaf = false;
32501                 }
32502                 if(this.c1 != c1 || this.c2 != c2){
32503                     Roo.fly(this.elNode).replaceClass(c1, c2);
32504                     this.c1 = c1; this.c2 = c2;
32505                 }
32506             }else{
32507                 if(!this.wasLeaf){
32508                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32509                     delete this.c1;
32510                     delete this.c2;
32511                     this.wasLeaf = true;
32512                 }
32513             }
32514             var ecc = "x-tree-ec-icon "+cls;
32515             if(this.ecc != ecc){
32516                 this.ecNode.className = ecc;
32517                 this.ecc = ecc;
32518             }
32519         }
32520     },
32521
32522     getChildIndent : function(){
32523         if(!this.childIndent){
32524             var buf = [];
32525             var p = this.node;
32526             while(p){
32527                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32528                     if(!p.isLast()) {
32529                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32530                     } else {
32531                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32532                     }
32533                 }
32534                 p = p.parentNode;
32535             }
32536             this.childIndent = buf.join("");
32537         }
32538         return this.childIndent;
32539     },
32540
32541     renderIndent : function(){
32542         if(this.rendered){
32543             var indent = "";
32544             var p = this.node.parentNode;
32545             if(p){
32546                 indent = p.ui.getChildIndent();
32547             }
32548             if(this.indentMarkup != indent){ // don't rerender if not required
32549                 this.indentNode.innerHTML = indent;
32550                 this.indentMarkup = indent;
32551             }
32552             this.updateExpandIcon();
32553         }
32554     }
32555 };
32556
32557 Roo.tree.RootTreeNodeUI = function(){
32558     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32559 };
32560 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32561     render : function(){
32562         if(!this.rendered){
32563             var targetNode = this.node.ownerTree.innerCt.dom;
32564             this.node.expanded = true;
32565             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32566             this.wrap = this.ctNode = targetNode.firstChild;
32567         }
32568     },
32569     collapse : function(){
32570     },
32571     expand : function(){
32572     }
32573 });/*
32574  * Based on:
32575  * Ext JS Library 1.1.1
32576  * Copyright(c) 2006-2007, Ext JS, LLC.
32577  *
32578  * Originally Released Under LGPL - original licence link has changed is not relivant.
32579  *
32580  * Fork - LGPL
32581  * <script type="text/javascript">
32582  */
32583 /**
32584  * @class Roo.tree.TreeLoader
32585  * @extends Roo.util.Observable
32586  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32587  * nodes from a specified URL. The response must be a javascript Array definition
32588  * who's elements are node definition objects. eg:
32589  * <pre><code>
32590    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32591     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32592 </code></pre>
32593  * <br><br>
32594  * A server request is sent, and child nodes are loaded only when a node is expanded.
32595  * The loading node's id is passed to the server under the parameter name "node" to
32596  * enable the server to produce the correct child nodes.
32597  * <br><br>
32598  * To pass extra parameters, an event handler may be attached to the "beforeload"
32599  * event, and the parameters specified in the TreeLoader's baseParams property:
32600  * <pre><code>
32601     myTreeLoader.on("beforeload", function(treeLoader, node) {
32602         this.baseParams.category = node.attributes.category;
32603     }, this);
32604 </code></pre><
32605  * This would pass an HTTP parameter called "category" to the server containing
32606  * the value of the Node's "category" attribute.
32607  * @constructor
32608  * Creates a new Treeloader.
32609  * @param {Object} config A config object containing config properties.
32610  */
32611 Roo.tree.TreeLoader = function(config){
32612     this.baseParams = {};
32613     this.requestMethod = "POST";
32614     Roo.apply(this, config);
32615
32616     this.addEvents({
32617     
32618         /**
32619          * @event beforeload
32620          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32621          * @param {Object} This TreeLoader object.
32622          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32623          * @param {Object} callback The callback function specified in the {@link #load} call.
32624          */
32625         beforeload : true,
32626         /**
32627          * @event load
32628          * Fires when the node has been successfuly loaded.
32629          * @param {Object} This TreeLoader object.
32630          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32631          * @param {Object} response The response object containing the data from the server.
32632          */
32633         load : true,
32634         /**
32635          * @event loadexception
32636          * Fires if the network request failed.
32637          * @param {Object} This TreeLoader object.
32638          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32639          * @param {Object} response The response object containing the data from the server.
32640          */
32641         loadexception : true,
32642         /**
32643          * @event create
32644          * Fires before a node is created, enabling you to return custom Node types 
32645          * @param {Object} This TreeLoader object.
32646          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32647          */
32648         create : true
32649     });
32650
32651     Roo.tree.TreeLoader.superclass.constructor.call(this);
32652 };
32653
32654 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32655     /**
32656     * @cfg {String} dataUrl The URL from which to request a Json string which
32657     * specifies an array of node definition object representing the child nodes
32658     * to be loaded.
32659     */
32660     /**
32661     * @cfg {Object} baseParams (optional) An object containing properties which
32662     * specify HTTP parameters to be passed to each request for child nodes.
32663     */
32664     /**
32665     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32666     * created by this loader. If the attributes sent by the server have an attribute in this object,
32667     * they take priority.
32668     */
32669     /**
32670     * @cfg {Object} uiProviders (optional) An object containing properties which
32671     * 
32672     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32673     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32674     * <i>uiProvider</i> attribute of a returned child node is a string rather
32675     * than a reference to a TreeNodeUI implementation, this that string value
32676     * is used as a property name in the uiProviders object. You can define the provider named
32677     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32678     */
32679     uiProviders : {},
32680
32681     /**
32682     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32683     * child nodes before loading.
32684     */
32685     clearOnLoad : true,
32686
32687     /**
32688     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32689     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32690     * Grid query { data : [ .....] }
32691     */
32692     
32693     root : false,
32694      /**
32695     * @cfg {String} queryParam (optional) 
32696     * Name of the query as it will be passed on the querystring (defaults to 'node')
32697     * eg. the request will be ?node=[id]
32698     */
32699     
32700     
32701     queryParam: false,
32702     
32703     /**
32704      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32705      * This is called automatically when a node is expanded, but may be used to reload
32706      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32707      * @param {Roo.tree.TreeNode} node
32708      * @param {Function} callback
32709      */
32710     load : function(node, callback){
32711         if(this.clearOnLoad){
32712             while(node.firstChild){
32713                 node.removeChild(node.firstChild);
32714             }
32715         }
32716         if(node.attributes.children){ // preloaded json children
32717             var cs = node.attributes.children;
32718             for(var i = 0, len = cs.length; i < len; i++){
32719                 node.appendChild(this.createNode(cs[i]));
32720             }
32721             if(typeof callback == "function"){
32722                 callback();
32723             }
32724         }else if(this.dataUrl){
32725             this.requestData(node, callback);
32726         }
32727     },
32728
32729     getParams: function(node){
32730         var buf = [], bp = this.baseParams;
32731         for(var key in bp){
32732             if(typeof bp[key] != "function"){
32733                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32734             }
32735         }
32736         var n = this.queryParam === false ? 'node' : this.queryParam;
32737         buf.push(n + "=", encodeURIComponent(node.id));
32738         return buf.join("");
32739     },
32740
32741     requestData : function(node, callback){
32742         if(this.fireEvent("beforeload", this, node, callback) !== false){
32743             this.transId = Roo.Ajax.request({
32744                 method:this.requestMethod,
32745                 url: this.dataUrl||this.url,
32746                 success: this.handleResponse,
32747                 failure: this.handleFailure,
32748                 scope: this,
32749                 argument: {callback: callback, node: node},
32750                 params: this.getParams(node)
32751             });
32752         }else{
32753             // if the load is cancelled, make sure we notify
32754             // the node that we are done
32755             if(typeof callback == "function"){
32756                 callback();
32757             }
32758         }
32759     },
32760
32761     isLoading : function(){
32762         return this.transId ? true : false;
32763     },
32764
32765     abort : function(){
32766         if(this.isLoading()){
32767             Roo.Ajax.abort(this.transId);
32768         }
32769     },
32770
32771     // private
32772     createNode : function(attr){
32773         // apply baseAttrs, nice idea Corey!
32774         if(this.baseAttrs){
32775             Roo.applyIf(attr, this.baseAttrs);
32776         }
32777         if(this.applyLoader !== false){
32778             attr.loader = this;
32779         }
32780         // uiProvider = depreciated..
32781         
32782         if(typeof(attr.uiProvider) == 'string'){
32783            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32784                 /**  eval:var:attr */ eval(attr.uiProvider);
32785         }
32786         if(typeof(this.uiProviders['default']) != 'undefined') {
32787             attr.uiProvider = this.uiProviders['default'];
32788         }
32789         
32790         this.fireEvent('create', this, attr);
32791         
32792         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32793         return(attr.leaf ?
32794                         new Roo.tree.TreeNode(attr) :
32795                         new Roo.tree.AsyncTreeNode(attr));
32796     },
32797
32798     processResponse : function(response, node, callback){
32799         var json = response.responseText;
32800         try {
32801             
32802             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32803             if (this.root !== false) {
32804                 o = o[this.root];
32805             }
32806             
32807             for(var i = 0, len = o.length; i < len; i++){
32808                 var n = this.createNode(o[i]);
32809                 if(n){
32810                     node.appendChild(n);
32811                 }
32812             }
32813             if(typeof callback == "function"){
32814                 callback(this, node);
32815             }
32816         }catch(e){
32817             this.handleFailure(response);
32818         }
32819     },
32820
32821     handleResponse : function(response){
32822         this.transId = false;
32823         var a = response.argument;
32824         this.processResponse(response, a.node, a.callback);
32825         this.fireEvent("load", this, a.node, response);
32826     },
32827
32828     handleFailure : function(response){
32829         this.transId = false;
32830         var a = response.argument;
32831         this.fireEvent("loadexception", this, a.node, response);
32832         if(typeof a.callback == "function"){
32833             a.callback(this, a.node);
32834         }
32835     }
32836 });/*
32837  * Based on:
32838  * Ext JS Library 1.1.1
32839  * Copyright(c) 2006-2007, Ext JS, LLC.
32840  *
32841  * Originally Released Under LGPL - original licence link has changed is not relivant.
32842  *
32843  * Fork - LGPL
32844  * <script type="text/javascript">
32845  */
32846
32847 /**
32848 * @class Roo.tree.TreeFilter
32849 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32850 * @param {TreePanel} tree
32851 * @param {Object} config (optional)
32852  */
32853 Roo.tree.TreeFilter = function(tree, config){
32854     this.tree = tree;
32855     this.filtered = {};
32856     Roo.apply(this, config);
32857 };
32858
32859 Roo.tree.TreeFilter.prototype = {
32860     clearBlank:false,
32861     reverse:false,
32862     autoClear:false,
32863     remove:false,
32864
32865      /**
32866      * Filter the data by a specific attribute.
32867      * @param {String/RegExp} value Either string that the attribute value
32868      * should start with or a RegExp to test against the attribute
32869      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32870      * @param {TreeNode} startNode (optional) The node to start the filter at.
32871      */
32872     filter : function(value, attr, startNode){
32873         attr = attr || "text";
32874         var f;
32875         if(typeof value == "string"){
32876             var vlen = value.length;
32877             // auto clear empty filter
32878             if(vlen == 0 && this.clearBlank){
32879                 this.clear();
32880                 return;
32881             }
32882             value = value.toLowerCase();
32883             f = function(n){
32884                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32885             };
32886         }else if(value.exec){ // regex?
32887             f = function(n){
32888                 return value.test(n.attributes[attr]);
32889             };
32890         }else{
32891             throw 'Illegal filter type, must be string or regex';
32892         }
32893         this.filterBy(f, null, startNode);
32894         },
32895
32896     /**
32897      * Filter by a function. The passed function will be called with each
32898      * node in the tree (or from the startNode). If the function returns true, the node is kept
32899      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32900      * @param {Function} fn The filter function
32901      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32902      */
32903     filterBy : function(fn, scope, startNode){
32904         startNode = startNode || this.tree.root;
32905         if(this.autoClear){
32906             this.clear();
32907         }
32908         var af = this.filtered, rv = this.reverse;
32909         var f = function(n){
32910             if(n == startNode){
32911                 return true;
32912             }
32913             if(af[n.id]){
32914                 return false;
32915             }
32916             var m = fn.call(scope || n, n);
32917             if(!m || rv){
32918                 af[n.id] = n;
32919                 n.ui.hide();
32920                 return false;
32921             }
32922             return true;
32923         };
32924         startNode.cascade(f);
32925         if(this.remove){
32926            for(var id in af){
32927                if(typeof id != "function"){
32928                    var n = af[id];
32929                    if(n && n.parentNode){
32930                        n.parentNode.removeChild(n);
32931                    }
32932                }
32933            }
32934         }
32935     },
32936
32937     /**
32938      * Clears the current filter. Note: with the "remove" option
32939      * set a filter cannot be cleared.
32940      */
32941     clear : function(){
32942         var t = this.tree;
32943         var af = this.filtered;
32944         for(var id in af){
32945             if(typeof id != "function"){
32946                 var n = af[id];
32947                 if(n){
32948                     n.ui.show();
32949                 }
32950             }
32951         }
32952         this.filtered = {};
32953     }
32954 };
32955 /*
32956  * Based on:
32957  * Ext JS Library 1.1.1
32958  * Copyright(c) 2006-2007, Ext JS, LLC.
32959  *
32960  * Originally Released Under LGPL - original licence link has changed is not relivant.
32961  *
32962  * Fork - LGPL
32963  * <script type="text/javascript">
32964  */
32965  
32966
32967 /**
32968  * @class Roo.tree.TreeSorter
32969  * Provides sorting of nodes in a TreePanel
32970  * 
32971  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32972  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32973  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32974  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32975  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32976  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32977  * @constructor
32978  * @param {TreePanel} tree
32979  * @param {Object} config
32980  */
32981 Roo.tree.TreeSorter = function(tree, config){
32982     Roo.apply(this, config);
32983     tree.on("beforechildrenrendered", this.doSort, this);
32984     tree.on("append", this.updateSort, this);
32985     tree.on("insert", this.updateSort, this);
32986     
32987     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32988     var p = this.property || "text";
32989     var sortType = this.sortType;
32990     var fs = this.folderSort;
32991     var cs = this.caseSensitive === true;
32992     var leafAttr = this.leafAttr || 'leaf';
32993
32994     this.sortFn = function(n1, n2){
32995         if(fs){
32996             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32997                 return 1;
32998             }
32999             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33000                 return -1;
33001             }
33002         }
33003         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33004         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33005         if(v1 < v2){
33006                         return dsc ? +1 : -1;
33007                 }else if(v1 > v2){
33008                         return dsc ? -1 : +1;
33009         }else{
33010                 return 0;
33011         }
33012     };
33013 };
33014
33015 Roo.tree.TreeSorter.prototype = {
33016     doSort : function(node){
33017         node.sort(this.sortFn);
33018     },
33019     
33020     compareNodes : function(n1, n2){
33021         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33022     },
33023     
33024     updateSort : function(tree, node){
33025         if(node.childrenRendered){
33026             this.doSort.defer(1, this, [node]);
33027         }
33028     }
33029 };/*
33030  * Based on:
33031  * Ext JS Library 1.1.1
33032  * Copyright(c) 2006-2007, Ext JS, LLC.
33033  *
33034  * Originally Released Under LGPL - original licence link has changed is not relivant.
33035  *
33036  * Fork - LGPL
33037  * <script type="text/javascript">
33038  */
33039
33040 if(Roo.dd.DropZone){
33041     
33042 Roo.tree.TreeDropZone = function(tree, config){
33043     this.allowParentInsert = false;
33044     this.allowContainerDrop = false;
33045     this.appendOnly = false;
33046     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33047     this.tree = tree;
33048     this.lastInsertClass = "x-tree-no-status";
33049     this.dragOverData = {};
33050 };
33051
33052 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33053     ddGroup : "TreeDD",
33054     
33055     expandDelay : 1000,
33056     
33057     expandNode : function(node){
33058         if(node.hasChildNodes() && !node.isExpanded()){
33059             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33060         }
33061     },
33062     
33063     queueExpand : function(node){
33064         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33065     },
33066     
33067     cancelExpand : function(){
33068         if(this.expandProcId){
33069             clearTimeout(this.expandProcId);
33070             this.expandProcId = false;
33071         }
33072     },
33073     
33074     isValidDropPoint : function(n, pt, dd, e, data){
33075         if(!n || !data){ return false; }
33076         var targetNode = n.node;
33077         var dropNode = data.node;
33078         // default drop rules
33079         if(!(targetNode && targetNode.isTarget && pt)){
33080             return false;
33081         }
33082         if(pt == "append" && targetNode.allowChildren === false){
33083             return false;
33084         }
33085         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33086             return false;
33087         }
33088         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33089             return false;
33090         }
33091         // reuse the object
33092         var overEvent = this.dragOverData;
33093         overEvent.tree = this.tree;
33094         overEvent.target = targetNode;
33095         overEvent.data = data;
33096         overEvent.point = pt;
33097         overEvent.source = dd;
33098         overEvent.rawEvent = e;
33099         overEvent.dropNode = dropNode;
33100         overEvent.cancel = false;  
33101         var result = this.tree.fireEvent("nodedragover", overEvent);
33102         return overEvent.cancel === false && result !== false;
33103     },
33104     
33105     getDropPoint : function(e, n, dd){
33106         var tn = n.node;
33107         if(tn.isRoot){
33108             return tn.allowChildren !== false ? "append" : false; // always append for root
33109         }
33110         var dragEl = n.ddel;
33111         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33112         var y = Roo.lib.Event.getPageY(e);
33113         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33114         
33115         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33116         var noAppend = tn.allowChildren === false;
33117         if(this.appendOnly || tn.parentNode.allowChildren === false){
33118             return noAppend ? false : "append";
33119         }
33120         var noBelow = false;
33121         if(!this.allowParentInsert){
33122             noBelow = tn.hasChildNodes() && tn.isExpanded();
33123         }
33124         var q = (b - t) / (noAppend ? 2 : 3);
33125         if(y >= t && y < (t + q)){
33126             return "above";
33127         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33128             return "below";
33129         }else{
33130             return "append";
33131         }
33132     },
33133     
33134     onNodeEnter : function(n, dd, e, data){
33135         this.cancelExpand();
33136     },
33137     
33138     onNodeOver : function(n, dd, e, data){
33139         var pt = this.getDropPoint(e, n, dd);
33140         var node = n.node;
33141         
33142         // auto node expand check
33143         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33144             this.queueExpand(node);
33145         }else if(pt != "append"){
33146             this.cancelExpand();
33147         }
33148         
33149         // set the insert point style on the target node
33150         var returnCls = this.dropNotAllowed;
33151         if(this.isValidDropPoint(n, pt, dd, e, data)){
33152            if(pt){
33153                var el = n.ddel;
33154                var cls;
33155                if(pt == "above"){
33156                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33157                    cls = "x-tree-drag-insert-above";
33158                }else if(pt == "below"){
33159                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33160                    cls = "x-tree-drag-insert-below";
33161                }else{
33162                    returnCls = "x-tree-drop-ok-append";
33163                    cls = "x-tree-drag-append";
33164                }
33165                if(this.lastInsertClass != cls){
33166                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33167                    this.lastInsertClass = cls;
33168                }
33169            }
33170        }
33171        return returnCls;
33172     },
33173     
33174     onNodeOut : function(n, dd, e, data){
33175         this.cancelExpand();
33176         this.removeDropIndicators(n);
33177     },
33178     
33179     onNodeDrop : function(n, dd, e, data){
33180         var point = this.getDropPoint(e, n, dd);
33181         var targetNode = n.node;
33182         targetNode.ui.startDrop();
33183         if(!this.isValidDropPoint(n, point, dd, e, data)){
33184             targetNode.ui.endDrop();
33185             return false;
33186         }
33187         // first try to find the drop node
33188         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33189         var dropEvent = {
33190             tree : this.tree,
33191             target: targetNode,
33192             data: data,
33193             point: point,
33194             source: dd,
33195             rawEvent: e,
33196             dropNode: dropNode,
33197             cancel: !dropNode   
33198         };
33199         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33200         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33201             targetNode.ui.endDrop();
33202             return false;
33203         }
33204         // allow target changing
33205         targetNode = dropEvent.target;
33206         if(point == "append" && !targetNode.isExpanded()){
33207             targetNode.expand(false, null, function(){
33208                 this.completeDrop(dropEvent);
33209             }.createDelegate(this));
33210         }else{
33211             this.completeDrop(dropEvent);
33212         }
33213         return true;
33214     },
33215     
33216     completeDrop : function(de){
33217         var ns = de.dropNode, p = de.point, t = de.target;
33218         if(!(ns instanceof Array)){
33219             ns = [ns];
33220         }
33221         var n;
33222         for(var i = 0, len = ns.length; i < len; i++){
33223             n = ns[i];
33224             if(p == "above"){
33225                 t.parentNode.insertBefore(n, t);
33226             }else if(p == "below"){
33227                 t.parentNode.insertBefore(n, t.nextSibling);
33228             }else{
33229                 t.appendChild(n);
33230             }
33231         }
33232         n.ui.focus();
33233         if(this.tree.hlDrop){
33234             n.ui.highlight();
33235         }
33236         t.ui.endDrop();
33237         this.tree.fireEvent("nodedrop", de);
33238     },
33239     
33240     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33241         if(this.tree.hlDrop){
33242             dropNode.ui.focus();
33243             dropNode.ui.highlight();
33244         }
33245         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33246     },
33247     
33248     getTree : function(){
33249         return this.tree;
33250     },
33251     
33252     removeDropIndicators : function(n){
33253         if(n && n.ddel){
33254             var el = n.ddel;
33255             Roo.fly(el).removeClass([
33256                     "x-tree-drag-insert-above",
33257                     "x-tree-drag-insert-below",
33258                     "x-tree-drag-append"]);
33259             this.lastInsertClass = "_noclass";
33260         }
33261     },
33262     
33263     beforeDragDrop : function(target, e, id){
33264         this.cancelExpand();
33265         return true;
33266     },
33267     
33268     afterRepair : function(data){
33269         if(data && Roo.enableFx){
33270             data.node.ui.highlight();
33271         }
33272         this.hideProxy();
33273     }    
33274 });
33275
33276 }
33277 /*
33278  * Based on:
33279  * Ext JS Library 1.1.1
33280  * Copyright(c) 2006-2007, Ext JS, LLC.
33281  *
33282  * Originally Released Under LGPL - original licence link has changed is not relivant.
33283  *
33284  * Fork - LGPL
33285  * <script type="text/javascript">
33286  */
33287  
33288
33289 if(Roo.dd.DragZone){
33290 Roo.tree.TreeDragZone = function(tree, config){
33291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33292     this.tree = tree;
33293 };
33294
33295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33296     ddGroup : "TreeDD",
33297     
33298     onBeforeDrag : function(data, e){
33299         var n = data.node;
33300         return n && n.draggable && !n.disabled;
33301     },
33302     
33303     onInitDrag : function(e){
33304         var data = this.dragData;
33305         this.tree.getSelectionModel().select(data.node);
33306         this.proxy.update("");
33307         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33308         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33309     },
33310     
33311     getRepairXY : function(e, data){
33312         return data.node.ui.getDDRepairXY();
33313     },
33314     
33315     onEndDrag : function(data, e){
33316         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33317     },
33318     
33319     onValidDrop : function(dd, e, id){
33320         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33321         this.hideProxy();
33322     },
33323     
33324     beforeInvalidDrop : function(e, id){
33325         // this scrolls the original position back into view
33326         var sm = this.tree.getSelectionModel();
33327         sm.clearSelections();
33328         sm.select(this.dragData.node);
33329     }
33330 });
33331 }/*
33332  * Based on:
33333  * Ext JS Library 1.1.1
33334  * Copyright(c) 2006-2007, Ext JS, LLC.
33335  *
33336  * Originally Released Under LGPL - original licence link has changed is not relivant.
33337  *
33338  * Fork - LGPL
33339  * <script type="text/javascript">
33340  */
33341 /**
33342  * @class Roo.tree.TreeEditor
33343  * @extends Roo.Editor
33344  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33345  * as the editor field.
33346  * @constructor
33347  * @param {TreePanel} tree
33348  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33349  */
33350 Roo.tree.TreeEditor = function(tree, config){
33351     config = config || {};
33352     var field = config.events ? config : new Roo.form.TextField(config);
33353     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33354
33355     this.tree = tree;
33356
33357     tree.on('beforeclick', this.beforeNodeClick, this);
33358     tree.getTreeEl().on('mousedown', this.hide, this);
33359     this.on('complete', this.updateNode, this);
33360     this.on('beforestartedit', this.fitToTree, this);
33361     this.on('startedit', this.bindScroll, this, {delay:10});
33362     this.on('specialkey', this.onSpecialKey, this);
33363 };
33364
33365 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33366     /**
33367      * @cfg {String} alignment
33368      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33369      */
33370     alignment: "l-l",
33371     // inherit
33372     autoSize: false,
33373     /**
33374      * @cfg {Boolean} hideEl
33375      * True to hide the bound element while the editor is displayed (defaults to false)
33376      */
33377     hideEl : false,
33378     /**
33379      * @cfg {String} cls
33380      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33381      */
33382     cls: "x-small-editor x-tree-editor",
33383     /**
33384      * @cfg {Boolean} shim
33385      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33386      */
33387     shim:false,
33388     // inherit
33389     shadow:"frame",
33390     /**
33391      * @cfg {Number} maxWidth
33392      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33393      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33394      * scroll and client offsets into account prior to each edit.
33395      */
33396     maxWidth: 250,
33397
33398     editDelay : 350,
33399
33400     // private
33401     fitToTree : function(ed, el){
33402         var td = this.tree.getTreeEl().dom, nd = el.dom;
33403         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33404             td.scrollLeft = nd.offsetLeft;
33405         }
33406         var w = Math.min(
33407                 this.maxWidth,
33408                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33409         this.setSize(w, '');
33410     },
33411
33412     // private
33413     triggerEdit : function(node){
33414         this.completeEdit();
33415         this.editNode = node;
33416         this.startEdit(node.ui.textNode, node.text);
33417     },
33418
33419     // private
33420     bindScroll : function(){
33421         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33422     },
33423
33424     // private
33425     beforeNodeClick : function(node, e){
33426         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33427         this.lastClick = new Date();
33428         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33429             e.stopEvent();
33430             this.triggerEdit(node);
33431             return false;
33432         }
33433     },
33434
33435     // private
33436     updateNode : function(ed, value){
33437         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33438         this.editNode.setText(value);
33439     },
33440
33441     // private
33442     onHide : function(){
33443         Roo.tree.TreeEditor.superclass.onHide.call(this);
33444         if(this.editNode){
33445             this.editNode.ui.focus();
33446         }
33447     },
33448
33449     // private
33450     onSpecialKey : function(field, e){
33451         var k = e.getKey();
33452         if(k == e.ESC){
33453             e.stopEvent();
33454             this.cancelEdit();
33455         }else if(k == e.ENTER && !e.hasModifier()){
33456             e.stopEvent();
33457             this.completeEdit();
33458         }
33459     }
33460 });//<Script type="text/javascript">
33461 /*
33462  * Based on:
33463  * Ext JS Library 1.1.1
33464  * Copyright(c) 2006-2007, Ext JS, LLC.
33465  *
33466  * Originally Released Under LGPL - original licence link has changed is not relivant.
33467  *
33468  * Fork - LGPL
33469  * <script type="text/javascript">
33470  */
33471  
33472 /**
33473  * Not documented??? - probably should be...
33474  */
33475
33476 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33477     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33478     
33479     renderElements : function(n, a, targetNode, bulkRender){
33480         //consel.log("renderElements?");
33481         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33482
33483         var t = n.getOwnerTree();
33484         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33485         
33486         var cols = t.columns;
33487         var bw = t.borderWidth;
33488         var c = cols[0];
33489         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33490          var cb = typeof a.checked == "boolean";
33491         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33492         var colcls = 'x-t-' + tid + '-c0';
33493         var buf = [
33494             '<li class="x-tree-node">',
33495             
33496                 
33497                 '<div class="x-tree-node-el ', a.cls,'">',
33498                     // extran...
33499                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33500                 
33501                 
33502                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33503                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33504                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33505                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33506                            (a.iconCls ? ' '+a.iconCls : ''),
33507                            '" unselectable="on" />',
33508                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33509                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33510                              
33511                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33512                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33513                             '<span unselectable="on" qtip="' + tx + '">',
33514                              tx,
33515                              '</span></a>' ,
33516                     '</div>',
33517                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33518                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33519                  ];
33520         for(var i = 1, len = cols.length; i < len; i++){
33521             c = cols[i];
33522             colcls = 'x-t-' + tid + '-c' +i;
33523             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33524             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33525                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33526                       "</div>");
33527          }
33528          
33529          buf.push(
33530             '</a>',
33531             '<div class="x-clear"></div></div>',
33532             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33533             "</li>");
33534         
33535         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33536             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33537                                 n.nextSibling.ui.getEl(), buf.join(""));
33538         }else{
33539             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33540         }
33541         var el = this.wrap.firstChild;
33542         this.elRow = el;
33543         this.elNode = el.firstChild;
33544         this.ranchor = el.childNodes[1];
33545         this.ctNode = this.wrap.childNodes[1];
33546         var cs = el.firstChild.childNodes;
33547         this.indentNode = cs[0];
33548         this.ecNode = cs[1];
33549         this.iconNode = cs[2];
33550         var index = 3;
33551         if(cb){
33552             this.checkbox = cs[3];
33553             index++;
33554         }
33555         this.anchor = cs[index];
33556         
33557         this.textNode = cs[index].firstChild;
33558         
33559         //el.on("click", this.onClick, this);
33560         //el.on("dblclick", this.onDblClick, this);
33561         
33562         
33563        // console.log(this);
33564     },
33565     initEvents : function(){
33566         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33567         
33568             
33569         var a = this.ranchor;
33570
33571         var el = Roo.get(a);
33572
33573         if(Roo.isOpera){ // opera render bug ignores the CSS
33574             el.setStyle("text-decoration", "none");
33575         }
33576
33577         el.on("click", this.onClick, this);
33578         el.on("dblclick", this.onDblClick, this);
33579         el.on("contextmenu", this.onContextMenu, this);
33580         
33581     },
33582     
33583     /*onSelectedChange : function(state){
33584         if(state){
33585             this.focus();
33586             this.addClass("x-tree-selected");
33587         }else{
33588             //this.blur();
33589             this.removeClass("x-tree-selected");
33590         }
33591     },*/
33592     addClass : function(cls){
33593         if(this.elRow){
33594             Roo.fly(this.elRow).addClass(cls);
33595         }
33596         
33597     },
33598     
33599     
33600     removeClass : function(cls){
33601         if(this.elRow){
33602             Roo.fly(this.elRow).removeClass(cls);
33603         }
33604     }
33605
33606     
33607     
33608 });//<Script type="text/javascript">
33609
33610 /*
33611  * Based on:
33612  * Ext JS Library 1.1.1
33613  * Copyright(c) 2006-2007, Ext JS, LLC.
33614  *
33615  * Originally Released Under LGPL - original licence link has changed is not relivant.
33616  *
33617  * Fork - LGPL
33618  * <script type="text/javascript">
33619  */
33620  
33621
33622 /**
33623  * @class Roo.tree.ColumnTree
33624  * @extends Roo.data.TreePanel
33625  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33626  * @cfg {int} borderWidth  compined right/left border allowance
33627  * @constructor
33628  * @param {String/HTMLElement/Element} el The container element
33629  * @param {Object} config
33630  */
33631 Roo.tree.ColumnTree =  function(el, config)
33632 {
33633    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33634    this.addEvents({
33635         /**
33636         * @event resize
33637         * Fire this event on a container when it resizes
33638         * @param {int} w Width
33639         * @param {int} h Height
33640         */
33641        "resize" : true
33642     });
33643     this.on('resize', this.onResize, this);
33644 };
33645
33646 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33647     //lines:false,
33648     
33649     
33650     borderWidth: Roo.isBorderBox ? 0 : 2, 
33651     headEls : false,
33652     
33653     render : function(){
33654         // add the header.....
33655        
33656         Roo.tree.ColumnTree.superclass.render.apply(this);
33657         
33658         this.el.addClass('x-column-tree');
33659         
33660         this.headers = this.el.createChild(
33661             {cls:'x-tree-headers'},this.innerCt.dom);
33662    
33663         var cols = this.columns, c;
33664         var totalWidth = 0;
33665         this.headEls = [];
33666         var  len = cols.length;
33667         for(var i = 0; i < len; i++){
33668              c = cols[i];
33669              totalWidth += c.width;
33670             this.headEls.push(this.headers.createChild({
33671                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33672                  cn: {
33673                      cls:'x-tree-hd-text',
33674                      html: c.header
33675                  },
33676                  style:'width:'+(c.width-this.borderWidth)+'px;'
33677              }));
33678         }
33679         this.headers.createChild({cls:'x-clear'});
33680         // prevent floats from wrapping when clipped
33681         this.headers.setWidth(totalWidth);
33682         //this.innerCt.setWidth(totalWidth);
33683         this.innerCt.setStyle({ overflow: 'auto' });
33684         this.onResize(this.width, this.height);
33685              
33686         
33687     },
33688     onResize : function(w,h)
33689     {
33690         this.height = h;
33691         this.width = w;
33692         // resize cols..
33693         this.innerCt.setWidth(this.width);
33694         this.innerCt.setHeight(this.height-20);
33695         
33696         // headers...
33697         var cols = this.columns, c;
33698         var totalWidth = 0;
33699         var expEl = false;
33700         var len = cols.length;
33701         for(var i = 0; i < len; i++){
33702             c = cols[i];
33703             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33704                 // it's the expander..
33705                 expEl  = this.headEls[i];
33706                 continue;
33707             }
33708             totalWidth += c.width;
33709             
33710         }
33711         if (expEl) {
33712             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33713         }
33714         this.headers.setWidth(w-20);
33715
33716         
33717         
33718         
33719     }
33720 });
33721 /*
33722  * Based on:
33723  * Ext JS Library 1.1.1
33724  * Copyright(c) 2006-2007, Ext JS, LLC.
33725  *
33726  * Originally Released Under LGPL - original licence link has changed is not relivant.
33727  *
33728  * Fork - LGPL
33729  * <script type="text/javascript">
33730  */
33731  
33732 /**
33733  * @class Roo.menu.Menu
33734  * @extends Roo.util.Observable
33735  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33736  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33737  * @constructor
33738  * Creates a new Menu
33739  * @param {Object} config Configuration options
33740  */
33741 Roo.menu.Menu = function(config){
33742     Roo.apply(this, config);
33743     this.id = this.id || Roo.id();
33744     this.addEvents({
33745         /**
33746          * @event beforeshow
33747          * Fires before this menu is displayed
33748          * @param {Roo.menu.Menu} this
33749          */
33750         beforeshow : true,
33751         /**
33752          * @event beforehide
33753          * Fires before this menu is hidden
33754          * @param {Roo.menu.Menu} this
33755          */
33756         beforehide : true,
33757         /**
33758          * @event show
33759          * Fires after this menu is displayed
33760          * @param {Roo.menu.Menu} this
33761          */
33762         show : true,
33763         /**
33764          * @event hide
33765          * Fires after this menu is hidden
33766          * @param {Roo.menu.Menu} this
33767          */
33768         hide : true,
33769         /**
33770          * @event click
33771          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33772          * @param {Roo.menu.Menu} this
33773          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33774          * @param {Roo.EventObject} e
33775          */
33776         click : true,
33777         /**
33778          * @event mouseover
33779          * Fires when the mouse is hovering over this menu
33780          * @param {Roo.menu.Menu} this
33781          * @param {Roo.EventObject} e
33782          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33783          */
33784         mouseover : true,
33785         /**
33786          * @event mouseout
33787          * Fires when the mouse exits this menu
33788          * @param {Roo.menu.Menu} this
33789          * @param {Roo.EventObject} e
33790          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33791          */
33792         mouseout : true,
33793         /**
33794          * @event itemclick
33795          * Fires when a menu item contained in this menu is clicked
33796          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33797          * @param {Roo.EventObject} e
33798          */
33799         itemclick: true
33800     });
33801     if (this.registerMenu) {
33802         Roo.menu.MenuMgr.register(this);
33803     }
33804     
33805     var mis = this.items;
33806     this.items = new Roo.util.MixedCollection();
33807     if(mis){
33808         this.add.apply(this, mis);
33809     }
33810 };
33811
33812 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33813     /**
33814      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33815      */
33816     minWidth : 120,
33817     /**
33818      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33819      * for bottom-right shadow (defaults to "sides")
33820      */
33821     shadow : "sides",
33822     /**
33823      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33824      * this menu (defaults to "tl-tr?")
33825      */
33826     subMenuAlign : "tl-tr?",
33827     /**
33828      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33829      * relative to its element of origin (defaults to "tl-bl?")
33830      */
33831     defaultAlign : "tl-bl?",
33832     /**
33833      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33834      */
33835     allowOtherMenus : false,
33836     /**
33837      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33838      */
33839     registerMenu : true,
33840
33841     hidden:true,
33842
33843     // private
33844     render : function(){
33845         if(this.el){
33846             return;
33847         }
33848         var el = this.el = new Roo.Layer({
33849             cls: "x-menu",
33850             shadow:this.shadow,
33851             constrain: false,
33852             parentEl: this.parentEl || document.body,
33853             zindex:15000
33854         });
33855
33856         this.keyNav = new Roo.menu.MenuNav(this);
33857
33858         if(this.plain){
33859             el.addClass("x-menu-plain");
33860         }
33861         if(this.cls){
33862             el.addClass(this.cls);
33863         }
33864         // generic focus element
33865         this.focusEl = el.createChild({
33866             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33867         });
33868         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33869         ul.on("click", this.onClick, this);
33870         ul.on("mouseover", this.onMouseOver, this);
33871         ul.on("mouseout", this.onMouseOut, this);
33872         this.items.each(function(item){
33873             var li = document.createElement("li");
33874             li.className = "x-menu-list-item";
33875             ul.dom.appendChild(li);
33876             item.render(li, this);
33877         }, this);
33878         this.ul = ul;
33879         this.autoWidth();
33880     },
33881
33882     // private
33883     autoWidth : function(){
33884         var el = this.el, ul = this.ul;
33885         if(!el){
33886             return;
33887         }
33888         var w = this.width;
33889         if(w){
33890             el.setWidth(w);
33891         }else if(Roo.isIE){
33892             el.setWidth(this.minWidth);
33893             var t = el.dom.offsetWidth; // force recalc
33894             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33895         }
33896     },
33897
33898     // private
33899     delayAutoWidth : function(){
33900         if(this.rendered){
33901             if(!this.awTask){
33902                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33903             }
33904             this.awTask.delay(20);
33905         }
33906     },
33907
33908     // private
33909     findTargetItem : function(e){
33910         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33911         if(t && t.menuItemId){
33912             return this.items.get(t.menuItemId);
33913         }
33914     },
33915
33916     // private
33917     onClick : function(e){
33918         var t;
33919         if(t = this.findTargetItem(e)){
33920             t.onClick(e);
33921             this.fireEvent("click", this, t, e);
33922         }
33923     },
33924
33925     // private
33926     setActiveItem : function(item, autoExpand){
33927         if(item != this.activeItem){
33928             if(this.activeItem){
33929                 this.activeItem.deactivate();
33930             }
33931             this.activeItem = item;
33932             item.activate(autoExpand);
33933         }else if(autoExpand){
33934             item.expandMenu();
33935         }
33936     },
33937
33938     // private
33939     tryActivate : function(start, step){
33940         var items = this.items;
33941         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33942             var item = items.get(i);
33943             if(!item.disabled && item.canActivate){
33944                 this.setActiveItem(item, false);
33945                 return item;
33946             }
33947         }
33948         return false;
33949     },
33950
33951     // private
33952     onMouseOver : function(e){
33953         var t;
33954         if(t = this.findTargetItem(e)){
33955             if(t.canActivate && !t.disabled){
33956                 this.setActiveItem(t, true);
33957             }
33958         }
33959         this.fireEvent("mouseover", this, e, t);
33960     },
33961
33962     // private
33963     onMouseOut : function(e){
33964         var t;
33965         if(t = this.findTargetItem(e)){
33966             if(t == this.activeItem && t.shouldDeactivate(e)){
33967                 this.activeItem.deactivate();
33968                 delete this.activeItem;
33969             }
33970         }
33971         this.fireEvent("mouseout", this, e, t);
33972     },
33973
33974     /**
33975      * Read-only.  Returns true if the menu is currently displayed, else false.
33976      * @type Boolean
33977      */
33978     isVisible : function(){
33979         return this.el && !this.hidden;
33980     },
33981
33982     /**
33983      * Displays this menu relative to another element
33984      * @param {String/HTMLElement/Roo.Element} element The element to align to
33985      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33986      * the element (defaults to this.defaultAlign)
33987      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33988      */
33989     show : function(el, pos, parentMenu){
33990         this.parentMenu = parentMenu;
33991         if(!this.el){
33992             this.render();
33993         }
33994         this.fireEvent("beforeshow", this);
33995         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33996     },
33997
33998     /**
33999      * Displays this menu at a specific xy position
34000      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34001      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34002      */
34003     showAt : function(xy, parentMenu, /* private: */_e){
34004         this.parentMenu = parentMenu;
34005         if(!this.el){
34006             this.render();
34007         }
34008         if(_e !== false){
34009             this.fireEvent("beforeshow", this);
34010             xy = this.el.adjustForConstraints(xy);
34011         }
34012         this.el.setXY(xy);
34013         this.el.show();
34014         this.hidden = false;
34015         this.focus();
34016         this.fireEvent("show", this);
34017     },
34018
34019     focus : function(){
34020         if(!this.hidden){
34021             this.doFocus.defer(50, this);
34022         }
34023     },
34024
34025     doFocus : function(){
34026         if(!this.hidden){
34027             this.focusEl.focus();
34028         }
34029     },
34030
34031     /**
34032      * Hides this menu and optionally all parent menus
34033      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34034      */
34035     hide : function(deep){
34036         if(this.el && this.isVisible()){
34037             this.fireEvent("beforehide", this);
34038             if(this.activeItem){
34039                 this.activeItem.deactivate();
34040                 this.activeItem = null;
34041             }
34042             this.el.hide();
34043             this.hidden = true;
34044             this.fireEvent("hide", this);
34045         }
34046         if(deep === true && this.parentMenu){
34047             this.parentMenu.hide(true);
34048         }
34049     },
34050
34051     /**
34052      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34053      * Any of the following are valid:
34054      * <ul>
34055      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34056      * <li>An HTMLElement object which will be converted to a menu item</li>
34057      * <li>A menu item config object that will be created as a new menu item</li>
34058      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34059      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34060      * </ul>
34061      * Usage:
34062      * <pre><code>
34063 // Create the menu
34064 var menu = new Roo.menu.Menu();
34065
34066 // Create a menu item to add by reference
34067 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34068
34069 // Add a bunch of items at once using different methods.
34070 // Only the last item added will be returned.
34071 var item = menu.add(
34072     menuItem,                // add existing item by ref
34073     'Dynamic Item',          // new TextItem
34074     '-',                     // new separator
34075     { text: 'Config Item' }  // new item by config
34076 );
34077 </code></pre>
34078      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34079      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34080      */
34081     add : function(){
34082         var a = arguments, l = a.length, item;
34083         for(var i = 0; i < l; i++){
34084             var el = a[i];
34085             if ((typeof(el) == "object") && el.xtype && el.xns) {
34086                 el = Roo.factory(el, Roo.menu);
34087             }
34088             
34089             if(el.render){ // some kind of Item
34090                 item = this.addItem(el);
34091             }else if(typeof el == "string"){ // string
34092                 if(el == "separator" || el == "-"){
34093                     item = this.addSeparator();
34094                 }else{
34095                     item = this.addText(el);
34096                 }
34097             }else if(el.tagName || el.el){ // element
34098                 item = this.addElement(el);
34099             }else if(typeof el == "object"){ // must be menu item config?
34100                 item = this.addMenuItem(el);
34101             }
34102         }
34103         return item;
34104     },
34105
34106     /**
34107      * Returns this menu's underlying {@link Roo.Element} object
34108      * @return {Roo.Element} The element
34109      */
34110     getEl : function(){
34111         if(!this.el){
34112             this.render();
34113         }
34114         return this.el;
34115     },
34116
34117     /**
34118      * Adds a separator bar to the menu
34119      * @return {Roo.menu.Item} The menu item that was added
34120      */
34121     addSeparator : function(){
34122         return this.addItem(new Roo.menu.Separator());
34123     },
34124
34125     /**
34126      * Adds an {@link Roo.Element} object to the menu
34127      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34128      * @return {Roo.menu.Item} The menu item that was added
34129      */
34130     addElement : function(el){
34131         return this.addItem(new Roo.menu.BaseItem(el));
34132     },
34133
34134     /**
34135      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34136      * @param {Roo.menu.Item} item The menu item to add
34137      * @return {Roo.menu.Item} The menu item that was added
34138      */
34139     addItem : function(item){
34140         this.items.add(item);
34141         if(this.ul){
34142             var li = document.createElement("li");
34143             li.className = "x-menu-list-item";
34144             this.ul.dom.appendChild(li);
34145             item.render(li, this);
34146             this.delayAutoWidth();
34147         }
34148         return item;
34149     },
34150
34151     /**
34152      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34153      * @param {Object} config A MenuItem config object
34154      * @return {Roo.menu.Item} The menu item that was added
34155      */
34156     addMenuItem : function(config){
34157         if(!(config instanceof Roo.menu.Item)){
34158             if(typeof config.checked == "boolean"){ // must be check menu item config?
34159                 config = new Roo.menu.CheckItem(config);
34160             }else{
34161                 config = new Roo.menu.Item(config);
34162             }
34163         }
34164         return this.addItem(config);
34165     },
34166
34167     /**
34168      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34169      * @param {String} text The text to display in the menu item
34170      * @return {Roo.menu.Item} The menu item that was added
34171      */
34172     addText : function(text){
34173         return this.addItem(new Roo.menu.TextItem({ text : text }));
34174     },
34175
34176     /**
34177      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34178      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34179      * @param {Roo.menu.Item} item The menu item to add
34180      * @return {Roo.menu.Item} The menu item that was added
34181      */
34182     insert : function(index, item){
34183         this.items.insert(index, item);
34184         if(this.ul){
34185             var li = document.createElement("li");
34186             li.className = "x-menu-list-item";
34187             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34188             item.render(li, this);
34189             this.delayAutoWidth();
34190         }
34191         return item;
34192     },
34193
34194     /**
34195      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34196      * @param {Roo.menu.Item} item The menu item to remove
34197      */
34198     remove : function(item){
34199         this.items.removeKey(item.id);
34200         item.destroy();
34201     },
34202
34203     /**
34204      * Removes and destroys all items in the menu
34205      */
34206     removeAll : function(){
34207         var f;
34208         while(f = this.items.first()){
34209             this.remove(f);
34210         }
34211     }
34212 });
34213
34214 // MenuNav is a private utility class used internally by the Menu
34215 Roo.menu.MenuNav = function(menu){
34216     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34217     this.scope = this.menu = menu;
34218 };
34219
34220 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34221     doRelay : function(e, h){
34222         var k = e.getKey();
34223         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34224             this.menu.tryActivate(0, 1);
34225             return false;
34226         }
34227         return h.call(this.scope || this, e, this.menu);
34228     },
34229
34230     up : function(e, m){
34231         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34232             m.tryActivate(m.items.length-1, -1);
34233         }
34234     },
34235
34236     down : function(e, m){
34237         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34238             m.tryActivate(0, 1);
34239         }
34240     },
34241
34242     right : function(e, m){
34243         if(m.activeItem){
34244             m.activeItem.expandMenu(true);
34245         }
34246     },
34247
34248     left : function(e, m){
34249         m.hide();
34250         if(m.parentMenu && m.parentMenu.activeItem){
34251             m.parentMenu.activeItem.activate();
34252         }
34253     },
34254
34255     enter : function(e, m){
34256         if(m.activeItem){
34257             e.stopPropagation();
34258             m.activeItem.onClick(e);
34259             m.fireEvent("click", this, m.activeItem);
34260             return true;
34261         }
34262     }
34263 });/*
34264  * Based on:
34265  * Ext JS Library 1.1.1
34266  * Copyright(c) 2006-2007, Ext JS, LLC.
34267  *
34268  * Originally Released Under LGPL - original licence link has changed is not relivant.
34269  *
34270  * Fork - LGPL
34271  * <script type="text/javascript">
34272  */
34273  
34274 /**
34275  * @class Roo.menu.MenuMgr
34276  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34277  * @singleton
34278  */
34279 Roo.menu.MenuMgr = function(){
34280    var menus, active, groups = {}, attached = false, lastShow = new Date();
34281
34282    // private - called when first menu is created
34283    function init(){
34284        menus = {};
34285        active = new Roo.util.MixedCollection();
34286        Roo.get(document).addKeyListener(27, function(){
34287            if(active.length > 0){
34288                hideAll();
34289            }
34290        });
34291    }
34292
34293    // private
34294    function hideAll(){
34295        if(active && active.length > 0){
34296            var c = active.clone();
34297            c.each(function(m){
34298                m.hide();
34299            });
34300        }
34301    }
34302
34303    // private
34304    function onHide(m){
34305        active.remove(m);
34306        if(active.length < 1){
34307            Roo.get(document).un("mousedown", onMouseDown);
34308            attached = false;
34309        }
34310    }
34311
34312    // private
34313    function onShow(m){
34314        var last = active.last();
34315        lastShow = new Date();
34316        active.add(m);
34317        if(!attached){
34318            Roo.get(document).on("mousedown", onMouseDown);
34319            attached = true;
34320        }
34321        if(m.parentMenu){
34322           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34323           m.parentMenu.activeChild = m;
34324        }else if(last && last.isVisible()){
34325           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34326        }
34327    }
34328
34329    // private
34330    function onBeforeHide(m){
34331        if(m.activeChild){
34332            m.activeChild.hide();
34333        }
34334        if(m.autoHideTimer){
34335            clearTimeout(m.autoHideTimer);
34336            delete m.autoHideTimer;
34337        }
34338    }
34339
34340    // private
34341    function onBeforeShow(m){
34342        var pm = m.parentMenu;
34343        if(!pm && !m.allowOtherMenus){
34344            hideAll();
34345        }else if(pm && pm.activeChild && active != m){
34346            pm.activeChild.hide();
34347        }
34348    }
34349
34350    // private
34351    function onMouseDown(e){
34352        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34353            hideAll();
34354        }
34355    }
34356
34357    // private
34358    function onBeforeCheck(mi, state){
34359        if(state){
34360            var g = groups[mi.group];
34361            for(var i = 0, l = g.length; i < l; i++){
34362                if(g[i] != mi){
34363                    g[i].setChecked(false);
34364                }
34365            }
34366        }
34367    }
34368
34369    return {
34370
34371        /**
34372         * Hides all menus that are currently visible
34373         */
34374        hideAll : function(){
34375             hideAll();  
34376        },
34377
34378        // private
34379        register : function(menu){
34380            if(!menus){
34381                init();
34382            }
34383            menus[menu.id] = menu;
34384            menu.on("beforehide", onBeforeHide);
34385            menu.on("hide", onHide);
34386            menu.on("beforeshow", onBeforeShow);
34387            menu.on("show", onShow);
34388            var g = menu.group;
34389            if(g && menu.events["checkchange"]){
34390                if(!groups[g]){
34391                    groups[g] = [];
34392                }
34393                groups[g].push(menu);
34394                menu.on("checkchange", onCheck);
34395            }
34396        },
34397
34398         /**
34399          * Returns a {@link Roo.menu.Menu} object
34400          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34401          * be used to generate and return a new Menu instance.
34402          */
34403        get : function(menu){
34404            if(typeof menu == "string"){ // menu id
34405                return menus[menu];
34406            }else if(menu.events){  // menu instance
34407                return menu;
34408            }else if(typeof menu.length == 'number'){ // array of menu items?
34409                return new Roo.menu.Menu({items:menu});
34410            }else{ // otherwise, must be a config
34411                return new Roo.menu.Menu(menu);
34412            }
34413        },
34414
34415        // private
34416        unregister : function(menu){
34417            delete menus[menu.id];
34418            menu.un("beforehide", onBeforeHide);
34419            menu.un("hide", onHide);
34420            menu.un("beforeshow", onBeforeShow);
34421            menu.un("show", onShow);
34422            var g = menu.group;
34423            if(g && menu.events["checkchange"]){
34424                groups[g].remove(menu);
34425                menu.un("checkchange", onCheck);
34426            }
34427        },
34428
34429        // private
34430        registerCheckable : function(menuItem){
34431            var g = menuItem.group;
34432            if(g){
34433                if(!groups[g]){
34434                    groups[g] = [];
34435                }
34436                groups[g].push(menuItem);
34437                menuItem.on("beforecheckchange", onBeforeCheck);
34438            }
34439        },
34440
34441        // private
34442        unregisterCheckable : function(menuItem){
34443            var g = menuItem.group;
34444            if(g){
34445                groups[g].remove(menuItem);
34446                menuItem.un("beforecheckchange", onBeforeCheck);
34447            }
34448        }
34449    };
34450 }();/*
34451  * Based on:
34452  * Ext JS Library 1.1.1
34453  * Copyright(c) 2006-2007, Ext JS, LLC.
34454  *
34455  * Originally Released Under LGPL - original licence link has changed is not relivant.
34456  *
34457  * Fork - LGPL
34458  * <script type="text/javascript">
34459  */
34460  
34461
34462 /**
34463  * @class Roo.menu.BaseItem
34464  * @extends Roo.Component
34465  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34466  * management and base configuration options shared by all menu components.
34467  * @constructor
34468  * Creates a new BaseItem
34469  * @param {Object} config Configuration options
34470  */
34471 Roo.menu.BaseItem = function(config){
34472     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34473
34474     this.addEvents({
34475         /**
34476          * @event click
34477          * Fires when this item is clicked
34478          * @param {Roo.menu.BaseItem} this
34479          * @param {Roo.EventObject} e
34480          */
34481         click: true,
34482         /**
34483          * @event activate
34484          * Fires when this item is activated
34485          * @param {Roo.menu.BaseItem} this
34486          */
34487         activate : true,
34488         /**
34489          * @event deactivate
34490          * Fires when this item is deactivated
34491          * @param {Roo.menu.BaseItem} this
34492          */
34493         deactivate : true
34494     });
34495
34496     if(this.handler){
34497         this.on("click", this.handler, this.scope, true);
34498     }
34499 };
34500
34501 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34502     /**
34503      * @cfg {Function} handler
34504      * A function that will handle the click event of this menu item (defaults to undefined)
34505      */
34506     /**
34507      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34508      */
34509     canActivate : false,
34510     /**
34511      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34512      */
34513     activeClass : "x-menu-item-active",
34514     /**
34515      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34516      */
34517     hideOnClick : true,
34518     /**
34519      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34520      */
34521     hideDelay : 100,
34522
34523     // private
34524     ctype: "Roo.menu.BaseItem",
34525
34526     // private
34527     actionMode : "container",
34528
34529     // private
34530     render : function(container, parentMenu){
34531         this.parentMenu = parentMenu;
34532         Roo.menu.BaseItem.superclass.render.call(this, container);
34533         this.container.menuItemId = this.id;
34534     },
34535
34536     // private
34537     onRender : function(container, position){
34538         this.el = Roo.get(this.el);
34539         container.dom.appendChild(this.el.dom);
34540     },
34541
34542     // private
34543     onClick : function(e){
34544         if(!this.disabled && this.fireEvent("click", this, e) !== false
34545                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34546             this.handleClick(e);
34547         }else{
34548             e.stopEvent();
34549         }
34550     },
34551
34552     // private
34553     activate : function(){
34554         if(this.disabled){
34555             return false;
34556         }
34557         var li = this.container;
34558         li.addClass(this.activeClass);
34559         this.region = li.getRegion().adjust(2, 2, -2, -2);
34560         this.fireEvent("activate", this);
34561         return true;
34562     },
34563
34564     // private
34565     deactivate : function(){
34566         this.container.removeClass(this.activeClass);
34567         this.fireEvent("deactivate", this);
34568     },
34569
34570     // private
34571     shouldDeactivate : function(e){
34572         return !this.region || !this.region.contains(e.getPoint());
34573     },
34574
34575     // private
34576     handleClick : function(e){
34577         if(this.hideOnClick){
34578             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34579         }
34580     },
34581
34582     // private
34583     expandMenu : function(autoActivate){
34584         // do nothing
34585     },
34586
34587     // private
34588     hideMenu : function(){
34589         // do nothing
34590     }
34591 });/*
34592  * Based on:
34593  * Ext JS Library 1.1.1
34594  * Copyright(c) 2006-2007, Ext JS, LLC.
34595  *
34596  * Originally Released Under LGPL - original licence link has changed is not relivant.
34597  *
34598  * Fork - LGPL
34599  * <script type="text/javascript">
34600  */
34601  
34602 /**
34603  * @class Roo.menu.Adapter
34604  * @extends Roo.menu.BaseItem
34605  * 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.
34606  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34607  * @constructor
34608  * Creates a new Adapter
34609  * @param {Object} config Configuration options
34610  */
34611 Roo.menu.Adapter = function(component, config){
34612     Roo.menu.Adapter.superclass.constructor.call(this, config);
34613     this.component = component;
34614 };
34615 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34616     // private
34617     canActivate : true,
34618
34619     // private
34620     onRender : function(container, position){
34621         this.component.render(container);
34622         this.el = this.component.getEl();
34623     },
34624
34625     // private
34626     activate : function(){
34627         if(this.disabled){
34628             return false;
34629         }
34630         this.component.focus();
34631         this.fireEvent("activate", this);
34632         return true;
34633     },
34634
34635     // private
34636     deactivate : function(){
34637         this.fireEvent("deactivate", this);
34638     },
34639
34640     // private
34641     disable : function(){
34642         this.component.disable();
34643         Roo.menu.Adapter.superclass.disable.call(this);
34644     },
34645
34646     // private
34647     enable : function(){
34648         this.component.enable();
34649         Roo.menu.Adapter.superclass.enable.call(this);
34650     }
34651 });/*
34652  * Based on:
34653  * Ext JS Library 1.1.1
34654  * Copyright(c) 2006-2007, Ext JS, LLC.
34655  *
34656  * Originally Released Under LGPL - original licence link has changed is not relivant.
34657  *
34658  * Fork - LGPL
34659  * <script type="text/javascript">
34660  */
34661
34662 /**
34663  * @class Roo.menu.TextItem
34664  * @extends Roo.menu.BaseItem
34665  * Adds a static text string to a menu, usually used as either a heading or group separator.
34666  * Note: old style constructor with text is still supported.
34667  * 
34668  * @constructor
34669  * Creates a new TextItem
34670  * @param {Object} cfg Configuration
34671  */
34672 Roo.menu.TextItem = function(cfg){
34673     if (typeof(cfg) == 'string') {
34674         this.text = cfg;
34675     } else {
34676         Roo.apply(this,cfg);
34677     }
34678     
34679     Roo.menu.TextItem.superclass.constructor.call(this);
34680 };
34681
34682 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34683     /**
34684      * @cfg {Boolean} text Text to show on item.
34685      */
34686     text : '',
34687     
34688     /**
34689      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34690      */
34691     hideOnClick : false,
34692     /**
34693      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34694      */
34695     itemCls : "x-menu-text",
34696
34697     // private
34698     onRender : function(){
34699         var s = document.createElement("span");
34700         s.className = this.itemCls;
34701         s.innerHTML = this.text;
34702         this.el = s;
34703         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34704     }
34705 });/*
34706  * Based on:
34707  * Ext JS Library 1.1.1
34708  * Copyright(c) 2006-2007, Ext JS, LLC.
34709  *
34710  * Originally Released Under LGPL - original licence link has changed is not relivant.
34711  *
34712  * Fork - LGPL
34713  * <script type="text/javascript">
34714  */
34715
34716 /**
34717  * @class Roo.menu.Separator
34718  * @extends Roo.menu.BaseItem
34719  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34720  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34721  * @constructor
34722  * @param {Object} config Configuration options
34723  */
34724 Roo.menu.Separator = function(config){
34725     Roo.menu.Separator.superclass.constructor.call(this, config);
34726 };
34727
34728 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34729     /**
34730      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34731      */
34732     itemCls : "x-menu-sep",
34733     /**
34734      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34735      */
34736     hideOnClick : false,
34737
34738     // private
34739     onRender : function(li){
34740         var s = document.createElement("span");
34741         s.className = this.itemCls;
34742         s.innerHTML = "&#160;";
34743         this.el = s;
34744         li.addClass("x-menu-sep-li");
34745         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34746     }
34747 });/*
34748  * Based on:
34749  * Ext JS Library 1.1.1
34750  * Copyright(c) 2006-2007, Ext JS, LLC.
34751  *
34752  * Originally Released Under LGPL - original licence link has changed is not relivant.
34753  *
34754  * Fork - LGPL
34755  * <script type="text/javascript">
34756  */
34757 /**
34758  * @class Roo.menu.Item
34759  * @extends Roo.menu.BaseItem
34760  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34761  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34762  * activation and click handling.
34763  * @constructor
34764  * Creates a new Item
34765  * @param {Object} config Configuration options
34766  */
34767 Roo.menu.Item = function(config){
34768     Roo.menu.Item.superclass.constructor.call(this, config);
34769     if(this.menu){
34770         this.menu = Roo.menu.MenuMgr.get(this.menu);
34771     }
34772 };
34773 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34774     
34775     /**
34776      * @cfg {String} text
34777      * The text to show on the menu item.
34778      */
34779     text: '',
34780      /**
34781      * @cfg {String} HTML to render in menu
34782      * The text to show on the menu item (HTML version).
34783      */
34784     html: '',
34785     /**
34786      * @cfg {String} icon
34787      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34788      */
34789     icon: undefined,
34790     /**
34791      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34792      */
34793     itemCls : "x-menu-item",
34794     /**
34795      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34796      */
34797     canActivate : true,
34798     /**
34799      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34800      */
34801     showDelay: 200,
34802     // doc'd in BaseItem
34803     hideDelay: 200,
34804
34805     // private
34806     ctype: "Roo.menu.Item",
34807     
34808     // private
34809     onRender : function(container, position){
34810         var el = document.createElement("a");
34811         el.hideFocus = true;
34812         el.unselectable = "on";
34813         el.href = this.href || "#";
34814         if(this.hrefTarget){
34815             el.target = this.hrefTarget;
34816         }
34817         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34818         
34819         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34820         
34821         el.innerHTML = String.format(
34822                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34823                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34824         this.el = el;
34825         Roo.menu.Item.superclass.onRender.call(this, container, position);
34826     },
34827
34828     /**
34829      * Sets the text to display in this menu item
34830      * @param {String} text The text to display
34831      * @param {Boolean} isHTML true to indicate text is pure html.
34832      */
34833     setText : function(text, isHTML){
34834         if (isHTML) {
34835             this.html = text;
34836         } else {
34837             this.text = text;
34838             this.html = '';
34839         }
34840         if(this.rendered){
34841             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34842      
34843             this.el.update(String.format(
34844                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34845                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34846             this.parentMenu.autoWidth();
34847         }
34848     },
34849
34850     // private
34851     handleClick : function(e){
34852         if(!this.href){ // if no link defined, stop the event automatically
34853             e.stopEvent();
34854         }
34855         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34856     },
34857
34858     // private
34859     activate : function(autoExpand){
34860         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34861             this.focus();
34862             if(autoExpand){
34863                 this.expandMenu();
34864             }
34865         }
34866         return true;
34867     },
34868
34869     // private
34870     shouldDeactivate : function(e){
34871         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34872             if(this.menu && this.menu.isVisible()){
34873                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34874             }
34875             return true;
34876         }
34877         return false;
34878     },
34879
34880     // private
34881     deactivate : function(){
34882         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34883         this.hideMenu();
34884     },
34885
34886     // private
34887     expandMenu : function(autoActivate){
34888         if(!this.disabled && this.menu){
34889             clearTimeout(this.hideTimer);
34890             delete this.hideTimer;
34891             if(!this.menu.isVisible() && !this.showTimer){
34892                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34893             }else if (this.menu.isVisible() && autoActivate){
34894                 this.menu.tryActivate(0, 1);
34895             }
34896         }
34897     },
34898
34899     // private
34900     deferExpand : function(autoActivate){
34901         delete this.showTimer;
34902         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34903         if(autoActivate){
34904             this.menu.tryActivate(0, 1);
34905         }
34906     },
34907
34908     // private
34909     hideMenu : function(){
34910         clearTimeout(this.showTimer);
34911         delete this.showTimer;
34912         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34913             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34914         }
34915     },
34916
34917     // private
34918     deferHide : function(){
34919         delete this.hideTimer;
34920         this.menu.hide();
34921     }
34922 });/*
34923  * Based on:
34924  * Ext JS Library 1.1.1
34925  * Copyright(c) 2006-2007, Ext JS, LLC.
34926  *
34927  * Originally Released Under LGPL - original licence link has changed is not relivant.
34928  *
34929  * Fork - LGPL
34930  * <script type="text/javascript">
34931  */
34932  
34933 /**
34934  * @class Roo.menu.CheckItem
34935  * @extends Roo.menu.Item
34936  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34937  * @constructor
34938  * Creates a new CheckItem
34939  * @param {Object} config Configuration options
34940  */
34941 Roo.menu.CheckItem = function(config){
34942     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34943     this.addEvents({
34944         /**
34945          * @event beforecheckchange
34946          * Fires before the checked value is set, providing an opportunity to cancel if needed
34947          * @param {Roo.menu.CheckItem} this
34948          * @param {Boolean} checked The new checked value that will be set
34949          */
34950         "beforecheckchange" : true,
34951         /**
34952          * @event checkchange
34953          * Fires after the checked value has been set
34954          * @param {Roo.menu.CheckItem} this
34955          * @param {Boolean} checked The checked value that was set
34956          */
34957         "checkchange" : true
34958     });
34959     if(this.checkHandler){
34960         this.on('checkchange', this.checkHandler, this.scope);
34961     }
34962 };
34963 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34964     /**
34965      * @cfg {String} group
34966      * All check items with the same group name will automatically be grouped into a single-select
34967      * radio button group (defaults to '')
34968      */
34969     /**
34970      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34971      */
34972     itemCls : "x-menu-item x-menu-check-item",
34973     /**
34974      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34975      */
34976     groupClass : "x-menu-group-item",
34977
34978     /**
34979      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34980      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34981      * initialized with checked = true will be rendered as checked.
34982      */
34983     checked: false,
34984
34985     // private
34986     ctype: "Roo.menu.CheckItem",
34987
34988     // private
34989     onRender : function(c){
34990         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34991         if(this.group){
34992             this.el.addClass(this.groupClass);
34993         }
34994         Roo.menu.MenuMgr.registerCheckable(this);
34995         if(this.checked){
34996             this.checked = false;
34997             this.setChecked(true, true);
34998         }
34999     },
35000
35001     // private
35002     destroy : function(){
35003         if(this.rendered){
35004             Roo.menu.MenuMgr.unregisterCheckable(this);
35005         }
35006         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35007     },
35008
35009     /**
35010      * Set the checked state of this item
35011      * @param {Boolean} checked The new checked value
35012      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35013      */
35014     setChecked : function(state, suppressEvent){
35015         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35016             if(this.container){
35017                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35018             }
35019             this.checked = state;
35020             if(suppressEvent !== true){
35021                 this.fireEvent("checkchange", this, state);
35022             }
35023         }
35024     },
35025
35026     // private
35027     handleClick : function(e){
35028        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35029            this.setChecked(!this.checked);
35030        }
35031        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35032     }
35033 });/*
35034  * Based on:
35035  * Ext JS Library 1.1.1
35036  * Copyright(c) 2006-2007, Ext JS, LLC.
35037  *
35038  * Originally Released Under LGPL - original licence link has changed is not relivant.
35039  *
35040  * Fork - LGPL
35041  * <script type="text/javascript">
35042  */
35043  
35044 /**
35045  * @class Roo.menu.DateItem
35046  * @extends Roo.menu.Adapter
35047  * A menu item that wraps the {@link Roo.DatPicker} component.
35048  * @constructor
35049  * Creates a new DateItem
35050  * @param {Object} config Configuration options
35051  */
35052 Roo.menu.DateItem = function(config){
35053     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35054     /** The Roo.DatePicker object @type Roo.DatePicker */
35055     this.picker = this.component;
35056     this.addEvents({select: true});
35057     
35058     this.picker.on("render", function(picker){
35059         picker.getEl().swallowEvent("click");
35060         picker.container.addClass("x-menu-date-item");
35061     });
35062
35063     this.picker.on("select", this.onSelect, this);
35064 };
35065
35066 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35067     // private
35068     onSelect : function(picker, date){
35069         this.fireEvent("select", this, date, picker);
35070         Roo.menu.DateItem.superclass.handleClick.call(this);
35071     }
35072 });/*
35073  * Based on:
35074  * Ext JS Library 1.1.1
35075  * Copyright(c) 2006-2007, Ext JS, LLC.
35076  *
35077  * Originally Released Under LGPL - original licence link has changed is not relivant.
35078  *
35079  * Fork - LGPL
35080  * <script type="text/javascript">
35081  */
35082  
35083 /**
35084  * @class Roo.menu.ColorItem
35085  * @extends Roo.menu.Adapter
35086  * A menu item that wraps the {@link Roo.ColorPalette} component.
35087  * @constructor
35088  * Creates a new ColorItem
35089  * @param {Object} config Configuration options
35090  */
35091 Roo.menu.ColorItem = function(config){
35092     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35093     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35094     this.palette = this.component;
35095     this.relayEvents(this.palette, ["select"]);
35096     if(this.selectHandler){
35097         this.on('select', this.selectHandler, this.scope);
35098     }
35099 };
35100 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35101  * Based on:
35102  * Ext JS Library 1.1.1
35103  * Copyright(c) 2006-2007, Ext JS, LLC.
35104  *
35105  * Originally Released Under LGPL - original licence link has changed is not relivant.
35106  *
35107  * Fork - LGPL
35108  * <script type="text/javascript">
35109  */
35110  
35111
35112 /**
35113  * @class Roo.menu.DateMenu
35114  * @extends Roo.menu.Menu
35115  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35116  * @constructor
35117  * Creates a new DateMenu
35118  * @param {Object} config Configuration options
35119  */
35120 Roo.menu.DateMenu = function(config){
35121     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35122     this.plain = true;
35123     var di = new Roo.menu.DateItem(config);
35124     this.add(di);
35125     /**
35126      * The {@link Roo.DatePicker} instance for this DateMenu
35127      * @type DatePicker
35128      */
35129     this.picker = di.picker;
35130     /**
35131      * @event select
35132      * @param {DatePicker} picker
35133      * @param {Date} date
35134      */
35135     this.relayEvents(di, ["select"]);
35136
35137     this.on('beforeshow', function(){
35138         if(this.picker){
35139             this.picker.hideMonthPicker(true);
35140         }
35141     }, this);
35142 };
35143 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35144     cls:'x-date-menu'
35145 });/*
35146  * Based on:
35147  * Ext JS Library 1.1.1
35148  * Copyright(c) 2006-2007, Ext JS, LLC.
35149  *
35150  * Originally Released Under LGPL - original licence link has changed is not relivant.
35151  *
35152  * Fork - LGPL
35153  * <script type="text/javascript">
35154  */
35155  
35156
35157 /**
35158  * @class Roo.menu.ColorMenu
35159  * @extends Roo.menu.Menu
35160  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35161  * @constructor
35162  * Creates a new ColorMenu
35163  * @param {Object} config Configuration options
35164  */
35165 Roo.menu.ColorMenu = function(config){
35166     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35167     this.plain = true;
35168     var ci = new Roo.menu.ColorItem(config);
35169     this.add(ci);
35170     /**
35171      * The {@link Roo.ColorPalette} instance for this ColorMenu
35172      * @type ColorPalette
35173      */
35174     this.palette = ci.palette;
35175     /**
35176      * @event select
35177      * @param {ColorPalette} palette
35178      * @param {String} color
35179      */
35180     this.relayEvents(ci, ["select"]);
35181 };
35182 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35183  * Based on:
35184  * Ext JS Library 1.1.1
35185  * Copyright(c) 2006-2007, Ext JS, LLC.
35186  *
35187  * Originally Released Under LGPL - original licence link has changed is not relivant.
35188  *
35189  * Fork - LGPL
35190  * <script type="text/javascript">
35191  */
35192  
35193 /**
35194  * @class Roo.form.Field
35195  * @extends Roo.BoxComponent
35196  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35197  * @constructor
35198  * Creates a new Field
35199  * @param {Object} config Configuration options
35200  */
35201 Roo.form.Field = function(config){
35202     Roo.form.Field.superclass.constructor.call(this, config);
35203 };
35204
35205 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35206     /**
35207      * @cfg {String} fieldLabel Label to use when rendering a form.
35208      */
35209        /**
35210      * @cfg {String} qtip Mouse over tip
35211      */
35212      
35213     /**
35214      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35215      */
35216     invalidClass : "x-form-invalid",
35217     /**
35218      * @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")
35219      */
35220     invalidText : "The value in this field is invalid",
35221     /**
35222      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35223      */
35224     focusClass : "x-form-focus",
35225     /**
35226      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35227       automatic validation (defaults to "keyup").
35228      */
35229     validationEvent : "keyup",
35230     /**
35231      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35232      */
35233     validateOnBlur : true,
35234     /**
35235      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35236      */
35237     validationDelay : 250,
35238     /**
35239      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35240      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35241      */
35242     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35243     /**
35244      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35245      */
35246     fieldClass : "x-form-field",
35247     /**
35248      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35249      *<pre>
35250 Value         Description
35251 -----------   ----------------------------------------------------------------------
35252 qtip          Display a quick tip when the user hovers over the field
35253 title         Display a default browser title attribute popup
35254 under         Add a block div beneath the field containing the error text
35255 side          Add an error icon to the right of the field with a popup on hover
35256 [element id]  Add the error text directly to the innerHTML of the specified element
35257 </pre>
35258      */
35259     msgTarget : 'qtip',
35260     /**
35261      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35262      */
35263     msgFx : 'normal',
35264
35265     /**
35266      * @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.
35267      */
35268     readOnly : false,
35269
35270     /**
35271      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35272      */
35273     disabled : false,
35274
35275     /**
35276      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35277      */
35278     inputType : undefined,
35279     
35280     /**
35281      * @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).
35282          */
35283         tabIndex : undefined,
35284         
35285     // private
35286     isFormField : true,
35287
35288     // private
35289     hasFocus : false,
35290     /**
35291      * @property {Roo.Element} fieldEl
35292      * Element Containing the rendered Field (with label etc.)
35293      */
35294     /**
35295      * @cfg {Mixed} value A value to initialize this field with.
35296      */
35297     value : undefined,
35298
35299     /**
35300      * @cfg {String} name The field's HTML name attribute.
35301      */
35302     /**
35303      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35304      */
35305
35306         // private ??
35307         initComponent : function(){
35308         Roo.form.Field.superclass.initComponent.call(this);
35309         this.addEvents({
35310             /**
35311              * @event focus
35312              * Fires when this field receives input focus.
35313              * @param {Roo.form.Field} this
35314              */
35315             focus : true,
35316             /**
35317              * @event blur
35318              * Fires when this field loses input focus.
35319              * @param {Roo.form.Field} this
35320              */
35321             blur : true,
35322             /**
35323              * @event specialkey
35324              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35325              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35326              * @param {Roo.form.Field} this
35327              * @param {Roo.EventObject} e The event object
35328              */
35329             specialkey : true,
35330             /**
35331              * @event change
35332              * Fires just before the field blurs if the field value has changed.
35333              * @param {Roo.form.Field} this
35334              * @param {Mixed} newValue The new value
35335              * @param {Mixed} oldValue The original value
35336              */
35337             change : true,
35338             /**
35339              * @event invalid
35340              * Fires after the field has been marked as invalid.
35341              * @param {Roo.form.Field} this
35342              * @param {String} msg The validation message
35343              */
35344             invalid : true,
35345             /**
35346              * @event valid
35347              * Fires after the field has been validated with no errors.
35348              * @param {Roo.form.Field} this
35349              */
35350             valid : true,
35351              /**
35352              * @event keyup
35353              * Fires after the key up
35354              * @param {Roo.form.Field} this
35355              * @param {Roo.EventObject}  e The event Object
35356              */
35357             keyup : true
35358         });
35359     },
35360
35361     /**
35362      * Returns the name attribute of the field if available
35363      * @return {String} name The field name
35364      */
35365     getName: function(){
35366          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35367     },
35368
35369     // private
35370     onRender : function(ct, position){
35371         Roo.form.Field.superclass.onRender.call(this, ct, position);
35372         if(!this.el){
35373             var cfg = this.getAutoCreate();
35374             if(!cfg.name){
35375                 cfg.name = this.name || this.id;
35376             }
35377             if(this.inputType){
35378                 cfg.type = this.inputType;
35379             }
35380             this.el = ct.createChild(cfg, position);
35381         }
35382         var type = this.el.dom.type;
35383         if(type){
35384             if(type == 'password'){
35385                 type = 'text';
35386             }
35387             this.el.addClass('x-form-'+type);
35388         }
35389         if(this.readOnly){
35390             this.el.dom.readOnly = true;
35391         }
35392         if(this.tabIndex !== undefined){
35393             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35394         }
35395
35396         this.el.addClass([this.fieldClass, this.cls]);
35397         this.initValue();
35398     },
35399
35400     /**
35401      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35402      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35403      * @return {Roo.form.Field} this
35404      */
35405     applyTo : function(target){
35406         this.allowDomMove = false;
35407         this.el = Roo.get(target);
35408         this.render(this.el.dom.parentNode);
35409         return this;
35410     },
35411
35412     // private
35413     initValue : function(){
35414         if(this.value !== undefined){
35415             this.setValue(this.value);
35416         }else if(this.el.dom.value.length > 0){
35417             this.setValue(this.el.dom.value);
35418         }
35419     },
35420
35421     /**
35422      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35423      */
35424     isDirty : function() {
35425         if(this.disabled) {
35426             return false;
35427         }
35428         return String(this.getValue()) !== String(this.originalValue);
35429     },
35430
35431     // private
35432     afterRender : function(){
35433         Roo.form.Field.superclass.afterRender.call(this);
35434         this.initEvents();
35435     },
35436
35437     // private
35438     fireKey : function(e){
35439         //Roo.log('field ' + e.getKey());
35440         if(e.isNavKeyPress()){
35441             this.fireEvent("specialkey", this, e);
35442         }
35443     },
35444
35445     /**
35446      * Resets the current field value to the originally loaded value and clears any validation messages
35447      */
35448     reset : function(){
35449         this.setValue(this.originalValue);
35450         this.clearInvalid();
35451     },
35452
35453     // private
35454     initEvents : function(){
35455         // safari killled keypress - so keydown is now used..
35456         this.el.on("keydown" , this.fireKey,  this);
35457         this.el.on("focus", this.onFocus,  this);
35458         this.el.on("blur", this.onBlur,  this);
35459         this.el.relayEvent('keyup', this);
35460
35461         // reference to original value for reset
35462         this.originalValue = this.getValue();
35463     },
35464
35465     // private
35466     onFocus : function(){
35467         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35468             this.el.addClass(this.focusClass);
35469         }
35470         if(!this.hasFocus){
35471             this.hasFocus = true;
35472             this.startValue = this.getValue();
35473             this.fireEvent("focus", this);
35474         }
35475     },
35476
35477     beforeBlur : Roo.emptyFn,
35478
35479     // private
35480     onBlur : function(){
35481         this.beforeBlur();
35482         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35483             this.el.removeClass(this.focusClass);
35484         }
35485         this.hasFocus = false;
35486         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35487             this.validate();
35488         }
35489         var v = this.getValue();
35490         if(String(v) !== String(this.startValue)){
35491             this.fireEvent('change', this, v, this.startValue);
35492         }
35493         this.fireEvent("blur", this);
35494     },
35495
35496     /**
35497      * Returns whether or not the field value is currently valid
35498      * @param {Boolean} preventMark True to disable marking the field invalid
35499      * @return {Boolean} True if the value is valid, else false
35500      */
35501     isValid : function(preventMark){
35502         if(this.disabled){
35503             return true;
35504         }
35505         var restore = this.preventMark;
35506         this.preventMark = preventMark === true;
35507         var v = this.validateValue(this.processValue(this.getRawValue()));
35508         this.preventMark = restore;
35509         return v;
35510     },
35511
35512     /**
35513      * Validates the field value
35514      * @return {Boolean} True if the value is valid, else false
35515      */
35516     validate : function(){
35517         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35518             this.clearInvalid();
35519             return true;
35520         }
35521         return false;
35522     },
35523
35524     processValue : function(value){
35525         return value;
35526     },
35527
35528     // private
35529     // Subclasses should provide the validation implementation by overriding this
35530     validateValue : function(value){
35531         return true;
35532     },
35533
35534     /**
35535      * Mark this field as invalid
35536      * @param {String} msg The validation message
35537      */
35538     markInvalid : function(msg){
35539         if(!this.rendered || this.preventMark){ // not rendered
35540             return;
35541         }
35542         this.el.addClass(this.invalidClass);
35543         msg = msg || this.invalidText;
35544         switch(this.msgTarget){
35545             case 'qtip':
35546                 this.el.dom.qtip = msg;
35547                 this.el.dom.qclass = 'x-form-invalid-tip';
35548                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35549                     Roo.QuickTips.enable();
35550                 }
35551                 break;
35552             case 'title':
35553                 this.el.dom.title = msg;
35554                 break;
35555             case 'under':
35556                 if(!this.errorEl){
35557                     var elp = this.el.findParent('.x-form-element', 5, true);
35558                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35559                     this.errorEl.setWidth(elp.getWidth(true)-20);
35560                 }
35561                 this.errorEl.update(msg);
35562                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35563                 break;
35564             case 'side':
35565                 if(!this.errorIcon){
35566                     var elp = this.el.findParent('.x-form-element', 5, true);
35567                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35568                 }
35569                 this.alignErrorIcon();
35570                 this.errorIcon.dom.qtip = msg;
35571                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35572                 this.errorIcon.show();
35573                 this.on('resize', this.alignErrorIcon, this);
35574                 break;
35575             default:
35576                 var t = Roo.getDom(this.msgTarget);
35577                 t.innerHTML = msg;
35578                 t.style.display = this.msgDisplay;
35579                 break;
35580         }
35581         this.fireEvent('invalid', this, msg);
35582     },
35583
35584     // private
35585     alignErrorIcon : function(){
35586         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35587     },
35588
35589     /**
35590      * Clear any invalid styles/messages for this field
35591      */
35592     clearInvalid : function(){
35593         if(!this.rendered || this.preventMark){ // not rendered
35594             return;
35595         }
35596         this.el.removeClass(this.invalidClass);
35597         switch(this.msgTarget){
35598             case 'qtip':
35599                 this.el.dom.qtip = '';
35600                 break;
35601             case 'title':
35602                 this.el.dom.title = '';
35603                 break;
35604             case 'under':
35605                 if(this.errorEl){
35606                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35607                 }
35608                 break;
35609             case 'side':
35610                 if(this.errorIcon){
35611                     this.errorIcon.dom.qtip = '';
35612                     this.errorIcon.hide();
35613                     this.un('resize', this.alignErrorIcon, this);
35614                 }
35615                 break;
35616             default:
35617                 var t = Roo.getDom(this.msgTarget);
35618                 t.innerHTML = '';
35619                 t.style.display = 'none';
35620                 break;
35621         }
35622         this.fireEvent('valid', this);
35623     },
35624
35625     /**
35626      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35627      * @return {Mixed} value The field value
35628      */
35629     getRawValue : function(){
35630         var v = this.el.getValue();
35631         if(v === this.emptyText){
35632             v = '';
35633         }
35634         return v;
35635     },
35636
35637     /**
35638      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35639      * @return {Mixed} value The field value
35640      */
35641     getValue : function(){
35642         var v = this.el.getValue();
35643         if(v === this.emptyText || v === undefined){
35644             v = '';
35645         }
35646         return v;
35647     },
35648
35649     /**
35650      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35651      * @param {Mixed} value The value to set
35652      */
35653     setRawValue : function(v){
35654         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35655     },
35656
35657     /**
35658      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35659      * @param {Mixed} value The value to set
35660      */
35661     setValue : function(v){
35662         this.value = v;
35663         if(this.rendered){
35664             this.el.dom.value = (v === null || v === undefined ? '' : v);
35665             this.validate();
35666         }
35667     },
35668
35669     adjustSize : function(w, h){
35670         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35671         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35672         return s;
35673     },
35674
35675     adjustWidth : function(tag, w){
35676         tag = tag.toLowerCase();
35677         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35678             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35679                 if(tag == 'input'){
35680                     return w + 2;
35681                 }
35682                 if(tag = 'textarea'){
35683                     return w-2;
35684                 }
35685             }else if(Roo.isOpera){
35686                 if(tag == 'input'){
35687                     return w + 2;
35688                 }
35689                 if(tag = 'textarea'){
35690                     return w-2;
35691                 }
35692             }
35693         }
35694         return w;
35695     }
35696 });
35697
35698
35699 // anything other than normal should be considered experimental
35700 Roo.form.Field.msgFx = {
35701     normal : {
35702         show: function(msgEl, f){
35703             msgEl.setDisplayed('block');
35704         },
35705
35706         hide : function(msgEl, f){
35707             msgEl.setDisplayed(false).update('');
35708         }
35709     },
35710
35711     slide : {
35712         show: function(msgEl, f){
35713             msgEl.slideIn('t', {stopFx:true});
35714         },
35715
35716         hide : function(msgEl, f){
35717             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35718         }
35719     },
35720
35721     slideRight : {
35722         show: function(msgEl, f){
35723             msgEl.fixDisplay();
35724             msgEl.alignTo(f.el, 'tl-tr');
35725             msgEl.slideIn('l', {stopFx:true});
35726         },
35727
35728         hide : function(msgEl, f){
35729             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35730         }
35731     }
35732 };/*
35733  * Based on:
35734  * Ext JS Library 1.1.1
35735  * Copyright(c) 2006-2007, Ext JS, LLC.
35736  *
35737  * Originally Released Under LGPL - original licence link has changed is not relivant.
35738  *
35739  * Fork - LGPL
35740  * <script type="text/javascript">
35741  */
35742  
35743
35744 /**
35745  * @class Roo.form.TextField
35746  * @extends Roo.form.Field
35747  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35748  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35749  * @constructor
35750  * Creates a new TextField
35751  * @param {Object} config Configuration options
35752  */
35753 Roo.form.TextField = function(config){
35754     Roo.form.TextField.superclass.constructor.call(this, config);
35755     this.addEvents({
35756         /**
35757          * @event autosize
35758          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35759          * according to the default logic, but this event provides a hook for the developer to apply additional
35760          * logic at runtime to resize the field if needed.
35761              * @param {Roo.form.Field} this This text field
35762              * @param {Number} width The new field width
35763              */
35764         autosize : true
35765     });
35766 };
35767
35768 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35769     /**
35770      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35771      */
35772     grow : false,
35773     /**
35774      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35775      */
35776     growMin : 30,
35777     /**
35778      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35779      */
35780     growMax : 800,
35781     /**
35782      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35783      */
35784     vtype : null,
35785     /**
35786      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35787      */
35788     maskRe : null,
35789     /**
35790      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35791      */
35792     disableKeyFilter : false,
35793     /**
35794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35795      */
35796     allowBlank : true,
35797     /**
35798      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35799      */
35800     minLength : 0,
35801     /**
35802      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35803      */
35804     maxLength : Number.MAX_VALUE,
35805     /**
35806      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35807      */
35808     minLengthText : "The minimum length for this field is {0}",
35809     /**
35810      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35811      */
35812     maxLengthText : "The maximum length for this field is {0}",
35813     /**
35814      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35815      */
35816     selectOnFocus : false,
35817     /**
35818      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35819      */
35820     blankText : "This field is required",
35821     /**
35822      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35823      * If available, this function will be called only after the basic validators all return true, and will be passed the
35824      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35825      */
35826     validator : null,
35827     /**
35828      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35829      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35830      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35831      */
35832     regex : null,
35833     /**
35834      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35835      */
35836     regexText : "",
35837     /**
35838      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35839      */
35840     emptyText : null,
35841     /**
35842      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35843      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35844      */
35845     emptyClass : 'x-form-empty-field',
35846
35847     // private
35848     initEvents : function(){
35849         Roo.form.TextField.superclass.initEvents.call(this);
35850         if(this.validationEvent == 'keyup'){
35851             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35852             this.el.on('keyup', this.filterValidation, this);
35853         }
35854         else if(this.validationEvent !== false){
35855             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35856         }
35857         if(this.selectOnFocus || this.emptyText){
35858             this.on("focus", this.preFocus, this);
35859             if(this.emptyText){
35860                 this.on('blur', this.postBlur, this);
35861                 this.applyEmptyText();
35862             }
35863         }
35864         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35865             this.el.on("keypress", this.filterKeys, this);
35866         }
35867         if(this.grow){
35868             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35869             this.el.on("click", this.autoSize,  this);
35870         }
35871     },
35872
35873     processValue : function(value){
35874         if(this.stripCharsRe){
35875             var newValue = value.replace(this.stripCharsRe, '');
35876             if(newValue !== value){
35877                 this.setRawValue(newValue);
35878                 return newValue;
35879             }
35880         }
35881         return value;
35882     },
35883
35884     filterValidation : function(e){
35885         if(!e.isNavKeyPress()){
35886             this.validationTask.delay(this.validationDelay);
35887         }
35888     },
35889
35890     // private
35891     onKeyUp : function(e){
35892         if(!e.isNavKeyPress()){
35893             this.autoSize();
35894         }
35895     },
35896
35897     /**
35898      * Resets the current field value to the originally-loaded value and clears any validation messages.
35899      * Also adds emptyText and emptyClass if the original value was blank.
35900      */
35901     reset : function(){
35902         Roo.form.TextField.superclass.reset.call(this);
35903         this.applyEmptyText();
35904     },
35905
35906     applyEmptyText : function(){
35907         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35908             this.setRawValue(this.emptyText);
35909             this.el.addClass(this.emptyClass);
35910         }
35911     },
35912
35913     // private
35914     preFocus : function(){
35915         if(this.emptyText){
35916             if(this.el.dom.value == this.emptyText){
35917                 this.setRawValue('');
35918             }
35919             this.el.removeClass(this.emptyClass);
35920         }
35921         if(this.selectOnFocus){
35922             this.el.dom.select();
35923         }
35924     },
35925
35926     // private
35927     postBlur : function(){
35928         this.applyEmptyText();
35929     },
35930
35931     // private
35932     filterKeys : function(e){
35933         var k = e.getKey();
35934         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35935             return;
35936         }
35937         var c = e.getCharCode(), cc = String.fromCharCode(c);
35938         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35939             return;
35940         }
35941         if(!this.maskRe.test(cc)){
35942             e.stopEvent();
35943         }
35944     },
35945
35946     setValue : function(v){
35947         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35948             this.el.removeClass(this.emptyClass);
35949         }
35950         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35951         this.applyEmptyText();
35952         this.autoSize();
35953     },
35954
35955     /**
35956      * Validates a value according to the field's validation rules and marks the field as invalid
35957      * if the validation fails
35958      * @param {Mixed} value The value to validate
35959      * @return {Boolean} True if the value is valid, else false
35960      */
35961     validateValue : function(value){
35962         if(value.length < 1 || value === this.emptyText){ // if it's blank
35963              if(this.allowBlank){
35964                 this.clearInvalid();
35965                 return true;
35966              }else{
35967                 this.markInvalid(this.blankText);
35968                 return false;
35969              }
35970         }
35971         if(value.length < this.minLength){
35972             this.markInvalid(String.format(this.minLengthText, this.minLength));
35973             return false;
35974         }
35975         if(value.length > this.maxLength){
35976             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35977             return false;
35978         }
35979         if(this.vtype){
35980             var vt = Roo.form.VTypes;
35981             if(!vt[this.vtype](value, this)){
35982                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35983                 return false;
35984             }
35985         }
35986         if(typeof this.validator == "function"){
35987             var msg = this.validator(value);
35988             if(msg !== true){
35989                 this.markInvalid(msg);
35990                 return false;
35991             }
35992         }
35993         if(this.regex && !this.regex.test(value)){
35994             this.markInvalid(this.regexText);
35995             return false;
35996         }
35997         return true;
35998     },
35999
36000     /**
36001      * Selects text in this field
36002      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36003      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36004      */
36005     selectText : function(start, end){
36006         var v = this.getRawValue();
36007         if(v.length > 0){
36008             start = start === undefined ? 0 : start;
36009             end = end === undefined ? v.length : end;
36010             var d = this.el.dom;
36011             if(d.setSelectionRange){
36012                 d.setSelectionRange(start, end);
36013             }else if(d.createTextRange){
36014                 var range = d.createTextRange();
36015                 range.moveStart("character", start);
36016                 range.moveEnd("character", v.length-end);
36017                 range.select();
36018             }
36019         }
36020     },
36021
36022     /**
36023      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36024      * This only takes effect if grow = true, and fires the autosize event.
36025      */
36026     autoSize : function(){
36027         if(!this.grow || !this.rendered){
36028             return;
36029         }
36030         if(!this.metrics){
36031             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36032         }
36033         var el = this.el;
36034         var v = el.dom.value;
36035         var d = document.createElement('div');
36036         d.appendChild(document.createTextNode(v));
36037         v = d.innerHTML;
36038         d = null;
36039         v += "&#160;";
36040         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36041         this.el.setWidth(w);
36042         this.fireEvent("autosize", this, w);
36043     }
36044 });/*
36045  * Based on:
36046  * Ext JS Library 1.1.1
36047  * Copyright(c) 2006-2007, Ext JS, LLC.
36048  *
36049  * Originally Released Under LGPL - original licence link has changed is not relivant.
36050  *
36051  * Fork - LGPL
36052  * <script type="text/javascript">
36053  */
36054  
36055 /**
36056  * @class Roo.form.Hidden
36057  * @extends Roo.form.TextField
36058  * Simple Hidden element used on forms 
36059  * 
36060  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36061  * 
36062  * @constructor
36063  * Creates a new Hidden form element.
36064  * @param {Object} config Configuration options
36065  */
36066
36067
36068
36069 // easy hidden field...
36070 Roo.form.Hidden = function(config){
36071     Roo.form.Hidden.superclass.constructor.call(this, config);
36072 };
36073   
36074 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36075     fieldLabel:      '',
36076     inputType:      'hidden',
36077     width:          50,
36078     allowBlank:     true,
36079     labelSeparator: '',
36080     hidden:         true,
36081     itemCls :       'x-form-item-display-none'
36082
36083
36084 });
36085
36086
36087 /*
36088  * Based on:
36089  * Ext JS Library 1.1.1
36090  * Copyright(c) 2006-2007, Ext JS, LLC.
36091  *
36092  * Originally Released Under LGPL - original licence link has changed is not relivant.
36093  *
36094  * Fork - LGPL
36095  * <script type="text/javascript">
36096  */
36097  
36098 /**
36099  * @class Roo.form.TriggerField
36100  * @extends Roo.form.TextField
36101  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36102  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36103  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36104  * for which you can provide a custom implementation.  For example:
36105  * <pre><code>
36106 var trigger = new Roo.form.TriggerField();
36107 trigger.onTriggerClick = myTriggerFn;
36108 trigger.applyTo('my-field');
36109 </code></pre>
36110  *
36111  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36112  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36113  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36114  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36115  * @constructor
36116  * Create a new TriggerField.
36117  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36118  * to the base TextField)
36119  */
36120 Roo.form.TriggerField = function(config){
36121     this.mimicing = false;
36122     Roo.form.TriggerField.superclass.constructor.call(this, config);
36123 };
36124
36125 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36126     /**
36127      * @cfg {String} triggerClass A CSS class to apply to the trigger
36128      */
36129     /**
36130      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36131      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36132      */
36133     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36134     /**
36135      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36136      */
36137     hideTrigger:false,
36138
36139     /** @cfg {Boolean} grow @hide */
36140     /** @cfg {Number} growMin @hide */
36141     /** @cfg {Number} growMax @hide */
36142
36143     /**
36144      * @hide 
36145      * @method
36146      */
36147     autoSize: Roo.emptyFn,
36148     // private
36149     monitorTab : true,
36150     // private
36151     deferHeight : true,
36152
36153     
36154     actionMode : 'wrap',
36155     // private
36156     onResize : function(w, h){
36157         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36158         if(typeof w == 'number'){
36159             var x = w - this.trigger.getWidth();
36160             this.el.setWidth(this.adjustWidth('input', x));
36161             this.trigger.setStyle('left', x+'px');
36162         }
36163     },
36164
36165     // private
36166     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36167
36168     // private
36169     getResizeEl : function(){
36170         return this.wrap;
36171     },
36172
36173     // private
36174     getPositionEl : function(){
36175         return this.wrap;
36176     },
36177
36178     // private
36179     alignErrorIcon : function(){
36180         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36181     },
36182
36183     // private
36184     onRender : function(ct, position){
36185         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36186         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36187         this.trigger = this.wrap.createChild(this.triggerConfig ||
36188                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36189         if(this.hideTrigger){
36190             this.trigger.setDisplayed(false);
36191         }
36192         this.initTrigger();
36193         if(!this.width){
36194             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36195         }
36196     },
36197
36198     // private
36199     initTrigger : function(){
36200         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36201         this.trigger.addClassOnOver('x-form-trigger-over');
36202         this.trigger.addClassOnClick('x-form-trigger-click');
36203     },
36204
36205     // private
36206     onDestroy : function(){
36207         if(this.trigger){
36208             this.trigger.removeAllListeners();
36209             this.trigger.remove();
36210         }
36211         if(this.wrap){
36212             this.wrap.remove();
36213         }
36214         Roo.form.TriggerField.superclass.onDestroy.call(this);
36215     },
36216
36217     // private
36218     onFocus : function(){
36219         Roo.form.TriggerField.superclass.onFocus.call(this);
36220         if(!this.mimicing){
36221             this.wrap.addClass('x-trigger-wrap-focus');
36222             this.mimicing = true;
36223             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36224             if(this.monitorTab){
36225                 this.el.on("keydown", this.checkTab, this);
36226             }
36227         }
36228     },
36229
36230     // private
36231     checkTab : function(e){
36232         if(e.getKey() == e.TAB){
36233             this.triggerBlur();
36234         }
36235     },
36236
36237     // private
36238     onBlur : function(){
36239         // do nothing
36240     },
36241
36242     // private
36243     mimicBlur : function(e, t){
36244         if(!this.wrap.contains(t) && this.validateBlur()){
36245             this.triggerBlur();
36246         }
36247     },
36248
36249     // private
36250     triggerBlur : function(){
36251         this.mimicing = false;
36252         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36253         if(this.monitorTab){
36254             this.el.un("keydown", this.checkTab, this);
36255         }
36256         this.wrap.removeClass('x-trigger-wrap-focus');
36257         Roo.form.TriggerField.superclass.onBlur.call(this);
36258     },
36259
36260     // private
36261     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36262     validateBlur : function(e, t){
36263         return true;
36264     },
36265
36266     // private
36267     onDisable : function(){
36268         Roo.form.TriggerField.superclass.onDisable.call(this);
36269         if(this.wrap){
36270             this.wrap.addClass('x-item-disabled');
36271         }
36272     },
36273
36274     // private
36275     onEnable : function(){
36276         Roo.form.TriggerField.superclass.onEnable.call(this);
36277         if(this.wrap){
36278             this.wrap.removeClass('x-item-disabled');
36279         }
36280     },
36281
36282     // private
36283     onShow : function(){
36284         var ae = this.getActionEl();
36285         
36286         if(ae){
36287             ae.dom.style.display = '';
36288             ae.dom.style.visibility = 'visible';
36289         }
36290     },
36291
36292     // private
36293     
36294     onHide : function(){
36295         var ae = this.getActionEl();
36296         ae.dom.style.display = 'none';
36297     },
36298
36299     /**
36300      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36301      * by an implementing function.
36302      * @method
36303      * @param {EventObject} e
36304      */
36305     onTriggerClick : Roo.emptyFn
36306 });
36307
36308 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36309 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36310 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36311 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36312     initComponent : function(){
36313         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36314
36315         this.triggerConfig = {
36316             tag:'span', cls:'x-form-twin-triggers', cn:[
36317             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36318             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36319         ]};
36320     },
36321
36322     getTrigger : function(index){
36323         return this.triggers[index];
36324     },
36325
36326     initTrigger : function(){
36327         var ts = this.trigger.select('.x-form-trigger', true);
36328         this.wrap.setStyle('overflow', 'hidden');
36329         var triggerField = this;
36330         ts.each(function(t, all, index){
36331             t.hide = function(){
36332                 var w = triggerField.wrap.getWidth();
36333                 this.dom.style.display = 'none';
36334                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36335             };
36336             t.show = function(){
36337                 var w = triggerField.wrap.getWidth();
36338                 this.dom.style.display = '';
36339                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36340             };
36341             var triggerIndex = 'Trigger'+(index+1);
36342
36343             if(this['hide'+triggerIndex]){
36344                 t.dom.style.display = 'none';
36345             }
36346             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36347             t.addClassOnOver('x-form-trigger-over');
36348             t.addClassOnClick('x-form-trigger-click');
36349         }, this);
36350         this.triggers = ts.elements;
36351     },
36352
36353     onTrigger1Click : Roo.emptyFn,
36354     onTrigger2Click : Roo.emptyFn
36355 });/*
36356  * Based on:
36357  * Ext JS Library 1.1.1
36358  * Copyright(c) 2006-2007, Ext JS, LLC.
36359  *
36360  * Originally Released Under LGPL - original licence link has changed is not relivant.
36361  *
36362  * Fork - LGPL
36363  * <script type="text/javascript">
36364  */
36365  
36366 /**
36367  * @class Roo.form.TextArea
36368  * @extends Roo.form.TextField
36369  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36370  * support for auto-sizing.
36371  * @constructor
36372  * Creates a new TextArea
36373  * @param {Object} config Configuration options
36374  */
36375 Roo.form.TextArea = function(config){
36376     Roo.form.TextArea.superclass.constructor.call(this, config);
36377     // these are provided exchanges for backwards compat
36378     // minHeight/maxHeight were replaced by growMin/growMax to be
36379     // compatible with TextField growing config values
36380     if(this.minHeight !== undefined){
36381         this.growMin = this.minHeight;
36382     }
36383     if(this.maxHeight !== undefined){
36384         this.growMax = this.maxHeight;
36385     }
36386 };
36387
36388 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36389     /**
36390      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36391      */
36392     growMin : 60,
36393     /**
36394      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36395      */
36396     growMax: 1000,
36397     /**
36398      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36399      * in the field (equivalent to setting overflow: hidden, defaults to false)
36400      */
36401     preventScrollbars: false,
36402     /**
36403      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36404      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36405      */
36406
36407     // private
36408     onRender : function(ct, position){
36409         if(!this.el){
36410             this.defaultAutoCreate = {
36411                 tag: "textarea",
36412                 style:"width:300px;height:60px;",
36413                 autocomplete: "off"
36414             };
36415         }
36416         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36417         if(this.grow){
36418             this.textSizeEl = Roo.DomHelper.append(document.body, {
36419                 tag: "pre", cls: "x-form-grow-sizer"
36420             });
36421             if(this.preventScrollbars){
36422                 this.el.setStyle("overflow", "hidden");
36423             }
36424             this.el.setHeight(this.growMin);
36425         }
36426     },
36427
36428     onDestroy : function(){
36429         if(this.textSizeEl){
36430             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36431         }
36432         Roo.form.TextArea.superclass.onDestroy.call(this);
36433     },
36434
36435     // private
36436     onKeyUp : function(e){
36437         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36438             this.autoSize();
36439         }
36440     },
36441
36442     /**
36443      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36444      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36445      */
36446     autoSize : function(){
36447         if(!this.grow || !this.textSizeEl){
36448             return;
36449         }
36450         var el = this.el;
36451         var v = el.dom.value;
36452         var ts = this.textSizeEl;
36453
36454         ts.innerHTML = '';
36455         ts.appendChild(document.createTextNode(v));
36456         v = ts.innerHTML;
36457
36458         Roo.fly(ts).setWidth(this.el.getWidth());
36459         if(v.length < 1){
36460             v = "&#160;&#160;";
36461         }else{
36462             if(Roo.isIE){
36463                 v = v.replace(/\n/g, '<p>&#160;</p>');
36464             }
36465             v += "&#160;\n&#160;";
36466         }
36467         ts.innerHTML = v;
36468         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36469         if(h != this.lastHeight){
36470             this.lastHeight = h;
36471             this.el.setHeight(h);
36472             this.fireEvent("autosize", this, h);
36473         }
36474     }
36475 });/*
36476  * Based on:
36477  * Ext JS Library 1.1.1
36478  * Copyright(c) 2006-2007, Ext JS, LLC.
36479  *
36480  * Originally Released Under LGPL - original licence link has changed is not relivant.
36481  *
36482  * Fork - LGPL
36483  * <script type="text/javascript">
36484  */
36485  
36486
36487 /**
36488  * @class Roo.form.NumberField
36489  * @extends Roo.form.TextField
36490  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36491  * @constructor
36492  * Creates a new NumberField
36493  * @param {Object} config Configuration options
36494  */
36495 Roo.form.NumberField = function(config){
36496     Roo.form.NumberField.superclass.constructor.call(this, config);
36497 };
36498
36499 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36500     /**
36501      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36502      */
36503     fieldClass: "x-form-field x-form-num-field",
36504     /**
36505      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36506      */
36507     allowDecimals : true,
36508     /**
36509      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36510      */
36511     decimalSeparator : ".",
36512     /**
36513      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36514      */
36515     decimalPrecision : 2,
36516     /**
36517      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36518      */
36519     allowNegative : true,
36520     /**
36521      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36522      */
36523     minValue : Number.NEGATIVE_INFINITY,
36524     /**
36525      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36526      */
36527     maxValue : Number.MAX_VALUE,
36528     /**
36529      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36530      */
36531     minText : "The minimum value for this field is {0}",
36532     /**
36533      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36534      */
36535     maxText : "The maximum value for this field is {0}",
36536     /**
36537      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36538      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36539      */
36540     nanText : "{0} is not a valid number",
36541
36542     // private
36543     initEvents : function(){
36544         Roo.form.NumberField.superclass.initEvents.call(this);
36545         var allowed = "0123456789";
36546         if(this.allowDecimals){
36547             allowed += this.decimalSeparator;
36548         }
36549         if(this.allowNegative){
36550             allowed += "-";
36551         }
36552         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36553         var keyPress = function(e){
36554             var k = e.getKey();
36555             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36556                 return;
36557             }
36558             var c = e.getCharCode();
36559             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36560                 e.stopEvent();
36561             }
36562         };
36563         this.el.on("keypress", keyPress, this);
36564     },
36565
36566     // private
36567     validateValue : function(value){
36568         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36569             return false;
36570         }
36571         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36572              return true;
36573         }
36574         var num = this.parseValue(value);
36575         if(isNaN(num)){
36576             this.markInvalid(String.format(this.nanText, value));
36577             return false;
36578         }
36579         if(num < this.minValue){
36580             this.markInvalid(String.format(this.minText, this.minValue));
36581             return false;
36582         }
36583         if(num > this.maxValue){
36584             this.markInvalid(String.format(this.maxText, this.maxValue));
36585             return false;
36586         }
36587         return true;
36588     },
36589
36590     getValue : function(){
36591         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36592     },
36593
36594     // private
36595     parseValue : function(value){
36596         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36597         return isNaN(value) ? '' : value;
36598     },
36599
36600     // private
36601     fixPrecision : function(value){
36602         var nan = isNaN(value);
36603         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36604             return nan ? '' : value;
36605         }
36606         return parseFloat(value).toFixed(this.decimalPrecision);
36607     },
36608
36609     setValue : function(v){
36610         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36611     },
36612
36613     // private
36614     decimalPrecisionFcn : function(v){
36615         return Math.floor(v);
36616     },
36617
36618     beforeBlur : function(){
36619         var v = this.parseValue(this.getRawValue());
36620         if(v){
36621             this.setValue(this.fixPrecision(v));
36622         }
36623     }
36624 });/*
36625  * Based on:
36626  * Ext JS Library 1.1.1
36627  * Copyright(c) 2006-2007, Ext JS, LLC.
36628  *
36629  * Originally Released Under LGPL - original licence link has changed is not relivant.
36630  *
36631  * Fork - LGPL
36632  * <script type="text/javascript">
36633  */
36634  
36635 /**
36636  * @class Roo.form.DateField
36637  * @extends Roo.form.TriggerField
36638  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36639 * @constructor
36640 * Create a new DateField
36641 * @param {Object} config
36642  */
36643 Roo.form.DateField = function(config){
36644     Roo.form.DateField.superclass.constructor.call(this, config);
36645     
36646       this.addEvents({
36647          
36648         /**
36649          * @event select
36650          * Fires when a date is selected
36651              * @param {Roo.form.DateField} combo This combo box
36652              * @param {Date} date The date selected
36653              */
36654         'select' : true
36655          
36656     });
36657     
36658     
36659     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36660     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36661     this.ddMatch = null;
36662     if(this.disabledDates){
36663         var dd = this.disabledDates;
36664         var re = "(?:";
36665         for(var i = 0; i < dd.length; i++){
36666             re += dd[i];
36667             if(i != dd.length-1) re += "|";
36668         }
36669         this.ddMatch = new RegExp(re + ")");
36670     }
36671 };
36672
36673 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36674     /**
36675      * @cfg {String} format
36676      * The default date format string which can be overriden for localization support.  The format must be
36677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36678      */
36679     format : "m/d/y",
36680     /**
36681      * @cfg {String} altFormats
36682      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36683      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36684      */
36685     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36686     /**
36687      * @cfg {Array} disabledDays
36688      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36689      */
36690     disabledDays : null,
36691     /**
36692      * @cfg {String} disabledDaysText
36693      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36694      */
36695     disabledDaysText : "Disabled",
36696     /**
36697      * @cfg {Array} disabledDates
36698      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36699      * expression so they are very powerful. Some examples:
36700      * <ul>
36701      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36702      * <li>["03/08", "09/16"] would disable those days for every year</li>
36703      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36704      * <li>["03/../2006"] would disable every day in March 2006</li>
36705      * <li>["^03"] would disable every day in every March</li>
36706      * </ul>
36707      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36708      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36709      */
36710     disabledDates : null,
36711     /**
36712      * @cfg {String} disabledDatesText
36713      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36714      */
36715     disabledDatesText : "Disabled",
36716     /**
36717      * @cfg {Date/String} minValue
36718      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36719      * valid format (defaults to null).
36720      */
36721     minValue : null,
36722     /**
36723      * @cfg {Date/String} maxValue
36724      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36725      * valid format (defaults to null).
36726      */
36727     maxValue : null,
36728     /**
36729      * @cfg {String} minText
36730      * The error text to display when the date in the cell is before minValue (defaults to
36731      * 'The date in this field must be after {minValue}').
36732      */
36733     minText : "The date in this field must be equal to or after {0}",
36734     /**
36735      * @cfg {String} maxText
36736      * The error text to display when the date in the cell is after maxValue (defaults to
36737      * 'The date in this field must be before {maxValue}').
36738      */
36739     maxText : "The date in this field must be equal to or before {0}",
36740     /**
36741      * @cfg {String} invalidText
36742      * The error text to display when the date in the field is invalid (defaults to
36743      * '{value} is not a valid date - it must be in the format {format}').
36744      */
36745     invalidText : "{0} is not a valid date - it must be in the format {1}",
36746     /**
36747      * @cfg {String} triggerClass
36748      * An additional CSS class used to style the trigger button.  The trigger will always get the
36749      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36750      * which displays a calendar icon).
36751      */
36752     triggerClass : 'x-form-date-trigger',
36753     
36754
36755     /**
36756      * @cfg {bool} useIso
36757      * if enabled, then the date field will use a hidden field to store the 
36758      * real value as iso formated date. default (false)
36759      */ 
36760     useIso : false,
36761     /**
36762      * @cfg {String/Object} autoCreate
36763      * A DomHelper element spec, or true for a default element spec (defaults to
36764      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36765      */ 
36766     // private
36767     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36768     
36769     // private
36770     hiddenField: false,
36771     
36772     onRender : function(ct, position)
36773     {
36774         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36775         if (this.useIso) {
36776             this.el.dom.removeAttribute('name'); 
36777             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36778                     'before', true);
36779             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36780             // prevent input submission
36781             this.hiddenName = this.name;
36782         }
36783             
36784             
36785     },
36786     
36787     // private
36788     validateValue : function(value)
36789     {
36790         value = this.formatDate(value);
36791         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36792             return false;
36793         }
36794         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36795              return true;
36796         }
36797         var svalue = value;
36798         value = this.parseDate(value);
36799         if(!value){
36800             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36801             return false;
36802         }
36803         var time = value.getTime();
36804         if(this.minValue && time < this.minValue.getTime()){
36805             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36806             return false;
36807         }
36808         if(this.maxValue && time > this.maxValue.getTime()){
36809             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36810             return false;
36811         }
36812         if(this.disabledDays){
36813             var day = value.getDay();
36814             for(var i = 0; i < this.disabledDays.length; i++) {
36815                 if(day === this.disabledDays[i]){
36816                     this.markInvalid(this.disabledDaysText);
36817                     return false;
36818                 }
36819             }
36820         }
36821         var fvalue = this.formatDate(value);
36822         if(this.ddMatch && this.ddMatch.test(fvalue)){
36823             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36824             return false;
36825         }
36826         return true;
36827     },
36828
36829     // private
36830     // Provides logic to override the default TriggerField.validateBlur which just returns true
36831     validateBlur : function(){
36832         return !this.menu || !this.menu.isVisible();
36833     },
36834
36835     /**
36836      * Returns the current date value of the date field.
36837      * @return {Date} The date value
36838      */
36839     getValue : function(){
36840         
36841         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36842     },
36843
36844     /**
36845      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36846      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36847      * (the default format used is "m/d/y").
36848      * <br />Usage:
36849      * <pre><code>
36850 //All of these calls set the same date value (May 4, 2006)
36851
36852 //Pass a date object:
36853 var dt = new Date('5/4/06');
36854 dateField.setValue(dt);
36855
36856 //Pass a date string (default format):
36857 dateField.setValue('5/4/06');
36858
36859 //Pass a date string (custom format):
36860 dateField.format = 'Y-m-d';
36861 dateField.setValue('2006-5-4');
36862 </code></pre>
36863      * @param {String/Date} date The date or valid date string
36864      */
36865     setValue : function(date){
36866         if (this.hiddenField) {
36867             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36868         }
36869         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36870     },
36871
36872     // private
36873     parseDate : function(value){
36874         if(!value || value instanceof Date){
36875             return value;
36876         }
36877         var v = Date.parseDate(value, this.format);
36878         if(!v && this.altFormats){
36879             if(!this.altFormatsArray){
36880                 this.altFormatsArray = this.altFormats.split("|");
36881             }
36882             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36883                 v = Date.parseDate(value, this.altFormatsArray[i]);
36884             }
36885         }
36886         return v;
36887     },
36888
36889     // private
36890     formatDate : function(date, fmt){
36891         return (!date || !(date instanceof Date)) ?
36892                date : date.dateFormat(fmt || this.format);
36893     },
36894
36895     // private
36896     menuListeners : {
36897         select: function(m, d){
36898             this.setValue(d);
36899             this.fireEvent('select', this, d);
36900         },
36901         show : function(){ // retain focus styling
36902             this.onFocus();
36903         },
36904         hide : function(){
36905             this.focus.defer(10, this);
36906             var ml = this.menuListeners;
36907             this.menu.un("select", ml.select,  this);
36908             this.menu.un("show", ml.show,  this);
36909             this.menu.un("hide", ml.hide,  this);
36910         }
36911     },
36912
36913     // private
36914     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36915     onTriggerClick : function(){
36916         if(this.disabled){
36917             return;
36918         }
36919         if(this.menu == null){
36920             this.menu = new Roo.menu.DateMenu();
36921         }
36922         Roo.apply(this.menu.picker,  {
36923             showClear: this.allowBlank,
36924             minDate : this.minValue,
36925             maxDate : this.maxValue,
36926             disabledDatesRE : this.ddMatch,
36927             disabledDatesText : this.disabledDatesText,
36928             disabledDays : this.disabledDays,
36929             disabledDaysText : this.disabledDaysText,
36930             format : this.format,
36931             minText : String.format(this.minText, this.formatDate(this.minValue)),
36932             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36933         });
36934         this.menu.on(Roo.apply({}, this.menuListeners, {
36935             scope:this
36936         }));
36937         this.menu.picker.setValue(this.getValue() || new Date());
36938         this.menu.show(this.el, "tl-bl?");
36939     },
36940
36941     beforeBlur : function(){
36942         var v = this.parseDate(this.getRawValue());
36943         if(v){
36944             this.setValue(v);
36945         }
36946     }
36947
36948     /** @cfg {Boolean} grow @hide */
36949     /** @cfg {Number} growMin @hide */
36950     /** @cfg {Number} growMax @hide */
36951     /**
36952      * @hide
36953      * @method autoSize
36954      */
36955 });/*
36956  * Based on:
36957  * Ext JS Library 1.1.1
36958  * Copyright(c) 2006-2007, Ext JS, LLC.
36959  *
36960  * Originally Released Under LGPL - original licence link has changed is not relivant.
36961  *
36962  * Fork - LGPL
36963  * <script type="text/javascript">
36964  */
36965  
36966
36967 /**
36968  * @class Roo.form.ComboBox
36969  * @extends Roo.form.TriggerField
36970  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36971  * @constructor
36972  * Create a new ComboBox.
36973  * @param {Object} config Configuration options
36974  */
36975 Roo.form.ComboBox = function(config){
36976     Roo.form.ComboBox.superclass.constructor.call(this, config);
36977     this.addEvents({
36978         /**
36979          * @event expand
36980          * Fires when the dropdown list is expanded
36981              * @param {Roo.form.ComboBox} combo This combo box
36982              */
36983         'expand' : true,
36984         /**
36985          * @event collapse
36986          * Fires when the dropdown list is collapsed
36987              * @param {Roo.form.ComboBox} combo This combo box
36988              */
36989         'collapse' : true,
36990         /**
36991          * @event beforeselect
36992          * Fires before a list item is selected. Return false to cancel the selection.
36993              * @param {Roo.form.ComboBox} combo This combo box
36994              * @param {Roo.data.Record} record The data record returned from the underlying store
36995              * @param {Number} index The index of the selected item in the dropdown list
36996              */
36997         'beforeselect' : true,
36998         /**
36999          * @event select
37000          * Fires when a list item is selected
37001              * @param {Roo.form.ComboBox} combo This combo box
37002              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37003              * @param {Number} index The index of the selected item in the dropdown list
37004              */
37005         'select' : true,
37006         /**
37007          * @event beforequery
37008          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37009          * The event object passed has these properties:
37010              * @param {Roo.form.ComboBox} combo This combo box
37011              * @param {String} query The query
37012              * @param {Boolean} forceAll true to force "all" query
37013              * @param {Boolean} cancel true to cancel the query
37014              * @param {Object} e The query event object
37015              */
37016         'beforequery': true,
37017          /**
37018          * @event add
37019          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37020              * @param {Roo.form.ComboBox} combo This combo box
37021              */
37022         'add' : true,
37023         /**
37024          * @event edit
37025          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37026              * @param {Roo.form.ComboBox} combo This combo box
37027              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37028              */
37029         'edit' : true
37030         
37031         
37032     });
37033     if(this.transform){
37034         this.allowDomMove = false;
37035         var s = Roo.getDom(this.transform);
37036         if(!this.hiddenName){
37037             this.hiddenName = s.name;
37038         }
37039         if(!this.store){
37040             this.mode = 'local';
37041             var d = [], opts = s.options;
37042             for(var i = 0, len = opts.length;i < len; i++){
37043                 var o = opts[i];
37044                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37045                 if(o.selected) {
37046                     this.value = value;
37047                 }
37048                 d.push([value, o.text]);
37049             }
37050             this.store = new Roo.data.SimpleStore({
37051                 'id': 0,
37052                 fields: ['value', 'text'],
37053                 data : d
37054             });
37055             this.valueField = 'value';
37056             this.displayField = 'text';
37057         }
37058         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37059         if(!this.lazyRender){
37060             this.target = true;
37061             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37062             s.parentNode.removeChild(s); // remove it
37063             this.render(this.el.parentNode);
37064         }else{
37065             s.parentNode.removeChild(s); // remove it
37066         }
37067
37068     }
37069     if (this.store) {
37070         this.store = Roo.factory(this.store, Roo.data);
37071     }
37072     
37073     this.selectedIndex = -1;
37074     if(this.mode == 'local'){
37075         if(config.queryDelay === undefined){
37076             this.queryDelay = 10;
37077         }
37078         if(config.minChars === undefined){
37079             this.minChars = 0;
37080         }
37081     }
37082 };
37083
37084 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37085     /**
37086      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37087      */
37088     /**
37089      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37090      * rendering into an Roo.Editor, defaults to false)
37091      */
37092     /**
37093      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37094      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37095      */
37096     /**
37097      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37098      */
37099     /**
37100      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37101      * the dropdown list (defaults to undefined, with no header element)
37102      */
37103
37104      /**
37105      * @cfg {String/Roo.Template} tpl The template to use to render the output
37106      */
37107      
37108     // private
37109     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37110     /**
37111      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37112      */
37113     listWidth: undefined,
37114     /**
37115      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37116      * mode = 'remote' or 'text' if mode = 'local')
37117      */
37118     displayField: undefined,
37119     /**
37120      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37121      * mode = 'remote' or 'value' if mode = 'local'). 
37122      * Note: use of a valueField requires the user make a selection
37123      * in order for a value to be mapped.
37124      */
37125     valueField: undefined,
37126     /**
37127      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37128      * field's data value (defaults to the underlying DOM element's name)
37129      */
37130     hiddenName: undefined,
37131     /**
37132      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37133      */
37134     listClass: '',
37135     /**
37136      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37137      */
37138     selectedClass: 'x-combo-selected',
37139     /**
37140      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37141      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37142      * which displays a downward arrow icon).
37143      */
37144     triggerClass : 'x-form-arrow-trigger',
37145     /**
37146      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37147      */
37148     shadow:'sides',
37149     /**
37150      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37151      * anchor positions (defaults to 'tl-bl')
37152      */
37153     listAlign: 'tl-bl?',
37154     /**
37155      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37156      */
37157     maxHeight: 300,
37158     /**
37159      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37160      * query specified by the allQuery config option (defaults to 'query')
37161      */
37162     triggerAction: 'query',
37163     /**
37164      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37165      * (defaults to 4, does not apply if editable = false)
37166      */
37167     minChars : 4,
37168     /**
37169      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37170      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37171      */
37172     typeAhead: false,
37173     /**
37174      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37175      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37176      */
37177     queryDelay: 500,
37178     /**
37179      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37180      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37181      */
37182     pageSize: 0,
37183     /**
37184      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37185      * when editable = true (defaults to false)
37186      */
37187     selectOnFocus:false,
37188     /**
37189      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37190      */
37191     queryParam: 'query',
37192     /**
37193      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37194      * when mode = 'remote' (defaults to 'Loading...')
37195      */
37196     loadingText: 'Loading...',
37197     /**
37198      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37199      */
37200     resizable: false,
37201     /**
37202      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37203      */
37204     handleHeight : 8,
37205     /**
37206      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37207      * traditional select (defaults to true)
37208      */
37209     editable: true,
37210     /**
37211      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37212      */
37213     allQuery: '',
37214     /**
37215      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37216      */
37217     mode: 'remote',
37218     /**
37219      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37220      * listWidth has a higher value)
37221      */
37222     minListWidth : 70,
37223     /**
37224      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37225      * allow the user to set arbitrary text into the field (defaults to false)
37226      */
37227     forceSelection:false,
37228     /**
37229      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37230      * if typeAhead = true (defaults to 250)
37231      */
37232     typeAheadDelay : 250,
37233     /**
37234      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37235      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37236      */
37237     valueNotFoundText : undefined,
37238     /**
37239      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37240      */
37241     blockFocus : false,
37242     
37243     /**
37244      * @cfg {Boolean} disableClear Disable showing of clear button.
37245      */
37246     disableClear : false,
37247     /**
37248      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37249      */
37250     alwaysQuery : false,
37251     
37252     //private
37253     addicon : false,
37254     editicon: false,
37255     
37256     
37257     // private
37258     onRender : function(ct, position){
37259         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37260         if(this.hiddenName){
37261             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37262                     'before', true);
37263             this.hiddenField.value =
37264                 this.hiddenValue !== undefined ? this.hiddenValue :
37265                 this.value !== undefined ? this.value : '';
37266
37267             // prevent input submission
37268             this.el.dom.removeAttribute('name');
37269         }
37270         if(Roo.isGecko){
37271             this.el.dom.setAttribute('autocomplete', 'off');
37272         }
37273
37274         var cls = 'x-combo-list';
37275
37276         this.list = new Roo.Layer({
37277             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37278         });
37279
37280         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37281         this.list.setWidth(lw);
37282         this.list.swallowEvent('mousewheel');
37283         this.assetHeight = 0;
37284
37285         if(this.title){
37286             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37287             this.assetHeight += this.header.getHeight();
37288         }
37289
37290         this.innerList = this.list.createChild({cls:cls+'-inner'});
37291         this.innerList.on('mouseover', this.onViewOver, this);
37292         this.innerList.on('mousemove', this.onViewMove, this);
37293         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37294         
37295         if(this.allowBlank && !this.pageSize && !this.disableClear){
37296             this.footer = this.list.createChild({cls:cls+'-ft'});
37297             this.pageTb = new Roo.Toolbar(this.footer);
37298            
37299         }
37300         if(this.pageSize){
37301             this.footer = this.list.createChild({cls:cls+'-ft'});
37302             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37303                     {pageSize: this.pageSize});
37304             
37305         }
37306         
37307         if (this.pageTb && this.allowBlank && !this.disableClear) {
37308             var _this = this;
37309             this.pageTb.add(new Roo.Toolbar.Fill(), {
37310                 cls: 'x-btn-icon x-btn-clear',
37311                 text: '&#160;',
37312                 handler: function()
37313                 {
37314                     _this.collapse();
37315                     _this.clearValue();
37316                     _this.onSelect(false, -1);
37317                 }
37318             });
37319         }
37320         if (this.footer) {
37321             this.assetHeight += this.footer.getHeight();
37322         }
37323         
37324
37325         if(!this.tpl){
37326             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37327         }
37328
37329         this.view = new Roo.View(this.innerList, this.tpl, {
37330             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37331         });
37332
37333         this.view.on('click', this.onViewClick, this);
37334
37335         this.store.on('beforeload', this.onBeforeLoad, this);
37336         this.store.on('load', this.onLoad, this);
37337         this.store.on('loadexception', this.collapse, this);
37338
37339         if(this.resizable){
37340             this.resizer = new Roo.Resizable(this.list,  {
37341                pinned:true, handles:'se'
37342             });
37343             this.resizer.on('resize', function(r, w, h){
37344                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37345                 this.listWidth = w;
37346                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37347                 this.restrictHeight();
37348             }, this);
37349             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37350         }
37351         if(!this.editable){
37352             this.editable = true;
37353             this.setEditable(false);
37354         }  
37355         
37356         
37357         if (typeof(this.events.add.listeners) != 'undefined') {
37358             
37359             this.addicon = this.wrap.createChild(
37360                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37361        
37362             this.addicon.on('click', function(e) {
37363                 this.fireEvent('add', this);
37364             }, this);
37365         }
37366         if (typeof(this.events.edit.listeners) != 'undefined') {
37367             
37368             this.editicon = this.wrap.createChild(
37369                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37370             if (this.addicon) {
37371                 this.editicon.setStyle('margin-left', '40px');
37372             }
37373             this.editicon.on('click', function(e) {
37374                 
37375                 // we fire even  if inothing is selected..
37376                 this.fireEvent('edit', this, this.lastData );
37377                 
37378             }, this);
37379         }
37380         
37381         
37382         
37383     },
37384
37385     // private
37386     initEvents : function(){
37387         Roo.form.ComboBox.superclass.initEvents.call(this);
37388
37389         this.keyNav = new Roo.KeyNav(this.el, {
37390             "up" : function(e){
37391                 this.inKeyMode = true;
37392                 this.selectPrev();
37393             },
37394
37395             "down" : function(e){
37396                 if(!this.isExpanded()){
37397                     this.onTriggerClick();
37398                 }else{
37399                     this.inKeyMode = true;
37400                     this.selectNext();
37401                 }
37402             },
37403
37404             "enter" : function(e){
37405                 this.onViewClick();
37406                 //return true;
37407             },
37408
37409             "esc" : function(e){
37410                 this.collapse();
37411             },
37412
37413             "tab" : function(e){
37414                 this.onViewClick(false);
37415                 return true;
37416             },
37417
37418             scope : this,
37419
37420             doRelay : function(foo, bar, hname){
37421                 if(hname == 'down' || this.scope.isExpanded()){
37422                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37423                 }
37424                 return true;
37425             },
37426
37427             forceKeyDown: true
37428         });
37429         this.queryDelay = Math.max(this.queryDelay || 10,
37430                 this.mode == 'local' ? 10 : 250);
37431         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37432         if(this.typeAhead){
37433             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37434         }
37435         if(this.editable !== false){
37436             this.el.on("keyup", this.onKeyUp, this);
37437         }
37438         if(this.forceSelection){
37439             this.on('blur', this.doForce, this);
37440         }
37441     },
37442
37443     onDestroy : function(){
37444         if(this.view){
37445             this.view.setStore(null);
37446             this.view.el.removeAllListeners();
37447             this.view.el.remove();
37448             this.view.purgeListeners();
37449         }
37450         if(this.list){
37451             this.list.destroy();
37452         }
37453         if(this.store){
37454             this.store.un('beforeload', this.onBeforeLoad, this);
37455             this.store.un('load', this.onLoad, this);
37456             this.store.un('loadexception', this.collapse, this);
37457         }
37458         Roo.form.ComboBox.superclass.onDestroy.call(this);
37459     },
37460
37461     // private
37462     fireKey : function(e){
37463         if(e.isNavKeyPress() && !this.list.isVisible()){
37464             this.fireEvent("specialkey", this, e);
37465         }
37466     },
37467
37468     // private
37469     onResize: function(w, h){
37470         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37471         
37472         if(typeof w != 'number'){
37473             // we do not handle it!?!?
37474             return;
37475         }
37476         var tw = this.trigger.getWidth();
37477         tw += this.addicon ? this.addicon.getWidth() : 0;
37478         tw += this.editicon ? this.editicon.getWidth() : 0;
37479         var x = w - tw;
37480         this.el.setWidth( this.adjustWidth('input', x));
37481             
37482         this.trigger.setStyle('left', x+'px');
37483         
37484         if(this.list && this.listWidth === undefined){
37485             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37486             this.list.setWidth(lw);
37487             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37488         }
37489         
37490     
37491         
37492     },
37493
37494     /**
37495      * Allow or prevent the user from directly editing the field text.  If false is passed,
37496      * the user will only be able to select from the items defined in the dropdown list.  This method
37497      * is the runtime equivalent of setting the 'editable' config option at config time.
37498      * @param {Boolean} value True to allow the user to directly edit the field text
37499      */
37500     setEditable : function(value){
37501         if(value == this.editable){
37502             return;
37503         }
37504         this.editable = value;
37505         if(!value){
37506             this.el.dom.setAttribute('readOnly', true);
37507             this.el.on('mousedown', this.onTriggerClick,  this);
37508             this.el.addClass('x-combo-noedit');
37509         }else{
37510             this.el.dom.setAttribute('readOnly', false);
37511             this.el.un('mousedown', this.onTriggerClick,  this);
37512             this.el.removeClass('x-combo-noedit');
37513         }
37514     },
37515
37516     // private
37517     onBeforeLoad : function(){
37518         if(!this.hasFocus){
37519             return;
37520         }
37521         this.innerList.update(this.loadingText ?
37522                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37523         this.restrictHeight();
37524         this.selectedIndex = -1;
37525     },
37526
37527     // private
37528     onLoad : function(){
37529         if(!this.hasFocus){
37530             return;
37531         }
37532         if(this.store.getCount() > 0){
37533             this.expand();
37534             this.restrictHeight();
37535             if(this.lastQuery == this.allQuery){
37536                 if(this.editable){
37537                     this.el.dom.select();
37538                 }
37539                 if(!this.selectByValue(this.value, true)){
37540                     this.select(0, true);
37541                 }
37542             }else{
37543                 this.selectNext();
37544                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37545                     this.taTask.delay(this.typeAheadDelay);
37546                 }
37547             }
37548         }else{
37549             this.onEmptyResults();
37550         }
37551         //this.el.focus();
37552     },
37553
37554     // private
37555     onTypeAhead : function(){
37556         if(this.store.getCount() > 0){
37557             var r = this.store.getAt(0);
37558             var newValue = r.data[this.displayField];
37559             var len = newValue.length;
37560             var selStart = this.getRawValue().length;
37561             if(selStart != len){
37562                 this.setRawValue(newValue);
37563                 this.selectText(selStart, newValue.length);
37564             }
37565         }
37566     },
37567
37568     // private
37569     onSelect : function(record, index){
37570         if(this.fireEvent('beforeselect', this, record, index) !== false){
37571             this.setFromData(index > -1 ? record.data : false);
37572             this.collapse();
37573             this.fireEvent('select', this, record, index);
37574         }
37575     },
37576
37577     /**
37578      * Returns the currently selected field value or empty string if no value is set.
37579      * @return {String} value The selected value
37580      */
37581     getValue : function(){
37582         if(this.valueField){
37583             return typeof this.value != 'undefined' ? this.value : '';
37584         }else{
37585             return Roo.form.ComboBox.superclass.getValue.call(this);
37586         }
37587     },
37588
37589     /**
37590      * Clears any text/value currently set in the field
37591      */
37592     clearValue : function(){
37593         if(this.hiddenField){
37594             this.hiddenField.value = '';
37595         }
37596         this.value = '';
37597         this.setRawValue('');
37598         this.lastSelectionText = '';
37599         this.applyEmptyText();
37600     },
37601
37602     /**
37603      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37604      * will be displayed in the field.  If the value does not match the data value of an existing item,
37605      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37606      * Otherwise the field will be blank (although the value will still be set).
37607      * @param {String} value The value to match
37608      */
37609     setValue : function(v){
37610         var text = v;
37611         if(this.valueField){
37612             var r = this.findRecord(this.valueField, v);
37613             if(r){
37614                 text = r.data[this.displayField];
37615             }else if(this.valueNotFoundText !== undefined){
37616                 text = this.valueNotFoundText;
37617             }
37618         }
37619         this.lastSelectionText = text;
37620         if(this.hiddenField){
37621             this.hiddenField.value = v;
37622         }
37623         Roo.form.ComboBox.superclass.setValue.call(this, text);
37624         this.value = v;
37625     },
37626     /**
37627      * @property {Object} the last set data for the element
37628      */
37629     
37630     lastData : false,
37631     /**
37632      * Sets the value of the field based on a object which is related to the record format for the store.
37633      * @param {Object} value the value to set as. or false on reset?
37634      */
37635     setFromData : function(o){
37636         var dv = ''; // display value
37637         var vv = ''; // value value..
37638         this.lastData = o;
37639         if (this.displayField) {
37640             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37641         } else {
37642             // this is an error condition!!!
37643             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37644         }
37645         
37646         if(this.valueField){
37647             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37648         }
37649         if(this.hiddenField){
37650             this.hiddenField.value = vv;
37651             
37652             this.lastSelectionText = dv;
37653             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37654             this.value = vv;
37655             return;
37656         }
37657         // no hidden field.. - we store the value in 'value', but still display
37658         // display field!!!!
37659         this.lastSelectionText = dv;
37660         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37661         this.value = vv;
37662         
37663         
37664     },
37665     // private
37666     reset : function(){
37667         // overridden so that last data is reset..
37668         this.setValue(this.originalValue);
37669         this.clearInvalid();
37670         this.lastData = false;
37671     },
37672     // private
37673     findRecord : function(prop, value){
37674         var record;
37675         if(this.store.getCount() > 0){
37676             this.store.each(function(r){
37677                 if(r.data[prop] == value){
37678                     record = r;
37679                     return false;
37680                 }
37681             });
37682         }
37683         return record;
37684     },
37685
37686     // private
37687     onViewMove : function(e, t){
37688         this.inKeyMode = false;
37689     },
37690
37691     // private
37692     onViewOver : function(e, t){
37693         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37694             return;
37695         }
37696         var item = this.view.findItemFromChild(t);
37697         if(item){
37698             var index = this.view.indexOf(item);
37699             this.select(index, false);
37700         }
37701     },
37702
37703     // private
37704     onViewClick : function(doFocus){
37705         var index = this.view.getSelectedIndexes()[0];
37706         var r = this.store.getAt(index);
37707         if(r){
37708             this.onSelect(r, index);
37709         }
37710         if(doFocus !== false && !this.blockFocus){
37711             this.el.focus();
37712         }
37713     },
37714
37715     // private
37716     restrictHeight : function(){
37717         this.innerList.dom.style.height = '';
37718         var inner = this.innerList.dom;
37719         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37720         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37721         this.list.beginUpdate();
37722         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37723         this.list.alignTo(this.el, this.listAlign);
37724         this.list.endUpdate();
37725     },
37726
37727     // private
37728     onEmptyResults : function(){
37729         this.collapse();
37730     },
37731
37732     /**
37733      * Returns true if the dropdown list is expanded, else false.
37734      */
37735     isExpanded : function(){
37736         return this.list.isVisible();
37737     },
37738
37739     /**
37740      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37741      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37742      * @param {String} value The data value of the item to select
37743      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37744      * selected item if it is not currently in view (defaults to true)
37745      * @return {Boolean} True if the value matched an item in the list, else false
37746      */
37747     selectByValue : function(v, scrollIntoView){
37748         if(v !== undefined && v !== null){
37749             var r = this.findRecord(this.valueField || this.displayField, v);
37750             if(r){
37751                 this.select(this.store.indexOf(r), scrollIntoView);
37752                 return true;
37753             }
37754         }
37755         return false;
37756     },
37757
37758     /**
37759      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37760      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37761      * @param {Number} index The zero-based index of the list item to select
37762      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37763      * selected item if it is not currently in view (defaults to true)
37764      */
37765     select : function(index, scrollIntoView){
37766         this.selectedIndex = index;
37767         this.view.select(index);
37768         if(scrollIntoView !== false){
37769             var el = this.view.getNode(index);
37770             if(el){
37771                 this.innerList.scrollChildIntoView(el, false);
37772             }
37773         }
37774     },
37775
37776     // private
37777     selectNext : function(){
37778         var ct = this.store.getCount();
37779         if(ct > 0){
37780             if(this.selectedIndex == -1){
37781                 this.select(0);
37782             }else if(this.selectedIndex < ct-1){
37783                 this.select(this.selectedIndex+1);
37784             }
37785         }
37786     },
37787
37788     // private
37789     selectPrev : function(){
37790         var ct = this.store.getCount();
37791         if(ct > 0){
37792             if(this.selectedIndex == -1){
37793                 this.select(0);
37794             }else if(this.selectedIndex != 0){
37795                 this.select(this.selectedIndex-1);
37796             }
37797         }
37798     },
37799
37800     // private
37801     onKeyUp : function(e){
37802         if(this.editable !== false && !e.isSpecialKey()){
37803             this.lastKey = e.getKey();
37804             this.dqTask.delay(this.queryDelay);
37805         }
37806     },
37807
37808     // private
37809     validateBlur : function(){
37810         return !this.list || !this.list.isVisible();   
37811     },
37812
37813     // private
37814     initQuery : function(){
37815         this.doQuery(this.getRawValue());
37816     },
37817
37818     // private
37819     doForce : function(){
37820         if(this.el.dom.value.length > 0){
37821             this.el.dom.value =
37822                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37823             this.applyEmptyText();
37824         }
37825     },
37826
37827     /**
37828      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37829      * query allowing the query action to be canceled if needed.
37830      * @param {String} query The SQL query to execute
37831      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37832      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37833      * saved in the current store (defaults to false)
37834      */
37835     doQuery : function(q, forceAll){
37836         if(q === undefined || q === null){
37837             q = '';
37838         }
37839         var qe = {
37840             query: q,
37841             forceAll: forceAll,
37842             combo: this,
37843             cancel:false
37844         };
37845         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37846             return false;
37847         }
37848         q = qe.query;
37849         forceAll = qe.forceAll;
37850         if(forceAll === true || (q.length >= this.minChars)){
37851             if(this.lastQuery != q || this.alwaysQuery){
37852                 this.lastQuery = q;
37853                 if(this.mode == 'local'){
37854                     this.selectedIndex = -1;
37855                     if(forceAll){
37856                         this.store.clearFilter();
37857                     }else{
37858                         this.store.filter(this.displayField, q);
37859                     }
37860                     this.onLoad();
37861                 }else{
37862                     this.store.baseParams[this.queryParam] = q;
37863                     this.store.load({
37864                         params: this.getParams(q)
37865                     });
37866                     this.expand();
37867                 }
37868             }else{
37869                 this.selectedIndex = -1;
37870                 this.onLoad();   
37871             }
37872         }
37873     },
37874
37875     // private
37876     getParams : function(q){
37877         var p = {};
37878         //p[this.queryParam] = q;
37879         if(this.pageSize){
37880             p.start = 0;
37881             p.limit = this.pageSize;
37882         }
37883         return p;
37884     },
37885
37886     /**
37887      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37888      */
37889     collapse : function(){
37890         if(!this.isExpanded()){
37891             return;
37892         }
37893         this.list.hide();
37894         Roo.get(document).un('mousedown', this.collapseIf, this);
37895         Roo.get(document).un('mousewheel', this.collapseIf, this);
37896         if (!this.editable) {
37897             Roo.get(document).un('keydown', this.listKeyPress, this);
37898         }
37899         this.fireEvent('collapse', this);
37900     },
37901
37902     // private
37903     collapseIf : function(e){
37904         if(!e.within(this.wrap) && !e.within(this.list)){
37905             this.collapse();
37906         }
37907     },
37908
37909     /**
37910      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37911      */
37912     expand : function(){
37913         if(this.isExpanded() || !this.hasFocus){
37914             return;
37915         }
37916         this.list.alignTo(this.el, this.listAlign);
37917         this.list.show();
37918         Roo.get(document).on('mousedown', this.collapseIf, this);
37919         Roo.get(document).on('mousewheel', this.collapseIf, this);
37920         if (!this.editable) {
37921             Roo.get(document).on('keydown', this.listKeyPress, this);
37922         }
37923         
37924         this.fireEvent('expand', this);
37925     },
37926
37927     // private
37928     // Implements the default empty TriggerField.onTriggerClick function
37929     onTriggerClick : function(){
37930         if(this.disabled){
37931             return;
37932         }
37933         if(this.isExpanded()){
37934             this.collapse();
37935             if (!this.blockFocus) {
37936                 this.el.focus();
37937             }
37938             
37939         }else {
37940             this.hasFocus = true;
37941             if(this.triggerAction == 'all') {
37942                 this.doQuery(this.allQuery, true);
37943             } else {
37944                 this.doQuery(this.getRawValue());
37945             }
37946             if (!this.blockFocus) {
37947                 this.el.focus();
37948             }
37949         }
37950     },
37951     listKeyPress : function(e)
37952     {
37953         //Roo.log('listkeypress');
37954         // scroll to first matching element based on key pres..
37955         if (e.isSpecialKey()) {
37956             return false;
37957         }
37958         var k = String.fromCharCode(e.getKey()).toUpperCase();
37959         //Roo.log(k);
37960         var match  = false;
37961         var csel = this.view.getSelectedNodes();
37962         var cselitem = false;
37963         if (csel.length) {
37964             var ix = this.view.indexOf(csel[0]);
37965             cselitem  = this.store.getAt(ix);
37966             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37967                 cselitem = false;
37968             }
37969             
37970         }
37971         
37972         this.store.each(function(v) { 
37973             if (cselitem) {
37974                 // start at existing selection.
37975                 if (cselitem.id == v.id) {
37976                     cselitem = false;
37977                 }
37978                 return;
37979             }
37980                 
37981             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37982                 match = this.store.indexOf(v);
37983                 return false;
37984             }
37985         }, this);
37986         
37987         if (match === false) {
37988             return true; // no more action?
37989         }
37990         // scroll to?
37991         this.view.select(match);
37992         var sn = Roo.get(this.view.getSelectedNodes()[0])
37993         sn.scrollIntoView(sn.dom.parentNode, false);
37994     }
37995
37996     /** 
37997     * @cfg {Boolean} grow 
37998     * @hide 
37999     */
38000     /** 
38001     * @cfg {Number} growMin 
38002     * @hide 
38003     */
38004     /** 
38005     * @cfg {Number} growMax 
38006     * @hide 
38007     */
38008     /**
38009      * @hide
38010      * @method autoSize
38011      */
38012 });/*
38013  * Based on:
38014  * Ext JS Library 1.1.1
38015  * Copyright(c) 2006-2007, Ext JS, LLC.
38016  *
38017  * Originally Released Under LGPL - original licence link has changed is not relivant.
38018  *
38019  * Fork - LGPL
38020  * <script type="text/javascript">
38021  */
38022 /**
38023  * @class Roo.form.Checkbox
38024  * @extends Roo.form.Field
38025  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38026  * @constructor
38027  * Creates a new Checkbox
38028  * @param {Object} config Configuration options
38029  */
38030 Roo.form.Checkbox = function(config){
38031     Roo.form.Checkbox.superclass.constructor.call(this, config);
38032     this.addEvents({
38033         /**
38034          * @event check
38035          * Fires when the checkbox is checked or unchecked.
38036              * @param {Roo.form.Checkbox} this This checkbox
38037              * @param {Boolean} checked The new checked value
38038              */
38039         check : true
38040     });
38041 };
38042
38043 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38044     /**
38045      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38046      */
38047     focusClass : undefined,
38048     /**
38049      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38050      */
38051     fieldClass: "x-form-field",
38052     /**
38053      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38054      */
38055     checked: false,
38056     /**
38057      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38058      * {tag: "input", type: "checkbox", autocomplete: "off"})
38059      */
38060     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38061     /**
38062      * @cfg {String} boxLabel The text that appears beside the checkbox
38063      */
38064     boxLabel : "",
38065     /**
38066      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38067      */  
38068     inputValue : '1',
38069     /**
38070      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38071      */
38072      valueOff: '0', // value when not checked..
38073
38074     actionMode : 'viewEl', 
38075     //
38076     // private
38077     itemCls : 'x-menu-check-item x-form-item',
38078     groupClass : 'x-menu-group-item',
38079     inputType : 'hidden',
38080     
38081     
38082     inSetChecked: false, // check that we are not calling self...
38083     
38084     inputElement: false, // real input element?
38085     basedOn: false, // ????
38086     
38087     isFormField: true, // not sure where this is needed!!!!
38088
38089     onResize : function(){
38090         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38091         if(!this.boxLabel){
38092             this.el.alignTo(this.wrap, 'c-c');
38093         }
38094     },
38095
38096     initEvents : function(){
38097         Roo.form.Checkbox.superclass.initEvents.call(this);
38098         this.el.on("click", this.onClick,  this);
38099         this.el.on("change", this.onClick,  this);
38100     },
38101
38102
38103     getResizeEl : function(){
38104         return this.wrap;
38105     },
38106
38107     getPositionEl : function(){
38108         return this.wrap;
38109     },
38110
38111     // private
38112     onRender : function(ct, position){
38113         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38114         /*
38115         if(this.inputValue !== undefined){
38116             this.el.dom.value = this.inputValue;
38117         }
38118         */
38119         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38120         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38121         var viewEl = this.wrap.createChild({ 
38122             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38123         this.viewEl = viewEl;   
38124         this.wrap.on('click', this.onClick,  this); 
38125         
38126         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38127         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38128         
38129         
38130         
38131         if(this.boxLabel){
38132             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38133         //    viewEl.on('click', this.onClick,  this); 
38134         }
38135         //if(this.checked){
38136             this.setChecked(this.checked);
38137         //}else{
38138             //this.checked = this.el.dom;
38139         //}
38140
38141     },
38142
38143     // private
38144     initValue : Roo.emptyFn,
38145
38146     /**
38147      * Returns the checked state of the checkbox.
38148      * @return {Boolean} True if checked, else false
38149      */
38150     getValue : function(){
38151         if(this.el){
38152             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38153         }
38154         return this.valueOff;
38155         
38156     },
38157
38158         // private
38159     onClick : function(){ 
38160         this.setChecked(!this.checked);
38161
38162         //if(this.el.dom.checked != this.checked){
38163         //    this.setValue(this.el.dom.checked);
38164        // }
38165     },
38166
38167     /**
38168      * Sets the checked state of the checkbox.
38169      * On is always based on a string comparison between inputValue and the param.
38170      * @param {Boolean/String} value - the value to set 
38171      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38172      */
38173     setValue : function(v,suppressEvent){
38174         
38175         
38176         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38177         //if(this.el && this.el.dom){
38178         //    this.el.dom.checked = this.checked;
38179         //    this.el.dom.defaultChecked = this.checked;
38180         //}
38181         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38182         //this.fireEvent("check", this, this.checked);
38183     },
38184     // private..
38185     setChecked : function(state,suppressEvent)
38186     {
38187         if (this.inSetChecked) {
38188             this.checked = state;
38189             return;
38190         }
38191         
38192     
38193         if(this.wrap){
38194             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38195         }
38196         this.checked = state;
38197         if(suppressEvent !== true){
38198             this.fireEvent('check', this, state);
38199         }
38200         this.inSetChecked = true;
38201         this.el.dom.value = state ? this.inputValue : this.valueOff;
38202         this.inSetChecked = false;
38203         
38204     },
38205     // handle setting of hidden value by some other method!!?!?
38206     setFromHidden: function()
38207     {
38208         if(!this.el){
38209             return;
38210         }
38211         //console.log("SET FROM HIDDEN");
38212         //alert('setFrom hidden');
38213         this.setValue(this.el.dom.value);
38214     },
38215     
38216     onDestroy : function()
38217     {
38218         if(this.viewEl){
38219             Roo.get(this.viewEl).remove();
38220         }
38221          
38222         Roo.form.Checkbox.superclass.onDestroy.call(this);
38223     }
38224
38225 });/*
38226  * Based on:
38227  * Ext JS Library 1.1.1
38228  * Copyright(c) 2006-2007, Ext JS, LLC.
38229  *
38230  * Originally Released Under LGPL - original licence link has changed is not relivant.
38231  *
38232  * Fork - LGPL
38233  * <script type="text/javascript">
38234  */
38235  
38236 /**
38237  * @class Roo.form.Radio
38238  * @extends Roo.form.Checkbox
38239  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38240  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38241  * @constructor
38242  * Creates a new Radio
38243  * @param {Object} config Configuration options
38244  */
38245 Roo.form.Radio = function(){
38246     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38247 };
38248 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38249     inputType: 'radio',
38250
38251     /**
38252      * If this radio is part of a group, it will return the selected value
38253      * @return {String}
38254      */
38255     getGroupValue : function(){
38256         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38257     }
38258 });//<script type="text/javascript">
38259
38260 /*
38261  * Ext JS Library 1.1.1
38262  * Copyright(c) 2006-2007, Ext JS, LLC.
38263  * licensing@extjs.com
38264  * 
38265  * http://www.extjs.com/license
38266  */
38267  
38268  /*
38269   * 
38270   * Known bugs:
38271   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38272   * - IE ? - no idea how much works there.
38273   * 
38274   * 
38275   * 
38276   */
38277  
38278
38279 /**
38280  * @class Ext.form.HtmlEditor
38281  * @extends Ext.form.Field
38282  * Provides a lightweight HTML Editor component.
38283  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38284  * 
38285  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38286  * supported by this editor.</b><br/><br/>
38287  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38288  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38289  */
38290 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38291       /**
38292      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38293      */
38294     toolbars : false,
38295     /**
38296      * @cfg {String} createLinkText The default text for the create link prompt
38297      */
38298     createLinkText : 'Please enter the URL for the link:',
38299     /**
38300      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38301      */
38302     defaultLinkValue : 'http:/'+'/',
38303    
38304     
38305     // id of frame..
38306     frameId: false,
38307     
38308     // private properties
38309     validationEvent : false,
38310     deferHeight: true,
38311     initialized : false,
38312     activated : false,
38313     sourceEditMode : false,
38314     onFocus : Roo.emptyFn,
38315     iframePad:3,
38316     hideMode:'offsets',
38317     defaultAutoCreate : {
38318         tag: "textarea",
38319         style:"width:500px;height:300px;",
38320         autocomplete: "off"
38321     },
38322
38323     // private
38324     initComponent : function(){
38325         this.addEvents({
38326             /**
38327              * @event initialize
38328              * Fires when the editor is fully initialized (including the iframe)
38329              * @param {HtmlEditor} this
38330              */
38331             initialize: true,
38332             /**
38333              * @event activate
38334              * Fires when the editor is first receives the focus. Any insertion must wait
38335              * until after this event.
38336              * @param {HtmlEditor} this
38337              */
38338             activate: true,
38339              /**
38340              * @event beforesync
38341              * Fires before the textarea is updated with content from the editor iframe. Return false
38342              * to cancel the sync.
38343              * @param {HtmlEditor} this
38344              * @param {String} html
38345              */
38346             beforesync: true,
38347              /**
38348              * @event beforepush
38349              * Fires before the iframe editor is updated with content from the textarea. Return false
38350              * to cancel the push.
38351              * @param {HtmlEditor} this
38352              * @param {String} html
38353              */
38354             beforepush: true,
38355              /**
38356              * @event sync
38357              * Fires when the textarea is updated with content from the editor iframe.
38358              * @param {HtmlEditor} this
38359              * @param {String} html
38360              */
38361             sync: true,
38362              /**
38363              * @event push
38364              * Fires when the iframe editor is updated with content from the textarea.
38365              * @param {HtmlEditor} this
38366              * @param {String} html
38367              */
38368             push: true,
38369              /**
38370              * @event editmodechange
38371              * Fires when the editor switches edit modes
38372              * @param {HtmlEditor} this
38373              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38374              */
38375             editmodechange: true,
38376             /**
38377              * @event editorevent
38378              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38379              * @param {HtmlEditor} this
38380              */
38381             editorevent: true
38382         })
38383     },
38384
38385     /**
38386      * Protected method that will not generally be called directly. It
38387      * is called when the editor creates its toolbar. Override this method if you need to
38388      * add custom toolbar buttons.
38389      * @param {HtmlEditor} editor
38390      */
38391     createToolbar : function(editor){
38392         if (!editor.toolbars || !editor.toolbars.length) {
38393             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38394         }
38395         
38396         for (var i =0 ; i < editor.toolbars.length;i++) {
38397             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38398             editor.toolbars[i].init(editor);
38399         }
38400          
38401         
38402     },
38403
38404     /**
38405      * Protected method that will not generally be called directly. It
38406      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38407      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38408      */
38409     getDocMarkup : function(){
38410         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38411     },
38412
38413     // private
38414     onRender : function(ct, position){
38415         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38416         this.el.dom.style.border = '0 none';
38417         this.el.dom.setAttribute('tabIndex', -1);
38418         this.el.addClass('x-hidden');
38419         if(Roo.isIE){ // fix IE 1px bogus margin
38420             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38421         }
38422         this.wrap = this.el.wrap({
38423             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38424         });
38425
38426         this.frameId = Roo.id();
38427         this.createToolbar(this);
38428         
38429         
38430         
38431         
38432       
38433         
38434         var iframe = this.wrap.createChild({
38435             tag: 'iframe',
38436             id: this.frameId,
38437             name: this.frameId,
38438             frameBorder : 'no',
38439             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38440         });
38441         
38442        // console.log(iframe);
38443         //this.wrap.dom.appendChild(iframe);
38444
38445         this.iframe = iframe.dom;
38446
38447          this.assignDocWin();
38448         
38449         this.doc.designMode = 'on';
38450        
38451         this.doc.open();
38452         this.doc.write(this.getDocMarkup());
38453         this.doc.close();
38454
38455         
38456         var task = { // must defer to wait for browser to be ready
38457             run : function(){
38458                 //console.log("run task?" + this.doc.readyState);
38459                 this.assignDocWin();
38460                 if(this.doc.body || this.doc.readyState == 'complete'){
38461                     try {
38462                         this.doc.designMode="on";
38463                     } catch (e) {
38464                         return;
38465                     }
38466                     Roo.TaskMgr.stop(task);
38467                     this.initEditor.defer(10, this);
38468                 }
38469             },
38470             interval : 10,
38471             duration:10000,
38472             scope: this
38473         };
38474         Roo.TaskMgr.start(task);
38475
38476         if(!this.width){
38477             this.setSize(this.el.getSize());
38478         }
38479     },
38480
38481     // private
38482     onResize : function(w, h){
38483         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38484         if(this.el && this.iframe){
38485             if(typeof w == 'number'){
38486                 var aw = w - this.wrap.getFrameWidth('lr');
38487                 this.el.setWidth(this.adjustWidth('textarea', aw));
38488                 this.iframe.style.width = aw + 'px';
38489             }
38490             if(typeof h == 'number'){
38491                 var tbh = 0;
38492                 for (var i =0; i < this.toolbars.length;i++) {
38493                     // fixme - ask toolbars for heights?
38494                     tbh += this.toolbars[i].tb.el.getHeight();
38495                 }
38496                 
38497                 
38498                 
38499                 
38500                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38501                 this.el.setHeight(this.adjustWidth('textarea', ah));
38502                 this.iframe.style.height = ah + 'px';
38503                 if(this.doc){
38504                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38505                 }
38506             }
38507         }
38508     },
38509
38510     /**
38511      * Toggles the editor between standard and source edit mode.
38512      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38513      */
38514     toggleSourceEdit : function(sourceEditMode){
38515         
38516         this.sourceEditMode = sourceEditMode === true;
38517         
38518         if(this.sourceEditMode){
38519           
38520             this.syncValue();
38521             this.iframe.className = 'x-hidden';
38522             this.el.removeClass('x-hidden');
38523             this.el.dom.removeAttribute('tabIndex');
38524             this.el.focus();
38525         }else{
38526              
38527             this.pushValue();
38528             this.iframe.className = '';
38529             this.el.addClass('x-hidden');
38530             this.el.dom.setAttribute('tabIndex', -1);
38531             this.deferFocus();
38532         }
38533         this.setSize(this.wrap.getSize());
38534         this.fireEvent('editmodechange', this, this.sourceEditMode);
38535     },
38536
38537     // private used internally
38538     createLink : function(){
38539         var url = prompt(this.createLinkText, this.defaultLinkValue);
38540         if(url && url != 'http:/'+'/'){
38541             this.relayCmd('createlink', url);
38542         }
38543     },
38544
38545     // private (for BoxComponent)
38546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38547
38548     // private (for BoxComponent)
38549     getResizeEl : function(){
38550         return this.wrap;
38551     },
38552
38553     // private (for BoxComponent)
38554     getPositionEl : function(){
38555         return this.wrap;
38556     },
38557
38558     // private
38559     initEvents : function(){
38560         this.originalValue = this.getValue();
38561     },
38562
38563     /**
38564      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38565      * @method
38566      */
38567     markInvalid : Roo.emptyFn,
38568     /**
38569      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38570      * @method
38571      */
38572     clearInvalid : Roo.emptyFn,
38573
38574     setValue : function(v){
38575         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38576         this.pushValue();
38577     },
38578
38579     /**
38580      * Protected method that will not generally be called directly. If you need/want
38581      * custom HTML cleanup, this is the method you should override.
38582      * @param {String} html The HTML to be cleaned
38583      * return {String} The cleaned HTML
38584      */
38585     cleanHtml : function(html){
38586         html = String(html);
38587         if(html.length > 5){
38588             if(Roo.isSafari){ // strip safari nonsense
38589                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38590             }
38591         }
38592         if(html == '&nbsp;'){
38593             html = '';
38594         }
38595         return html;
38596     },
38597
38598     /**
38599      * Protected method that will not generally be called directly. Syncs the contents
38600      * of the editor iframe with the textarea.
38601      */
38602     syncValue : function(){
38603         if(this.initialized){
38604             var bd = (this.doc.body || this.doc.documentElement);
38605             var html = bd.innerHTML;
38606             if(Roo.isSafari){
38607                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38608                 var m = bs.match(/text-align:(.*?);/i);
38609                 if(m && m[1]){
38610                     html = '<div style="'+m[0]+'">' + html + '</div>';
38611                 }
38612             }
38613             html = this.cleanHtml(html);
38614             if(this.fireEvent('beforesync', this, html) !== false){
38615                 this.el.dom.value = html;
38616                 this.fireEvent('sync', this, html);
38617             }
38618         }
38619     },
38620
38621     /**
38622      * Protected method that will not generally be called directly. Pushes the value of the textarea
38623      * into the iframe editor.
38624      */
38625     pushValue : function(){
38626         if(this.initialized){
38627             var v = this.el.dom.value;
38628             if(v.length < 1){
38629                 v = '&#160;';
38630             }
38631             if(this.fireEvent('beforepush', this, v) !== false){
38632                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38633                 this.fireEvent('push', this, v);
38634             }
38635         }
38636     },
38637
38638     // private
38639     deferFocus : function(){
38640         this.focus.defer(10, this);
38641     },
38642
38643     // doc'ed in Field
38644     focus : function(){
38645         if(this.win && !this.sourceEditMode){
38646             this.win.focus();
38647         }else{
38648             this.el.focus();
38649         }
38650     },
38651     
38652     assignDocWin: function()
38653     {
38654         var iframe = this.iframe;
38655         
38656          if(Roo.isIE){
38657             this.doc = iframe.contentWindow.document;
38658             this.win = iframe.contentWindow;
38659         } else {
38660             if (!Roo.get(this.frameId)) {
38661                 return;
38662             }
38663             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38664             this.win = Roo.get(this.frameId).dom.contentWindow;
38665         }
38666     },
38667     
38668     // private
38669     initEditor : function(){
38670         //console.log("INIT EDITOR");
38671         this.assignDocWin();
38672         
38673         
38674         
38675         this.doc.designMode="on";
38676         this.doc.open();
38677         this.doc.write(this.getDocMarkup());
38678         this.doc.close();
38679         
38680         var dbody = (this.doc.body || this.doc.documentElement);
38681         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38682         // this copies styles from the containing element into thsi one..
38683         // not sure why we need all of this..
38684         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38685         ss['background-attachment'] = 'fixed'; // w3c
38686         dbody.bgProperties = 'fixed'; // ie
38687         Roo.DomHelper.applyStyles(dbody, ss);
38688         Roo.EventManager.on(this.doc, {
38689             'mousedown': this.onEditorEvent,
38690             'dblclick': this.onEditorEvent,
38691             'click': this.onEditorEvent,
38692             'keyup': this.onEditorEvent,
38693             buffer:100,
38694             scope: this
38695         });
38696         if(Roo.isGecko){
38697             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38698         }
38699         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38700             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38701         }
38702         this.initialized = true;
38703
38704         this.fireEvent('initialize', this);
38705         this.pushValue();
38706     },
38707
38708     // private
38709     onDestroy : function(){
38710         
38711         
38712         
38713         if(this.rendered){
38714             
38715             for (var i =0; i < this.toolbars.length;i++) {
38716                 // fixme - ask toolbars for heights?
38717                 this.toolbars[i].onDestroy();
38718             }
38719             
38720             this.wrap.dom.innerHTML = '';
38721             this.wrap.remove();
38722         }
38723     },
38724
38725     // private
38726     onFirstFocus : function(){
38727         
38728         this.assignDocWin();
38729         
38730         
38731         this.activated = true;
38732         for (var i =0; i < this.toolbars.length;i++) {
38733             this.toolbars[i].onFirstFocus();
38734         }
38735        
38736         if(Roo.isGecko){ // prevent silly gecko errors
38737             this.win.focus();
38738             var s = this.win.getSelection();
38739             if(!s.focusNode || s.focusNode.nodeType != 3){
38740                 var r = s.getRangeAt(0);
38741                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38742                 r.collapse(true);
38743                 this.deferFocus();
38744             }
38745             try{
38746                 this.execCmd('useCSS', true);
38747                 this.execCmd('styleWithCSS', false);
38748             }catch(e){}
38749         }
38750         this.fireEvent('activate', this);
38751     },
38752
38753     // private
38754     adjustFont: function(btn){
38755         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38756         //if(Roo.isSafari){ // safari
38757         //    adjust *= 2;
38758        // }
38759         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38760         if(Roo.isSafari){ // safari
38761             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38762             v =  (v < 10) ? 10 : v;
38763             v =  (v > 48) ? 48 : v;
38764             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38765             
38766         }
38767         
38768         
38769         v = Math.max(1, v+adjust);
38770         
38771         this.execCmd('FontSize', v  );
38772     },
38773
38774     onEditorEvent : function(e){
38775         this.fireEvent('editorevent', this, e);
38776       //  this.updateToolbar();
38777         this.syncValue();
38778     },
38779
38780     insertTag : function(tg)
38781     {
38782         // could be a bit smarter... -> wrap the current selected tRoo..
38783         
38784         this.execCmd("formatblock",   tg);
38785         
38786     },
38787     
38788     insertText : function(txt)
38789     {
38790         
38791         
38792         range = this.createRange();
38793         range.deleteContents();
38794                //alert(Sender.getAttribute('label'));
38795                
38796         range.insertNode(this.doc.createTextNode(txt));
38797     } ,
38798     
38799     // private
38800     relayBtnCmd : function(btn){
38801         this.relayCmd(btn.cmd);
38802     },
38803
38804     /**
38805      * Executes a Midas editor command on the editor document and performs necessary focus and
38806      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38807      * @param {String} cmd The Midas command
38808      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38809      */
38810     relayCmd : function(cmd, value){
38811         this.win.focus();
38812         this.execCmd(cmd, value);
38813         this.fireEvent('editorevent', this);
38814         //this.updateToolbar();
38815         this.deferFocus();
38816     },
38817
38818     /**
38819      * Executes a Midas editor command directly on the editor document.
38820      * For visual commands, you should use {@link #relayCmd} instead.
38821      * <b>This should only be called after the editor is initialized.</b>
38822      * @param {String} cmd The Midas command
38823      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38824      */
38825     execCmd : function(cmd, value){
38826         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38827         this.syncValue();
38828     },
38829
38830    
38831     /**
38832      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38833      * to insert tRoo.
38834      * @param {String} text
38835      */
38836     insertAtCursor : function(text){
38837         if(!this.activated){
38838             return;
38839         }
38840         if(Roo.isIE){
38841             this.win.focus();
38842             var r = this.doc.selection.createRange();
38843             if(r){
38844                 r.collapse(true);
38845                 r.pasteHTML(text);
38846                 this.syncValue();
38847                 this.deferFocus();
38848             }
38849         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38850             this.win.focus();
38851             this.execCmd('InsertHTML', text);
38852             this.deferFocus();
38853         }
38854     },
38855  // private
38856     mozKeyPress : function(e){
38857         if(e.ctrlKey){
38858             var c = e.getCharCode(), cmd;
38859           
38860             if(c > 0){
38861                 c = String.fromCharCode(c).toLowerCase();
38862                 switch(c){
38863                     case 'b':
38864                         cmd = 'bold';
38865                     break;
38866                     case 'i':
38867                         cmd = 'italic';
38868                     break;
38869                     case 'u':
38870                         cmd = 'underline';
38871                     case 'v':
38872                         this.cleanUpPaste.defer(100, this);
38873                         return;
38874                     break;
38875                 }
38876                 if(cmd){
38877                     this.win.focus();
38878                     this.execCmd(cmd);
38879                     this.deferFocus();
38880                     e.preventDefault();
38881                 }
38882                 
38883             }
38884         }
38885     },
38886
38887     // private
38888     fixKeys : function(){ // load time branching for fastest keydown performance
38889         if(Roo.isIE){
38890             return function(e){
38891                 var k = e.getKey(), r;
38892                 if(k == e.TAB){
38893                     e.stopEvent();
38894                     r = this.doc.selection.createRange();
38895                     if(r){
38896                         r.collapse(true);
38897                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38898                         this.deferFocus();
38899                     }
38900                     return;
38901                 }
38902                 
38903                 if(k == e.ENTER){
38904                     r = this.doc.selection.createRange();
38905                     if(r){
38906                         var target = r.parentElement();
38907                         if(!target || target.tagName.toLowerCase() != 'li'){
38908                             e.stopEvent();
38909                             r.pasteHTML('<br />');
38910                             r.collapse(false);
38911                             r.select();
38912                         }
38913                     }
38914                 }
38915                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38916                     this.cleanUpPaste.defer(100, this);
38917                     return;
38918                 }
38919                 
38920                 
38921             };
38922         }else if(Roo.isOpera){
38923             return function(e){
38924                 var k = e.getKey();
38925                 if(k == e.TAB){
38926                     e.stopEvent();
38927                     this.win.focus();
38928                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38929                     this.deferFocus();
38930                 }
38931                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38932                     this.cleanUpPaste.defer(100, this);
38933                     return;
38934                 }
38935                 
38936             };
38937         }else if(Roo.isSafari){
38938             return function(e){
38939                 var k = e.getKey();
38940                 
38941                 if(k == e.TAB){
38942                     e.stopEvent();
38943                     this.execCmd('InsertText','\t');
38944                     this.deferFocus();
38945                     return;
38946                 }
38947                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38948                     this.cleanUpPaste.defer(100, this);
38949                     return;
38950                 }
38951                 
38952              };
38953         }
38954     }(),
38955     
38956     getAllAncestors: function()
38957     {
38958         var p = this.getSelectedNode();
38959         var a = [];
38960         if (!p) {
38961             a.push(p); // push blank onto stack..
38962             p = this.getParentElement();
38963         }
38964         
38965         
38966         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38967             a.push(p);
38968             p = p.parentNode;
38969         }
38970         a.push(this.doc.body);
38971         return a;
38972     },
38973     lastSel : false,
38974     lastSelNode : false,
38975     
38976     
38977     getSelection : function() 
38978     {
38979         this.assignDocWin();
38980         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38981     },
38982     
38983     getSelectedNode: function() 
38984     {
38985         // this may only work on Gecko!!!
38986         
38987         // should we cache this!!!!
38988         
38989         
38990         
38991          
38992         var range = this.createRange(this.getSelection());
38993         
38994         if (Roo.isIE) {
38995             var parent = range.parentElement();
38996             while (true) {
38997                 var testRange = range.duplicate();
38998                 testRange.moveToElementText(parent);
38999                 if (testRange.inRange(range)) {
39000                     break;
39001                 }
39002                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39003                     break;
39004                 }
39005                 parent = parent.parentElement;
39006             }
39007             return parent;
39008         }
39009         
39010         
39011         var ar = range.endContainer.childNodes;
39012         if (!ar.length) {
39013             ar = range.commonAncestorContainer.childNodes;
39014             //alert(ar.length);
39015         }
39016         var nodes = [];
39017         var other_nodes = [];
39018         var has_other_nodes = false;
39019         for (var i=0;i<ar.length;i++) {
39020             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39021                 continue;
39022             }
39023             // fullly contained node.
39024             
39025             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39026                 nodes.push(ar[i]);
39027                 continue;
39028             }
39029             
39030             // probably selected..
39031             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39032                 other_nodes.push(ar[i]);
39033                 continue;
39034             }
39035             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39036                 continue;
39037             }
39038             
39039             
39040             has_other_nodes = true;
39041         }
39042         if (!nodes.length && other_nodes.length) {
39043             nodes= other_nodes;
39044         }
39045         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39046             return false;
39047         }
39048         
39049         return nodes[0];
39050     },
39051     createRange: function(sel)
39052     {
39053         // this has strange effects when using with 
39054         // top toolbar - not sure if it's a great idea.
39055         //this.editor.contentWindow.focus();
39056         if (typeof sel != "undefined") {
39057             try {
39058                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39059             } catch(e) {
39060                 return this.doc.createRange();
39061             }
39062         } else {
39063             return this.doc.createRange();
39064         }
39065     },
39066     getParentElement: function()
39067     {
39068         
39069         this.assignDocWin();
39070         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39071         
39072         var range = this.createRange(sel);
39073          
39074         try {
39075             var p = range.commonAncestorContainer;
39076             while (p.nodeType == 3) { // text node
39077                 p = p.parentNode;
39078             }
39079             return p;
39080         } catch (e) {
39081             return null;
39082         }
39083     
39084     },
39085     
39086     
39087     
39088     // BC Hacks - cause I cant work out what i was trying to do..
39089     rangeIntersectsNode : function(range, node)
39090     {
39091         var nodeRange = node.ownerDocument.createRange();
39092         try {
39093             nodeRange.selectNode(node);
39094         }
39095         catch (e) {
39096             nodeRange.selectNodeContents(node);
39097         }
39098
39099         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39100                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39101     },
39102     rangeCompareNode : function(range, node) {
39103         var nodeRange = node.ownerDocument.createRange();
39104         try {
39105             nodeRange.selectNode(node);
39106         } catch (e) {
39107             nodeRange.selectNodeContents(node);
39108         }
39109         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39110         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39111
39112         if (nodeIsBefore && !nodeIsAfter)
39113             return 0;
39114         if (!nodeIsBefore && nodeIsAfter)
39115             return 1;
39116         if (nodeIsBefore && nodeIsAfter)
39117             return 2;
39118
39119         return 3;
39120     },
39121
39122     // private? - in a new class?
39123     cleanUpPaste :  function()
39124     {
39125         // cleans up the whole document..
39126       //  console.log('cleanuppaste');
39127         this.cleanUpChildren(this.doc.body)
39128         
39129         
39130     },
39131     cleanUpChildren : function (n)
39132     {
39133         if (!n.childNodes.length) {
39134             return;
39135         }
39136         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39137            this.cleanUpChild(n.childNodes[i]);
39138         }
39139     },
39140     
39141     
39142         
39143     
39144     cleanUpChild : function (node)
39145     {
39146         //console.log(node);
39147         if (node.nodeName == "#text") {
39148             // clean up silly Windows -- stuff?
39149             return; 
39150         }
39151         if (node.nodeName == "#comment") {
39152             node.parentNode.removeChild(node);
39153             // clean up silly Windows -- stuff?
39154             return; 
39155         }
39156         
39157         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39158             // remove node.
39159             node.parentNode.removeChild(node);
39160             return;
39161             
39162         }
39163         if (!node.attributes || !node.attributes.length) {
39164             this.cleanUpChildren(node);
39165             return;
39166         }
39167         
39168         function cleanAttr(n,v)
39169         {
39170             
39171             if (v.match(/^\./) || v.match(/^\//)) {
39172                 return;
39173             }
39174             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39175                 return;
39176             }
39177             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39178             node.removeAttribute(n);
39179             
39180         }
39181         
39182         function cleanStyle(n,v)
39183         {
39184             if (v.match(/expression/)) { //XSS?? should we even bother..
39185                 node.removeAttribute(n);
39186                 return;
39187             }
39188             
39189             
39190             var parts = v.split(/;/);
39191             Roo.each(parts, function(p) {
39192                 p = p.replace(/\s+/g,'');
39193                 if (!p.length) {
39194                     return;
39195                 }
39196                 var l = p.split(':').shift().replace(/\s+/g,'');
39197                 
39198                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39199                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39200                     node.removeAttribute(n);
39201                     return false;
39202                 }
39203             });
39204             
39205             
39206         }
39207         
39208         
39209         for (var i = node.attributes.length-1; i > -1 ; i--) {
39210             var a = node.attributes[i];
39211             //console.log(a);
39212             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39213                 node.removeAttribute(a.name);
39214                 return;
39215             }
39216             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39217                 cleanAttr(a.name,a.value); // fixme..
39218                 return;
39219             }
39220             if (a.name == 'style') {
39221                 cleanStyle(a.name,a.value);
39222             }
39223             /// clean up MS crap..
39224             if (a.name == 'class') {
39225                 if (a.value.match(/^Mso/)) {
39226                     node.className = '';
39227                 }
39228             }
39229             
39230             // style cleanup!?
39231             // class cleanup?
39232             
39233         }
39234         
39235         
39236         this.cleanUpChildren(node);
39237         
39238         
39239     }
39240     
39241     
39242     // hide stuff that is not compatible
39243     /**
39244      * @event blur
39245      * @hide
39246      */
39247     /**
39248      * @event change
39249      * @hide
39250      */
39251     /**
39252      * @event focus
39253      * @hide
39254      */
39255     /**
39256      * @event specialkey
39257      * @hide
39258      */
39259     /**
39260      * @cfg {String} fieldClass @hide
39261      */
39262     /**
39263      * @cfg {String} focusClass @hide
39264      */
39265     /**
39266      * @cfg {String} autoCreate @hide
39267      */
39268     /**
39269      * @cfg {String} inputType @hide
39270      */
39271     /**
39272      * @cfg {String} invalidClass @hide
39273      */
39274     /**
39275      * @cfg {String} invalidText @hide
39276      */
39277     /**
39278      * @cfg {String} msgFx @hide
39279      */
39280     /**
39281      * @cfg {String} validateOnBlur @hide
39282      */
39283 });
39284
39285 Roo.form.HtmlEditor.white = [
39286         'area', 'br', 'img', 'input', 'hr', 'wbr',
39287         
39288        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39289        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39290        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39291        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39292        'table',   'ul',         'xmp', 
39293        
39294        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39295       'thead',   'tr', 
39296      
39297       'dir', 'menu', 'ol', 'ul', 'dl',
39298        
39299       'embed',  'object'
39300 ];
39301
39302
39303 Roo.form.HtmlEditor.black = [
39304     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39305         'applet', // 
39306         'base',   'basefont', 'bgsound', 'blink',  'body', 
39307         'frame',  'frameset', 'head',    'html',   'ilayer', 
39308         'iframe', 'layer',  'link',     'meta',    'object',   
39309         'script', 'style' ,'title',  'xml' // clean later..
39310 ];
39311 Roo.form.HtmlEditor.clean = [
39312     'script', 'style', 'title', 'xml'
39313 ];
39314
39315 // attributes..
39316
39317 Roo.form.HtmlEditor.ablack = [
39318     'on'
39319 ];
39320     
39321 Roo.form.HtmlEditor.aclean = [ 
39322     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39323 ];
39324
39325 // protocols..
39326 Roo.form.HtmlEditor.pwhite= [
39327         'http',  'https',  'mailto'
39328 ];
39329
39330 Roo.form.HtmlEditor.cwhite= [
39331         'text-align',
39332         'font-size'
39333 ];
39334
39335 // <script type="text/javascript">
39336 /*
39337  * Based on
39338  * Ext JS Library 1.1.1
39339  * Copyright(c) 2006-2007, Ext JS, LLC.
39340  *  
39341  
39342  */
39343
39344 /**
39345  * @class Roo.form.HtmlEditorToolbar1
39346  * Basic Toolbar
39347  * 
39348  * Usage:
39349  *
39350  new Roo.form.HtmlEditor({
39351     ....
39352     toolbars : [
39353         new Roo.form.HtmlEditorToolbar1({
39354             disable : { fonts: 1 , format: 1, ..., ... , ...],
39355             btns : [ .... ]
39356         })
39357     }
39358      
39359  * 
39360  * @cfg {Object} disable List of elements to disable..
39361  * @cfg {Array} btns List of additional buttons.
39362  * 
39363  * 
39364  * NEEDS Extra CSS? 
39365  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39366  */
39367  
39368 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39369 {
39370     
39371     Roo.apply(this, config);
39372     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39373     // dont call parent... till later.
39374 }
39375
39376 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39377     
39378     tb: false,
39379     
39380     rendered: false,
39381     
39382     editor : false,
39383     /**
39384      * @cfg {Object} disable  List of toolbar elements to disable
39385          
39386      */
39387     disable : false,
39388       /**
39389      * @cfg {Array} fontFamilies An array of available font families
39390      */
39391     fontFamilies : [
39392         'Arial',
39393         'Courier New',
39394         'Tahoma',
39395         'Times New Roman',
39396         'Verdana'
39397     ],
39398     
39399     specialChars : [
39400            "&#169;",
39401           "&#174;",     
39402           "&#8482;",    
39403           "&#163;" ,    
39404          // "&#8212;",    
39405           "&#8230;",    
39406           "&#247;" ,    
39407         //  "&#225;" ,     ?? a acute?
39408            "&#8364;"    , //Euro
39409        //   "&#8220;"    ,
39410         //  "&#8221;"    ,
39411         //  "&#8226;"    ,
39412           "&#176;"  //   , // degrees
39413
39414          // "&#233;"     , // e ecute
39415          // "&#250;"     , // u ecute?
39416     ],
39417     inputElements : [ 
39418             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39419             "input:submit", "input:button", "select", "textarea", "label" ],
39420     formats : [
39421         ["p"] ,  
39422         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39423         ["pre"],[ "code"], 
39424         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39425     ],
39426      /**
39427      * @cfg {String} defaultFont default font to use.
39428      */
39429     defaultFont: 'tahoma',
39430    
39431     fontSelect : false,
39432     
39433     
39434     formatCombo : false,
39435     
39436     init : function(editor)
39437     {
39438         this.editor = editor;
39439         
39440         
39441         var fid = editor.frameId;
39442         var etb = this;
39443         function btn(id, toggle, handler){
39444             var xid = fid + '-'+ id ;
39445             return {
39446                 id : xid,
39447                 cmd : id,
39448                 cls : 'x-btn-icon x-edit-'+id,
39449                 enableToggle:toggle !== false,
39450                 scope: editor, // was editor...
39451                 handler:handler||editor.relayBtnCmd,
39452                 clickEvent:'mousedown',
39453                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39454                 tabIndex:-1
39455             };
39456         }
39457         
39458         
39459         
39460         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39461         this.tb = tb;
39462          // stop form submits
39463         tb.el.on('click', function(e){
39464             e.preventDefault(); // what does this do?
39465         });
39466
39467         if(!this.disable.font && !Roo.isSafari){
39468             /* why no safari for fonts
39469             editor.fontSelect = tb.el.createChild({
39470                 tag:'select',
39471                 tabIndex: -1,
39472                 cls:'x-font-select',
39473                 html: editor.createFontOptions()
39474             });
39475             editor.fontSelect.on('change', function(){
39476                 var font = editor.fontSelect.dom.value;
39477                 editor.relayCmd('fontname', font);
39478                 editor.deferFocus();
39479             }, editor);
39480             tb.add(
39481                 editor.fontSelect.dom,
39482                 '-'
39483             );
39484             */
39485         };
39486         if(!this.disable.formats){
39487             this.formatCombo = new Roo.form.ComboBox({
39488                 store: new Roo.data.SimpleStore({
39489                     id : 'tag',
39490                     fields: ['tag'],
39491                     data : this.formats // from states.js
39492                 }),
39493                 blockFocus : true,
39494                 //autoCreate : {tag: "div",  size: "20"},
39495                 displayField:'tag',
39496                 typeAhead: false,
39497                 mode: 'local',
39498                 editable : false,
39499                 triggerAction: 'all',
39500                 emptyText:'Add tag',
39501                 selectOnFocus:true,
39502                 width:135,
39503                 listeners : {
39504                     'select': function(c, r, i) {
39505                         editor.insertTag(r.get('tag'));
39506                         editor.focus();
39507                     }
39508                 }
39509
39510             });
39511             tb.addField(this.formatCombo);
39512             
39513         }
39514         
39515         if(!this.disable.format){
39516             tb.add(
39517                 btn('bold'),
39518                 btn('italic'),
39519                 btn('underline')
39520             );
39521         };
39522         if(!this.disable.fontSize){
39523             tb.add(
39524                 '-',
39525                 
39526                 
39527                 btn('increasefontsize', false, editor.adjustFont),
39528                 btn('decreasefontsize', false, editor.adjustFont)
39529             );
39530         };
39531         
39532         
39533         if(this.disable.colors){
39534             tb.add(
39535                 '-', {
39536                     id:editor.frameId +'-forecolor',
39537                     cls:'x-btn-icon x-edit-forecolor',
39538                     clickEvent:'mousedown',
39539                     tooltip: this.buttonTips['forecolor'] || undefined,
39540                     tabIndex:-1,
39541                     menu : new Roo.menu.ColorMenu({
39542                         allowReselect: true,
39543                         focus: Roo.emptyFn,
39544                         value:'000000',
39545                         plain:true,
39546                         selectHandler: function(cp, color){
39547                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39548                             editor.deferFocus();
39549                         },
39550                         scope: editor,
39551                         clickEvent:'mousedown'
39552                     })
39553                 }, {
39554                     id:editor.frameId +'backcolor',
39555                     cls:'x-btn-icon x-edit-backcolor',
39556                     clickEvent:'mousedown',
39557                     tooltip: this.buttonTips['backcolor'] || undefined,
39558                     tabIndex:-1,
39559                     menu : new Roo.menu.ColorMenu({
39560                         focus: Roo.emptyFn,
39561                         value:'FFFFFF',
39562                         plain:true,
39563                         allowReselect: true,
39564                         selectHandler: function(cp, color){
39565                             if(Roo.isGecko){
39566                                 editor.execCmd('useCSS', false);
39567                                 editor.execCmd('hilitecolor', color);
39568                                 editor.execCmd('useCSS', true);
39569                                 editor.deferFocus();
39570                             }else{
39571                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39572                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39573                                 editor.deferFocus();
39574                             }
39575                         },
39576                         scope:editor,
39577                         clickEvent:'mousedown'
39578                     })
39579                 }
39580             );
39581         };
39582         // now add all the items...
39583         
39584
39585         if(!this.disable.alignments){
39586             tb.add(
39587                 '-',
39588                 btn('justifyleft'),
39589                 btn('justifycenter'),
39590                 btn('justifyright')
39591             );
39592         };
39593
39594         //if(!Roo.isSafari){
39595             if(!this.disable.links){
39596                 tb.add(
39597                     '-',
39598                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39599                 );
39600             };
39601
39602             if(!this.disable.lists){
39603                 tb.add(
39604                     '-',
39605                     btn('insertorderedlist'),
39606                     btn('insertunorderedlist')
39607                 );
39608             }
39609             if(!this.disable.sourceEdit){
39610                 tb.add(
39611                     '-',
39612                     btn('sourceedit', true, function(btn){
39613                         this.toggleSourceEdit(btn.pressed);
39614                     })
39615                 );
39616             }
39617         //}
39618         
39619         var smenu = { };
39620         // special menu.. - needs to be tidied up..
39621         if (!this.disable.special) {
39622             smenu = {
39623                 text: "&#169;",
39624                 cls: 'x-edit-none',
39625                 menu : {
39626                     items : []
39627                    }
39628             };
39629             for (var i =0; i < this.specialChars.length; i++) {
39630                 smenu.menu.items.push({
39631                     
39632                     html: this.specialChars[i],
39633                     handler: function(a,b) {
39634                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39635                         
39636                     },
39637                     tabIndex:-1
39638                 });
39639             }
39640             
39641             
39642             tb.add(smenu);
39643             
39644             
39645         }
39646         if (this.btns) {
39647             for(var i =0; i< this.btns.length;i++) {
39648                 var b = this.btns[i];
39649                 b.cls =  'x-edit-none';
39650                 b.scope = editor;
39651                 tb.add(b);
39652             }
39653         
39654         }
39655         
39656         
39657         
39658         // disable everything...
39659         
39660         this.tb.items.each(function(item){
39661            if(item.id != editor.frameId+ '-sourceedit'){
39662                 item.disable();
39663             }
39664         });
39665         this.rendered = true;
39666         
39667         // the all the btns;
39668         editor.on('editorevent', this.updateToolbar, this);
39669         // other toolbars need to implement this..
39670         //editor.on('editmodechange', this.updateToolbar, this);
39671     },
39672     
39673     
39674     
39675     /**
39676      * Protected method that will not generally be called directly. It triggers
39677      * a toolbar update by reading the markup state of the current selection in the editor.
39678      */
39679     updateToolbar: function(){
39680
39681         if(!this.editor.activated){
39682             this.editor.onFirstFocus();
39683             return;
39684         }
39685
39686         var btns = this.tb.items.map, 
39687             doc = this.editor.doc,
39688             frameId = this.editor.frameId;
39689
39690         if(!this.disable.font && !Roo.isSafari){
39691             /*
39692             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39693             if(name != this.fontSelect.dom.value){
39694                 this.fontSelect.dom.value = name;
39695             }
39696             */
39697         }
39698         if(!this.disable.format){
39699             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39700             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39701             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39702         }
39703         if(!this.disable.alignments){
39704             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39705             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39706             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39707         }
39708         if(!Roo.isSafari && !this.disable.lists){
39709             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39710             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39711         }
39712         
39713         var ans = this.editor.getAllAncestors();
39714         if (this.formatCombo) {
39715             
39716             
39717             var store = this.formatCombo.store;
39718             this.formatCombo.setValue("");
39719             for (var i =0; i < ans.length;i++) {
39720                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39721                     // select it..
39722                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39723                     break;
39724                 }
39725             }
39726         }
39727         
39728         
39729         
39730         // hides menus... - so this cant be on a menu...
39731         Roo.menu.MenuMgr.hideAll();
39732
39733         //this.editorsyncValue();
39734     },
39735    
39736     
39737     createFontOptions : function(){
39738         var buf = [], fs = this.fontFamilies, ff, lc;
39739         for(var i = 0, len = fs.length; i< len; i++){
39740             ff = fs[i];
39741             lc = ff.toLowerCase();
39742             buf.push(
39743                 '<option value="',lc,'" style="font-family:',ff,';"',
39744                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39745                     ff,
39746                 '</option>'
39747             );
39748         }
39749         return buf.join('');
39750     },
39751     
39752     toggleSourceEdit : function(sourceEditMode){
39753         if(sourceEditMode === undefined){
39754             sourceEditMode = !this.sourceEditMode;
39755         }
39756         this.sourceEditMode = sourceEditMode === true;
39757         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39758         // just toggle the button?
39759         if(btn.pressed !== this.editor.sourceEditMode){
39760             btn.toggle(this.editor.sourceEditMode);
39761             return;
39762         }
39763         
39764         if(this.sourceEditMode){
39765             this.tb.items.each(function(item){
39766                 if(item.cmd != 'sourceedit'){
39767                     item.disable();
39768                 }
39769             });
39770           
39771         }else{
39772             if(this.initialized){
39773                 this.tb.items.each(function(item){
39774                     item.enable();
39775                 });
39776             }
39777             
39778         }
39779         // tell the editor that it's been pressed..
39780         this.editor.toggleSourceEdit(sourceEditMode);
39781        
39782     },
39783      /**
39784      * Object collection of toolbar tooltips for the buttons in the editor. The key
39785      * is the command id associated with that button and the value is a valid QuickTips object.
39786      * For example:
39787 <pre><code>
39788 {
39789     bold : {
39790         title: 'Bold (Ctrl+B)',
39791         text: 'Make the selected text bold.',
39792         cls: 'x-html-editor-tip'
39793     },
39794     italic : {
39795         title: 'Italic (Ctrl+I)',
39796         text: 'Make the selected text italic.',
39797         cls: 'x-html-editor-tip'
39798     },
39799     ...
39800 </code></pre>
39801     * @type Object
39802      */
39803     buttonTips : {
39804         bold : {
39805             title: 'Bold (Ctrl+B)',
39806             text: 'Make the selected text bold.',
39807             cls: 'x-html-editor-tip'
39808         },
39809         italic : {
39810             title: 'Italic (Ctrl+I)',
39811             text: 'Make the selected text italic.',
39812             cls: 'x-html-editor-tip'
39813         },
39814         underline : {
39815             title: 'Underline (Ctrl+U)',
39816             text: 'Underline the selected text.',
39817             cls: 'x-html-editor-tip'
39818         },
39819         increasefontsize : {
39820             title: 'Grow Text',
39821             text: 'Increase the font size.',
39822             cls: 'x-html-editor-tip'
39823         },
39824         decreasefontsize : {
39825             title: 'Shrink Text',
39826             text: 'Decrease the font size.',
39827             cls: 'x-html-editor-tip'
39828         },
39829         backcolor : {
39830             title: 'Text Highlight Color',
39831             text: 'Change the background color of the selected text.',
39832             cls: 'x-html-editor-tip'
39833         },
39834         forecolor : {
39835             title: 'Font Color',
39836             text: 'Change the color of the selected text.',
39837             cls: 'x-html-editor-tip'
39838         },
39839         justifyleft : {
39840             title: 'Align Text Left',
39841             text: 'Align text to the left.',
39842             cls: 'x-html-editor-tip'
39843         },
39844         justifycenter : {
39845             title: 'Center Text',
39846             text: 'Center text in the editor.',
39847             cls: 'x-html-editor-tip'
39848         },
39849         justifyright : {
39850             title: 'Align Text Right',
39851             text: 'Align text to the right.',
39852             cls: 'x-html-editor-tip'
39853         },
39854         insertunorderedlist : {
39855             title: 'Bullet List',
39856             text: 'Start a bulleted list.',
39857             cls: 'x-html-editor-tip'
39858         },
39859         insertorderedlist : {
39860             title: 'Numbered List',
39861             text: 'Start a numbered list.',
39862             cls: 'x-html-editor-tip'
39863         },
39864         createlink : {
39865             title: 'Hyperlink',
39866             text: 'Make the selected text a hyperlink.',
39867             cls: 'x-html-editor-tip'
39868         },
39869         sourceedit : {
39870             title: 'Source Edit',
39871             text: 'Switch to source editing mode.',
39872             cls: 'x-html-editor-tip'
39873         }
39874     },
39875     // private
39876     onDestroy : function(){
39877         if(this.rendered){
39878             
39879             this.tb.items.each(function(item){
39880                 if(item.menu){
39881                     item.menu.removeAll();
39882                     if(item.menu.el){
39883                         item.menu.el.destroy();
39884                     }
39885                 }
39886                 item.destroy();
39887             });
39888              
39889         }
39890     },
39891     onFirstFocus: function() {
39892         this.tb.items.each(function(item){
39893            item.enable();
39894         });
39895     }
39896 });
39897
39898
39899
39900
39901 // <script type="text/javascript">
39902 /*
39903  * Based on
39904  * Ext JS Library 1.1.1
39905  * Copyright(c) 2006-2007, Ext JS, LLC.
39906  *  
39907  
39908  */
39909
39910  
39911 /**
39912  * @class Roo.form.HtmlEditor.ToolbarContext
39913  * Context Toolbar
39914  * 
39915  * Usage:
39916  *
39917  new Roo.form.HtmlEditor({
39918     ....
39919     toolbars : [
39920         new Roo.form.HtmlEditor.ToolbarStandard(),
39921         new Roo.form.HtmlEditor.ToolbarContext()
39922         })
39923     }
39924      
39925  * 
39926  * @config : {Object} disable List of elements to disable.. (not done yet.)
39927  * 
39928  * 
39929  */
39930
39931 Roo.form.HtmlEditor.ToolbarContext = function(config)
39932 {
39933     
39934     Roo.apply(this, config);
39935     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39936     // dont call parent... till later.
39937 }
39938 Roo.form.HtmlEditor.ToolbarContext.types = {
39939     'IMG' : {
39940         width : {
39941             title: "Width",
39942             width: 40
39943         },
39944         height:  {
39945             title: "Height",
39946             width: 40
39947         },
39948         align: {
39949             title: "Align",
39950             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39951             width : 80
39952             
39953         },
39954         border: {
39955             title: "Border",
39956             width: 40
39957         },
39958         alt: {
39959             title: "Alt",
39960             width: 120
39961         },
39962         src : {
39963             title: "Src",
39964             width: 220
39965         }
39966         
39967     },
39968     'A' : {
39969         name : {
39970             title: "Name",
39971             width: 50
39972         },
39973         href:  {
39974             title: "Href",
39975             width: 220
39976         } // border?
39977         
39978     },
39979     'TABLE' : {
39980         rows : {
39981             title: "Rows",
39982             width: 20
39983         },
39984         cols : {
39985             title: "Cols",
39986             width: 20
39987         },
39988         width : {
39989             title: "Width",
39990             width: 40
39991         },
39992         height : {
39993             title: "Height",
39994             width: 40
39995         },
39996         border : {
39997             title: "Border",
39998             width: 20
39999         }
40000     },
40001     'TD' : {
40002         width : {
40003             title: "Width",
40004             width: 40
40005         },
40006         height : {
40007             title: "Height",
40008             width: 40
40009         },   
40010         align: {
40011             title: "Align",
40012             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40013             width: 40
40014         },
40015         valign: {
40016             title: "Valign",
40017             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40018             width: 40
40019         },
40020         colspan: {
40021             title: "Colspan",
40022             width: 20
40023             
40024         }
40025     },
40026     'INPUT' : {
40027         name : {
40028             title: "name",
40029             width: 120
40030         },
40031         value : {
40032             title: "Value",
40033             width: 120
40034         },
40035         width : {
40036             title: "Width",
40037             width: 40
40038         }
40039     },
40040     'LABEL' : {
40041         'for' : {
40042             title: "For",
40043             width: 120
40044         }
40045     },
40046     'TEXTAREA' : {
40047           name : {
40048             title: "name",
40049             width: 120
40050         },
40051         rows : {
40052             title: "Rows",
40053             width: 20
40054         },
40055         cols : {
40056             title: "Cols",
40057             width: 20
40058         }
40059     },
40060     'SELECT' : {
40061         name : {
40062             title: "name",
40063             width: 120
40064         },
40065         selectoptions : {
40066             title: "Options",
40067             width: 200
40068         }
40069     },
40070     'BODY' : {
40071         title : {
40072             title: "title",
40073             width: 120,
40074             disabled : true
40075         }
40076     }
40077 };
40078
40079
40080
40081 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40082     
40083     tb: false,
40084     
40085     rendered: false,
40086     
40087     editor : false,
40088     /**
40089      * @cfg {Object} disable  List of toolbar elements to disable
40090          
40091      */
40092     disable : false,
40093     
40094     
40095     
40096     toolbars : false,
40097     
40098     init : function(editor)
40099     {
40100         this.editor = editor;
40101         
40102         
40103         var fid = editor.frameId;
40104         var etb = this;
40105         function btn(id, toggle, handler){
40106             var xid = fid + '-'+ id ;
40107             return {
40108                 id : xid,
40109                 cmd : id,
40110                 cls : 'x-btn-icon x-edit-'+id,
40111                 enableToggle:toggle !== false,
40112                 scope: editor, // was editor...
40113                 handler:handler||editor.relayBtnCmd,
40114                 clickEvent:'mousedown',
40115                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40116                 tabIndex:-1
40117             };
40118         }
40119         // create a new element.
40120         var wdiv = editor.wrap.createChild({
40121                 tag: 'div'
40122             }, editor.wrap.dom.firstChild.nextSibling, true);
40123         
40124         // can we do this more than once??
40125         
40126          // stop form submits
40127       
40128  
40129         // disable everything...
40130         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40131         this.toolbars = {};
40132            
40133         for (var i in  ty) {
40134           
40135             this.toolbars[i] = this.buildToolbar(ty[i],i);
40136         }
40137         this.tb = this.toolbars.BODY;
40138         this.tb.el.show();
40139         
40140          
40141         this.rendered = true;
40142         
40143         // the all the btns;
40144         editor.on('editorevent', this.updateToolbar, this);
40145         // other toolbars need to implement this..
40146         //editor.on('editmodechange', this.updateToolbar, this);
40147     },
40148     
40149     
40150     
40151     /**
40152      * Protected method that will not generally be called directly. It triggers
40153      * a toolbar update by reading the markup state of the current selection in the editor.
40154      */
40155     updateToolbar: function(){
40156
40157         if(!this.editor.activated){
40158             this.editor.onFirstFocus();
40159             return;
40160         }
40161
40162         
40163         var ans = this.editor.getAllAncestors();
40164         
40165         // pick
40166         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40167         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40168         sel = sel ? sel : this.editor.doc.body;
40169         sel = sel.tagName.length ? sel : this.editor.doc.body;
40170         var tn = sel.tagName.toUpperCase();
40171         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40172         tn = sel.tagName.toUpperCase();
40173         if (this.tb.name  == tn) {
40174             return; // no change
40175         }
40176         this.tb.el.hide();
40177         ///console.log("show: " + tn);
40178         this.tb =  this.toolbars[tn];
40179         this.tb.el.show();
40180         this.tb.fields.each(function(e) {
40181             e.setValue(sel.getAttribute(e.name));
40182         });
40183         this.tb.selectedNode = sel;
40184         
40185         
40186         Roo.menu.MenuMgr.hideAll();
40187
40188         //this.editorsyncValue();
40189     },
40190    
40191        
40192     // private
40193     onDestroy : function(){
40194         if(this.rendered){
40195             
40196             this.tb.items.each(function(item){
40197                 if(item.menu){
40198                     item.menu.removeAll();
40199                     if(item.menu.el){
40200                         item.menu.el.destroy();
40201                     }
40202                 }
40203                 item.destroy();
40204             });
40205              
40206         }
40207     },
40208     onFirstFocus: function() {
40209         // need to do this for all the toolbars..
40210         this.tb.items.each(function(item){
40211            item.enable();
40212         });
40213     },
40214     buildToolbar: function(tlist, nm)
40215     {
40216         var editor = this.editor;
40217          // create a new element.
40218         var wdiv = editor.wrap.createChild({
40219                 tag: 'div'
40220             }, editor.wrap.dom.firstChild.nextSibling, true);
40221         
40222        
40223         var tb = new Roo.Toolbar(wdiv);
40224         tb.add(nm+ ":&nbsp;");
40225         for (var i in tlist) {
40226             var item = tlist[i];
40227             tb.add(item.title + ":&nbsp;");
40228             if (item.opts) {
40229                 // fixme
40230                 
40231               
40232                 tb.addField( new Roo.form.ComboBox({
40233                     store: new Roo.data.SimpleStore({
40234                         id : 'val',
40235                         fields: ['val'],
40236                         data : item.opts // from states.js
40237                     }),
40238                     name : i,
40239                     displayField:'val',
40240                     typeAhead: false,
40241                     mode: 'local',
40242                     editable : false,
40243                     triggerAction: 'all',
40244                     emptyText:'Select',
40245                     selectOnFocus:true,
40246                     width: item.width ? item.width  : 130,
40247                     listeners : {
40248                         'select': function(c, r, i) {
40249                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40250                         }
40251                     }
40252
40253                 }));
40254                 continue;
40255                     
40256                 
40257                 
40258                 
40259                 
40260                 tb.addField( new Roo.form.TextField({
40261                     name: i,
40262                     width: 100,
40263                     //allowBlank:false,
40264                     value: ''
40265                 }));
40266                 continue;
40267             }
40268             tb.addField( new Roo.form.TextField({
40269                 name: i,
40270                 width: item.width,
40271                 //allowBlank:true,
40272                 value: '',
40273                 listeners: {
40274                     'change' : function(f, nv, ov) {
40275                         tb.selectedNode.setAttribute(f.name, nv);
40276                     }
40277                 }
40278             }));
40279              
40280         }
40281         tb.el.on('click', function(e){
40282             e.preventDefault(); // what does this do?
40283         });
40284         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40285         tb.el.hide();
40286         tb.name = nm;
40287         // dont need to disable them... as they will get hidden
40288         return tb;
40289          
40290         
40291     }
40292     
40293     
40294     
40295     
40296 });
40297
40298
40299
40300
40301
40302 /*
40303  * Based on:
40304  * Ext JS Library 1.1.1
40305  * Copyright(c) 2006-2007, Ext JS, LLC.
40306  *
40307  * Originally Released Under LGPL - original licence link has changed is not relivant.
40308  *
40309  * Fork - LGPL
40310  * <script type="text/javascript">
40311  */
40312  
40313 /**
40314  * @class Roo.form.BasicForm
40315  * @extends Roo.util.Observable
40316  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40317  * @constructor
40318  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40319  * @param {Object} config Configuration options
40320  */
40321 Roo.form.BasicForm = function(el, config){
40322     this.allItems = [];
40323     this.childForms = [];
40324     Roo.apply(this, config);
40325     /*
40326      * The Roo.form.Field items in this form.
40327      * @type MixedCollection
40328      */
40329      
40330      
40331     this.items = new Roo.util.MixedCollection(false, function(o){
40332         return o.id || (o.id = Roo.id());
40333     });
40334     this.addEvents({
40335         /**
40336          * @event beforeaction
40337          * Fires before any action is performed. Return false to cancel the action.
40338          * @param {Form} this
40339          * @param {Action} action The action to be performed
40340          */
40341         beforeaction: true,
40342         /**
40343          * @event actionfailed
40344          * Fires when an action fails.
40345          * @param {Form} this
40346          * @param {Action} action The action that failed
40347          */
40348         actionfailed : true,
40349         /**
40350          * @event actioncomplete
40351          * Fires when an action is completed.
40352          * @param {Form} this
40353          * @param {Action} action The action that completed
40354          */
40355         actioncomplete : true
40356     });
40357     if(el){
40358         this.initEl(el);
40359     }
40360     Roo.form.BasicForm.superclass.constructor.call(this);
40361 };
40362
40363 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40364     /**
40365      * @cfg {String} method
40366      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40367      */
40368     /**
40369      * @cfg {DataReader} reader
40370      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40371      * This is optional as there is built-in support for processing JSON.
40372      */
40373     /**
40374      * @cfg {DataReader} errorReader
40375      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40376      * This is completely optional as there is built-in support for processing JSON.
40377      */
40378     /**
40379      * @cfg {String} url
40380      * The URL to use for form actions if one isn't supplied in the action options.
40381      */
40382     /**
40383      * @cfg {Boolean} fileUpload
40384      * Set to true if this form is a file upload.
40385      */
40386      
40387     /**
40388      * @cfg {Object} baseParams
40389      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40390      */
40391      /**
40392      
40393     /**
40394      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40395      */
40396     timeout: 30,
40397
40398     // private
40399     activeAction : null,
40400
40401     /**
40402      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40403      * or setValues() data instead of when the form was first created.
40404      */
40405     trackResetOnLoad : false,
40406     
40407     
40408     /**
40409      * childForms - used for multi-tab forms
40410      * @type {Array}
40411      */
40412     childForms : false,
40413     
40414     /**
40415      * allItems - full list of fields.
40416      * @type {Array}
40417      */
40418     allItems : false,
40419     
40420     /**
40421      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40422      * element by passing it or its id or mask the form itself by passing in true.
40423      * @type Mixed
40424      */
40425     waitMsgTarget : false,
40426
40427     // private
40428     initEl : function(el){
40429         this.el = Roo.get(el);
40430         this.id = this.el.id || Roo.id();
40431         this.el.on('submit', this.onSubmit, this);
40432         this.el.addClass('x-form');
40433     },
40434
40435     // private
40436     onSubmit : function(e){
40437         e.stopEvent();
40438     },
40439
40440     /**
40441      * Returns true if client-side validation on the form is successful.
40442      * @return Boolean
40443      */
40444     isValid : function(){
40445         var valid = true;
40446         this.items.each(function(f){
40447            if(!f.validate()){
40448                valid = false;
40449            }
40450         });
40451         return valid;
40452     },
40453
40454     /**
40455      * Returns true if any fields in this form have changed since their original load.
40456      * @return Boolean
40457      */
40458     isDirty : function(){
40459         var dirty = false;
40460         this.items.each(function(f){
40461            if(f.isDirty()){
40462                dirty = true;
40463                return false;
40464            }
40465         });
40466         return dirty;
40467     },
40468
40469     /**
40470      * Performs a predefined action (submit or load) or custom actions you define on this form.
40471      * @param {String} actionName The name of the action type
40472      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40473      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40474      * accept other config options):
40475      * <pre>
40476 Property          Type             Description
40477 ----------------  ---------------  ----------------------------------------------------------------------------------
40478 url               String           The url for the action (defaults to the form's url)
40479 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40480 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40481 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40482                                    validate the form on the client (defaults to false)
40483      * </pre>
40484      * @return {BasicForm} this
40485      */
40486     doAction : function(action, options){
40487         if(typeof action == 'string'){
40488             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40489         }
40490         if(this.fireEvent('beforeaction', this, action) !== false){
40491             this.beforeAction(action);
40492             action.run.defer(100, action);
40493         }
40494         return this;
40495     },
40496
40497     /**
40498      * Shortcut to do a submit action.
40499      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40500      * @return {BasicForm} this
40501      */
40502     submit : function(options){
40503         this.doAction('submit', options);
40504         return this;
40505     },
40506
40507     /**
40508      * Shortcut to do a load action.
40509      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40510      * @return {BasicForm} this
40511      */
40512     load : function(options){
40513         this.doAction('load', options);
40514         return this;
40515     },
40516
40517     /**
40518      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40519      * @param {Record} record The record to edit
40520      * @return {BasicForm} this
40521      */
40522     updateRecord : function(record){
40523         record.beginEdit();
40524         var fs = record.fields;
40525         fs.each(function(f){
40526             var field = this.findField(f.name);
40527             if(field){
40528                 record.set(f.name, field.getValue());
40529             }
40530         }, this);
40531         record.endEdit();
40532         return this;
40533     },
40534
40535     /**
40536      * Loads an Roo.data.Record into this form.
40537      * @param {Record} record The record to load
40538      * @return {BasicForm} this
40539      */
40540     loadRecord : function(record){
40541         this.setValues(record.data);
40542         return this;
40543     },
40544
40545     // private
40546     beforeAction : function(action){
40547         var o = action.options;
40548         
40549        
40550         if(this.waitMsgTarget === true){
40551             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40552         }else if(this.waitMsgTarget){
40553             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40554             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40555         }else {
40556             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40557         }
40558          
40559     },
40560
40561     // private
40562     afterAction : function(action, success){
40563         this.activeAction = null;
40564         var o = action.options;
40565         
40566         if(this.waitMsgTarget === true){
40567             this.el.unmask();
40568         }else if(this.waitMsgTarget){
40569             this.waitMsgTarget.unmask();
40570         }else{
40571             Roo.MessageBox.updateProgress(1);
40572             Roo.MessageBox.hide();
40573         }
40574          
40575         if(success){
40576             if(o.reset){
40577                 this.reset();
40578             }
40579             Roo.callback(o.success, o.scope, [this, action]);
40580             this.fireEvent('actioncomplete', this, action);
40581             
40582         }else{
40583             Roo.callback(o.failure, o.scope, [this, action]);
40584             // show an error message if no failed handler is set..
40585             if (!this.hasListener('actionfailed')) {
40586                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40587             }
40588             
40589             this.fireEvent('actionfailed', this, action);
40590         }
40591         
40592     },
40593
40594     /**
40595      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40596      * @param {String} id The value to search for
40597      * @return Field
40598      */
40599     findField : function(id){
40600         var field = this.items.get(id);
40601         if(!field){
40602             this.items.each(function(f){
40603                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40604                     field = f;
40605                     return false;
40606                 }
40607             });
40608         }
40609         return field || null;
40610     },
40611
40612     /**
40613      * Add a secondary form to this one, 
40614      * Used to provide tabbed forms. One form is primary, with hidden values 
40615      * which mirror the elements from the other forms.
40616      * 
40617      * @param {Roo.form.Form} form to add.
40618      * 
40619      */
40620     addForm : function(form)
40621     {
40622        
40623         if (this.childForms.indexOf(form) > -1) {
40624             // already added..
40625             return;
40626         }
40627         this.childForms.push(form);
40628         var n = '';
40629         Roo.each(form.allItems, function (fe) {
40630             
40631             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40632             if (this.findField(n)) { // already added..
40633                 return;
40634             }
40635             var add = new Roo.form.Hidden({
40636                 name : n
40637             });
40638             add.render(this.el);
40639             
40640             this.add( add );
40641         }, this);
40642         
40643     },
40644     /**
40645      * Mark fields in this form invalid in bulk.
40646      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40647      * @return {BasicForm} this
40648      */
40649     markInvalid : function(errors){
40650         if(errors instanceof Array){
40651             for(var i = 0, len = errors.length; i < len; i++){
40652                 var fieldError = errors[i];
40653                 var f = this.findField(fieldError.id);
40654                 if(f){
40655                     f.markInvalid(fieldError.msg);
40656                 }
40657             }
40658         }else{
40659             var field, id;
40660             for(id in errors){
40661                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40662                     field.markInvalid(errors[id]);
40663                 }
40664             }
40665         }
40666         Roo.each(this.childForms || [], function (f) {
40667             f.markInvalid(errors);
40668         });
40669         
40670         return this;
40671     },
40672
40673     /**
40674      * Set values for fields in this form in bulk.
40675      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40676      * @return {BasicForm} this
40677      */
40678     setValues : function(values){
40679         if(values instanceof Array){ // array of objects
40680             for(var i = 0, len = values.length; i < len; i++){
40681                 var v = values[i];
40682                 var f = this.findField(v.id);
40683                 if(f){
40684                     f.setValue(v.value);
40685                     if(this.trackResetOnLoad){
40686                         f.originalValue = f.getValue();
40687                     }
40688                 }
40689             }
40690         }else{ // object hash
40691             var field, id;
40692             for(id in values){
40693                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40694                     
40695                     if (field.setFromData && 
40696                         field.valueField && 
40697                         field.displayField &&
40698                         // combos' with local stores can 
40699                         // be queried via setValue()
40700                         // to set their value..
40701                         (field.store && !field.store.isLocal)
40702                         ) {
40703                         // it's a combo
40704                         var sd = { };
40705                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40706                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40707                         field.setFromData(sd);
40708                         
40709                     } else {
40710                         field.setValue(values[id]);
40711                     }
40712                     
40713                     
40714                     if(this.trackResetOnLoad){
40715                         field.originalValue = field.getValue();
40716                     }
40717                 }
40718             }
40719         }
40720          
40721         Roo.each(this.childForms || [], function (f) {
40722             f.setValues(values);
40723         });
40724                 
40725         return this;
40726     },
40727
40728     /**
40729      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40730      * they are returned as an array.
40731      * @param {Boolean} asString
40732      * @return {Object}
40733      */
40734     getValues : function(asString){
40735         if (this.childForms) {
40736             // copy values from the child forms
40737             Roo.each(this.childForms, function (f) {
40738                 this.setValues(f.getValues());
40739             }, this);
40740         }
40741         
40742         
40743         
40744         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40745         if(asString === true){
40746             return fs;
40747         }
40748         return Roo.urlDecode(fs);
40749     },
40750     
40751     /**
40752      * Returns the fields in this form as an object with key/value pairs. 
40753      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40754      * @return {Object}
40755      */
40756     getFieldValues : function()
40757     {
40758         if (this.childForms) {
40759             // copy values from the child forms
40760             Roo.each(this.childForms, function (f) {
40761                 this.setValues(f.getValues());
40762             }, this);
40763         }
40764         
40765         var ret = {};
40766         this.items.each(function(f){
40767             if (!f.getName()) {
40768                 return;
40769             }
40770             var v = f.getValue();
40771             if ((typeof(v) == 'object') && f.getRawValue) {
40772                 v = f.getRawValue() ; // dates..
40773             }
40774             ret[f.getName()] = v;
40775         });
40776         
40777         return ret;
40778     },
40779
40780     /**
40781      * Clears all invalid messages in this form.
40782      * @return {BasicForm} this
40783      */
40784     clearInvalid : function(){
40785         this.items.each(function(f){
40786            f.clearInvalid();
40787         });
40788         
40789         Roo.each(this.childForms || [], function (f) {
40790             f.clearInvalid();
40791         });
40792         
40793         
40794         return this;
40795     },
40796
40797     /**
40798      * Resets this form.
40799      * @return {BasicForm} this
40800      */
40801     reset : function(){
40802         this.items.each(function(f){
40803             f.reset();
40804         });
40805         
40806         Roo.each(this.childForms || [], function (f) {
40807             f.reset();
40808         });
40809        
40810         
40811         return this;
40812     },
40813
40814     /**
40815      * Add Roo.form components to this form.
40816      * @param {Field} field1
40817      * @param {Field} field2 (optional)
40818      * @param {Field} etc (optional)
40819      * @return {BasicForm} this
40820      */
40821     add : function(){
40822         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40823         return this;
40824     },
40825
40826
40827     /**
40828      * Removes a field from the items collection (does NOT remove its markup).
40829      * @param {Field} field
40830      * @return {BasicForm} this
40831      */
40832     remove : function(field){
40833         this.items.remove(field);
40834         return this;
40835     },
40836
40837     /**
40838      * Looks at the fields in this form, checks them for an id attribute,
40839      * and calls applyTo on the existing dom element with that id.
40840      * @return {BasicForm} this
40841      */
40842     render : function(){
40843         this.items.each(function(f){
40844             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40845                 f.applyTo(f.id);
40846             }
40847         });
40848         return this;
40849     },
40850
40851     /**
40852      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40853      * @param {Object} values
40854      * @return {BasicForm} this
40855      */
40856     applyToFields : function(o){
40857         this.items.each(function(f){
40858            Roo.apply(f, o);
40859         });
40860         return this;
40861     },
40862
40863     /**
40864      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40865      * @param {Object} values
40866      * @return {BasicForm} this
40867      */
40868     applyIfToFields : function(o){
40869         this.items.each(function(f){
40870            Roo.applyIf(f, o);
40871         });
40872         return this;
40873     }
40874 });
40875
40876 // back compat
40877 Roo.BasicForm = Roo.form.BasicForm;/*
40878  * Based on:
40879  * Ext JS Library 1.1.1
40880  * Copyright(c) 2006-2007, Ext JS, LLC.
40881  *
40882  * Originally Released Under LGPL - original licence link has changed is not relivant.
40883  *
40884  * Fork - LGPL
40885  * <script type="text/javascript">
40886  */
40887
40888 /**
40889  * @class Roo.form.Form
40890  * @extends Roo.form.BasicForm
40891  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40892  * @constructor
40893  * @param {Object} config Configuration options
40894  */
40895 Roo.form.Form = function(config){
40896     var xitems =  [];
40897     if (config.items) {
40898         xitems = config.items;
40899         delete config.items;
40900     }
40901    
40902     
40903     Roo.form.Form.superclass.constructor.call(this, null, config);
40904     this.url = this.url || this.action;
40905     if(!this.root){
40906         this.root = new Roo.form.Layout(Roo.applyIf({
40907             id: Roo.id()
40908         }, config));
40909     }
40910     this.active = this.root;
40911     /**
40912      * Array of all the buttons that have been added to this form via {@link addButton}
40913      * @type Array
40914      */
40915     this.buttons = [];
40916     this.allItems = [];
40917     this.addEvents({
40918         /**
40919          * @event clientvalidation
40920          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40921          * @param {Form} this
40922          * @param {Boolean} valid true if the form has passed client-side validation
40923          */
40924         clientvalidation: true,
40925         /**
40926          * @event rendered
40927          * Fires when the form is rendered
40928          * @param {Roo.form.Form} form
40929          */
40930         rendered : true
40931     });
40932     
40933     if (this.progressUrl) {
40934             // push a hidden field onto the list of fields..
40935             this.addxtype( {
40936                     xns: Roo.form, 
40937                     xtype : 'Hidden', 
40938                     name : 'UPLOAD_IDENTIFIER' 
40939             });
40940         }
40941         
40942     
40943     Roo.each(xitems, this.addxtype, this);
40944     
40945     
40946     
40947 };
40948
40949 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40950     /**
40951      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40952      */
40953     /**
40954      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40955      */
40956     /**
40957      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40958      */
40959     buttonAlign:'center',
40960
40961     /**
40962      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40963      */
40964     minButtonWidth:75,
40965
40966     /**
40967      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40968      * This property cascades to child containers if not set.
40969      */
40970     labelAlign:'left',
40971
40972     /**
40973      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40974      * fires a looping event with that state. This is required to bind buttons to the valid
40975      * state using the config value formBind:true on the button.
40976      */
40977     monitorValid : false,
40978
40979     /**
40980      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40981      */
40982     monitorPoll : 200,
40983     
40984     /**
40985      * @cfg {String} progressUrl - Url to return progress data 
40986      */
40987     
40988     progressUrl : false,
40989   
40990     /**
40991      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40992      * fields are added and the column is closed. If no fields are passed the column remains open
40993      * until end() is called.
40994      * @param {Object} config The config to pass to the column
40995      * @param {Field} field1 (optional)
40996      * @param {Field} field2 (optional)
40997      * @param {Field} etc (optional)
40998      * @return Column The column container object
40999      */
41000     column : function(c){
41001         var col = new Roo.form.Column(c);
41002         this.start(col);
41003         if(arguments.length > 1){ // duplicate code required because of Opera
41004             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41005             this.end();
41006         }
41007         return col;
41008     },
41009
41010     /**
41011      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41012      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41013      * until end() is called.
41014      * @param {Object} config The config to pass to the fieldset
41015      * @param {Field} field1 (optional)
41016      * @param {Field} field2 (optional)
41017      * @param {Field} etc (optional)
41018      * @return FieldSet The fieldset container object
41019      */
41020     fieldset : function(c){
41021         var fs = new Roo.form.FieldSet(c);
41022         this.start(fs);
41023         if(arguments.length > 1){ // duplicate code required because of Opera
41024             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41025             this.end();
41026         }
41027         return fs;
41028     },
41029
41030     /**
41031      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41032      * fields are added and the container is closed. If no fields are passed the container remains open
41033      * until end() is called.
41034      * @param {Object} config The config to pass to the Layout
41035      * @param {Field} field1 (optional)
41036      * @param {Field} field2 (optional)
41037      * @param {Field} etc (optional)
41038      * @return Layout The container object
41039      */
41040     container : function(c){
41041         var l = new Roo.form.Layout(c);
41042         this.start(l);
41043         if(arguments.length > 1){ // duplicate code required because of Opera
41044             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41045             this.end();
41046         }
41047         return l;
41048     },
41049
41050     /**
41051      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41052      * @param {Object} container A Roo.form.Layout or subclass of Layout
41053      * @return {Form} this
41054      */
41055     start : function(c){
41056         // cascade label info
41057         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41058         this.active.stack.push(c);
41059         c.ownerCt = this.active;
41060         this.active = c;
41061         return this;
41062     },
41063
41064     /**
41065      * Closes the current open container
41066      * @return {Form} this
41067      */
41068     end : function(){
41069         if(this.active == this.root){
41070             return this;
41071         }
41072         this.active = this.active.ownerCt;
41073         return this;
41074     },
41075
41076     /**
41077      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41078      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41079      * as the label of the field.
41080      * @param {Field} field1
41081      * @param {Field} field2 (optional)
41082      * @param {Field} etc. (optional)
41083      * @return {Form} this
41084      */
41085     add : function(){
41086         this.active.stack.push.apply(this.active.stack, arguments);
41087         this.allItems.push.apply(this.allItems,arguments);
41088         var r = [];
41089         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41090             if(a[i].isFormField){
41091                 r.push(a[i]);
41092             }
41093         }
41094         if(r.length > 0){
41095             Roo.form.Form.superclass.add.apply(this, r);
41096         }
41097         return this;
41098     },
41099     
41100
41101     
41102     
41103     
41104      /**
41105      * Find any element that has been added to a form, using it's ID or name
41106      * This can include framesets, columns etc. along with regular fields..
41107      * @param {String} id - id or name to find.
41108      
41109      * @return {Element} e - or false if nothing found.
41110      */
41111     findbyId : function(id)
41112     {
41113         var ret = false;
41114         if (!id) {
41115             return ret;
41116         }
41117         Ext.each(this.allItems, function(f){
41118             if (f.id == id || f.name == id ){
41119                 ret = f;
41120                 return false;
41121             }
41122         });
41123         return ret;
41124     },
41125
41126     
41127     
41128     /**
41129      * Render this form into the passed container. This should only be called once!
41130      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41131      * @return {Form} this
41132      */
41133     render : function(ct)
41134     {
41135         
41136         
41137         
41138         ct = Roo.get(ct);
41139         var o = this.autoCreate || {
41140             tag: 'form',
41141             method : this.method || 'POST',
41142             id : this.id || Roo.id()
41143         };
41144         this.initEl(ct.createChild(o));
41145
41146         this.root.render(this.el);
41147         
41148        
41149              
41150         this.items.each(function(f){
41151             f.render('x-form-el-'+f.id);
41152         });
41153
41154         if(this.buttons.length > 0){
41155             // tables are required to maintain order and for correct IE layout
41156             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41157                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41158                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41159             }}, null, true);
41160             var tr = tb.getElementsByTagName('tr')[0];
41161             for(var i = 0, len = this.buttons.length; i < len; i++) {
41162                 var b = this.buttons[i];
41163                 var td = document.createElement('td');
41164                 td.className = 'x-form-btn-td';
41165                 b.render(tr.appendChild(td));
41166             }
41167         }
41168         if(this.monitorValid){ // initialize after render
41169             this.startMonitoring();
41170         }
41171         this.fireEvent('rendered', this);
41172         return this;
41173     },
41174
41175     /**
41176      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41177      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41178      * object or a valid Roo.DomHelper element config
41179      * @param {Function} handler The function called when the button is clicked
41180      * @param {Object} scope (optional) The scope of the handler function
41181      * @return {Roo.Button}
41182      */
41183     addButton : function(config, handler, scope){
41184         var bc = {
41185             handler: handler,
41186             scope: scope,
41187             minWidth: this.minButtonWidth,
41188             hideParent:true
41189         };
41190         if(typeof config == "string"){
41191             bc.text = config;
41192         }else{
41193             Roo.apply(bc, config);
41194         }
41195         var btn = new Roo.Button(null, bc);
41196         this.buttons.push(btn);
41197         return btn;
41198     },
41199
41200      /**
41201      * Adds a series of form elements (using the xtype property as the factory method.
41202      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41203      * @param {Object} config 
41204      */
41205     
41206     addxtype : function()
41207     {
41208         var ar = Array.prototype.slice.call(arguments, 0);
41209         var ret = false;
41210         for(var i = 0; i < ar.length; i++) {
41211             if (!ar[i]) {
41212                 continue; // skip -- if this happends something invalid got sent, we 
41213                 // should ignore it, as basically that interface element will not show up
41214                 // and that should be pretty obvious!!
41215             }
41216             
41217             if (Roo.form[ar[i].xtype]) {
41218                 ar[i].form = this;
41219                 var fe = Roo.factory(ar[i], Roo.form);
41220                 if (!ret) {
41221                     ret = fe;
41222                 }
41223                 fe.form = this;
41224                 if (fe.store) {
41225                     fe.store.form = this;
41226                 }
41227                 if (fe.isLayout) {  
41228                          
41229                     this.start(fe);
41230                     this.allItems.push(fe);
41231                     if (fe.items && fe.addxtype) {
41232                         fe.addxtype.apply(fe, fe.items);
41233                         delete fe.items;
41234                     }
41235                      this.end();
41236                     continue;
41237                 }
41238                 
41239                 
41240                  
41241                 this.add(fe);
41242               //  console.log('adding ' + ar[i].xtype);
41243             }
41244             if (ar[i].xtype == 'Button') {  
41245                 //console.log('adding button');
41246                 //console.log(ar[i]);
41247                 this.addButton(ar[i]);
41248                 this.allItems.push(fe);
41249                 continue;
41250             }
41251             
41252             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41253                 alert('end is not supported on xtype any more, use items');
41254             //    this.end();
41255             //    //console.log('adding end');
41256             }
41257             
41258         }
41259         return ret;
41260     },
41261     
41262     /**
41263      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41264      * option "monitorValid"
41265      */
41266     startMonitoring : function(){
41267         if(!this.bound){
41268             this.bound = true;
41269             Roo.TaskMgr.start({
41270                 run : this.bindHandler,
41271                 interval : this.monitorPoll || 200,
41272                 scope: this
41273             });
41274         }
41275     },
41276
41277     /**
41278      * Stops monitoring of the valid state of this form
41279      */
41280     stopMonitoring : function(){
41281         this.bound = false;
41282     },
41283
41284     // private
41285     bindHandler : function(){
41286         if(!this.bound){
41287             return false; // stops binding
41288         }
41289         var valid = true;
41290         this.items.each(function(f){
41291             if(!f.isValid(true)){
41292                 valid = false;
41293                 return false;
41294             }
41295         });
41296         for(var i = 0, len = this.buttons.length; i < len; i++){
41297             var btn = this.buttons[i];
41298             if(btn.formBind === true && btn.disabled === valid){
41299                 btn.setDisabled(!valid);
41300             }
41301         }
41302         this.fireEvent('clientvalidation', this, valid);
41303     }
41304     
41305     
41306     
41307     
41308     
41309     
41310     
41311     
41312 });
41313
41314
41315 // back compat
41316 Roo.Form = Roo.form.Form;
41317 /*
41318  * Based on:
41319  * Ext JS Library 1.1.1
41320  * Copyright(c) 2006-2007, Ext JS, LLC.
41321  *
41322  * Originally Released Under LGPL - original licence link has changed is not relivant.
41323  *
41324  * Fork - LGPL
41325  * <script type="text/javascript">
41326  */
41327  
41328  /**
41329  * @class Roo.form.Action
41330  * Internal Class used to handle form actions
41331  * @constructor
41332  * @param {Roo.form.BasicForm} el The form element or its id
41333  * @param {Object} config Configuration options
41334  */
41335  
41336  
41337 // define the action interface
41338 Roo.form.Action = function(form, options){
41339     this.form = form;
41340     this.options = options || {};
41341 };
41342 /**
41343  * Client Validation Failed
41344  * @const 
41345  */
41346 Roo.form.Action.CLIENT_INVALID = 'client';
41347 /**
41348  * Server Validation Failed
41349  * @const 
41350  */
41351  Roo.form.Action.SERVER_INVALID = 'server';
41352  /**
41353  * Connect to Server Failed
41354  * @const 
41355  */
41356 Roo.form.Action.CONNECT_FAILURE = 'connect';
41357 /**
41358  * Reading Data from Server Failed
41359  * @const 
41360  */
41361 Roo.form.Action.LOAD_FAILURE = 'load';
41362
41363 Roo.form.Action.prototype = {
41364     type : 'default',
41365     failureType : undefined,
41366     response : undefined,
41367     result : undefined,
41368
41369     // interface method
41370     run : function(options){
41371
41372     },
41373
41374     // interface method
41375     success : function(response){
41376
41377     },
41378
41379     // interface method
41380     handleResponse : function(response){
41381
41382     },
41383
41384     // default connection failure
41385     failure : function(response){
41386         
41387         this.response = response;
41388         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41389         this.form.afterAction(this, false);
41390     },
41391
41392     processResponse : function(response){
41393         this.response = response;
41394         if(!response.responseText){
41395             return true;
41396         }
41397         this.result = this.handleResponse(response);
41398         return this.result;
41399     },
41400
41401     // utility functions used internally
41402     getUrl : function(appendParams){
41403         var url = this.options.url || this.form.url || this.form.el.dom.action;
41404         if(appendParams){
41405             var p = this.getParams();
41406             if(p){
41407                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41408             }
41409         }
41410         return url;
41411     },
41412
41413     getMethod : function(){
41414         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41415     },
41416
41417     getParams : function(){
41418         var bp = this.form.baseParams;
41419         var p = this.options.params;
41420         if(p){
41421             if(typeof p == "object"){
41422                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41423             }else if(typeof p == 'string' && bp){
41424                 p += '&' + Roo.urlEncode(bp);
41425             }
41426         }else if(bp){
41427             p = Roo.urlEncode(bp);
41428         }
41429         return p;
41430     },
41431
41432     createCallback : function(){
41433         return {
41434             success: this.success,
41435             failure: this.failure,
41436             scope: this,
41437             timeout: (this.form.timeout*1000),
41438             upload: this.form.fileUpload ? this.success : undefined
41439         };
41440     }
41441 };
41442
41443 Roo.form.Action.Submit = function(form, options){
41444     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41445 };
41446
41447 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41448     type : 'submit',
41449
41450     haveProgress : false,
41451     uploadComplete : false,
41452     
41453     // uploadProgress indicator.
41454     uploadProgress : function()
41455     {
41456         if (!this.form.progressUrl) {
41457             return;
41458         }
41459         
41460         if (!this.haveProgress) {
41461             Roo.MessageBox.progress("Uploading", "Uploading");
41462         }
41463         if (this.uploadComplete) {
41464            Roo.MessageBox.hide();
41465            return;
41466         }
41467         
41468         this.haveProgress = true;
41469    
41470         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41471         
41472         var c = new Roo.data.Connection();
41473         c.request({
41474             url : this.form.progressUrl,
41475             params: {
41476                 id : uid
41477             },
41478             method: 'GET',
41479             success : function(req){
41480                //console.log(data);
41481                 var rdata = false;
41482                 var edata;
41483                 try  {
41484                    rdata = Roo.decode(req.responseText)
41485                 } catch (e) {
41486                     Roo.log("Invalid data from server..");
41487                     Roo.log(edata);
41488                     return;
41489                 }
41490                 if (!rdata || !rdata.success) {
41491                     Roo.log(rdata);
41492                     return;
41493                 }
41494                 var data = rdata.data;
41495                 
41496                 if (this.uploadComplete) {
41497                    Roo.MessageBox.hide();
41498                    return;
41499                 }
41500                    
41501                 if (data){
41502                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41503                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41504                     );
41505                 }
41506                 this.uploadProgress.defer(2000,this);
41507             },
41508        
41509             failure: function(data) {
41510                 Roo.log('progress url failed ');
41511                 Roo.log(data);
41512             },
41513             scope : this
41514         });
41515            
41516     },
41517     
41518     
41519     run : function()
41520     {
41521         // run get Values on the form, so it syncs any secondary forms.
41522         this.form.getValues();
41523         
41524         var o = this.options;
41525         var method = this.getMethod();
41526         var isPost = method == 'POST';
41527         if(o.clientValidation === false || this.form.isValid()){
41528             
41529             if (this.form.progressUrl) {
41530                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41531                     (new Date() * 1) + '' + Math.random());
41532                     
41533             } 
41534             
41535             
41536             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41537                 form:this.form.el.dom,
41538                 url:this.getUrl(!isPost),
41539                 method: method,
41540                 params:isPost ? this.getParams() : null,
41541                 isUpload: this.form.fileUpload
41542             }));
41543             
41544             this.uploadProgress();
41545
41546         }else if (o.clientValidation !== false){ // client validation failed
41547             this.failureType = Roo.form.Action.CLIENT_INVALID;
41548             this.form.afterAction(this, false);
41549         }
41550     },
41551
41552     success : function(response)
41553     {
41554         this.uploadComplete= true;
41555         if (this.haveProgress) {
41556             Roo.MessageBox.hide();
41557         }
41558         
41559         
41560         var result = this.processResponse(response);
41561         if(result === true || result.success){
41562             this.form.afterAction(this, true);
41563             return;
41564         }
41565         if(result.errors){
41566             this.form.markInvalid(result.errors);
41567             this.failureType = Roo.form.Action.SERVER_INVALID;
41568         }
41569         this.form.afterAction(this, false);
41570     },
41571     failure : function(response)
41572     {
41573         this.uploadComplete= true;
41574         if (this.haveProgress) {
41575             Roo.MessageBox.hide();
41576         }
41577         
41578         
41579         this.response = response;
41580         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41581         this.form.afterAction(this, false);
41582     },
41583     
41584     handleResponse : function(response){
41585         if(this.form.errorReader){
41586             var rs = this.form.errorReader.read(response);
41587             var errors = [];
41588             if(rs.records){
41589                 for(var i = 0, len = rs.records.length; i < len; i++) {
41590                     var r = rs.records[i];
41591                     errors[i] = r.data;
41592                 }
41593             }
41594             if(errors.length < 1){
41595                 errors = null;
41596             }
41597             return {
41598                 success : rs.success,
41599                 errors : errors
41600             };
41601         }
41602         var ret = false;
41603         try {
41604             ret = Roo.decode(response.responseText);
41605         } catch (e) {
41606             ret = {
41607                 success: false,
41608                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41609                 errors : []
41610             };
41611         }
41612         return ret;
41613         
41614     }
41615 });
41616
41617
41618 Roo.form.Action.Load = function(form, options){
41619     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41620     this.reader = this.form.reader;
41621 };
41622
41623 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41624     type : 'load',
41625
41626     run : function(){
41627         
41628         Roo.Ajax.request(Roo.apply(
41629                 this.createCallback(), {
41630                     method:this.getMethod(),
41631                     url:this.getUrl(false),
41632                     params:this.getParams()
41633         }));
41634     },
41635
41636     success : function(response){
41637         
41638         var result = this.processResponse(response);
41639         if(result === true || !result.success || !result.data){
41640             this.failureType = Roo.form.Action.LOAD_FAILURE;
41641             this.form.afterAction(this, false);
41642             return;
41643         }
41644         this.form.clearInvalid();
41645         this.form.setValues(result.data);
41646         this.form.afterAction(this, true);
41647     },
41648
41649     handleResponse : function(response){
41650         if(this.form.reader){
41651             var rs = this.form.reader.read(response);
41652             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41653             return {
41654                 success : rs.success,
41655                 data : data
41656             };
41657         }
41658         return Roo.decode(response.responseText);
41659     }
41660 });
41661
41662 Roo.form.Action.ACTION_TYPES = {
41663     'load' : Roo.form.Action.Load,
41664     'submit' : Roo.form.Action.Submit
41665 };/*
41666  * Based on:
41667  * Ext JS Library 1.1.1
41668  * Copyright(c) 2006-2007, Ext JS, LLC.
41669  *
41670  * Originally Released Under LGPL - original licence link has changed is not relivant.
41671  *
41672  * Fork - LGPL
41673  * <script type="text/javascript">
41674  */
41675  
41676 /**
41677  * @class Roo.form.Layout
41678  * @extends Roo.Component
41679  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41680  * @constructor
41681  * @param {Object} config Configuration options
41682  */
41683 Roo.form.Layout = function(config){
41684     var xitems = [];
41685     if (config.items) {
41686         xitems = config.items;
41687         delete config.items;
41688     }
41689     Roo.form.Layout.superclass.constructor.call(this, config);
41690     this.stack = [];
41691     Roo.each(xitems, this.addxtype, this);
41692      
41693 };
41694
41695 Roo.extend(Roo.form.Layout, Roo.Component, {
41696     /**
41697      * @cfg {String/Object} autoCreate
41698      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41699      */
41700     /**
41701      * @cfg {String/Object/Function} style
41702      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41703      * a function which returns such a specification.
41704      */
41705     /**
41706      * @cfg {String} labelAlign
41707      * Valid values are "left," "top" and "right" (defaults to "left")
41708      */
41709     /**
41710      * @cfg {Number} labelWidth
41711      * Fixed width in pixels of all field labels (defaults to undefined)
41712      */
41713     /**
41714      * @cfg {Boolean} clear
41715      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41716      */
41717     clear : true,
41718     /**
41719      * @cfg {String} labelSeparator
41720      * The separator to use after field labels (defaults to ':')
41721      */
41722     labelSeparator : ':',
41723     /**
41724      * @cfg {Boolean} hideLabels
41725      * True to suppress the display of field labels in this layout (defaults to false)
41726      */
41727     hideLabels : false,
41728
41729     // private
41730     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41731     
41732     isLayout : true,
41733     
41734     // private
41735     onRender : function(ct, position){
41736         if(this.el){ // from markup
41737             this.el = Roo.get(this.el);
41738         }else {  // generate
41739             var cfg = this.getAutoCreate();
41740             this.el = ct.createChild(cfg, position);
41741         }
41742         if(this.style){
41743             this.el.applyStyles(this.style);
41744         }
41745         if(this.labelAlign){
41746             this.el.addClass('x-form-label-'+this.labelAlign);
41747         }
41748         if(this.hideLabels){
41749             this.labelStyle = "display:none";
41750             this.elementStyle = "padding-left:0;";
41751         }else{
41752             if(typeof this.labelWidth == 'number'){
41753                 this.labelStyle = "width:"+this.labelWidth+"px;";
41754                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41755             }
41756             if(this.labelAlign == 'top'){
41757                 this.labelStyle = "width:auto;";
41758                 this.elementStyle = "padding-left:0;";
41759             }
41760         }
41761         var stack = this.stack;
41762         var slen = stack.length;
41763         if(slen > 0){
41764             if(!this.fieldTpl){
41765                 var t = new Roo.Template(
41766                     '<div class="x-form-item {5}">',
41767                         '<label for="{0}" style="{2}">{1}{4}</label>',
41768                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41769                         '</div>',
41770                     '</div><div class="x-form-clear-left"></div>'
41771                 );
41772                 t.disableFormats = true;
41773                 t.compile();
41774                 Roo.form.Layout.prototype.fieldTpl = t;
41775             }
41776             for(var i = 0; i < slen; i++) {
41777                 if(stack[i].isFormField){
41778                     this.renderField(stack[i]);
41779                 }else{
41780                     this.renderComponent(stack[i]);
41781                 }
41782             }
41783         }
41784         if(this.clear){
41785             this.el.createChild({cls:'x-form-clear'});
41786         }
41787     },
41788
41789     // private
41790     renderField : function(f){
41791         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41792                f.id, //0
41793                f.fieldLabel, //1
41794                f.labelStyle||this.labelStyle||'', //2
41795                this.elementStyle||'', //3
41796                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41797                f.itemCls||this.itemCls||''  //5
41798        ], true).getPrevSibling());
41799     },
41800
41801     // private
41802     renderComponent : function(c){
41803         c.render(c.isLayout ? this.el : this.el.createChild());    
41804     },
41805     /**
41806      * Adds a object form elements (using the xtype property as the factory method.)
41807      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41808      * @param {Object} config 
41809      */
41810     addxtype : function(o)
41811     {
41812         // create the lement.
41813         o.form = this.form;
41814         var fe = Roo.factory(o, Roo.form);
41815         this.form.allItems.push(fe);
41816         this.stack.push(fe);
41817         
41818         if (fe.isFormField) {
41819             this.form.items.add(fe);
41820         }
41821          
41822         return fe;
41823     }
41824 });
41825
41826 /**
41827  * @class Roo.form.Column
41828  * @extends Roo.form.Layout
41829  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41830  * @constructor
41831  * @param {Object} config Configuration options
41832  */
41833 Roo.form.Column = function(config){
41834     Roo.form.Column.superclass.constructor.call(this, config);
41835 };
41836
41837 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41838     /**
41839      * @cfg {Number/String} width
41840      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41841      */
41842     /**
41843      * @cfg {String/Object} autoCreate
41844      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41845      */
41846
41847     // private
41848     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41849
41850     // private
41851     onRender : function(ct, position){
41852         Roo.form.Column.superclass.onRender.call(this, ct, position);
41853         if(this.width){
41854             this.el.setWidth(this.width);
41855         }
41856     }
41857 });
41858
41859
41860 /**
41861  * @class Roo.form.Row
41862  * @extends Roo.form.Layout
41863  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41864  * @constructor
41865  * @param {Object} config Configuration options
41866  */
41867
41868  
41869 Roo.form.Row = function(config){
41870     Roo.form.Row.superclass.constructor.call(this, config);
41871 };
41872  
41873 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41874       /**
41875      * @cfg {Number/String} width
41876      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41877      */
41878     /**
41879      * @cfg {Number/String} height
41880      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41881      */
41882     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41883     
41884     padWidth : 20,
41885     // private
41886     onRender : function(ct, position){
41887         //console.log('row render');
41888         if(!this.rowTpl){
41889             var t = new Roo.Template(
41890                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41891                     '<label for="{0}" style="{2}">{1}{4}</label>',
41892                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41893                     '</div>',
41894                 '</div>'
41895             );
41896             t.disableFormats = true;
41897             t.compile();
41898             Roo.form.Layout.prototype.rowTpl = t;
41899         }
41900         this.fieldTpl = this.rowTpl;
41901         
41902         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41903         var labelWidth = 100;
41904         
41905         if ((this.labelAlign != 'top')) {
41906             if (typeof this.labelWidth == 'number') {
41907                 labelWidth = this.labelWidth
41908             }
41909             this.padWidth =  20 + labelWidth;
41910             
41911         }
41912         
41913         Roo.form.Column.superclass.onRender.call(this, ct, position);
41914         if(this.width){
41915             this.el.setWidth(this.width);
41916         }
41917         if(this.height){
41918             this.el.setHeight(this.height);
41919         }
41920     },
41921     
41922     // private
41923     renderField : function(f){
41924         f.fieldEl = this.fieldTpl.append(this.el, [
41925                f.id, f.fieldLabel,
41926                f.labelStyle||this.labelStyle||'',
41927                this.elementStyle||'',
41928                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41929                f.itemCls||this.itemCls||'',
41930                f.width ? f.width + this.padWidth : 160 + this.padWidth
41931        ],true);
41932     }
41933 });
41934  
41935
41936 /**
41937  * @class Roo.form.FieldSet
41938  * @extends Roo.form.Layout
41939  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41940  * @constructor
41941  * @param {Object} config Configuration options
41942  */
41943 Roo.form.FieldSet = function(config){
41944     Roo.form.FieldSet.superclass.constructor.call(this, config);
41945 };
41946
41947 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41948     /**
41949      * @cfg {String} legend
41950      * The text to display as the legend for the FieldSet (defaults to '')
41951      */
41952     /**
41953      * @cfg {String/Object} autoCreate
41954      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41955      */
41956
41957     // private
41958     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41959
41960     // private
41961     onRender : function(ct, position){
41962         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41963         if(this.legend){
41964             this.setLegend(this.legend);
41965         }
41966     },
41967
41968     // private
41969     setLegend : function(text){
41970         if(this.rendered){
41971             this.el.child('legend').update(text);
41972         }
41973     }
41974 });/*
41975  * Based on:
41976  * Ext JS Library 1.1.1
41977  * Copyright(c) 2006-2007, Ext JS, LLC.
41978  *
41979  * Originally Released Under LGPL - original licence link has changed is not relivant.
41980  *
41981  * Fork - LGPL
41982  * <script type="text/javascript">
41983  */
41984 /**
41985  * @class Roo.form.VTypes
41986  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41987  * @singleton
41988  */
41989 Roo.form.VTypes = function(){
41990     // closure these in so they are only created once.
41991     var alpha = /^[a-zA-Z_]+$/;
41992     var alphanum = /^[a-zA-Z0-9_]+$/;
41993     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41994     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41995
41996     // All these messages and functions are configurable
41997     return {
41998         /**
41999          * The function used to validate email addresses
42000          * @param {String} value The email address
42001          */
42002         'email' : function(v){
42003             return email.test(v);
42004         },
42005         /**
42006          * The error text to display when the email validation function returns false
42007          * @type String
42008          */
42009         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42010         /**
42011          * The keystroke filter mask to be applied on email input
42012          * @type RegExp
42013          */
42014         'emailMask' : /[a-z0-9_\.\-@]/i,
42015
42016         /**
42017          * The function used to validate URLs
42018          * @param {String} value The URL
42019          */
42020         'url' : function(v){
42021             return url.test(v);
42022         },
42023         /**
42024          * The error text to display when the url validation function returns false
42025          * @type String
42026          */
42027         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42028         
42029         /**
42030          * The function used to validate alpha values
42031          * @param {String} value The value
42032          */
42033         'alpha' : function(v){
42034             return alpha.test(v);
42035         },
42036         /**
42037          * The error text to display when the alpha validation function returns false
42038          * @type String
42039          */
42040         'alphaText' : 'This field should only contain letters and _',
42041         /**
42042          * The keystroke filter mask to be applied on alpha input
42043          * @type RegExp
42044          */
42045         'alphaMask' : /[a-z_]/i,
42046
42047         /**
42048          * The function used to validate alphanumeric values
42049          * @param {String} value The value
42050          */
42051         'alphanum' : function(v){
42052             return alphanum.test(v);
42053         },
42054         /**
42055          * The error text to display when the alphanumeric validation function returns false
42056          * @type String
42057          */
42058         'alphanumText' : 'This field should only contain letters, numbers and _',
42059         /**
42060          * The keystroke filter mask to be applied on alphanumeric input
42061          * @type RegExp
42062          */
42063         'alphanumMask' : /[a-z0-9_]/i
42064     };
42065 }();//<script type="text/javascript">
42066
42067 /**
42068  * @class Roo.form.FCKeditor
42069  * @extends Roo.form.TextArea
42070  * Wrapper around the FCKEditor http://www.fckeditor.net
42071  * @constructor
42072  * Creates a new FCKeditor
42073  * @param {Object} config Configuration options
42074  */
42075 Roo.form.FCKeditor = function(config){
42076     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42077     this.addEvents({
42078          /**
42079          * @event editorinit
42080          * Fired when the editor is initialized - you can add extra handlers here..
42081          * @param {FCKeditor} this
42082          * @param {Object} the FCK object.
42083          */
42084         editorinit : true
42085     });
42086     
42087     
42088 };
42089 Roo.form.FCKeditor.editors = { };
42090 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42091 {
42092     //defaultAutoCreate : {
42093     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42094     //},
42095     // private
42096     /**
42097      * @cfg {Object} fck options - see fck manual for details.
42098      */
42099     fckconfig : false,
42100     
42101     /**
42102      * @cfg {Object} fck toolbar set (Basic or Default)
42103      */
42104     toolbarSet : 'Basic',
42105     /**
42106      * @cfg {Object} fck BasePath
42107      */ 
42108     basePath : '/fckeditor/',
42109     
42110     
42111     frame : false,
42112     
42113     value : '',
42114     
42115    
42116     onRender : function(ct, position)
42117     {
42118         if(!this.el){
42119             this.defaultAutoCreate = {
42120                 tag: "textarea",
42121                 style:"width:300px;height:60px;",
42122                 autocomplete: "off"
42123             };
42124         }
42125         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42126         /*
42127         if(this.grow){
42128             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42129             if(this.preventScrollbars){
42130                 this.el.setStyle("overflow", "hidden");
42131             }
42132             this.el.setHeight(this.growMin);
42133         }
42134         */
42135         //console.log('onrender' + this.getId() );
42136         Roo.form.FCKeditor.editors[this.getId()] = this;
42137          
42138
42139         this.replaceTextarea() ;
42140         
42141     },
42142     
42143     getEditor : function() {
42144         return this.fckEditor;
42145     },
42146     /**
42147      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42148      * @param {Mixed} value The value to set
42149      */
42150     
42151     
42152     setValue : function(value)
42153     {
42154         //console.log('setValue: ' + value);
42155         
42156         if(typeof(value) == 'undefined') { // not sure why this is happending...
42157             return;
42158         }
42159         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42160         
42161         //if(!this.el || !this.getEditor()) {
42162         //    this.value = value;
42163             //this.setValue.defer(100,this,[value]);    
42164         //    return;
42165         //} 
42166         
42167         if(!this.getEditor()) {
42168             return;
42169         }
42170         
42171         this.getEditor().SetData(value);
42172         
42173         //
42174
42175     },
42176
42177     /**
42178      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42179      * @return {Mixed} value The field value
42180      */
42181     getValue : function()
42182     {
42183         
42184         if (this.frame && this.frame.dom.style.display == 'none') {
42185             return Roo.form.FCKeditor.superclass.getValue.call(this);
42186         }
42187         
42188         if(!this.el || !this.getEditor()) {
42189            
42190            // this.getValue.defer(100,this); 
42191             return this.value;
42192         }
42193        
42194         
42195         var value=this.getEditor().GetData();
42196         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42197         return Roo.form.FCKeditor.superclass.getValue.call(this);
42198         
42199
42200     },
42201
42202     /**
42203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42204      * @return {Mixed} value The field value
42205      */
42206     getRawValue : function()
42207     {
42208         if (this.frame && this.frame.dom.style.display == 'none') {
42209             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42210         }
42211         
42212         if(!this.el || !this.getEditor()) {
42213             //this.getRawValue.defer(100,this); 
42214             return this.value;
42215             return;
42216         }
42217         
42218         
42219         
42220         var value=this.getEditor().GetData();
42221         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42222         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42223          
42224     },
42225     
42226     setSize : function(w,h) {
42227         
42228         
42229         
42230         //if (this.frame && this.frame.dom.style.display == 'none') {
42231         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42232         //    return;
42233         //}
42234         //if(!this.el || !this.getEditor()) {
42235         //    this.setSize.defer(100,this, [w,h]); 
42236         //    return;
42237         //}
42238         
42239         
42240         
42241         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42242         
42243         this.frame.dom.setAttribute('width', w);
42244         this.frame.dom.setAttribute('height', h);
42245         this.frame.setSize(w,h);
42246         
42247     },
42248     
42249     toggleSourceEdit : function(value) {
42250         
42251       
42252          
42253         this.el.dom.style.display = value ? '' : 'none';
42254         this.frame.dom.style.display = value ?  'none' : '';
42255         
42256     },
42257     
42258     
42259     focus: function(tag)
42260     {
42261         if (this.frame.dom.style.display == 'none') {
42262             return Roo.form.FCKeditor.superclass.focus.call(this);
42263         }
42264         if(!this.el || !this.getEditor()) {
42265             this.focus.defer(100,this, [tag]); 
42266             return;
42267         }
42268         
42269         
42270         
42271         
42272         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42273         this.getEditor().Focus();
42274         if (tgs.length) {
42275             if (!this.getEditor().Selection.GetSelection()) {
42276                 this.focus.defer(100,this, [tag]); 
42277                 return;
42278             }
42279             
42280             
42281             var r = this.getEditor().EditorDocument.createRange();
42282             r.setStart(tgs[0],0);
42283             r.setEnd(tgs[0],0);
42284             this.getEditor().Selection.GetSelection().removeAllRanges();
42285             this.getEditor().Selection.GetSelection().addRange(r);
42286             this.getEditor().Focus();
42287         }
42288         
42289     },
42290     
42291     
42292     
42293     replaceTextarea : function()
42294     {
42295         if ( document.getElementById( this.getId() + '___Frame' ) )
42296             return ;
42297         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42298         //{
42299             // We must check the elements firstly using the Id and then the name.
42300         var oTextarea = document.getElementById( this.getId() );
42301         
42302         var colElementsByName = document.getElementsByName( this.getId() ) ;
42303          
42304         oTextarea.style.display = 'none' ;
42305
42306         if ( oTextarea.tabIndex ) {            
42307             this.TabIndex = oTextarea.tabIndex ;
42308         }
42309         
42310         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42311         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42312         this.frame = Roo.get(this.getId() + '___Frame')
42313     },
42314     
42315     _getConfigHtml : function()
42316     {
42317         var sConfig = '' ;
42318
42319         for ( var o in this.fckconfig ) {
42320             sConfig += sConfig.length > 0  ? '&amp;' : '';
42321             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42322         }
42323
42324         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42325     },
42326     
42327     
42328     _getIFrameHtml : function()
42329     {
42330         var sFile = 'fckeditor.html' ;
42331         /* no idea what this is about..
42332         try
42333         {
42334             if ( (/fcksource=true/i).test( window.top.location.search ) )
42335                 sFile = 'fckeditor.original.html' ;
42336         }
42337         catch (e) { 
42338         */
42339
42340         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42341         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42342         
42343         
42344         var html = '<iframe id="' + this.getId() +
42345             '___Frame" src="' + sLink +
42346             '" width="' + this.width +
42347             '" height="' + this.height + '"' +
42348             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42349             ' frameborder="0" scrolling="no"></iframe>' ;
42350
42351         return html ;
42352     },
42353     
42354     _insertHtmlBefore : function( html, element )
42355     {
42356         if ( element.insertAdjacentHTML )       {
42357             // IE
42358             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42359         } else { // Gecko
42360             var oRange = document.createRange() ;
42361             oRange.setStartBefore( element ) ;
42362             var oFragment = oRange.createContextualFragment( html );
42363             element.parentNode.insertBefore( oFragment, element ) ;
42364         }
42365     }
42366     
42367     
42368   
42369     
42370     
42371     
42372     
42373
42374 });
42375
42376 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42377
42378 function FCKeditor_OnComplete(editorInstance){
42379     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42380     f.fckEditor = editorInstance;
42381     //console.log("loaded");
42382     f.fireEvent('editorinit', f, editorInstance);
42383
42384   
42385
42386  
42387
42388
42389
42390
42391
42392
42393
42394
42395
42396
42397
42398
42399
42400
42401
42402 //<script type="text/javascript">
42403 /**
42404  * @class Roo.form.GridField
42405  * @extends Roo.form.Field
42406  * Embed a grid (or editable grid into a form)
42407  * STATUS ALPHA
42408  * 
42409  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42410  * it needs 
42411  * xgrid.store = Roo.data.Store
42412  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42413  * xgrid.store.reader = Roo.data.JsonReader 
42414  * 
42415  * 
42416  * @constructor
42417  * Creates a new GridField
42418  * @param {Object} config Configuration options
42419  */
42420 Roo.form.GridField = function(config){
42421     Roo.form.GridField.superclass.constructor.call(this, config);
42422      
42423 };
42424
42425 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42426     /**
42427      * @cfg {Number} width  - used to restrict width of grid..
42428      */
42429     width : 100,
42430     /**
42431      * @cfg {Number} height - used to restrict height of grid..
42432      */
42433     height : 50,
42434      /**
42435      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42436          * 
42437          *}
42438      */
42439     xgrid : false, 
42440     /**
42441      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42442      * {tag: "input", type: "checkbox", autocomplete: "off"})
42443      */
42444    // defaultAutoCreate : { tag: 'div' },
42445     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42446     /**
42447      * @cfg {String} addTitle Text to include for adding a title.
42448      */
42449     addTitle : false,
42450     //
42451     onResize : function(){
42452         Roo.form.Field.superclass.onResize.apply(this, arguments);
42453     },
42454
42455     initEvents : function(){
42456         // Roo.form.Checkbox.superclass.initEvents.call(this);
42457         // has no events...
42458        
42459     },
42460
42461
42462     getResizeEl : function(){
42463         return this.wrap;
42464     },
42465
42466     getPositionEl : function(){
42467         return this.wrap;
42468     },
42469
42470     // private
42471     onRender : function(ct, position){
42472         
42473         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42474         var style = this.style;
42475         delete this.style;
42476         
42477         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42478         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42479         this.viewEl = this.wrap.createChild({ tag: 'div' });
42480         if (style) {
42481             this.viewEl.applyStyles(style);
42482         }
42483         if (this.width) {
42484             this.viewEl.setWidth(this.width);
42485         }
42486         if (this.height) {
42487             this.viewEl.setHeight(this.height);
42488         }
42489         //if(this.inputValue !== undefined){
42490         //this.setValue(this.value);
42491         
42492         
42493         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42494         
42495         
42496         this.grid.render();
42497         this.grid.getDataSource().on('remove', this.refreshValue, this);
42498         this.grid.getDataSource().on('update', this.refreshValue, this);
42499         this.grid.on('afteredit', this.refreshValue, this);
42500  
42501     },
42502      
42503     
42504     /**
42505      * Sets the value of the item. 
42506      * @param {String} either an object  or a string..
42507      */
42508     setValue : function(v){
42509         //this.value = v;
42510         v = v || []; // empty set..
42511         // this does not seem smart - it really only affects memoryproxy grids..
42512         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42513             var ds = this.grid.getDataSource();
42514             // assumes a json reader..
42515             var data = {}
42516             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42517             ds.loadData( data);
42518         }
42519         Roo.form.GridField.superclass.setValue.call(this, v);
42520         this.refreshValue();
42521         // should load data in the grid really....
42522     },
42523     
42524     // private
42525     refreshValue: function() {
42526          var val = [];
42527         this.grid.getDataSource().each(function(r) {
42528             val.push(r.data);
42529         });
42530         this.el.dom.value = Roo.encode(val);
42531     }
42532     
42533      
42534     
42535     
42536 });/*
42537  * Based on:
42538  * Ext JS Library 1.1.1
42539  * Copyright(c) 2006-2007, Ext JS, LLC.
42540  *
42541  * Originally Released Under LGPL - original licence link has changed is not relivant.
42542  *
42543  * Fork - LGPL
42544  * <script type="text/javascript">
42545  */
42546 /**
42547  * @class Roo.form.DisplayField
42548  * @extends Roo.form.Field
42549  * A generic Field to display non-editable data.
42550  * @constructor
42551  * Creates a new Display Field item.
42552  * @param {Object} config Configuration options
42553  */
42554 Roo.form.DisplayField = function(config){
42555     Roo.form.DisplayField.superclass.constructor.call(this, config);
42556     
42557 };
42558
42559 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42560     inputType:      'hidden',
42561     allowBlank:     true,
42562     readOnly:         true,
42563     
42564  
42565     /**
42566      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42567      */
42568     focusClass : undefined,
42569     /**
42570      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42571      */
42572     fieldClass: 'x-form-field',
42573     
42574      /**
42575      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42576      */
42577     valueRenderer: undefined,
42578     
42579     width: 100,
42580     /**
42581      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42582      * {tag: "input", type: "checkbox", autocomplete: "off"})
42583      */
42584      
42585  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42586
42587     onResize : function(){
42588         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42589         
42590     },
42591
42592     initEvents : function(){
42593         // Roo.form.Checkbox.superclass.initEvents.call(this);
42594         // has no events...
42595        
42596     },
42597
42598
42599     getResizeEl : function(){
42600         return this.wrap;
42601     },
42602
42603     getPositionEl : function(){
42604         return this.wrap;
42605     },
42606
42607     // private
42608     onRender : function(ct, position){
42609         
42610         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42611         //if(this.inputValue !== undefined){
42612         this.wrap = this.el.wrap();
42613         
42614         this.viewEl = this.wrap.createChild({ tag: 'div'});
42615         
42616         if (this.bodyStyle) {
42617             this.viewEl.applyStyles(this.bodyStyle);
42618         }
42619         //this.viewEl.setStyle('padding', '2px');
42620         
42621         this.setValue(this.value);
42622         
42623     },
42624 /*
42625     // private
42626     initValue : Roo.emptyFn,
42627
42628   */
42629
42630         // private
42631     onClick : function(){
42632         
42633     },
42634
42635     /**
42636      * Sets the checked state of the checkbox.
42637      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42638      */
42639     setValue : function(v){
42640         this.value = v;
42641         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42642         // this might be called before we have a dom element..
42643         if (!this.viewEl) {
42644             return;
42645         }
42646         this.viewEl.dom.innerHTML = html;
42647         Roo.form.DisplayField.superclass.setValue.call(this, v);
42648
42649     }
42650 });//<script type="text/javasscript">
42651  
42652
42653 /**
42654  * @class Roo.DDView
42655  * A DnD enabled version of Roo.View.
42656  * @param {Element/String} container The Element in which to create the View.
42657  * @param {String} tpl The template string used to create the markup for each element of the View
42658  * @param {Object} config The configuration properties. These include all the config options of
42659  * {@link Roo.View} plus some specific to this class.<br>
42660  * <p>
42661  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42662  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42663  * <p>
42664  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42665 .x-view-drag-insert-above {
42666         border-top:1px dotted #3366cc;
42667 }
42668 .x-view-drag-insert-below {
42669         border-bottom:1px dotted #3366cc;
42670 }
42671 </code></pre>
42672  * 
42673  */
42674  
42675 Roo.DDView = function(container, tpl, config) {
42676     Roo.DDView.superclass.constructor.apply(this, arguments);
42677     this.getEl().setStyle("outline", "0px none");
42678     this.getEl().unselectable();
42679     if (this.dragGroup) {
42680                 this.setDraggable(this.dragGroup.split(","));
42681     }
42682     if (this.dropGroup) {
42683                 this.setDroppable(this.dropGroup.split(","));
42684     }
42685     if (this.deletable) {
42686         this.setDeletable();
42687     }
42688     this.isDirtyFlag = false;
42689         this.addEvents({
42690                 "drop" : true
42691         });
42692 };
42693
42694 Roo.extend(Roo.DDView, Roo.View, {
42695 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42696 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42697 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42698 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42699
42700         isFormField: true,
42701
42702         reset: Roo.emptyFn,
42703         
42704         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42705
42706         validate: function() {
42707                 return true;
42708         },
42709         
42710         destroy: function() {
42711                 this.purgeListeners();
42712                 this.getEl.removeAllListeners();
42713                 this.getEl().remove();
42714                 if (this.dragZone) {
42715                         if (this.dragZone.destroy) {
42716                                 this.dragZone.destroy();
42717                         }
42718                 }
42719                 if (this.dropZone) {
42720                         if (this.dropZone.destroy) {
42721                                 this.dropZone.destroy();
42722                         }
42723                 }
42724         },
42725
42726 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42727         getName: function() {
42728                 return this.name;
42729         },
42730
42731 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42732         setValue: function(v) {
42733                 if (!this.store) {
42734                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42735                 }
42736                 var data = {};
42737                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42738                 this.store.proxy = new Roo.data.MemoryProxy(data);
42739                 this.store.load();
42740         },
42741
42742 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42743         getValue: function() {
42744                 var result = '(';
42745                 this.store.each(function(rec) {
42746                         result += rec.id + ',';
42747                 });
42748                 return result.substr(0, result.length - 1) + ')';
42749         },
42750         
42751         getIds: function() {
42752                 var i = 0, result = new Array(this.store.getCount());
42753                 this.store.each(function(rec) {
42754                         result[i++] = rec.id;
42755                 });
42756                 return result;
42757         },
42758         
42759         isDirty: function() {
42760                 return this.isDirtyFlag;
42761         },
42762
42763 /**
42764  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42765  *      whole Element becomes the target, and this causes the drop gesture to append.
42766  */
42767     getTargetFromEvent : function(e) {
42768                 var target = e.getTarget();
42769                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42770                 target = target.parentNode;
42771                 }
42772                 if (!target) {
42773                         target = this.el.dom.lastChild || this.el.dom;
42774                 }
42775                 return target;
42776     },
42777
42778 /**
42779  *      Create the drag data which consists of an object which has the property "ddel" as
42780  *      the drag proxy element. 
42781  */
42782     getDragData : function(e) {
42783         var target = this.findItemFromChild(e.getTarget());
42784                 if(target) {
42785                         this.handleSelection(e);
42786                         var selNodes = this.getSelectedNodes();
42787             var dragData = {
42788                 source: this,
42789                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42790                 nodes: selNodes,
42791                 records: []
42792                         };
42793                         var selectedIndices = this.getSelectedIndexes();
42794                         for (var i = 0; i < selectedIndices.length; i++) {
42795                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42796                         }
42797                         if (selNodes.length == 1) {
42798                                 dragData.ddel = target.cloneNode(true); // the div element
42799                         } else {
42800                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42801                                 div.className = 'multi-proxy';
42802                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42803                                         div.appendChild(selNodes[i].cloneNode(true));
42804                                 }
42805                                 dragData.ddel = div;
42806                         }
42807             //console.log(dragData)
42808             //console.log(dragData.ddel.innerHTML)
42809                         return dragData;
42810                 }
42811         //console.log('nodragData')
42812                 return false;
42813     },
42814     
42815 /**     Specify to which ddGroup items in this DDView may be dragged. */
42816     setDraggable: function(ddGroup) {
42817         if (ddGroup instanceof Array) {
42818                 Roo.each(ddGroup, this.setDraggable, this);
42819                 return;
42820         }
42821         if (this.dragZone) {
42822                 this.dragZone.addToGroup(ddGroup);
42823         } else {
42824                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42825                                 containerScroll: true,
42826                                 ddGroup: ddGroup 
42827
42828                         });
42829 //                      Draggability implies selection. DragZone's mousedown selects the element.
42830                         if (!this.multiSelect) { this.singleSelect = true; }
42831
42832 //                      Wire the DragZone's handlers up to methods in *this*
42833                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42834                 }
42835     },
42836
42837 /**     Specify from which ddGroup this DDView accepts drops. */
42838     setDroppable: function(ddGroup) {
42839         if (ddGroup instanceof Array) {
42840                 Roo.each(ddGroup, this.setDroppable, this);
42841                 return;
42842         }
42843         if (this.dropZone) {
42844                 this.dropZone.addToGroup(ddGroup);
42845         } else {
42846                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42847                                 containerScroll: true,
42848                                 ddGroup: ddGroup
42849                         });
42850
42851 //                      Wire the DropZone's handlers up to methods in *this*
42852                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42853                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42854                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42855                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42856                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42857                 }
42858     },
42859
42860 /**     Decide whether to drop above or below a View node. */
42861     getDropPoint : function(e, n, dd){
42862         if (n == this.el.dom) { return "above"; }
42863                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42864                 var c = t + (b - t) / 2;
42865                 var y = Roo.lib.Event.getPageY(e);
42866                 if(y <= c) {
42867                         return "above";
42868                 }else{
42869                         return "below";
42870                 }
42871     },
42872
42873     onNodeEnter : function(n, dd, e, data){
42874                 return false;
42875     },
42876     
42877     onNodeOver : function(n, dd, e, data){
42878                 var pt = this.getDropPoint(e, n, dd);
42879                 // set the insert point style on the target node
42880                 var dragElClass = this.dropNotAllowed;
42881                 if (pt) {
42882                         var targetElClass;
42883                         if (pt == "above"){
42884                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42885                                 targetElClass = "x-view-drag-insert-above";
42886                         } else {
42887                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42888                                 targetElClass = "x-view-drag-insert-below";
42889                         }
42890                         if (this.lastInsertClass != targetElClass){
42891                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42892                                 this.lastInsertClass = targetElClass;
42893                         }
42894                 }
42895                 return dragElClass;
42896         },
42897
42898     onNodeOut : function(n, dd, e, data){
42899                 this.removeDropIndicators(n);
42900     },
42901
42902     onNodeDrop : function(n, dd, e, data){
42903         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42904                 return false;
42905         }
42906         var pt = this.getDropPoint(e, n, dd);
42907                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42908                 if (pt == "below") { insertAt++; }
42909                 for (var i = 0; i < data.records.length; i++) {
42910                         var r = data.records[i];
42911                         var dup = this.store.getById(r.id);
42912                         if (dup && (dd != this.dragZone)) {
42913                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42914                         } else {
42915                                 if (data.copy) {
42916                                         this.store.insert(insertAt++, r.copy());
42917                                 } else {
42918                                         data.source.isDirtyFlag = true;
42919                                         r.store.remove(r);
42920                                         this.store.insert(insertAt++, r);
42921                                 }
42922                                 this.isDirtyFlag = true;
42923                         }
42924                 }
42925                 this.dragZone.cachedTarget = null;
42926                 return true;
42927     },
42928
42929     removeDropIndicators : function(n){
42930                 if(n){
42931                         Roo.fly(n).removeClass([
42932                                 "x-view-drag-insert-above",
42933                                 "x-view-drag-insert-below"]);
42934                         this.lastInsertClass = "_noclass";
42935                 }
42936     },
42937
42938 /**
42939  *      Utility method. Add a delete option to the DDView's context menu.
42940  *      @param {String} imageUrl The URL of the "delete" icon image.
42941  */
42942         setDeletable: function(imageUrl) {
42943                 if (!this.singleSelect && !this.multiSelect) {
42944                         this.singleSelect = true;
42945                 }
42946                 var c = this.getContextMenu();
42947                 this.contextMenu.on("itemclick", function(item) {
42948                         switch (item.id) {
42949                                 case "delete":
42950                                         this.remove(this.getSelectedIndexes());
42951                                         break;
42952                         }
42953                 }, this);
42954                 this.contextMenu.add({
42955                         icon: imageUrl,
42956                         id: "delete",
42957                         text: 'Delete'
42958                 });
42959         },
42960         
42961 /**     Return the context menu for this DDView. */
42962         getContextMenu: function() {
42963                 if (!this.contextMenu) {
42964 //                      Create the View's context menu
42965                         this.contextMenu = new Roo.menu.Menu({
42966                                 id: this.id + "-contextmenu"
42967                         });
42968                         this.el.on("contextmenu", this.showContextMenu, this);
42969                 }
42970                 return this.contextMenu;
42971         },
42972         
42973         disableContextMenu: function() {
42974                 if (this.contextMenu) {
42975                         this.el.un("contextmenu", this.showContextMenu, this);
42976                 }
42977         },
42978
42979         showContextMenu: function(e, item) {
42980         item = this.findItemFromChild(e.getTarget());
42981                 if (item) {
42982                         e.stopEvent();
42983                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42984                         this.contextMenu.showAt(e.getXY());
42985             }
42986     },
42987
42988 /**
42989  *      Remove {@link Roo.data.Record}s at the specified indices.
42990  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42991  */
42992     remove: function(selectedIndices) {
42993                 selectedIndices = [].concat(selectedIndices);
42994                 for (var i = 0; i < selectedIndices.length; i++) {
42995                         var rec = this.store.getAt(selectedIndices[i]);
42996                         this.store.remove(rec);
42997                 }
42998     },
42999
43000 /**
43001  *      Double click fires the event, but also, if this is draggable, and there is only one other
43002  *      related DropZone, it transfers the selected node.
43003  */
43004     onDblClick : function(e){
43005         var item = this.findItemFromChild(e.getTarget());
43006         if(item){
43007             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43008                 return false;
43009             }
43010             if (this.dragGroup) {
43011                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43012                     while (targets.indexOf(this.dropZone) > -1) {
43013                             targets.remove(this.dropZone);
43014                                 }
43015                     if (targets.length == 1) {
43016                                         this.dragZone.cachedTarget = null;
43017                         var el = Roo.get(targets[0].getEl());
43018                         var box = el.getBox(true);
43019                         targets[0].onNodeDrop(el.dom, {
43020                                 target: el.dom,
43021                                 xy: [box.x, box.y + box.height - 1]
43022                         }, null, this.getDragData(e));
43023                     }
43024                 }
43025         }
43026     },
43027     
43028     handleSelection: function(e) {
43029                 this.dragZone.cachedTarget = null;
43030         var item = this.findItemFromChild(e.getTarget());
43031         if (!item) {
43032                 this.clearSelections(true);
43033                 return;
43034         }
43035                 if (item && (this.multiSelect || this.singleSelect)){
43036                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43037                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43038                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43039                                 this.unselect(item);
43040                         } else {
43041                                 this.select(item, this.multiSelect && e.ctrlKey);
43042                                 this.lastSelection = item;
43043                         }
43044                 }
43045     },
43046
43047     onItemClick : function(item, index, e){
43048                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43049                         return false;
43050                 }
43051                 return true;
43052     },
43053
43054     unselect : function(nodeInfo, suppressEvent){
43055                 var node = this.getNode(nodeInfo);
43056                 if(node && this.isSelected(node)){
43057                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43058                                 Roo.fly(node).removeClass(this.selectedClass);
43059                                 this.selections.remove(node);
43060                                 if(!suppressEvent){
43061                                         this.fireEvent("selectionchange", this, this.selections);
43062                                 }
43063                         }
43064                 }
43065     }
43066 });
43067 /*
43068  * Based on:
43069  * Ext JS Library 1.1.1
43070  * Copyright(c) 2006-2007, Ext JS, LLC.
43071  *
43072  * Originally Released Under LGPL - original licence link has changed is not relivant.
43073  *
43074  * Fork - LGPL
43075  * <script type="text/javascript">
43076  */
43077  
43078 /**
43079  * @class Roo.LayoutManager
43080  * @extends Roo.util.Observable
43081  * Base class for layout managers.
43082  */
43083 Roo.LayoutManager = function(container, config){
43084     Roo.LayoutManager.superclass.constructor.call(this);
43085     this.el = Roo.get(container);
43086     // ie scrollbar fix
43087     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43088         document.body.scroll = "no";
43089     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43090         this.el.position('relative');
43091     }
43092     this.id = this.el.id;
43093     this.el.addClass("x-layout-container");
43094     /** false to disable window resize monitoring @type Boolean */
43095     this.monitorWindowResize = true;
43096     this.regions = {};
43097     this.addEvents({
43098         /**
43099          * @event layout
43100          * Fires when a layout is performed. 
43101          * @param {Roo.LayoutManager} this
43102          */
43103         "layout" : true,
43104         /**
43105          * @event regionresized
43106          * Fires when the user resizes a region. 
43107          * @param {Roo.LayoutRegion} region The resized region
43108          * @param {Number} newSize The new size (width for east/west, height for north/south)
43109          */
43110         "regionresized" : true,
43111         /**
43112          * @event regioncollapsed
43113          * Fires when a region is collapsed. 
43114          * @param {Roo.LayoutRegion} region The collapsed region
43115          */
43116         "regioncollapsed" : true,
43117         /**
43118          * @event regionexpanded
43119          * Fires when a region is expanded.  
43120          * @param {Roo.LayoutRegion} region The expanded region
43121          */
43122         "regionexpanded" : true
43123     });
43124     this.updating = false;
43125     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43126 };
43127
43128 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43129     /**
43130      * Returns true if this layout is currently being updated
43131      * @return {Boolean}
43132      */
43133     isUpdating : function(){
43134         return this.updating; 
43135     },
43136     
43137     /**
43138      * Suspend the LayoutManager from doing auto-layouts while
43139      * making multiple add or remove calls
43140      */
43141     beginUpdate : function(){
43142         this.updating = true;    
43143     },
43144     
43145     /**
43146      * Restore auto-layouts and optionally disable the manager from performing a layout
43147      * @param {Boolean} noLayout true to disable a layout update 
43148      */
43149     endUpdate : function(noLayout){
43150         this.updating = false;
43151         if(!noLayout){
43152             this.layout();
43153         }    
43154     },
43155     
43156     layout: function(){
43157         
43158     },
43159     
43160     onRegionResized : function(region, newSize){
43161         this.fireEvent("regionresized", region, newSize);
43162         this.layout();
43163     },
43164     
43165     onRegionCollapsed : function(region){
43166         this.fireEvent("regioncollapsed", region);
43167     },
43168     
43169     onRegionExpanded : function(region){
43170         this.fireEvent("regionexpanded", region);
43171     },
43172         
43173     /**
43174      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43175      * performs box-model adjustments.
43176      * @return {Object} The size as an object {width: (the width), height: (the height)}
43177      */
43178     getViewSize : function(){
43179         var size;
43180         if(this.el.dom != document.body){
43181             size = this.el.getSize();
43182         }else{
43183             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43184         }
43185         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43186         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43187         return size;
43188     },
43189     
43190     /**
43191      * Returns the Element this layout is bound to.
43192      * @return {Roo.Element}
43193      */
43194     getEl : function(){
43195         return this.el;
43196     },
43197     
43198     /**
43199      * Returns the specified region.
43200      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43201      * @return {Roo.LayoutRegion}
43202      */
43203     getRegion : function(target){
43204         return this.regions[target.toLowerCase()];
43205     },
43206     
43207     onWindowResize : function(){
43208         if(this.monitorWindowResize){
43209             this.layout();
43210         }
43211     }
43212 });/*
43213  * Based on:
43214  * Ext JS Library 1.1.1
43215  * Copyright(c) 2006-2007, Ext JS, LLC.
43216  *
43217  * Originally Released Under LGPL - original licence link has changed is not relivant.
43218  *
43219  * Fork - LGPL
43220  * <script type="text/javascript">
43221  */
43222 /**
43223  * @class Roo.BorderLayout
43224  * @extends Roo.LayoutManager
43225  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43226  * please see: <br><br>
43227  * <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>
43228  * <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>
43229  * Example:
43230  <pre><code>
43231  var layout = new Roo.BorderLayout(document.body, {
43232     north: {
43233         initialSize: 25,
43234         titlebar: false
43235     },
43236     west: {
43237         split:true,
43238         initialSize: 200,
43239         minSize: 175,
43240         maxSize: 400,
43241         titlebar: true,
43242         collapsible: true
43243     },
43244     east: {
43245         split:true,
43246         initialSize: 202,
43247         minSize: 175,
43248         maxSize: 400,
43249         titlebar: true,
43250         collapsible: true
43251     },
43252     south: {
43253         split:true,
43254         initialSize: 100,
43255         minSize: 100,
43256         maxSize: 200,
43257         titlebar: true,
43258         collapsible: true
43259     },
43260     center: {
43261         titlebar: true,
43262         autoScroll:true,
43263         resizeTabs: true,
43264         minTabWidth: 50,
43265         preferredTabWidth: 150
43266     }
43267 });
43268
43269 // shorthand
43270 var CP = Roo.ContentPanel;
43271
43272 layout.beginUpdate();
43273 layout.add("north", new CP("north", "North"));
43274 layout.add("south", new CP("south", {title: "South", closable: true}));
43275 layout.add("west", new CP("west", {title: "West"}));
43276 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43277 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43278 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43279 layout.getRegion("center").showPanel("center1");
43280 layout.endUpdate();
43281 </code></pre>
43282
43283 <b>The container the layout is rendered into can be either the body element or any other element.
43284 If it is not the body element, the container needs to either be an absolute positioned element,
43285 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43286 the container size if it is not the body element.</b>
43287
43288 * @constructor
43289 * Create a new BorderLayout
43290 * @param {String/HTMLElement/Element} container The container this layout is bound to
43291 * @param {Object} config Configuration options
43292  */
43293 Roo.BorderLayout = function(container, config){
43294     config = config || {};
43295     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43296     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43297     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43298         var target = this.factory.validRegions[i];
43299         if(config[target]){
43300             this.addRegion(target, config[target]);
43301         }
43302     }
43303 };
43304
43305 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43306     /**
43307      * Creates and adds a new region if it doesn't already exist.
43308      * @param {String} target The target region key (north, south, east, west or center).
43309      * @param {Object} config The regions config object
43310      * @return {BorderLayoutRegion} The new region
43311      */
43312     addRegion : function(target, config){
43313         if(!this.regions[target]){
43314             var r = this.factory.create(target, this, config);
43315             this.bindRegion(target, r);
43316         }
43317         return this.regions[target];
43318     },
43319
43320     // private (kinda)
43321     bindRegion : function(name, r){
43322         this.regions[name] = r;
43323         r.on("visibilitychange", this.layout, this);
43324         r.on("paneladded", this.layout, this);
43325         r.on("panelremoved", this.layout, this);
43326         r.on("invalidated", this.layout, this);
43327         r.on("resized", this.onRegionResized, this);
43328         r.on("collapsed", this.onRegionCollapsed, this);
43329         r.on("expanded", this.onRegionExpanded, this);
43330     },
43331
43332     /**
43333      * Performs a layout update.
43334      */
43335     layout : function(){
43336         if(this.updating) return;
43337         var size = this.getViewSize();
43338         var w = size.width;
43339         var h = size.height;
43340         var centerW = w;
43341         var centerH = h;
43342         var centerY = 0;
43343         var centerX = 0;
43344         //var x = 0, y = 0;
43345
43346         var rs = this.regions;
43347         var north = rs["north"];
43348         var south = rs["south"]; 
43349         var west = rs["west"];
43350         var east = rs["east"];
43351         var center = rs["center"];
43352         //if(this.hideOnLayout){ // not supported anymore
43353             //c.el.setStyle("display", "none");
43354         //}
43355         if(north && north.isVisible()){
43356             var b = north.getBox();
43357             var m = north.getMargins();
43358             b.width = w - (m.left+m.right);
43359             b.x = m.left;
43360             b.y = m.top;
43361             centerY = b.height + b.y + m.bottom;
43362             centerH -= centerY;
43363             north.updateBox(this.safeBox(b));
43364         }
43365         if(south && south.isVisible()){
43366             var b = south.getBox();
43367             var m = south.getMargins();
43368             b.width = w - (m.left+m.right);
43369             b.x = m.left;
43370             var totalHeight = (b.height + m.top + m.bottom);
43371             b.y = h - totalHeight + m.top;
43372             centerH -= totalHeight;
43373             south.updateBox(this.safeBox(b));
43374         }
43375         if(west && west.isVisible()){
43376             var b = west.getBox();
43377             var m = west.getMargins();
43378             b.height = centerH - (m.top+m.bottom);
43379             b.x = m.left;
43380             b.y = centerY + m.top;
43381             var totalWidth = (b.width + m.left + m.right);
43382             centerX += totalWidth;
43383             centerW -= totalWidth;
43384             west.updateBox(this.safeBox(b));
43385         }
43386         if(east && east.isVisible()){
43387             var b = east.getBox();
43388             var m = east.getMargins();
43389             b.height = centerH - (m.top+m.bottom);
43390             var totalWidth = (b.width + m.left + m.right);
43391             b.x = w - totalWidth + m.left;
43392             b.y = centerY + m.top;
43393             centerW -= totalWidth;
43394             east.updateBox(this.safeBox(b));
43395         }
43396         if(center){
43397             var m = center.getMargins();
43398             var centerBox = {
43399                 x: centerX + m.left,
43400                 y: centerY + m.top,
43401                 width: centerW - (m.left+m.right),
43402                 height: centerH - (m.top+m.bottom)
43403             };
43404             //if(this.hideOnLayout){
43405                 //center.el.setStyle("display", "block");
43406             //}
43407             center.updateBox(this.safeBox(centerBox));
43408         }
43409         this.el.repaint();
43410         this.fireEvent("layout", this);
43411     },
43412
43413     // private
43414     safeBox : function(box){
43415         box.width = Math.max(0, box.width);
43416         box.height = Math.max(0, box.height);
43417         return box;
43418     },
43419
43420     /**
43421      * Adds a ContentPanel (or subclass) to this layout.
43422      * @param {String} target The target region key (north, south, east, west or center).
43423      * @param {Roo.ContentPanel} panel The panel to add
43424      * @return {Roo.ContentPanel} The added panel
43425      */
43426     add : function(target, panel){
43427          
43428         target = target.toLowerCase();
43429         return this.regions[target].add(panel);
43430     },
43431
43432     /**
43433      * Remove a ContentPanel (or subclass) to this layout.
43434      * @param {String} target The target region key (north, south, east, west or center).
43435      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43436      * @return {Roo.ContentPanel} The removed panel
43437      */
43438     remove : function(target, panel){
43439         target = target.toLowerCase();
43440         return this.regions[target].remove(panel);
43441     },
43442
43443     /**
43444      * Searches all regions for a panel with the specified id
43445      * @param {String} panelId
43446      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43447      */
43448     findPanel : function(panelId){
43449         var rs = this.regions;
43450         for(var target in rs){
43451             if(typeof rs[target] != "function"){
43452                 var p = rs[target].getPanel(panelId);
43453                 if(p){
43454                     return p;
43455                 }
43456             }
43457         }
43458         return null;
43459     },
43460
43461     /**
43462      * Searches all regions for a panel with the specified id and activates (shows) it.
43463      * @param {String/ContentPanel} panelId The panels id or the panel itself
43464      * @return {Roo.ContentPanel} The shown panel or null
43465      */
43466     showPanel : function(panelId) {
43467       var rs = this.regions;
43468       for(var target in rs){
43469          var r = rs[target];
43470          if(typeof r != "function"){
43471             if(r.hasPanel(panelId)){
43472                return r.showPanel(panelId);
43473             }
43474          }
43475       }
43476       return null;
43477    },
43478
43479    /**
43480      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43481      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43482      */
43483     restoreState : function(provider){
43484         if(!provider){
43485             provider = Roo.state.Manager;
43486         }
43487         var sm = new Roo.LayoutStateManager();
43488         sm.init(this, provider);
43489     },
43490
43491     /**
43492      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43493      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43494      * a valid ContentPanel config object.  Example:
43495      * <pre><code>
43496 // Create the main layout
43497 var layout = new Roo.BorderLayout('main-ct', {
43498     west: {
43499         split:true,
43500         minSize: 175,
43501         titlebar: true
43502     },
43503     center: {
43504         title:'Components'
43505     }
43506 }, 'main-ct');
43507
43508 // Create and add multiple ContentPanels at once via configs
43509 layout.batchAdd({
43510    west: {
43511        id: 'source-files',
43512        autoCreate:true,
43513        title:'Ext Source Files',
43514        autoScroll:true,
43515        fitToFrame:true
43516    },
43517    center : {
43518        el: cview,
43519        autoScroll:true,
43520        fitToFrame:true,
43521        toolbar: tb,
43522        resizeEl:'cbody'
43523    }
43524 });
43525 </code></pre>
43526      * @param {Object} regions An object containing ContentPanel configs by region name
43527      */
43528     batchAdd : function(regions){
43529         this.beginUpdate();
43530         for(var rname in regions){
43531             var lr = this.regions[rname];
43532             if(lr){
43533                 this.addTypedPanels(lr, regions[rname]);
43534             }
43535         }
43536         this.endUpdate();
43537     },
43538
43539     // private
43540     addTypedPanels : function(lr, ps){
43541         if(typeof ps == 'string'){
43542             lr.add(new Roo.ContentPanel(ps));
43543         }
43544         else if(ps instanceof Array){
43545             for(var i =0, len = ps.length; i < len; i++){
43546                 this.addTypedPanels(lr, ps[i]);
43547             }
43548         }
43549         else if(!ps.events){ // raw config?
43550             var el = ps.el;
43551             delete ps.el; // prevent conflict
43552             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43553         }
43554         else {  // panel object assumed!
43555             lr.add(ps);
43556         }
43557     },
43558     /**
43559      * Adds a xtype elements to the layout.
43560      * <pre><code>
43561
43562 layout.addxtype({
43563        xtype : 'ContentPanel',
43564        region: 'west',
43565        items: [ .... ]
43566    }
43567 );
43568
43569 layout.addxtype({
43570         xtype : 'NestedLayoutPanel',
43571         region: 'west',
43572         layout: {
43573            center: { },
43574            west: { }   
43575         },
43576         items : [ ... list of content panels or nested layout panels.. ]
43577    }
43578 );
43579 </code></pre>
43580      * @param {Object} cfg Xtype definition of item to add.
43581      */
43582     addxtype : function(cfg)
43583     {
43584         // basically accepts a pannel...
43585         // can accept a layout region..!?!?
43586        // console.log('BorderLayout add ' + cfg.xtype)
43587         
43588         if (!cfg.xtype.match(/Panel$/)) {
43589             return false;
43590         }
43591         var ret = false;
43592         var region = cfg.region;
43593         delete cfg.region;
43594         
43595           
43596         var xitems = [];
43597         if (cfg.items) {
43598             xitems = cfg.items;
43599             delete cfg.items;
43600         }
43601         
43602         
43603         switch(cfg.xtype) 
43604         {
43605             case 'ContentPanel':  // ContentPanel (el, cfg)
43606             case 'ScrollPanel':  // ContentPanel (el, cfg)
43607                 if(cfg.autoCreate) {
43608                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43609                 } else {
43610                     var el = this.el.createChild();
43611                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43612                 }
43613                 
43614                 this.add(region, ret);
43615                 break;
43616             
43617             
43618             case 'TreePanel': // our new panel!
43619                 cfg.el = this.el.createChild();
43620                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43621                 this.add(region, ret);
43622                 break;
43623             
43624             case 'NestedLayoutPanel': 
43625                 // create a new Layout (which is  a Border Layout...
43626                 var el = this.el.createChild();
43627                 var clayout = cfg.layout;
43628                 delete cfg.layout;
43629                 clayout.items   = clayout.items  || [];
43630                 // replace this exitems with the clayout ones..
43631                 xitems = clayout.items;
43632                  
43633                 
43634                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43635                     cfg.background = false;
43636                 }
43637                 var layout = new Roo.BorderLayout(el, clayout);
43638                 
43639                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43640                 //console.log('adding nested layout panel '  + cfg.toSource());
43641                 this.add(region, ret);
43642                 
43643                 break;
43644                 
43645             case 'GridPanel': 
43646             
43647                 // needs grid and region
43648                 
43649                 //var el = this.getRegion(region).el.createChild();
43650                 var el = this.el.createChild();
43651                 // create the grid first...
43652                 
43653                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43654                 delete cfg.grid;
43655                 if (region == 'center' && this.active ) {
43656                     cfg.background = false;
43657                 }
43658                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43659                 
43660                 this.add(region, ret);
43661                 if (cfg.background) {
43662                     ret.on('activate', function(gp) {
43663                         if (!gp.grid.rendered) {
43664                             gp.grid.render();
43665                         }
43666                     });
43667                 } else {
43668                     grid.render();
43669                 }
43670                 break;
43671            
43672                
43673                 
43674                 
43675             default: 
43676                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43677                 return;
43678              // GridPanel (grid, cfg)
43679             
43680         }
43681         this.beginUpdate();
43682         // add children..
43683         Roo.each(xitems, function(i)  {
43684             ret.addxtype(i);
43685         });
43686         this.endUpdate();
43687         return ret;
43688         
43689     }
43690 });
43691
43692 /**
43693  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43694  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43695  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43696  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43697  * <pre><code>
43698 // shorthand
43699 var CP = Roo.ContentPanel;
43700
43701 var layout = Roo.BorderLayout.create({
43702     north: {
43703         initialSize: 25,
43704         titlebar: false,
43705         panels: [new CP("north", "North")]
43706     },
43707     west: {
43708         split:true,
43709         initialSize: 200,
43710         minSize: 175,
43711         maxSize: 400,
43712         titlebar: true,
43713         collapsible: true,
43714         panels: [new CP("west", {title: "West"})]
43715     },
43716     east: {
43717         split:true,
43718         initialSize: 202,
43719         minSize: 175,
43720         maxSize: 400,
43721         titlebar: true,
43722         collapsible: true,
43723         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43724     },
43725     south: {
43726         split:true,
43727         initialSize: 100,
43728         minSize: 100,
43729         maxSize: 200,
43730         titlebar: true,
43731         collapsible: true,
43732         panels: [new CP("south", {title: "South", closable: true})]
43733     },
43734     center: {
43735         titlebar: true,
43736         autoScroll:true,
43737         resizeTabs: true,
43738         minTabWidth: 50,
43739         preferredTabWidth: 150,
43740         panels: [
43741             new CP("center1", {title: "Close Me", closable: true}),
43742             new CP("center2", {title: "Center Panel", closable: false})
43743         ]
43744     }
43745 }, document.body);
43746
43747 layout.getRegion("center").showPanel("center1");
43748 </code></pre>
43749  * @param config
43750  * @param targetEl
43751  */
43752 Roo.BorderLayout.create = function(config, targetEl){
43753     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43754     layout.beginUpdate();
43755     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43756     for(var j = 0, jlen = regions.length; j < jlen; j++){
43757         var lr = regions[j];
43758         if(layout.regions[lr] && config[lr].panels){
43759             var r = layout.regions[lr];
43760             var ps = config[lr].panels;
43761             layout.addTypedPanels(r, ps);
43762         }
43763     }
43764     layout.endUpdate();
43765     return layout;
43766 };
43767
43768 // private
43769 Roo.BorderLayout.RegionFactory = {
43770     // private
43771     validRegions : ["north","south","east","west","center"],
43772
43773     // private
43774     create : function(target, mgr, config){
43775         target = target.toLowerCase();
43776         if(config.lightweight || config.basic){
43777             return new Roo.BasicLayoutRegion(mgr, config, target);
43778         }
43779         switch(target){
43780             case "north":
43781                 return new Roo.NorthLayoutRegion(mgr, config);
43782             case "south":
43783                 return new Roo.SouthLayoutRegion(mgr, config);
43784             case "east":
43785                 return new Roo.EastLayoutRegion(mgr, config);
43786             case "west":
43787                 return new Roo.WestLayoutRegion(mgr, config);
43788             case "center":
43789                 return new Roo.CenterLayoutRegion(mgr, config);
43790         }
43791         throw 'Layout region "'+target+'" not supported.';
43792     }
43793 };/*
43794  * Based on:
43795  * Ext JS Library 1.1.1
43796  * Copyright(c) 2006-2007, Ext JS, LLC.
43797  *
43798  * Originally Released Under LGPL - original licence link has changed is not relivant.
43799  *
43800  * Fork - LGPL
43801  * <script type="text/javascript">
43802  */
43803  
43804 /**
43805  * @class Roo.BasicLayoutRegion
43806  * @extends Roo.util.Observable
43807  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43808  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43809  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43810  */
43811 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43812     this.mgr = mgr;
43813     this.position  = pos;
43814     this.events = {
43815         /**
43816          * @scope Roo.BasicLayoutRegion
43817          */
43818         
43819         /**
43820          * @event beforeremove
43821          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43822          * @param {Roo.LayoutRegion} this
43823          * @param {Roo.ContentPanel} panel The panel
43824          * @param {Object} e The cancel event object
43825          */
43826         "beforeremove" : true,
43827         /**
43828          * @event invalidated
43829          * Fires when the layout for this region is changed.
43830          * @param {Roo.LayoutRegion} this
43831          */
43832         "invalidated" : true,
43833         /**
43834          * @event visibilitychange
43835          * Fires when this region is shown or hidden 
43836          * @param {Roo.LayoutRegion} this
43837          * @param {Boolean} visibility true or false
43838          */
43839         "visibilitychange" : true,
43840         /**
43841          * @event paneladded
43842          * Fires when a panel is added. 
43843          * @param {Roo.LayoutRegion} this
43844          * @param {Roo.ContentPanel} panel The panel
43845          */
43846         "paneladded" : true,
43847         /**
43848          * @event panelremoved
43849          * Fires when a panel is removed. 
43850          * @param {Roo.LayoutRegion} this
43851          * @param {Roo.ContentPanel} panel The panel
43852          */
43853         "panelremoved" : true,
43854         /**
43855          * @event collapsed
43856          * Fires when this region is collapsed.
43857          * @param {Roo.LayoutRegion} this
43858          */
43859         "collapsed" : true,
43860         /**
43861          * @event expanded
43862          * Fires when this region is expanded.
43863          * @param {Roo.LayoutRegion} this
43864          */
43865         "expanded" : true,
43866         /**
43867          * @event slideshow
43868          * Fires when this region is slid into view.
43869          * @param {Roo.LayoutRegion} this
43870          */
43871         "slideshow" : true,
43872         /**
43873          * @event slidehide
43874          * Fires when this region slides out of view. 
43875          * @param {Roo.LayoutRegion} this
43876          */
43877         "slidehide" : true,
43878         /**
43879          * @event panelactivated
43880          * Fires when a panel is activated. 
43881          * @param {Roo.LayoutRegion} this
43882          * @param {Roo.ContentPanel} panel The activated panel
43883          */
43884         "panelactivated" : true,
43885         /**
43886          * @event resized
43887          * Fires when the user resizes this region. 
43888          * @param {Roo.LayoutRegion} this
43889          * @param {Number} newSize The new size (width for east/west, height for north/south)
43890          */
43891         "resized" : true
43892     };
43893     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43894     this.panels = new Roo.util.MixedCollection();
43895     this.panels.getKey = this.getPanelId.createDelegate(this);
43896     this.box = null;
43897     this.activePanel = null;
43898     // ensure listeners are added...
43899     
43900     if (config.listeners || config.events) {
43901         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43902             listeners : config.listeners || {},
43903             events : config.events || {}
43904         });
43905     }
43906     
43907     if(skipConfig !== true){
43908         this.applyConfig(config);
43909     }
43910 };
43911
43912 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43913     getPanelId : function(p){
43914         return p.getId();
43915     },
43916     
43917     applyConfig : function(config){
43918         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43919         this.config = config;
43920         
43921     },
43922     
43923     /**
43924      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43925      * the width, for horizontal (north, south) the height.
43926      * @param {Number} newSize The new width or height
43927      */
43928     resizeTo : function(newSize){
43929         var el = this.el ? this.el :
43930                  (this.activePanel ? this.activePanel.getEl() : null);
43931         if(el){
43932             switch(this.position){
43933                 case "east":
43934                 case "west":
43935                     el.setWidth(newSize);
43936                     this.fireEvent("resized", this, newSize);
43937                 break;
43938                 case "north":
43939                 case "south":
43940                     el.setHeight(newSize);
43941                     this.fireEvent("resized", this, newSize);
43942                 break;                
43943             }
43944         }
43945     },
43946     
43947     getBox : function(){
43948         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43949     },
43950     
43951     getMargins : function(){
43952         return this.margins;
43953     },
43954     
43955     updateBox : function(box){
43956         this.box = box;
43957         var el = this.activePanel.getEl();
43958         el.dom.style.left = box.x + "px";
43959         el.dom.style.top = box.y + "px";
43960         this.activePanel.setSize(box.width, box.height);
43961     },
43962     
43963     /**
43964      * Returns the container element for this region.
43965      * @return {Roo.Element}
43966      */
43967     getEl : function(){
43968         return this.activePanel;
43969     },
43970     
43971     /**
43972      * Returns true if this region is currently visible.
43973      * @return {Boolean}
43974      */
43975     isVisible : function(){
43976         return this.activePanel ? true : false;
43977     },
43978     
43979     setActivePanel : function(panel){
43980         panel = this.getPanel(panel);
43981         if(this.activePanel && this.activePanel != panel){
43982             this.activePanel.setActiveState(false);
43983             this.activePanel.getEl().setLeftTop(-10000,-10000);
43984         }
43985         this.activePanel = panel;
43986         panel.setActiveState(true);
43987         if(this.box){
43988             panel.setSize(this.box.width, this.box.height);
43989         }
43990         this.fireEvent("panelactivated", this, panel);
43991         this.fireEvent("invalidated");
43992     },
43993     
43994     /**
43995      * Show the specified panel.
43996      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43997      * @return {Roo.ContentPanel} The shown panel or null
43998      */
43999     showPanel : function(panel){
44000         if(panel = this.getPanel(panel)){
44001             this.setActivePanel(panel);
44002         }
44003         return panel;
44004     },
44005     
44006     /**
44007      * Get the active panel for this region.
44008      * @return {Roo.ContentPanel} The active panel or null
44009      */
44010     getActivePanel : function(){
44011         return this.activePanel;
44012     },
44013     
44014     /**
44015      * Add the passed ContentPanel(s)
44016      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44017      * @return {Roo.ContentPanel} The panel added (if only one was added)
44018      */
44019     add : function(panel){
44020         if(arguments.length > 1){
44021             for(var i = 0, len = arguments.length; i < len; i++) {
44022                 this.add(arguments[i]);
44023             }
44024             return null;
44025         }
44026         if(this.hasPanel(panel)){
44027             this.showPanel(panel);
44028             return panel;
44029         }
44030         var el = panel.getEl();
44031         if(el.dom.parentNode != this.mgr.el.dom){
44032             this.mgr.el.dom.appendChild(el.dom);
44033         }
44034         if(panel.setRegion){
44035             panel.setRegion(this);
44036         }
44037         this.panels.add(panel);
44038         el.setStyle("position", "absolute");
44039         if(!panel.background){
44040             this.setActivePanel(panel);
44041             if(this.config.initialSize && this.panels.getCount()==1){
44042                 this.resizeTo(this.config.initialSize);
44043             }
44044         }
44045         this.fireEvent("paneladded", this, panel);
44046         return panel;
44047     },
44048     
44049     /**
44050      * Returns true if the panel is in this region.
44051      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44052      * @return {Boolean}
44053      */
44054     hasPanel : function(panel){
44055         if(typeof panel == "object"){ // must be panel obj
44056             panel = panel.getId();
44057         }
44058         return this.getPanel(panel) ? true : false;
44059     },
44060     
44061     /**
44062      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44063      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44064      * @param {Boolean} preservePanel Overrides the config preservePanel option
44065      * @return {Roo.ContentPanel} The panel that was removed
44066      */
44067     remove : function(panel, preservePanel){
44068         panel = this.getPanel(panel);
44069         if(!panel){
44070             return null;
44071         }
44072         var e = {};
44073         this.fireEvent("beforeremove", this, panel, e);
44074         if(e.cancel === true){
44075             return null;
44076         }
44077         var panelId = panel.getId();
44078         this.panels.removeKey(panelId);
44079         return panel;
44080     },
44081     
44082     /**
44083      * Returns the panel specified or null if it's not in this region.
44084      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44085      * @return {Roo.ContentPanel}
44086      */
44087     getPanel : function(id){
44088         if(typeof id == "object"){ // must be panel obj
44089             return id;
44090         }
44091         return this.panels.get(id);
44092     },
44093     
44094     /**
44095      * Returns this regions position (north/south/east/west/center).
44096      * @return {String} 
44097      */
44098     getPosition: function(){
44099         return this.position;    
44100     }
44101 });/*
44102  * Based on:
44103  * Ext JS Library 1.1.1
44104  * Copyright(c) 2006-2007, Ext JS, LLC.
44105  *
44106  * Originally Released Under LGPL - original licence link has changed is not relivant.
44107  *
44108  * Fork - LGPL
44109  * <script type="text/javascript">
44110  */
44111  
44112 /**
44113  * @class Roo.LayoutRegion
44114  * @extends Roo.BasicLayoutRegion
44115  * This class represents a region in a layout manager.
44116  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44117  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44118  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44119  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44120  * @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})
44121  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44122  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44123  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44124  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44125  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44126  * @cfg {String} title The title for the region (overrides panel titles)
44127  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44128  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44129  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44130  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44131  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44132  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44133  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44134  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44135  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44136  * @cfg {Boolean} showPin True to show a pin button
44137 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44138 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44139 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44140 * @cfg {Number} width  For East/West panels
44141 * @cfg {Number} height For North/South panels
44142 * @cfg {Boolean} split To show the splitter
44143  */
44144 Roo.LayoutRegion = function(mgr, config, pos){
44145     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44146     var dh = Roo.DomHelper;
44147     /** This region's container element 
44148     * @type Roo.Element */
44149     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44150     /** This region's title element 
44151     * @type Roo.Element */
44152
44153     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44154         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44155         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44156     ]}, true);
44157     this.titleEl.enableDisplayMode();
44158     /** This region's title text element 
44159     * @type HTMLElement */
44160     this.titleTextEl = this.titleEl.dom.firstChild;
44161     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44162     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44163     this.closeBtn.enableDisplayMode();
44164     this.closeBtn.on("click", this.closeClicked, this);
44165     this.closeBtn.hide();
44166
44167     this.createBody(config);
44168     this.visible = true;
44169     this.collapsed = false;
44170
44171     if(config.hideWhenEmpty){
44172         this.hide();
44173         this.on("paneladded", this.validateVisibility, this);
44174         this.on("panelremoved", this.validateVisibility, this);
44175     }
44176     this.applyConfig(config);
44177 };
44178
44179 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44180
44181     createBody : function(){
44182         /** This region's body element 
44183         * @type Roo.Element */
44184         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44185     },
44186
44187     applyConfig : function(c){
44188         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44189             var dh = Roo.DomHelper;
44190             if(c.titlebar !== false){
44191                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44192                 this.collapseBtn.on("click", this.collapse, this);
44193                 this.collapseBtn.enableDisplayMode();
44194
44195                 if(c.showPin === true || this.showPin){
44196                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44197                     this.stickBtn.enableDisplayMode();
44198                     this.stickBtn.on("click", this.expand, this);
44199                     this.stickBtn.hide();
44200                 }
44201             }
44202             /** This region's collapsed element
44203             * @type Roo.Element */
44204             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44205                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44206             ]}, true);
44207             if(c.floatable !== false){
44208                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44209                this.collapsedEl.on("click", this.collapseClick, this);
44210             }
44211
44212             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44213                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44214                    id: "message", unselectable: "on", style:{"float":"left"}});
44215                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44216              }
44217             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44218             this.expandBtn.on("click", this.expand, this);
44219         }
44220         if(this.collapseBtn){
44221             this.collapseBtn.setVisible(c.collapsible == true);
44222         }
44223         this.cmargins = c.cmargins || this.cmargins ||
44224                          (this.position == "west" || this.position == "east" ?
44225                              {top: 0, left: 2, right:2, bottom: 0} :
44226                              {top: 2, left: 0, right:0, bottom: 2});
44227         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44228         this.bottomTabs = c.tabPosition != "top";
44229         this.autoScroll = c.autoScroll || false;
44230         if(this.autoScroll){
44231             this.bodyEl.setStyle("overflow", "auto");
44232         }else{
44233             this.bodyEl.setStyle("overflow", "hidden");
44234         }
44235         //if(c.titlebar !== false){
44236             if((!c.titlebar && !c.title) || c.titlebar === false){
44237                 this.titleEl.hide();
44238             }else{
44239                 this.titleEl.show();
44240                 if(c.title){
44241                     this.titleTextEl.innerHTML = c.title;
44242                 }
44243             }
44244         //}
44245         this.duration = c.duration || .30;
44246         this.slideDuration = c.slideDuration || .45;
44247         this.config = c;
44248         if(c.collapsed){
44249             this.collapse(true);
44250         }
44251         if(c.hidden){
44252             this.hide();
44253         }
44254     },
44255     /**
44256      * Returns true if this region is currently visible.
44257      * @return {Boolean}
44258      */
44259     isVisible : function(){
44260         return this.visible;
44261     },
44262
44263     /**
44264      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44265      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44266      */
44267     setCollapsedTitle : function(title){
44268         title = title || "&#160;";
44269         if(this.collapsedTitleTextEl){
44270             this.collapsedTitleTextEl.innerHTML = title;
44271         }
44272     },
44273
44274     getBox : function(){
44275         var b;
44276         if(!this.collapsed){
44277             b = this.el.getBox(false, true);
44278         }else{
44279             b = this.collapsedEl.getBox(false, true);
44280         }
44281         return b;
44282     },
44283
44284     getMargins : function(){
44285         return this.collapsed ? this.cmargins : this.margins;
44286     },
44287
44288     highlight : function(){
44289         this.el.addClass("x-layout-panel-dragover");
44290     },
44291
44292     unhighlight : function(){
44293         this.el.removeClass("x-layout-panel-dragover");
44294     },
44295
44296     updateBox : function(box){
44297         this.box = box;
44298         if(!this.collapsed){
44299             this.el.dom.style.left = box.x + "px";
44300             this.el.dom.style.top = box.y + "px";
44301             this.updateBody(box.width, box.height);
44302         }else{
44303             this.collapsedEl.dom.style.left = box.x + "px";
44304             this.collapsedEl.dom.style.top = box.y + "px";
44305             this.collapsedEl.setSize(box.width, box.height);
44306         }
44307         if(this.tabs){
44308             this.tabs.autoSizeTabs();
44309         }
44310     },
44311
44312     updateBody : function(w, h){
44313         if(w !== null){
44314             this.el.setWidth(w);
44315             w -= this.el.getBorderWidth("rl");
44316             if(this.config.adjustments){
44317                 w += this.config.adjustments[0];
44318             }
44319         }
44320         if(h !== null){
44321             this.el.setHeight(h);
44322             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44323             h -= this.el.getBorderWidth("tb");
44324             if(this.config.adjustments){
44325                 h += this.config.adjustments[1];
44326             }
44327             this.bodyEl.setHeight(h);
44328             if(this.tabs){
44329                 h = this.tabs.syncHeight(h);
44330             }
44331         }
44332         if(this.panelSize){
44333             w = w !== null ? w : this.panelSize.width;
44334             h = h !== null ? h : this.panelSize.height;
44335         }
44336         if(this.activePanel){
44337             var el = this.activePanel.getEl();
44338             w = w !== null ? w : el.getWidth();
44339             h = h !== null ? h : el.getHeight();
44340             this.panelSize = {width: w, height: h};
44341             this.activePanel.setSize(w, h);
44342         }
44343         if(Roo.isIE && this.tabs){
44344             this.tabs.el.repaint();
44345         }
44346     },
44347
44348     /**
44349      * Returns the container element for this region.
44350      * @return {Roo.Element}
44351      */
44352     getEl : function(){
44353         return this.el;
44354     },
44355
44356     /**
44357      * Hides this region.
44358      */
44359     hide : function(){
44360         if(!this.collapsed){
44361             this.el.dom.style.left = "-2000px";
44362             this.el.hide();
44363         }else{
44364             this.collapsedEl.dom.style.left = "-2000px";
44365             this.collapsedEl.hide();
44366         }
44367         this.visible = false;
44368         this.fireEvent("visibilitychange", this, false);
44369     },
44370
44371     /**
44372      * Shows this region if it was previously hidden.
44373      */
44374     show : function(){
44375         if(!this.collapsed){
44376             this.el.show();
44377         }else{
44378             this.collapsedEl.show();
44379         }
44380         this.visible = true;
44381         this.fireEvent("visibilitychange", this, true);
44382     },
44383
44384     closeClicked : function(){
44385         if(this.activePanel){
44386             this.remove(this.activePanel);
44387         }
44388     },
44389
44390     collapseClick : function(e){
44391         if(this.isSlid){
44392            e.stopPropagation();
44393            this.slideIn();
44394         }else{
44395            e.stopPropagation();
44396            this.slideOut();
44397         }
44398     },
44399
44400     /**
44401      * Collapses this region.
44402      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44403      */
44404     collapse : function(skipAnim){
44405         if(this.collapsed) return;
44406         this.collapsed = true;
44407         if(this.split){
44408             this.split.el.hide();
44409         }
44410         if(this.config.animate && skipAnim !== true){
44411             this.fireEvent("invalidated", this);
44412             this.animateCollapse();
44413         }else{
44414             this.el.setLocation(-20000,-20000);
44415             this.el.hide();
44416             this.collapsedEl.show();
44417             this.fireEvent("collapsed", this);
44418             this.fireEvent("invalidated", this);
44419         }
44420     },
44421
44422     animateCollapse : function(){
44423         // overridden
44424     },
44425
44426     /**
44427      * Expands this region if it was previously collapsed.
44428      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44429      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44430      */
44431     expand : function(e, skipAnim){
44432         if(e) e.stopPropagation();
44433         if(!this.collapsed || this.el.hasActiveFx()) return;
44434         if(this.isSlid){
44435             this.afterSlideIn();
44436             skipAnim = true;
44437         }
44438         this.collapsed = false;
44439         if(this.config.animate && skipAnim !== true){
44440             this.animateExpand();
44441         }else{
44442             this.el.show();
44443             if(this.split){
44444                 this.split.el.show();
44445             }
44446             this.collapsedEl.setLocation(-2000,-2000);
44447             this.collapsedEl.hide();
44448             this.fireEvent("invalidated", this);
44449             this.fireEvent("expanded", this);
44450         }
44451     },
44452
44453     animateExpand : function(){
44454         // overridden
44455     },
44456
44457     initTabs : function(){
44458         this.bodyEl.setStyle("overflow", "hidden");
44459         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44460             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44461             disableTooltips: this.config.disableTabTips
44462         });
44463         if(this.config.hideTabs){
44464             ts.stripWrap.setDisplayed(false);
44465         }
44466         this.tabs = ts;
44467         ts.resizeTabs = this.config.resizeTabs === true;
44468         ts.minTabWidth = this.config.minTabWidth || 40;
44469         ts.maxTabWidth = this.config.maxTabWidth || 250;
44470         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44471         ts.monitorResize = false;
44472         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44473         ts.bodyEl.addClass('x-layout-tabs-body');
44474         this.panels.each(this.initPanelAsTab, this);
44475     },
44476
44477     initPanelAsTab : function(panel){
44478         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44479                     this.config.closeOnTab && panel.isClosable());
44480         if(panel.tabTip !== undefined){
44481             ti.setTooltip(panel.tabTip);
44482         }
44483         ti.on("activate", function(){
44484               this.setActivePanel(panel);
44485         }, this);
44486         if(this.config.closeOnTab){
44487             ti.on("beforeclose", function(t, e){
44488                 e.cancel = true;
44489                 this.remove(panel);
44490             }, this);
44491         }
44492         return ti;
44493     },
44494
44495     updatePanelTitle : function(panel, title){
44496         if(this.activePanel == panel){
44497             this.updateTitle(title);
44498         }
44499         if(this.tabs){
44500             var ti = this.tabs.getTab(panel.getEl().id);
44501             ti.setText(title);
44502             if(panel.tabTip !== undefined){
44503                 ti.setTooltip(panel.tabTip);
44504             }
44505         }
44506     },
44507
44508     updateTitle : function(title){
44509         if(this.titleTextEl && !this.config.title){
44510             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44511         }
44512     },
44513
44514     setActivePanel : function(panel){
44515         panel = this.getPanel(panel);
44516         if(this.activePanel && this.activePanel != panel){
44517             this.activePanel.setActiveState(false);
44518         }
44519         this.activePanel = panel;
44520         panel.setActiveState(true);
44521         if(this.panelSize){
44522             panel.setSize(this.panelSize.width, this.panelSize.height);
44523         }
44524         if(this.closeBtn){
44525             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44526         }
44527         this.updateTitle(panel.getTitle());
44528         if(this.tabs){
44529             this.fireEvent("invalidated", this);
44530         }
44531         this.fireEvent("panelactivated", this, panel);
44532     },
44533
44534     /**
44535      * Shows the specified panel.
44536      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44537      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44538      */
44539     showPanel : function(panel){
44540         if(panel = this.getPanel(panel)){
44541             if(this.tabs){
44542                 var tab = this.tabs.getTab(panel.getEl().id);
44543                 if(tab.isHidden()){
44544                     this.tabs.unhideTab(tab.id);
44545                 }
44546                 tab.activate();
44547             }else{
44548                 this.setActivePanel(panel);
44549             }
44550         }
44551         return panel;
44552     },
44553
44554     /**
44555      * Get the active panel for this region.
44556      * @return {Roo.ContentPanel} The active panel or null
44557      */
44558     getActivePanel : function(){
44559         return this.activePanel;
44560     },
44561
44562     validateVisibility : function(){
44563         if(this.panels.getCount() < 1){
44564             this.updateTitle("&#160;");
44565             this.closeBtn.hide();
44566             this.hide();
44567         }else{
44568             if(!this.isVisible()){
44569                 this.show();
44570             }
44571         }
44572     },
44573
44574     /**
44575      * Adds the passed ContentPanel(s) to this region.
44576      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44577      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44578      */
44579     add : function(panel){
44580         if(arguments.length > 1){
44581             for(var i = 0, len = arguments.length; i < len; i++) {
44582                 this.add(arguments[i]);
44583             }
44584             return null;
44585         }
44586         if(this.hasPanel(panel)){
44587             this.showPanel(panel);
44588             return panel;
44589         }
44590         panel.setRegion(this);
44591         this.panels.add(panel);
44592         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44593             this.bodyEl.dom.appendChild(panel.getEl().dom);
44594             if(panel.background !== true){
44595                 this.setActivePanel(panel);
44596             }
44597             this.fireEvent("paneladded", this, panel);
44598             return panel;
44599         }
44600         if(!this.tabs){
44601             this.initTabs();
44602         }else{
44603             this.initPanelAsTab(panel);
44604         }
44605         if(panel.background !== true){
44606             this.tabs.activate(panel.getEl().id);
44607         }
44608         this.fireEvent("paneladded", this, panel);
44609         return panel;
44610     },
44611
44612     /**
44613      * Hides the tab for the specified panel.
44614      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44615      */
44616     hidePanel : function(panel){
44617         if(this.tabs && (panel = this.getPanel(panel))){
44618             this.tabs.hideTab(panel.getEl().id);
44619         }
44620     },
44621
44622     /**
44623      * Unhides the tab for a previously hidden panel.
44624      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44625      */
44626     unhidePanel : function(panel){
44627         if(this.tabs && (panel = this.getPanel(panel))){
44628             this.tabs.unhideTab(panel.getEl().id);
44629         }
44630     },
44631
44632     clearPanels : function(){
44633         while(this.panels.getCount() > 0){
44634              this.remove(this.panels.first());
44635         }
44636     },
44637
44638     /**
44639      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44640      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44641      * @param {Boolean} preservePanel Overrides the config preservePanel option
44642      * @return {Roo.ContentPanel} The panel that was removed
44643      */
44644     remove : function(panel, preservePanel){
44645         panel = this.getPanel(panel);
44646         if(!panel){
44647             return null;
44648         }
44649         var e = {};
44650         this.fireEvent("beforeremove", this, panel, e);
44651         if(e.cancel === true){
44652             return null;
44653         }
44654         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44655         var panelId = panel.getId();
44656         this.panels.removeKey(panelId);
44657         if(preservePanel){
44658             document.body.appendChild(panel.getEl().dom);
44659         }
44660         if(this.tabs){
44661             this.tabs.removeTab(panel.getEl().id);
44662         }else if (!preservePanel){
44663             this.bodyEl.dom.removeChild(panel.getEl().dom);
44664         }
44665         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44666             var p = this.panels.first();
44667             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44668             tempEl.appendChild(p.getEl().dom);
44669             this.bodyEl.update("");
44670             this.bodyEl.dom.appendChild(p.getEl().dom);
44671             tempEl = null;
44672             this.updateTitle(p.getTitle());
44673             this.tabs = null;
44674             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44675             this.setActivePanel(p);
44676         }
44677         panel.setRegion(null);
44678         if(this.activePanel == panel){
44679             this.activePanel = null;
44680         }
44681         if(this.config.autoDestroy !== false && preservePanel !== true){
44682             try{panel.destroy();}catch(e){}
44683         }
44684         this.fireEvent("panelremoved", this, panel);
44685         return panel;
44686     },
44687
44688     /**
44689      * Returns the TabPanel component used by this region
44690      * @return {Roo.TabPanel}
44691      */
44692     getTabs : function(){
44693         return this.tabs;
44694     },
44695
44696     createTool : function(parentEl, className){
44697         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44698             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44699         btn.addClassOnOver("x-layout-tools-button-over");
44700         return btn;
44701     }
44702 });/*
44703  * Based on:
44704  * Ext JS Library 1.1.1
44705  * Copyright(c) 2006-2007, Ext JS, LLC.
44706  *
44707  * Originally Released Under LGPL - original licence link has changed is not relivant.
44708  *
44709  * Fork - LGPL
44710  * <script type="text/javascript">
44711  */
44712  
44713
44714
44715 /**
44716  * @class Roo.SplitLayoutRegion
44717  * @extends Roo.LayoutRegion
44718  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44719  */
44720 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44721     this.cursor = cursor;
44722     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44723 };
44724
44725 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44726     splitTip : "Drag to resize.",
44727     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44728     useSplitTips : false,
44729
44730     applyConfig : function(config){
44731         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44732         if(config.split){
44733             if(!this.split){
44734                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44735                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44736                 /** The SplitBar for this region 
44737                 * @type Roo.SplitBar */
44738                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44739                 this.split.on("moved", this.onSplitMove, this);
44740                 this.split.useShim = config.useShim === true;
44741                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44742                 if(this.useSplitTips){
44743                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44744                 }
44745                 if(config.collapsible){
44746                     this.split.el.on("dblclick", this.collapse,  this);
44747                 }
44748             }
44749             if(typeof config.minSize != "undefined"){
44750                 this.split.minSize = config.minSize;
44751             }
44752             if(typeof config.maxSize != "undefined"){
44753                 this.split.maxSize = config.maxSize;
44754             }
44755             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44756                 this.hideSplitter();
44757             }
44758         }
44759     },
44760
44761     getHMaxSize : function(){
44762          var cmax = this.config.maxSize || 10000;
44763          var center = this.mgr.getRegion("center");
44764          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44765     },
44766
44767     getVMaxSize : function(){
44768          var cmax = this.config.maxSize || 10000;
44769          var center = this.mgr.getRegion("center");
44770          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44771     },
44772
44773     onSplitMove : function(split, newSize){
44774         this.fireEvent("resized", this, newSize);
44775     },
44776     
44777     /** 
44778      * Returns the {@link Roo.SplitBar} for this region.
44779      * @return {Roo.SplitBar}
44780      */
44781     getSplitBar : function(){
44782         return this.split;
44783     },
44784     
44785     hide : function(){
44786         this.hideSplitter();
44787         Roo.SplitLayoutRegion.superclass.hide.call(this);
44788     },
44789
44790     hideSplitter : function(){
44791         if(this.split){
44792             this.split.el.setLocation(-2000,-2000);
44793             this.split.el.hide();
44794         }
44795     },
44796
44797     show : function(){
44798         if(this.split){
44799             this.split.el.show();
44800         }
44801         Roo.SplitLayoutRegion.superclass.show.call(this);
44802     },
44803     
44804     beforeSlide: function(){
44805         if(Roo.isGecko){// firefox overflow auto bug workaround
44806             this.bodyEl.clip();
44807             if(this.tabs) this.tabs.bodyEl.clip();
44808             if(this.activePanel){
44809                 this.activePanel.getEl().clip();
44810                 
44811                 if(this.activePanel.beforeSlide){
44812                     this.activePanel.beforeSlide();
44813                 }
44814             }
44815         }
44816     },
44817     
44818     afterSlide : function(){
44819         if(Roo.isGecko){// firefox overflow auto bug workaround
44820             this.bodyEl.unclip();
44821             if(this.tabs) this.tabs.bodyEl.unclip();
44822             if(this.activePanel){
44823                 this.activePanel.getEl().unclip();
44824                 if(this.activePanel.afterSlide){
44825                     this.activePanel.afterSlide();
44826                 }
44827             }
44828         }
44829     },
44830
44831     initAutoHide : function(){
44832         if(this.autoHide !== false){
44833             if(!this.autoHideHd){
44834                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44835                 this.autoHideHd = {
44836                     "mouseout": function(e){
44837                         if(!e.within(this.el, true)){
44838                             st.delay(500);
44839                         }
44840                     },
44841                     "mouseover" : function(e){
44842                         st.cancel();
44843                     },
44844                     scope : this
44845                 };
44846             }
44847             this.el.on(this.autoHideHd);
44848         }
44849     },
44850
44851     clearAutoHide : function(){
44852         if(this.autoHide !== false){
44853             this.el.un("mouseout", this.autoHideHd.mouseout);
44854             this.el.un("mouseover", this.autoHideHd.mouseover);
44855         }
44856     },
44857
44858     clearMonitor : function(){
44859         Roo.get(document).un("click", this.slideInIf, this);
44860     },
44861
44862     // these names are backwards but not changed for compat
44863     slideOut : function(){
44864         if(this.isSlid || this.el.hasActiveFx()){
44865             return;
44866         }
44867         this.isSlid = true;
44868         if(this.collapseBtn){
44869             this.collapseBtn.hide();
44870         }
44871         this.closeBtnState = this.closeBtn.getStyle('display');
44872         this.closeBtn.hide();
44873         if(this.stickBtn){
44874             this.stickBtn.show();
44875         }
44876         this.el.show();
44877         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44878         this.beforeSlide();
44879         this.el.setStyle("z-index", 10001);
44880         this.el.slideIn(this.getSlideAnchor(), {
44881             callback: function(){
44882                 this.afterSlide();
44883                 this.initAutoHide();
44884                 Roo.get(document).on("click", this.slideInIf, this);
44885                 this.fireEvent("slideshow", this);
44886             },
44887             scope: this,
44888             block: true
44889         });
44890     },
44891
44892     afterSlideIn : function(){
44893         this.clearAutoHide();
44894         this.isSlid = false;
44895         this.clearMonitor();
44896         this.el.setStyle("z-index", "");
44897         if(this.collapseBtn){
44898             this.collapseBtn.show();
44899         }
44900         this.closeBtn.setStyle('display', this.closeBtnState);
44901         if(this.stickBtn){
44902             this.stickBtn.hide();
44903         }
44904         this.fireEvent("slidehide", this);
44905     },
44906
44907     slideIn : function(cb){
44908         if(!this.isSlid || this.el.hasActiveFx()){
44909             Roo.callback(cb);
44910             return;
44911         }
44912         this.isSlid = false;
44913         this.beforeSlide();
44914         this.el.slideOut(this.getSlideAnchor(), {
44915             callback: function(){
44916                 this.el.setLeftTop(-10000, -10000);
44917                 this.afterSlide();
44918                 this.afterSlideIn();
44919                 Roo.callback(cb);
44920             },
44921             scope: this,
44922             block: true
44923         });
44924     },
44925     
44926     slideInIf : function(e){
44927         if(!e.within(this.el)){
44928             this.slideIn();
44929         }
44930     },
44931
44932     animateCollapse : function(){
44933         this.beforeSlide();
44934         this.el.setStyle("z-index", 20000);
44935         var anchor = this.getSlideAnchor();
44936         this.el.slideOut(anchor, {
44937             callback : function(){
44938                 this.el.setStyle("z-index", "");
44939                 this.collapsedEl.slideIn(anchor, {duration:.3});
44940                 this.afterSlide();
44941                 this.el.setLocation(-10000,-10000);
44942                 this.el.hide();
44943                 this.fireEvent("collapsed", this);
44944             },
44945             scope: this,
44946             block: true
44947         });
44948     },
44949
44950     animateExpand : function(){
44951         this.beforeSlide();
44952         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44953         this.el.setStyle("z-index", 20000);
44954         this.collapsedEl.hide({
44955             duration:.1
44956         });
44957         this.el.slideIn(this.getSlideAnchor(), {
44958             callback : function(){
44959                 this.el.setStyle("z-index", "");
44960                 this.afterSlide();
44961                 if(this.split){
44962                     this.split.el.show();
44963                 }
44964                 this.fireEvent("invalidated", this);
44965                 this.fireEvent("expanded", this);
44966             },
44967             scope: this,
44968             block: true
44969         });
44970     },
44971
44972     anchors : {
44973         "west" : "left",
44974         "east" : "right",
44975         "north" : "top",
44976         "south" : "bottom"
44977     },
44978
44979     sanchors : {
44980         "west" : "l",
44981         "east" : "r",
44982         "north" : "t",
44983         "south" : "b"
44984     },
44985
44986     canchors : {
44987         "west" : "tl-tr",
44988         "east" : "tr-tl",
44989         "north" : "tl-bl",
44990         "south" : "bl-tl"
44991     },
44992
44993     getAnchor : function(){
44994         return this.anchors[this.position];
44995     },
44996
44997     getCollapseAnchor : function(){
44998         return this.canchors[this.position];
44999     },
45000
45001     getSlideAnchor : function(){
45002         return this.sanchors[this.position];
45003     },
45004
45005     getAlignAdj : function(){
45006         var cm = this.cmargins;
45007         switch(this.position){
45008             case "west":
45009                 return [0, 0];
45010             break;
45011             case "east":
45012                 return [0, 0];
45013             break;
45014             case "north":
45015                 return [0, 0];
45016             break;
45017             case "south":
45018                 return [0, 0];
45019             break;
45020         }
45021     },
45022
45023     getExpandAdj : function(){
45024         var c = this.collapsedEl, cm = this.cmargins;
45025         switch(this.position){
45026             case "west":
45027                 return [-(cm.right+c.getWidth()+cm.left), 0];
45028             break;
45029             case "east":
45030                 return [cm.right+c.getWidth()+cm.left, 0];
45031             break;
45032             case "north":
45033                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45034             break;
45035             case "south":
45036                 return [0, cm.top+cm.bottom+c.getHeight()];
45037             break;
45038         }
45039     }
45040 });/*
45041  * Based on:
45042  * Ext JS Library 1.1.1
45043  * Copyright(c) 2006-2007, Ext JS, LLC.
45044  *
45045  * Originally Released Under LGPL - original licence link has changed is not relivant.
45046  *
45047  * Fork - LGPL
45048  * <script type="text/javascript">
45049  */
45050 /*
45051  * These classes are private internal classes
45052  */
45053 Roo.CenterLayoutRegion = function(mgr, config){
45054     Roo.LayoutRegion.call(this, mgr, config, "center");
45055     this.visible = true;
45056     this.minWidth = config.minWidth || 20;
45057     this.minHeight = config.minHeight || 20;
45058 };
45059
45060 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45061     hide : function(){
45062         // center panel can't be hidden
45063     },
45064     
45065     show : function(){
45066         // center panel can't be hidden
45067     },
45068     
45069     getMinWidth: function(){
45070         return this.minWidth;
45071     },
45072     
45073     getMinHeight: function(){
45074         return this.minHeight;
45075     }
45076 });
45077
45078
45079 Roo.NorthLayoutRegion = function(mgr, config){
45080     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45081     if(this.split){
45082         this.split.placement = Roo.SplitBar.TOP;
45083         this.split.orientation = Roo.SplitBar.VERTICAL;
45084         this.split.el.addClass("x-layout-split-v");
45085     }
45086     var size = config.initialSize || config.height;
45087     if(typeof size != "undefined"){
45088         this.el.setHeight(size);
45089     }
45090 };
45091 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45092     orientation: Roo.SplitBar.VERTICAL,
45093     getBox : function(){
45094         if(this.collapsed){
45095             return this.collapsedEl.getBox();
45096         }
45097         var box = this.el.getBox();
45098         if(this.split){
45099             box.height += this.split.el.getHeight();
45100         }
45101         return box;
45102     },
45103     
45104     updateBox : function(box){
45105         if(this.split && !this.collapsed){
45106             box.height -= this.split.el.getHeight();
45107             this.split.el.setLeft(box.x);
45108             this.split.el.setTop(box.y+box.height);
45109             this.split.el.setWidth(box.width);
45110         }
45111         if(this.collapsed){
45112             this.updateBody(box.width, null);
45113         }
45114         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45115     }
45116 });
45117
45118 Roo.SouthLayoutRegion = function(mgr, config){
45119     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45120     if(this.split){
45121         this.split.placement = Roo.SplitBar.BOTTOM;
45122         this.split.orientation = Roo.SplitBar.VERTICAL;
45123         this.split.el.addClass("x-layout-split-v");
45124     }
45125     var size = config.initialSize || config.height;
45126     if(typeof size != "undefined"){
45127         this.el.setHeight(size);
45128     }
45129 };
45130 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45131     orientation: Roo.SplitBar.VERTICAL,
45132     getBox : function(){
45133         if(this.collapsed){
45134             return this.collapsedEl.getBox();
45135         }
45136         var box = this.el.getBox();
45137         if(this.split){
45138             var sh = this.split.el.getHeight();
45139             box.height += sh;
45140             box.y -= sh;
45141         }
45142         return box;
45143     },
45144     
45145     updateBox : function(box){
45146         if(this.split && !this.collapsed){
45147             var sh = this.split.el.getHeight();
45148             box.height -= sh;
45149             box.y += sh;
45150             this.split.el.setLeft(box.x);
45151             this.split.el.setTop(box.y-sh);
45152             this.split.el.setWidth(box.width);
45153         }
45154         if(this.collapsed){
45155             this.updateBody(box.width, null);
45156         }
45157         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45158     }
45159 });
45160
45161 Roo.EastLayoutRegion = function(mgr, config){
45162     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45163     if(this.split){
45164         this.split.placement = Roo.SplitBar.RIGHT;
45165         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45166         this.split.el.addClass("x-layout-split-h");
45167     }
45168     var size = config.initialSize || config.width;
45169     if(typeof size != "undefined"){
45170         this.el.setWidth(size);
45171     }
45172 };
45173 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45174     orientation: Roo.SplitBar.HORIZONTAL,
45175     getBox : function(){
45176         if(this.collapsed){
45177             return this.collapsedEl.getBox();
45178         }
45179         var box = this.el.getBox();
45180         if(this.split){
45181             var sw = this.split.el.getWidth();
45182             box.width += sw;
45183             box.x -= sw;
45184         }
45185         return box;
45186     },
45187
45188     updateBox : function(box){
45189         if(this.split && !this.collapsed){
45190             var sw = this.split.el.getWidth();
45191             box.width -= sw;
45192             this.split.el.setLeft(box.x);
45193             this.split.el.setTop(box.y);
45194             this.split.el.setHeight(box.height);
45195             box.x += sw;
45196         }
45197         if(this.collapsed){
45198             this.updateBody(null, box.height);
45199         }
45200         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45201     }
45202 });
45203
45204 Roo.WestLayoutRegion = function(mgr, config){
45205     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45206     if(this.split){
45207         this.split.placement = Roo.SplitBar.LEFT;
45208         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45209         this.split.el.addClass("x-layout-split-h");
45210     }
45211     var size = config.initialSize || config.width;
45212     if(typeof size != "undefined"){
45213         this.el.setWidth(size);
45214     }
45215 };
45216 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45217     orientation: Roo.SplitBar.HORIZONTAL,
45218     getBox : function(){
45219         if(this.collapsed){
45220             return this.collapsedEl.getBox();
45221         }
45222         var box = this.el.getBox();
45223         if(this.split){
45224             box.width += this.split.el.getWidth();
45225         }
45226         return box;
45227     },
45228     
45229     updateBox : function(box){
45230         if(this.split && !this.collapsed){
45231             var sw = this.split.el.getWidth();
45232             box.width -= sw;
45233             this.split.el.setLeft(box.x+box.width);
45234             this.split.el.setTop(box.y);
45235             this.split.el.setHeight(box.height);
45236         }
45237         if(this.collapsed){
45238             this.updateBody(null, box.height);
45239         }
45240         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45241     }
45242 });
45243 /*
45244  * Based on:
45245  * Ext JS Library 1.1.1
45246  * Copyright(c) 2006-2007, Ext JS, LLC.
45247  *
45248  * Originally Released Under LGPL - original licence link has changed is not relivant.
45249  *
45250  * Fork - LGPL
45251  * <script type="text/javascript">
45252  */
45253  
45254  
45255 /*
45256  * Private internal class for reading and applying state
45257  */
45258 Roo.LayoutStateManager = function(layout){
45259      // default empty state
45260      this.state = {
45261         north: {},
45262         south: {},
45263         east: {},
45264         west: {}       
45265     };
45266 };
45267
45268 Roo.LayoutStateManager.prototype = {
45269     init : function(layout, provider){
45270         this.provider = provider;
45271         var state = provider.get(layout.id+"-layout-state");
45272         if(state){
45273             var wasUpdating = layout.isUpdating();
45274             if(!wasUpdating){
45275                 layout.beginUpdate();
45276             }
45277             for(var key in state){
45278                 if(typeof state[key] != "function"){
45279                     var rstate = state[key];
45280                     var r = layout.getRegion(key);
45281                     if(r && rstate){
45282                         if(rstate.size){
45283                             r.resizeTo(rstate.size);
45284                         }
45285                         if(rstate.collapsed == true){
45286                             r.collapse(true);
45287                         }else{
45288                             r.expand(null, true);
45289                         }
45290                     }
45291                 }
45292             }
45293             if(!wasUpdating){
45294                 layout.endUpdate();
45295             }
45296             this.state = state; 
45297         }
45298         this.layout = layout;
45299         layout.on("regionresized", this.onRegionResized, this);
45300         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45301         layout.on("regionexpanded", this.onRegionExpanded, this);
45302     },
45303     
45304     storeState : function(){
45305         this.provider.set(this.layout.id+"-layout-state", this.state);
45306     },
45307     
45308     onRegionResized : function(region, newSize){
45309         this.state[region.getPosition()].size = newSize;
45310         this.storeState();
45311     },
45312     
45313     onRegionCollapsed : function(region){
45314         this.state[region.getPosition()].collapsed = true;
45315         this.storeState();
45316     },
45317     
45318     onRegionExpanded : function(region){
45319         this.state[region.getPosition()].collapsed = false;
45320         this.storeState();
45321     }
45322 };/*
45323  * Based on:
45324  * Ext JS Library 1.1.1
45325  * Copyright(c) 2006-2007, Ext JS, LLC.
45326  *
45327  * Originally Released Under LGPL - original licence link has changed is not relivant.
45328  *
45329  * Fork - LGPL
45330  * <script type="text/javascript">
45331  */
45332 /**
45333  * @class Roo.ContentPanel
45334  * @extends Roo.util.Observable
45335  * A basic ContentPanel element.
45336  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45337  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45338  * @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
45339  * @cfg {Boolean} closable True if the panel can be closed/removed
45340  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45341  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45342  * @cfg {Toolbar} toolbar A toolbar for this panel
45343  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45344  * @cfg {String} title The title for this panel
45345  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45346  * @cfg {String} url Calls {@link #setUrl} with this value
45347  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45348  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45349  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45350  * @constructor
45351  * Create a new ContentPanel.
45352  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45353  * @param {String/Object} config A string to set only the title or a config object
45354  * @param {String} content (optional) Set the HTML content for this panel
45355  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45356  */
45357 Roo.ContentPanel = function(el, config, content){
45358     
45359      
45360     /*
45361     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45362         config = el;
45363         el = Roo.id();
45364     }
45365     if (config && config.parentLayout) { 
45366         el = config.parentLayout.el.createChild(); 
45367     }
45368     */
45369     if(el.autoCreate){ // xtype is available if this is called from factory
45370         config = el;
45371         el = Roo.id();
45372     }
45373     this.el = Roo.get(el);
45374     if(!this.el && config && config.autoCreate){
45375         if(typeof config.autoCreate == "object"){
45376             if(!config.autoCreate.id){
45377                 config.autoCreate.id = config.id||el;
45378             }
45379             this.el = Roo.DomHelper.append(document.body,
45380                         config.autoCreate, true);
45381         }else{
45382             this.el = Roo.DomHelper.append(document.body,
45383                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45384         }
45385     }
45386     this.closable = false;
45387     this.loaded = false;
45388     this.active = false;
45389     if(typeof config == "string"){
45390         this.title = config;
45391     }else{
45392         Roo.apply(this, config);
45393     }
45394     
45395     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45396         this.wrapEl = this.el.wrap();    
45397         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45398         
45399     }
45400     
45401     
45402     
45403     if(this.resizeEl){
45404         this.resizeEl = Roo.get(this.resizeEl, true);
45405     }else{
45406         this.resizeEl = this.el;
45407     }
45408     this.addEvents({
45409         /**
45410          * @event activate
45411          * Fires when this panel is activated. 
45412          * @param {Roo.ContentPanel} this
45413          */
45414         "activate" : true,
45415         /**
45416          * @event deactivate
45417          * Fires when this panel is activated. 
45418          * @param {Roo.ContentPanel} this
45419          */
45420         "deactivate" : true,
45421
45422         /**
45423          * @event resize
45424          * Fires when this panel is resized if fitToFrame is true.
45425          * @param {Roo.ContentPanel} this
45426          * @param {Number} width The width after any component adjustments
45427          * @param {Number} height The height after any component adjustments
45428          */
45429         "resize" : true
45430     });
45431     if(this.autoScroll){
45432         this.resizeEl.setStyle("overflow", "auto");
45433     } else {
45434         // fix randome scrolling
45435         this.el.on('scroll', function() {
45436             this.scrollTo('top',0); 
45437         });
45438     }
45439     content = content || this.content;
45440     if(content){
45441         this.setContent(content);
45442     }
45443     if(config && config.url){
45444         this.setUrl(this.url, this.params, this.loadOnce);
45445     }
45446     
45447     
45448     
45449     Roo.ContentPanel.superclass.constructor.call(this);
45450 };
45451
45452 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45453     tabTip:'',
45454     setRegion : function(region){
45455         this.region = region;
45456         if(region){
45457            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45458         }else{
45459            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45460         } 
45461     },
45462     
45463     /**
45464      * Returns the toolbar for this Panel if one was configured. 
45465      * @return {Roo.Toolbar} 
45466      */
45467     getToolbar : function(){
45468         return this.toolbar;
45469     },
45470     
45471     setActiveState : function(active){
45472         this.active = active;
45473         if(!active){
45474             this.fireEvent("deactivate", this);
45475         }else{
45476             this.fireEvent("activate", this);
45477         }
45478     },
45479     /**
45480      * Updates this panel's element
45481      * @param {String} content The new content
45482      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45483     */
45484     setContent : function(content, loadScripts){
45485         this.el.update(content, loadScripts);
45486     },
45487
45488     ignoreResize : function(w, h){
45489         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45490             return true;
45491         }else{
45492             this.lastSize = {width: w, height: h};
45493             return false;
45494         }
45495     },
45496     /**
45497      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45498      * @return {Roo.UpdateManager} The UpdateManager
45499      */
45500     getUpdateManager : function(){
45501         return this.el.getUpdateManager();
45502     },
45503      /**
45504      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45505      * @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:
45506 <pre><code>
45507 panel.load({
45508     url: "your-url.php",
45509     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45510     callback: yourFunction,
45511     scope: yourObject, //(optional scope)
45512     discardUrl: false,
45513     nocache: false,
45514     text: "Loading...",
45515     timeout: 30,
45516     scripts: false
45517 });
45518 </code></pre>
45519      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45520      * 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.
45521      * @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}
45522      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45523      * @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.
45524      * @return {Roo.ContentPanel} this
45525      */
45526     load : function(){
45527         var um = this.el.getUpdateManager();
45528         um.update.apply(um, arguments);
45529         return this;
45530     },
45531
45532
45533     /**
45534      * 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.
45535      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45536      * @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)
45537      * @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)
45538      * @return {Roo.UpdateManager} The UpdateManager
45539      */
45540     setUrl : function(url, params, loadOnce){
45541         if(this.refreshDelegate){
45542             this.removeListener("activate", this.refreshDelegate);
45543         }
45544         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45545         this.on("activate", this.refreshDelegate);
45546         return this.el.getUpdateManager();
45547     },
45548     
45549     _handleRefresh : function(url, params, loadOnce){
45550         if(!loadOnce || !this.loaded){
45551             var updater = this.el.getUpdateManager();
45552             updater.update(url, params, this._setLoaded.createDelegate(this));
45553         }
45554     },
45555     
45556     _setLoaded : function(){
45557         this.loaded = true;
45558     }, 
45559     
45560     /**
45561      * Returns this panel's id
45562      * @return {String} 
45563      */
45564     getId : function(){
45565         return this.el.id;
45566     },
45567     
45568     /** 
45569      * Returns this panel's element - used by regiosn to add.
45570      * @return {Roo.Element} 
45571      */
45572     getEl : function(){
45573         return this.wrapEl || this.el;
45574     },
45575     
45576     adjustForComponents : function(width, height){
45577         if(this.resizeEl != this.el){
45578             width -= this.el.getFrameWidth('lr');
45579             height -= this.el.getFrameWidth('tb');
45580         }
45581         if(this.toolbar){
45582             var te = this.toolbar.getEl();
45583             height -= te.getHeight();
45584             te.setWidth(width);
45585         }
45586         if(this.adjustments){
45587             width += this.adjustments[0];
45588             height += this.adjustments[1];
45589         }
45590         return {"width": width, "height": height};
45591     },
45592     
45593     setSize : function(width, height){
45594         if(this.fitToFrame && !this.ignoreResize(width, height)){
45595             if(this.fitContainer && this.resizeEl != this.el){
45596                 this.el.setSize(width, height);
45597             }
45598             var size = this.adjustForComponents(width, height);
45599             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45600             this.fireEvent('resize', this, size.width, size.height);
45601         }
45602     },
45603     
45604     /**
45605      * Returns this panel's title
45606      * @return {String} 
45607      */
45608     getTitle : function(){
45609         return this.title;
45610     },
45611     
45612     /**
45613      * Set this panel's title
45614      * @param {String} title
45615      */
45616     setTitle : function(title){
45617         this.title = title;
45618         if(this.region){
45619             this.region.updatePanelTitle(this, title);
45620         }
45621     },
45622     
45623     /**
45624      * Returns true is this panel was configured to be closable
45625      * @return {Boolean} 
45626      */
45627     isClosable : function(){
45628         return this.closable;
45629     },
45630     
45631     beforeSlide : function(){
45632         this.el.clip();
45633         this.resizeEl.clip();
45634     },
45635     
45636     afterSlide : function(){
45637         this.el.unclip();
45638         this.resizeEl.unclip();
45639     },
45640     
45641     /**
45642      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45643      *   Will fail silently if the {@link #setUrl} method has not been called.
45644      *   This does not activate the panel, just updates its content.
45645      */
45646     refresh : function(){
45647         if(this.refreshDelegate){
45648            this.loaded = false;
45649            this.refreshDelegate();
45650         }
45651     },
45652     
45653     /**
45654      * Destroys this panel
45655      */
45656     destroy : function(){
45657         this.el.removeAllListeners();
45658         var tempEl = document.createElement("span");
45659         tempEl.appendChild(this.el.dom);
45660         tempEl.innerHTML = "";
45661         this.el.remove();
45662         this.el = null;
45663     },
45664     
45665       /**
45666      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45667      * <pre><code>
45668
45669 layout.addxtype({
45670        xtype : 'Form',
45671        items: [ .... ]
45672    }
45673 );
45674
45675 </code></pre>
45676      * @param {Object} cfg Xtype definition of item to add.
45677      */
45678     
45679     addxtype : function(cfg) {
45680         // add form..
45681         if (cfg.xtype.match(/^Form$/)) {
45682             var el = this.el.createChild();
45683
45684             this.form = new  Roo.form.Form(cfg);
45685             
45686             
45687             if ( this.form.allItems.length) this.form.render(el.dom);
45688             return this.form;
45689         }
45690         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45691             // views..
45692             cfg.el = this.el.appendChild(document.createElement("div"));
45693             // factory?
45694             var ret = new Roo[cfg.xtype](cfg);
45695             ret.render(false, ''); // render blank..
45696             return ret;
45697             
45698         }
45699         return false;
45700         
45701     }
45702 });
45703
45704 /**
45705  * @class Roo.GridPanel
45706  * @extends Roo.ContentPanel
45707  * @constructor
45708  * Create a new GridPanel.
45709  * @param {Roo.grid.Grid} grid The grid for this panel
45710  * @param {String/Object} config A string to set only the panel's title, or a config object
45711  */
45712 Roo.GridPanel = function(grid, config){
45713     
45714   
45715     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45716         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45717         
45718     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45719     
45720     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45721     
45722     if(this.toolbar){
45723         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45724     }
45725     // xtype created footer. - not sure if will work as we normally have to render first..
45726     if (this.footer && !this.footer.el && this.footer.xtype) {
45727         
45728         this.footer.container = this.grid.getView().getFooterPanel(true);
45729         this.footer.dataSource = this.grid.dataSource;
45730         this.footer = Roo.factory(this.footer, Roo);
45731         
45732     }
45733     
45734     grid.monitorWindowResize = false; // turn off autosizing
45735     grid.autoHeight = false;
45736     grid.autoWidth = false;
45737     this.grid = grid;
45738     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45739 };
45740
45741 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45742     getId : function(){
45743         return this.grid.id;
45744     },
45745     
45746     /**
45747      * Returns the grid for this panel
45748      * @return {Roo.grid.Grid} 
45749      */
45750     getGrid : function(){
45751         return this.grid;    
45752     },
45753     
45754     setSize : function(width, height){
45755         if(!this.ignoreResize(width, height)){
45756             var grid = this.grid;
45757             var size = this.adjustForComponents(width, height);
45758             grid.getGridEl().setSize(size.width, size.height);
45759             grid.autoSize();
45760         }
45761     },
45762     
45763     beforeSlide : function(){
45764         this.grid.getView().scroller.clip();
45765     },
45766     
45767     afterSlide : function(){
45768         this.grid.getView().scroller.unclip();
45769     },
45770     
45771     destroy : function(){
45772         this.grid.destroy();
45773         delete this.grid;
45774         Roo.GridPanel.superclass.destroy.call(this); 
45775     }
45776 });
45777
45778
45779 /**
45780  * @class Roo.NestedLayoutPanel
45781  * @extends Roo.ContentPanel
45782  * @constructor
45783  * Create a new NestedLayoutPanel.
45784  * 
45785  * 
45786  * @param {Roo.BorderLayout} layout The layout for this panel
45787  * @param {String/Object} config A string to set only the title or a config object
45788  */
45789 Roo.NestedLayoutPanel = function(layout, config)
45790 {
45791     // construct with only one argument..
45792     /* FIXME - implement nicer consturctors
45793     if (layout.layout) {
45794         config = layout;
45795         layout = config.layout;
45796         delete config.layout;
45797     }
45798     if (layout.xtype && !layout.getEl) {
45799         // then layout needs constructing..
45800         layout = Roo.factory(layout, Roo);
45801     }
45802     */
45803     
45804     
45805     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45806     
45807     layout.monitorWindowResize = false; // turn off autosizing
45808     this.layout = layout;
45809     this.layout.getEl().addClass("x-layout-nested-layout");
45810     
45811     
45812     
45813     
45814 };
45815
45816 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45817
45818     setSize : function(width, height){
45819         if(!this.ignoreResize(width, height)){
45820             var size = this.adjustForComponents(width, height);
45821             var el = this.layout.getEl();
45822             el.setSize(size.width, size.height);
45823             var touch = el.dom.offsetWidth;
45824             this.layout.layout();
45825             // ie requires a double layout on the first pass
45826             if(Roo.isIE && !this.initialized){
45827                 this.initialized = true;
45828                 this.layout.layout();
45829             }
45830         }
45831     },
45832     
45833     // activate all subpanels if not currently active..
45834     
45835     setActiveState : function(active){
45836         this.active = active;
45837         if(!active){
45838             this.fireEvent("deactivate", this);
45839             return;
45840         }
45841         
45842         this.fireEvent("activate", this);
45843         // not sure if this should happen before or after..
45844         if (!this.layout) {
45845             return; // should not happen..
45846         }
45847         var reg = false;
45848         for (var r in this.layout.regions) {
45849             reg = this.layout.getRegion(r);
45850             if (reg.getActivePanel()) {
45851                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45852                 reg.setActivePanel(reg.getActivePanel());
45853                 continue;
45854             }
45855             if (!reg.panels.length) {
45856                 continue;
45857             }
45858             reg.showPanel(reg.getPanel(0));
45859         }
45860         
45861         
45862         
45863         
45864     },
45865     
45866     /**
45867      * Returns the nested BorderLayout for this panel
45868      * @return {Roo.BorderLayout} 
45869      */
45870     getLayout : function(){
45871         return this.layout;
45872     },
45873     
45874      /**
45875      * Adds a xtype elements to the layout of the nested panel
45876      * <pre><code>
45877
45878 panel.addxtype({
45879        xtype : 'ContentPanel',
45880        region: 'west',
45881        items: [ .... ]
45882    }
45883 );
45884
45885 panel.addxtype({
45886         xtype : 'NestedLayoutPanel',
45887         region: 'west',
45888         layout: {
45889            center: { },
45890            west: { }   
45891         },
45892         items : [ ... list of content panels or nested layout panels.. ]
45893    }
45894 );
45895 </code></pre>
45896      * @param {Object} cfg Xtype definition of item to add.
45897      */
45898     addxtype : function(cfg) {
45899         return this.layout.addxtype(cfg);
45900     
45901     }
45902 });
45903
45904 Roo.ScrollPanel = function(el, config, content){
45905     config = config || {};
45906     config.fitToFrame = true;
45907     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45908     
45909     this.el.dom.style.overflow = "hidden";
45910     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45911     this.el.removeClass("x-layout-inactive-content");
45912     this.el.on("mousewheel", this.onWheel, this);
45913
45914     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45915     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45916     up.unselectable(); down.unselectable();
45917     up.on("click", this.scrollUp, this);
45918     down.on("click", this.scrollDown, this);
45919     up.addClassOnOver("x-scroller-btn-over");
45920     down.addClassOnOver("x-scroller-btn-over");
45921     up.addClassOnClick("x-scroller-btn-click");
45922     down.addClassOnClick("x-scroller-btn-click");
45923     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45924
45925     this.resizeEl = this.el;
45926     this.el = wrap; this.up = up; this.down = down;
45927 };
45928
45929 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45930     increment : 100,
45931     wheelIncrement : 5,
45932     scrollUp : function(){
45933         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45934     },
45935
45936     scrollDown : function(){
45937         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45938     },
45939
45940     afterScroll : function(){
45941         var el = this.resizeEl;
45942         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45943         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45944         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45945     },
45946
45947     setSize : function(){
45948         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45949         this.afterScroll();
45950     },
45951
45952     onWheel : function(e){
45953         var d = e.getWheelDelta();
45954         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45955         this.afterScroll();
45956         e.stopEvent();
45957     },
45958
45959     setContent : function(content, loadScripts){
45960         this.resizeEl.update(content, loadScripts);
45961     }
45962
45963 });
45964
45965
45966
45967
45968
45969
45970
45971
45972
45973 /**
45974  * @class Roo.TreePanel
45975  * @extends Roo.ContentPanel
45976  * @constructor
45977  * Create a new TreePanel. - defaults to fit/scoll contents.
45978  * @param {String/Object} config A string to set only the panel's title, or a config object
45979  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45980  */
45981 Roo.TreePanel = function(config){
45982     var el = config.el;
45983     var tree = config.tree;
45984     delete config.tree; 
45985     delete config.el; // hopefull!
45986     
45987     // wrapper for IE7 strict & safari scroll issue
45988     
45989     var treeEl = el.createChild();
45990     config.resizeEl = treeEl;
45991     
45992     
45993     
45994     Roo.TreePanel.superclass.constructor.call(this, el, config);
45995  
45996  
45997     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45998     //console.log(tree);
45999     this.on('activate', function()
46000     {
46001         if (this.tree.rendered) {
46002             return;
46003         }
46004         //console.log('render tree');
46005         this.tree.render();
46006     });
46007     
46008     this.on('resize',  function (cp, w, h) {
46009             this.tree.innerCt.setWidth(w);
46010             this.tree.innerCt.setHeight(h);
46011             this.tree.innerCt.setStyle('overflow-y', 'auto');
46012     });
46013
46014         
46015     
46016 };
46017
46018 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46019     fitToFrame : true,
46020     autoScroll : true
46021 });
46022
46023
46024
46025
46026
46027
46028
46029
46030
46031
46032
46033 /*
46034  * Based on:
46035  * Ext JS Library 1.1.1
46036  * Copyright(c) 2006-2007, Ext JS, LLC.
46037  *
46038  * Originally Released Under LGPL - original licence link has changed is not relivant.
46039  *
46040  * Fork - LGPL
46041  * <script type="text/javascript">
46042  */
46043  
46044
46045 /**
46046  * @class Roo.ReaderLayout
46047  * @extends Roo.BorderLayout
46048  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46049  * center region containing two nested regions (a top one for a list view and one for item preview below),
46050  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46051  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46052  * expedites the setup of the overall layout and regions for this common application style.
46053  * Example:
46054  <pre><code>
46055 var reader = new Roo.ReaderLayout();
46056 var CP = Roo.ContentPanel;  // shortcut for adding
46057
46058 reader.beginUpdate();
46059 reader.add("north", new CP("north", "North"));
46060 reader.add("west", new CP("west", {title: "West"}));
46061 reader.add("east", new CP("east", {title: "East"}));
46062
46063 reader.regions.listView.add(new CP("listView", "List"));
46064 reader.regions.preview.add(new CP("preview", "Preview"));
46065 reader.endUpdate();
46066 </code></pre>
46067 * @constructor
46068 * Create a new ReaderLayout
46069 * @param {Object} config Configuration options
46070 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46071 * document.body if omitted)
46072 */
46073 Roo.ReaderLayout = function(config, renderTo){
46074     var c = config || {size:{}};
46075     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46076         north: c.north !== false ? Roo.apply({
46077             split:false,
46078             initialSize: 32,
46079             titlebar: false
46080         }, c.north) : false,
46081         west: c.west !== false ? Roo.apply({
46082             split:true,
46083             initialSize: 200,
46084             minSize: 175,
46085             maxSize: 400,
46086             titlebar: true,
46087             collapsible: true,
46088             animate: true,
46089             margins:{left:5,right:0,bottom:5,top:5},
46090             cmargins:{left:5,right:5,bottom:5,top:5}
46091         }, c.west) : false,
46092         east: c.east !== false ? Roo.apply({
46093             split:true,
46094             initialSize: 200,
46095             minSize: 175,
46096             maxSize: 400,
46097             titlebar: true,
46098             collapsible: true,
46099             animate: true,
46100             margins:{left:0,right:5,bottom:5,top:5},
46101             cmargins:{left:5,right:5,bottom:5,top:5}
46102         }, c.east) : false,
46103         center: Roo.apply({
46104             tabPosition: 'top',
46105             autoScroll:false,
46106             closeOnTab: true,
46107             titlebar:false,
46108             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46109         }, c.center)
46110     });
46111
46112     this.el.addClass('x-reader');
46113
46114     this.beginUpdate();
46115
46116     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46117         south: c.preview !== false ? Roo.apply({
46118             split:true,
46119             initialSize: 200,
46120             minSize: 100,
46121             autoScroll:true,
46122             collapsible:true,
46123             titlebar: true,
46124             cmargins:{top:5,left:0, right:0, bottom:0}
46125         }, c.preview) : false,
46126         center: Roo.apply({
46127             autoScroll:false,
46128             titlebar:false,
46129             minHeight:200
46130         }, c.listView)
46131     });
46132     this.add('center', new Roo.NestedLayoutPanel(inner,
46133             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46134
46135     this.endUpdate();
46136
46137     this.regions.preview = inner.getRegion('south');
46138     this.regions.listView = inner.getRegion('center');
46139 };
46140
46141 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46142  * Based on:
46143  * Ext JS Library 1.1.1
46144  * Copyright(c) 2006-2007, Ext JS, LLC.
46145  *
46146  * Originally Released Under LGPL - original licence link has changed is not relivant.
46147  *
46148  * Fork - LGPL
46149  * <script type="text/javascript">
46150  */
46151  
46152 /**
46153  * @class Roo.grid.Grid
46154  * @extends Roo.util.Observable
46155  * This class represents the primary interface of a component based grid control.
46156  * <br><br>Usage:<pre><code>
46157  var grid = new Roo.grid.Grid("my-container-id", {
46158      ds: myDataStore,
46159      cm: myColModel,
46160      selModel: mySelectionModel,
46161      autoSizeColumns: true,
46162      monitorWindowResize: false,
46163      trackMouseOver: true
46164  });
46165  // set any options
46166  grid.render();
46167  * </code></pre>
46168  * <b>Common Problems:</b><br/>
46169  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46170  * element will correct this<br/>
46171  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46172  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46173  * are unpredictable.<br/>
46174  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46175  * grid to calculate dimensions/offsets.<br/>
46176   * @constructor
46177  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46178  * The container MUST have some type of size defined for the grid to fill. The container will be
46179  * automatically set to position relative if it isn't already.
46180  * @param {Object} config A config object that sets properties on this grid.
46181  */
46182 Roo.grid.Grid = function(container, config){
46183         // initialize the container
46184         this.container = Roo.get(container);
46185         this.container.update("");
46186         this.container.setStyle("overflow", "hidden");
46187     this.container.addClass('x-grid-container');
46188
46189     this.id = this.container.id;
46190
46191     Roo.apply(this, config);
46192     // check and correct shorthanded configs
46193     if(this.ds){
46194         this.dataSource = this.ds;
46195         delete this.ds;
46196     }
46197     if(this.cm){
46198         this.colModel = this.cm;
46199         delete this.cm;
46200     }
46201     if(this.sm){
46202         this.selModel = this.sm;
46203         delete this.sm;
46204     }
46205
46206     if (this.selModel) {
46207         this.selModel = Roo.factory(this.selModel, Roo.grid);
46208         this.sm = this.selModel;
46209         this.sm.xmodule = this.xmodule || false;
46210     }
46211     if (typeof(this.colModel.config) == 'undefined') {
46212         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46213         this.cm = this.colModel;
46214         this.cm.xmodule = this.xmodule || false;
46215     }
46216     if (this.dataSource) {
46217         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46218         this.ds = this.dataSource;
46219         this.ds.xmodule = this.xmodule || false;
46220         
46221     }
46222     
46223     
46224     
46225     if(this.width){
46226         this.container.setWidth(this.width);
46227     }
46228
46229     if(this.height){
46230         this.container.setHeight(this.height);
46231     }
46232     /** @private */
46233         this.addEvents({
46234         // raw events
46235         /**
46236          * @event click
46237          * The raw click event for the entire grid.
46238          * @param {Roo.EventObject} e
46239          */
46240         "click" : true,
46241         /**
46242          * @event dblclick
46243          * The raw dblclick event for the entire grid.
46244          * @param {Roo.EventObject} e
46245          */
46246         "dblclick" : true,
46247         /**
46248          * @event contextmenu
46249          * The raw contextmenu event for the entire grid.
46250          * @param {Roo.EventObject} e
46251          */
46252         "contextmenu" : true,
46253         /**
46254          * @event mousedown
46255          * The raw mousedown event for the entire grid.
46256          * @param {Roo.EventObject} e
46257          */
46258         "mousedown" : true,
46259         /**
46260          * @event mouseup
46261          * The raw mouseup event for the entire grid.
46262          * @param {Roo.EventObject} e
46263          */
46264         "mouseup" : true,
46265         /**
46266          * @event mouseover
46267          * The raw mouseover event for the entire grid.
46268          * @param {Roo.EventObject} e
46269          */
46270         "mouseover" : true,
46271         /**
46272          * @event mouseout
46273          * The raw mouseout event for the entire grid.
46274          * @param {Roo.EventObject} e
46275          */
46276         "mouseout" : true,
46277         /**
46278          * @event keypress
46279          * The raw keypress event for the entire grid.
46280          * @param {Roo.EventObject} e
46281          */
46282         "keypress" : true,
46283         /**
46284          * @event keydown
46285          * The raw keydown event for the entire grid.
46286          * @param {Roo.EventObject} e
46287          */
46288         "keydown" : true,
46289
46290         // custom events
46291
46292         /**
46293          * @event cellclick
46294          * Fires when a cell is clicked
46295          * @param {Grid} this
46296          * @param {Number} rowIndex
46297          * @param {Number} columnIndex
46298          * @param {Roo.EventObject} e
46299          */
46300         "cellclick" : true,
46301         /**
46302          * @event celldblclick
46303          * Fires when a cell is double clicked
46304          * @param {Grid} this
46305          * @param {Number} rowIndex
46306          * @param {Number} columnIndex
46307          * @param {Roo.EventObject} e
46308          */
46309         "celldblclick" : true,
46310         /**
46311          * @event rowclick
46312          * Fires when a row is clicked
46313          * @param {Grid} this
46314          * @param {Number} rowIndex
46315          * @param {Roo.EventObject} e
46316          */
46317         "rowclick" : true,
46318         /**
46319          * @event rowdblclick
46320          * Fires when a row is double clicked
46321          * @param {Grid} this
46322          * @param {Number} rowIndex
46323          * @param {Roo.EventObject} e
46324          */
46325         "rowdblclick" : true,
46326         /**
46327          * @event headerclick
46328          * Fires when a header is clicked
46329          * @param {Grid} this
46330          * @param {Number} columnIndex
46331          * @param {Roo.EventObject} e
46332          */
46333         "headerclick" : true,
46334         /**
46335          * @event headerdblclick
46336          * Fires when a header cell is double clicked
46337          * @param {Grid} this
46338          * @param {Number} columnIndex
46339          * @param {Roo.EventObject} e
46340          */
46341         "headerdblclick" : true,
46342         /**
46343          * @event rowcontextmenu
46344          * Fires when a row is right clicked
46345          * @param {Grid} this
46346          * @param {Number} rowIndex
46347          * @param {Roo.EventObject} e
46348          */
46349         "rowcontextmenu" : true,
46350         /**
46351          * @event cellcontextmenu
46352          * Fires when a cell is right clicked
46353          * @param {Grid} this
46354          * @param {Number} rowIndex
46355          * @param {Number} cellIndex
46356          * @param {Roo.EventObject} e
46357          */
46358          "cellcontextmenu" : true,
46359         /**
46360          * @event headercontextmenu
46361          * Fires when a header is right clicked
46362          * @param {Grid} this
46363          * @param {Number} columnIndex
46364          * @param {Roo.EventObject} e
46365          */
46366         "headercontextmenu" : true,
46367         /**
46368          * @event bodyscroll
46369          * Fires when the body element is scrolled
46370          * @param {Number} scrollLeft
46371          * @param {Number} scrollTop
46372          */
46373         "bodyscroll" : true,
46374         /**
46375          * @event columnresize
46376          * Fires when the user resizes a column
46377          * @param {Number} columnIndex
46378          * @param {Number} newSize
46379          */
46380         "columnresize" : true,
46381         /**
46382          * @event columnmove
46383          * Fires when the user moves a column
46384          * @param {Number} oldIndex
46385          * @param {Number} newIndex
46386          */
46387         "columnmove" : true,
46388         /**
46389          * @event startdrag
46390          * Fires when row(s) start being dragged
46391          * @param {Grid} this
46392          * @param {Roo.GridDD} dd The drag drop object
46393          * @param {event} e The raw browser event
46394          */
46395         "startdrag" : true,
46396         /**
46397          * @event enddrag
46398          * Fires when a drag operation is complete
46399          * @param {Grid} this
46400          * @param {Roo.GridDD} dd The drag drop object
46401          * @param {event} e The raw browser event
46402          */
46403         "enddrag" : true,
46404         /**
46405          * @event dragdrop
46406          * Fires when dragged row(s) are dropped on a valid DD target
46407          * @param {Grid} this
46408          * @param {Roo.GridDD} dd The drag drop object
46409          * @param {String} targetId The target drag drop object
46410          * @param {event} e The raw browser event
46411          */
46412         "dragdrop" : true,
46413         /**
46414          * @event dragover
46415          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46416          * @param {Grid} this
46417          * @param {Roo.GridDD} dd The drag drop object
46418          * @param {String} targetId The target drag drop object
46419          * @param {event} e The raw browser event
46420          */
46421         "dragover" : true,
46422         /**
46423          * @event dragenter
46424          *  Fires when the dragged row(s) first cross another DD target while being dragged
46425          * @param {Grid} this
46426          * @param {Roo.GridDD} dd The drag drop object
46427          * @param {String} targetId The target drag drop object
46428          * @param {event} e The raw browser event
46429          */
46430         "dragenter" : true,
46431         /**
46432          * @event dragout
46433          * Fires when the dragged row(s) leave another DD target while being dragged
46434          * @param {Grid} this
46435          * @param {Roo.GridDD} dd The drag drop object
46436          * @param {String} targetId The target drag drop object
46437          * @param {event} e The raw browser event
46438          */
46439         "dragout" : true,
46440         /**
46441          * @event rowclass
46442          * Fires when a row is rendered, so you can change add a style to it.
46443          * @param {GridView} gridview The grid view
46444          * @param {Object} rowcfg contains record, rowIndex and rowClass - set rowClass to add a style.
46445          */
46446         'rowclass' : true,
46447
46448         /**
46449          * @event render
46450          * Fires when the grid is rendered
46451          * @param {Grid} grid
46452          */
46453         'render' : true
46454     });
46455
46456     Roo.grid.Grid.superclass.constructor.call(this);
46457 };
46458 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46459     
46460     /**
46461      * @cfg {String} ddGroup - drag drop group.
46462          */
46463     
46464     /**
46465      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46466          */
46467         minColumnWidth : 25,
46468
46469     /**
46470          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46471          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46472          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46473          */
46474         autoSizeColumns : false,
46475
46476         /**
46477          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46478          */
46479         autoSizeHeaders : true,
46480
46481         /**
46482          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46483          */
46484         monitorWindowResize : true,
46485
46486         /**
46487          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46488          * rows measured to get a columns size. Default is 0 (all rows).
46489          */
46490         maxRowsToMeasure : 0,
46491
46492         /**
46493          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46494          */
46495         trackMouseOver : true,
46496
46497     /**
46498          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46499          */
46500     
46501         /**
46502          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46503          */
46504         enableDragDrop : false,
46505
46506         /**
46507          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46508          */
46509         enableColumnMove : true,
46510
46511         /**
46512          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46513          */
46514         enableColumnHide : true,
46515
46516         /**
46517          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46518          */
46519         enableRowHeightSync : false,
46520
46521         /**
46522          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46523          */
46524         stripeRows : true,
46525
46526         /**
46527          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46528          */
46529         autoHeight : false,
46530
46531     /**
46532      * @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.
46533      */
46534     autoExpandColumn : false,
46535
46536     /**
46537     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46538     * Default is 50.
46539     */
46540     autoExpandMin : 50,
46541
46542     /**
46543     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46544     */
46545     autoExpandMax : 1000,
46546
46547     /**
46548          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46549          */
46550         view : null,
46551
46552         /**
46553      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46554          */
46555         loadMask : false,
46556     /**
46557      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46558          */
46559         dropTarget: false,
46560     // private
46561     rendered : false,
46562
46563     /**
46564     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46565     * of a fixed width. Default is false.
46566     */
46567     /**
46568     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46569     */
46570     /**
46571      * Called once after all setup has been completed and the grid is ready to be rendered.
46572      * @return {Roo.grid.Grid} this
46573      */
46574     render : function(){
46575         var c = this.container;
46576         // try to detect autoHeight/width mode
46577         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46578             this.autoHeight = true;
46579         }
46580         var view = this.getView();
46581         view.init(this);
46582
46583         c.on("click", this.onClick, this);
46584         c.on("dblclick", this.onDblClick, this);
46585         c.on("contextmenu", this.onContextMenu, this);
46586         c.on("keydown", this.onKeyDown, this);
46587
46588         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46589
46590         this.getSelectionModel().init(this);
46591
46592         view.render();
46593
46594         if(this.loadMask){
46595             this.loadMask = new Roo.LoadMask(this.container,
46596                     Roo.apply({store:this.dataSource}, this.loadMask));
46597         }
46598         
46599         
46600         if (this.toolbar && this.toolbar.xtype) {
46601             this.toolbar.container = this.getView().getHeaderPanel(true);
46602             this.toolbar = new Ext.Toolbar(this.toolbar);
46603         }
46604         if (this.footer && this.footer.xtype) {
46605             this.footer.dataSource = this.getDataSource();
46606             this.footer.container = this.getView().getFooterPanel(true);
46607             this.footer = Roo.factory(this.footer, Roo);
46608         }
46609         if (this.dropTarget && this.dropTarget.xtype) {
46610             delete this.dropTarget.xtype;
46611             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46612         }
46613         
46614         
46615         this.rendered = true;
46616         this.fireEvent('render', this);
46617         return this;
46618     },
46619
46620         /**
46621          * Reconfigures the grid to use a different Store and Column Model.
46622          * The View will be bound to the new objects and refreshed.
46623          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46624          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46625          */
46626     reconfigure : function(dataSource, colModel){
46627         if(this.loadMask){
46628             this.loadMask.destroy();
46629             this.loadMask = new Roo.LoadMask(this.container,
46630                     Roo.apply({store:dataSource}, this.loadMask));
46631         }
46632         this.view.bind(dataSource, colModel);
46633         this.dataSource = dataSource;
46634         this.colModel = colModel;
46635         this.view.refresh(true);
46636     },
46637
46638     // private
46639     onKeyDown : function(e){
46640         this.fireEvent("keydown", e);
46641     },
46642
46643     /**
46644      * Destroy this grid.
46645      * @param {Boolean} removeEl True to remove the element
46646      */
46647     destroy : function(removeEl, keepListeners){
46648         if(this.loadMask){
46649             this.loadMask.destroy();
46650         }
46651         var c = this.container;
46652         c.removeAllListeners();
46653         this.view.destroy();
46654         this.colModel.purgeListeners();
46655         if(!keepListeners){
46656             this.purgeListeners();
46657         }
46658         c.update("");
46659         if(removeEl === true){
46660             c.remove();
46661         }
46662     },
46663
46664     // private
46665     processEvent : function(name, e){
46666         this.fireEvent(name, e);
46667         var t = e.getTarget();
46668         var v = this.view;
46669         var header = v.findHeaderIndex(t);
46670         if(header !== false){
46671             this.fireEvent("header" + name, this, header, e);
46672         }else{
46673             var row = v.findRowIndex(t);
46674             var cell = v.findCellIndex(t);
46675             if(row !== false){
46676                 this.fireEvent("row" + name, this, row, e);
46677                 if(cell !== false){
46678                     this.fireEvent("cell" + name, this, row, cell, e);
46679                 }
46680             }
46681         }
46682     },
46683
46684     // private
46685     onClick : function(e){
46686         this.processEvent("click", e);
46687     },
46688
46689     // private
46690     onContextMenu : function(e, t){
46691         this.processEvent("contextmenu", e);
46692     },
46693
46694     // private
46695     onDblClick : function(e){
46696         this.processEvent("dblclick", e);
46697     },
46698
46699     // private
46700     walkCells : function(row, col, step, fn, scope){
46701         var cm = this.colModel, clen = cm.getColumnCount();
46702         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46703         if(step < 0){
46704             if(col < 0){
46705                 row--;
46706                 first = false;
46707             }
46708             while(row >= 0){
46709                 if(!first){
46710                     col = clen-1;
46711                 }
46712                 first = false;
46713                 while(col >= 0){
46714                     if(fn.call(scope || this, row, col, cm) === true){
46715                         return [row, col];
46716                     }
46717                     col--;
46718                 }
46719                 row--;
46720             }
46721         } else {
46722             if(col >= clen){
46723                 row++;
46724                 first = false;
46725             }
46726             while(row < rlen){
46727                 if(!first){
46728                     col = 0;
46729                 }
46730                 first = false;
46731                 while(col < clen){
46732                     if(fn.call(scope || this, row, col, cm) === true){
46733                         return [row, col];
46734                     }
46735                     col++;
46736                 }
46737                 row++;
46738             }
46739         }
46740         return null;
46741     },
46742
46743     // private
46744     getSelections : function(){
46745         return this.selModel.getSelections();
46746     },
46747
46748     /**
46749      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46750      * but if manual update is required this method will initiate it.
46751      */
46752     autoSize : function(){
46753         if(this.rendered){
46754             this.view.layout();
46755             if(this.view.adjustForScroll){
46756                 this.view.adjustForScroll();
46757             }
46758         }
46759     },
46760
46761     /**
46762      * Returns the grid's underlying element.
46763      * @return {Element} The element
46764      */
46765     getGridEl : function(){
46766         return this.container;
46767     },
46768
46769     // private for compatibility, overridden by editor grid
46770     stopEditing : function(){},
46771
46772     /**
46773      * Returns the grid's SelectionModel.
46774      * @return {SelectionModel}
46775      */
46776     getSelectionModel : function(){
46777         if(!this.selModel){
46778             this.selModel = new Roo.grid.RowSelectionModel();
46779         }
46780         return this.selModel;
46781     },
46782
46783     /**
46784      * Returns the grid's DataSource.
46785      * @return {DataSource}
46786      */
46787     getDataSource : function(){
46788         return this.dataSource;
46789     },
46790
46791     /**
46792      * Returns the grid's ColumnModel.
46793      * @return {ColumnModel}
46794      */
46795     getColumnModel : function(){
46796         return this.colModel;
46797     },
46798
46799     /**
46800      * Returns the grid's GridView object.
46801      * @return {GridView}
46802      */
46803     getView : function(){
46804         if(!this.view){
46805             this.view = new Roo.grid.GridView(this.viewConfig);
46806         }
46807         return this.view;
46808     },
46809     /**
46810      * Called to get grid's drag proxy text, by default returns this.ddText.
46811      * @return {String}
46812      */
46813     getDragDropText : function(){
46814         var count = this.selModel.getCount();
46815         return String.format(this.ddText, count, count == 1 ? '' : 's');
46816     }
46817 });
46818 /**
46819  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46820  * %0 is replaced with the number of selected rows.
46821  * @type String
46822  */
46823 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46824  * Based on:
46825  * Ext JS Library 1.1.1
46826  * Copyright(c) 2006-2007, Ext JS, LLC.
46827  *
46828  * Originally Released Under LGPL - original licence link has changed is not relivant.
46829  *
46830  * Fork - LGPL
46831  * <script type="text/javascript">
46832  */
46833  
46834 Roo.grid.AbstractGridView = function(){
46835         this.grid = null;
46836         
46837         this.events = {
46838             "beforerowremoved" : true,
46839             "beforerowsinserted" : true,
46840             "beforerefresh" : true,
46841             "rowremoved" : true,
46842             "rowsinserted" : true,
46843             "rowupdated" : true,
46844             "refresh" : true
46845         };
46846     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46847 };
46848
46849 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46850     rowClass : "x-grid-row",
46851     cellClass : "x-grid-cell",
46852     tdClass : "x-grid-td",
46853     hdClass : "x-grid-hd",
46854     splitClass : "x-grid-hd-split",
46855     
46856         init: function(grid){
46857         this.grid = grid;
46858                 var cid = this.grid.getGridEl().id;
46859         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46860         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46861         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46862         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46863         },
46864         
46865         getColumnRenderers : function(){
46866         var renderers = [];
46867         var cm = this.grid.colModel;
46868         var colCount = cm.getColumnCount();
46869         for(var i = 0; i < colCount; i++){
46870             renderers[i] = cm.getRenderer(i);
46871         }
46872         return renderers;
46873     },
46874     
46875     getColumnIds : function(){
46876         var ids = [];
46877         var cm = this.grid.colModel;
46878         var colCount = cm.getColumnCount();
46879         for(var i = 0; i < colCount; i++){
46880             ids[i] = cm.getColumnId(i);
46881         }
46882         return ids;
46883     },
46884     
46885     getDataIndexes : function(){
46886         if(!this.indexMap){
46887             this.indexMap = this.buildIndexMap();
46888         }
46889         return this.indexMap.colToData;
46890     },
46891     
46892     getColumnIndexByDataIndex : function(dataIndex){
46893         if(!this.indexMap){
46894             this.indexMap = this.buildIndexMap();
46895         }
46896         return this.indexMap.dataToCol[dataIndex];
46897     },
46898     
46899     /**
46900      * Set a css style for a column dynamically. 
46901      * @param {Number} colIndex The index of the column
46902      * @param {String} name The css property name
46903      * @param {String} value The css value
46904      */
46905     setCSSStyle : function(colIndex, name, value){
46906         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46907         Roo.util.CSS.updateRule(selector, name, value);
46908     },
46909     
46910     generateRules : function(cm){
46911         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46912         Roo.util.CSS.removeStyleSheet(rulesId);
46913         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46914             var cid = cm.getColumnId(i);
46915             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46916                          this.tdSelector, cid, " {\n}\n",
46917                          this.hdSelector, cid, " {\n}\n",
46918                          this.splitSelector, cid, " {\n}\n");
46919         }
46920         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46921     }
46922 });/*
46923  * Based on:
46924  * Ext JS Library 1.1.1
46925  * Copyright(c) 2006-2007, Ext JS, LLC.
46926  *
46927  * Originally Released Under LGPL - original licence link has changed is not relivant.
46928  *
46929  * Fork - LGPL
46930  * <script type="text/javascript">
46931  */
46932
46933 // private
46934 // This is a support class used internally by the Grid components
46935 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46936     this.grid = grid;
46937     this.view = grid.getView();
46938     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46939     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46940     if(hd2){
46941         this.setHandleElId(Roo.id(hd));
46942         this.setOuterHandleElId(Roo.id(hd2));
46943     }
46944     this.scroll = false;
46945 };
46946 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46947     maxDragWidth: 120,
46948     getDragData : function(e){
46949         var t = Roo.lib.Event.getTarget(e);
46950         var h = this.view.findHeaderCell(t);
46951         if(h){
46952             return {ddel: h.firstChild, header:h};
46953         }
46954         return false;
46955     },
46956
46957     onInitDrag : function(e){
46958         this.view.headersDisabled = true;
46959         var clone = this.dragData.ddel.cloneNode(true);
46960         clone.id = Roo.id();
46961         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46962         this.proxy.update(clone);
46963         return true;
46964     },
46965
46966     afterValidDrop : function(){
46967         var v = this.view;
46968         setTimeout(function(){
46969             v.headersDisabled = false;
46970         }, 50);
46971     },
46972
46973     afterInvalidDrop : function(){
46974         var v = this.view;
46975         setTimeout(function(){
46976             v.headersDisabled = false;
46977         }, 50);
46978     }
46979 });
46980 /*
46981  * Based on:
46982  * Ext JS Library 1.1.1
46983  * Copyright(c) 2006-2007, Ext JS, LLC.
46984  *
46985  * Originally Released Under LGPL - original licence link has changed is not relivant.
46986  *
46987  * Fork - LGPL
46988  * <script type="text/javascript">
46989  */
46990 // private
46991 // This is a support class used internally by the Grid components
46992 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46993     this.grid = grid;
46994     this.view = grid.getView();
46995     // split the proxies so they don't interfere with mouse events
46996     this.proxyTop = Roo.DomHelper.append(document.body, {
46997         cls:"col-move-top", html:"&#160;"
46998     }, true);
46999     this.proxyBottom = Roo.DomHelper.append(document.body, {
47000         cls:"col-move-bottom", html:"&#160;"
47001     }, true);
47002     this.proxyTop.hide = this.proxyBottom.hide = function(){
47003         this.setLeftTop(-100,-100);
47004         this.setStyle("visibility", "hidden");
47005     };
47006     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47007     // temporarily disabled
47008     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47009     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47010 };
47011 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47012     proxyOffsets : [-4, -9],
47013     fly: Roo.Element.fly,
47014
47015     getTargetFromEvent : function(e){
47016         var t = Roo.lib.Event.getTarget(e);
47017         var cindex = this.view.findCellIndex(t);
47018         if(cindex !== false){
47019             return this.view.getHeaderCell(cindex);
47020         }
47021     },
47022
47023     nextVisible : function(h){
47024         var v = this.view, cm = this.grid.colModel;
47025         h = h.nextSibling;
47026         while(h){
47027             if(!cm.isHidden(v.getCellIndex(h))){
47028                 return h;
47029             }
47030             h = h.nextSibling;
47031         }
47032         return null;
47033     },
47034
47035     prevVisible : function(h){
47036         var v = this.view, cm = this.grid.colModel;
47037         h = h.prevSibling;
47038         while(h){
47039             if(!cm.isHidden(v.getCellIndex(h))){
47040                 return h;
47041             }
47042             h = h.prevSibling;
47043         }
47044         return null;
47045     },
47046
47047     positionIndicator : function(h, n, e){
47048         var x = Roo.lib.Event.getPageX(e);
47049         var r = Roo.lib.Dom.getRegion(n.firstChild);
47050         var px, pt, py = r.top + this.proxyOffsets[1];
47051         if((r.right - x) <= (r.right-r.left)/2){
47052             px = r.right+this.view.borderWidth;
47053             pt = "after";
47054         }else{
47055             px = r.left;
47056             pt = "before";
47057         }
47058         var oldIndex = this.view.getCellIndex(h);
47059         var newIndex = this.view.getCellIndex(n);
47060
47061         if(this.grid.colModel.isFixed(newIndex)){
47062             return false;
47063         }
47064
47065         var locked = this.grid.colModel.isLocked(newIndex);
47066
47067         if(pt == "after"){
47068             newIndex++;
47069         }
47070         if(oldIndex < newIndex){
47071             newIndex--;
47072         }
47073         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47074             return false;
47075         }
47076         px +=  this.proxyOffsets[0];
47077         this.proxyTop.setLeftTop(px, py);
47078         this.proxyTop.show();
47079         if(!this.bottomOffset){
47080             this.bottomOffset = this.view.mainHd.getHeight();
47081         }
47082         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47083         this.proxyBottom.show();
47084         return pt;
47085     },
47086
47087     onNodeEnter : function(n, dd, e, data){
47088         if(data.header != n){
47089             this.positionIndicator(data.header, n, e);
47090         }
47091     },
47092
47093     onNodeOver : function(n, dd, e, data){
47094         var result = false;
47095         if(data.header != n){
47096             result = this.positionIndicator(data.header, n, e);
47097         }
47098         if(!result){
47099             this.proxyTop.hide();
47100             this.proxyBottom.hide();
47101         }
47102         return result ? this.dropAllowed : this.dropNotAllowed;
47103     },
47104
47105     onNodeOut : function(n, dd, e, data){
47106         this.proxyTop.hide();
47107         this.proxyBottom.hide();
47108     },
47109
47110     onNodeDrop : function(n, dd, e, data){
47111         var h = data.header;
47112         if(h != n){
47113             var cm = this.grid.colModel;
47114             var x = Roo.lib.Event.getPageX(e);
47115             var r = Roo.lib.Dom.getRegion(n.firstChild);
47116             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47117             var oldIndex = this.view.getCellIndex(h);
47118             var newIndex = this.view.getCellIndex(n);
47119             var locked = cm.isLocked(newIndex);
47120             if(pt == "after"){
47121                 newIndex++;
47122             }
47123             if(oldIndex < newIndex){
47124                 newIndex--;
47125             }
47126             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47127                 return false;
47128             }
47129             cm.setLocked(oldIndex, locked, true);
47130             cm.moveColumn(oldIndex, newIndex);
47131             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47132             return true;
47133         }
47134         return false;
47135     }
47136 });
47137 /*
47138  * Based on:
47139  * Ext JS Library 1.1.1
47140  * Copyright(c) 2006-2007, Ext JS, LLC.
47141  *
47142  * Originally Released Under LGPL - original licence link has changed is not relivant.
47143  *
47144  * Fork - LGPL
47145  * <script type="text/javascript">
47146  */
47147   
47148 /**
47149  * @class Roo.grid.GridView
47150  * @extends Roo.util.Observable
47151  *
47152  * @constructor
47153  * @param {Object} config
47154  */
47155 Roo.grid.GridView = function(config){
47156     Roo.grid.GridView.superclass.constructor.call(this);
47157     this.el = null;
47158
47159     Roo.apply(this, config);
47160 };
47161
47162 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47163
47164     /**
47165      * Override this function to apply custom css classes to rows during rendering
47166      * @param {Record} record The record
47167      * @param {Number} index
47168      * @method getRowClass
47169      */
47170     rowClass : "x-grid-row",
47171
47172     cellClass : "x-grid-col",
47173
47174     tdClass : "x-grid-td",
47175
47176     hdClass : "x-grid-hd",
47177
47178     splitClass : "x-grid-split",
47179
47180     sortClasses : ["sort-asc", "sort-desc"],
47181
47182     enableMoveAnim : false,
47183
47184     hlColor: "C3DAF9",
47185
47186     dh : Roo.DomHelper,
47187
47188     fly : Roo.Element.fly,
47189
47190     css : Roo.util.CSS,
47191
47192     borderWidth: 1,
47193
47194     splitOffset: 3,
47195
47196     scrollIncrement : 22,
47197
47198     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47199
47200     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47201
47202     bind : function(ds, cm){
47203         if(this.ds){
47204             this.ds.un("load", this.onLoad, this);
47205             this.ds.un("datachanged", this.onDataChange, this);
47206             this.ds.un("add", this.onAdd, this);
47207             this.ds.un("remove", this.onRemove, this);
47208             this.ds.un("update", this.onUpdate, this);
47209             this.ds.un("clear", this.onClear, this);
47210         }
47211         if(ds){
47212             ds.on("load", this.onLoad, this);
47213             ds.on("datachanged", this.onDataChange, this);
47214             ds.on("add", this.onAdd, this);
47215             ds.on("remove", this.onRemove, this);
47216             ds.on("update", this.onUpdate, this);
47217             ds.on("clear", this.onClear, this);
47218         }
47219         this.ds = ds;
47220
47221         if(this.cm){
47222             this.cm.un("widthchange", this.onColWidthChange, this);
47223             this.cm.un("headerchange", this.onHeaderChange, this);
47224             this.cm.un("hiddenchange", this.onHiddenChange, this);
47225             this.cm.un("columnmoved", this.onColumnMove, this);
47226             this.cm.un("columnlockchange", this.onColumnLock, this);
47227         }
47228         if(cm){
47229             this.generateRules(cm);
47230             cm.on("widthchange", this.onColWidthChange, this);
47231             cm.on("headerchange", this.onHeaderChange, this);
47232             cm.on("hiddenchange", this.onHiddenChange, this);
47233             cm.on("columnmoved", this.onColumnMove, this);
47234             cm.on("columnlockchange", this.onColumnLock, this);
47235         }
47236         this.cm = cm;
47237     },
47238
47239     init: function(grid){
47240                 Roo.grid.GridView.superclass.init.call(this, grid);
47241
47242                 this.bind(grid.dataSource, grid.colModel);
47243
47244             grid.on("headerclick", this.handleHeaderClick, this);
47245
47246         if(grid.trackMouseOver){
47247             grid.on("mouseover", this.onRowOver, this);
47248                 grid.on("mouseout", this.onRowOut, this);
47249             }
47250             grid.cancelTextSelection = function(){};
47251                 this.gridId = grid.id;
47252
47253                 var tpls = this.templates || {};
47254
47255                 if(!tpls.master){
47256                     tpls.master = new Roo.Template(
47257                        '<div class="x-grid" hidefocus="true">',
47258                           '<div class="x-grid-topbar"></div>',
47259                           '<div class="x-grid-scroller"><div></div></div>',
47260                           '<div class="x-grid-locked">',
47261                               '<div class="x-grid-header">{lockedHeader}</div>',
47262                               '<div class="x-grid-body">{lockedBody}</div>',
47263                           "</div>",
47264                           '<div class="x-grid-viewport">',
47265                               '<div class="x-grid-header">{header}</div>',
47266                               '<div class="x-grid-body">{body}</div>',
47267                           "</div>",
47268                           '<div class="x-grid-bottombar"></div>',
47269                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47270                           '<div class="x-grid-resize-proxy">&#160;</div>',
47271                        "</div>"
47272                     );
47273                     tpls.master.disableformats = true;
47274                 }
47275
47276                 if(!tpls.header){
47277                     tpls.header = new Roo.Template(
47278                        '<table border="0" cellspacing="0" cellpadding="0">',
47279                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47280                        "</table>{splits}"
47281                     );
47282                     tpls.header.disableformats = true;
47283                 }
47284                 tpls.header.compile();
47285
47286                 if(!tpls.hcell){
47287                     tpls.hcell = new Roo.Template(
47288                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47289                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47290                         "</div></td>"
47291                      );
47292                      tpls.hcell.disableFormats = true;
47293                 }
47294                 tpls.hcell.compile();
47295
47296                 if(!tpls.hsplit){
47297                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47298                     tpls.hsplit.disableFormats = true;
47299                 }
47300                 tpls.hsplit.compile();
47301
47302                 if(!tpls.body){
47303                     tpls.body = new Roo.Template(
47304                        '<table border="0" cellspacing="0" cellpadding="0">',
47305                        "<tbody>{rows}</tbody>",
47306                        "</table>"
47307                     );
47308                     tpls.body.disableFormats = true;
47309                 }
47310                 tpls.body.compile();
47311
47312                 if(!tpls.row){
47313                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47314                     tpls.row.disableFormats = true;
47315                 }
47316                 tpls.row.compile();
47317
47318                 if(!tpls.cell){
47319                     tpls.cell = new Roo.Template(
47320                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47321                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47322                         "</td>"
47323                     );
47324             tpls.cell.disableFormats = true;
47325         }
47326                 tpls.cell.compile();
47327
47328                 this.templates = tpls;
47329         },
47330
47331         // remap these for backwards compat
47332     onColWidthChange : function(){
47333         this.updateColumns.apply(this, arguments);
47334     },
47335     onHeaderChange : function(){
47336         this.updateHeaders.apply(this, arguments);
47337     }, 
47338     onHiddenChange : function(){
47339         this.handleHiddenChange.apply(this, arguments);
47340     },
47341     onColumnMove : function(){
47342         this.handleColumnMove.apply(this, arguments);
47343     },
47344     onColumnLock : function(){
47345         this.handleLockChange.apply(this, arguments);
47346     },
47347
47348     onDataChange : function(){
47349         this.refresh();
47350         this.updateHeaderSortState();
47351     },
47352
47353         onClear : function(){
47354         this.refresh();
47355     },
47356
47357         onUpdate : function(ds, record){
47358         this.refreshRow(record);
47359     },
47360
47361     refreshRow : function(record){
47362         var ds = this.ds, index;
47363         if(typeof record == 'number'){
47364             index = record;
47365             record = ds.getAt(index);
47366         }else{
47367             index = ds.indexOf(record);
47368         }
47369         this.insertRows(ds, index, index, true);
47370         this.onRemove(ds, record, index+1, true);
47371         this.syncRowHeights(index, index);
47372         this.layout();
47373         this.fireEvent("rowupdated", this, index, record);
47374     },
47375
47376     onAdd : function(ds, records, index){
47377         this.insertRows(ds, index, index + (records.length-1));
47378     },
47379
47380     onRemove : function(ds, record, index, isUpdate){
47381         if(isUpdate !== true){
47382             this.fireEvent("beforerowremoved", this, index, record);
47383         }
47384         var bt = this.getBodyTable(), lt = this.getLockedTable();
47385         if(bt.rows[index]){
47386             bt.firstChild.removeChild(bt.rows[index]);
47387         }
47388         if(lt.rows[index]){
47389             lt.firstChild.removeChild(lt.rows[index]);
47390         }
47391         if(isUpdate !== true){
47392             this.stripeRows(index);
47393             this.syncRowHeights(index, index);
47394             this.layout();
47395             this.fireEvent("rowremoved", this, index, record);
47396         }
47397     },
47398
47399     onLoad : function(){
47400         this.scrollToTop();
47401     },
47402
47403     /**
47404      * Scrolls the grid to the top
47405      */
47406     scrollToTop : function(){
47407         if(this.scroller){
47408             this.scroller.dom.scrollTop = 0;
47409             this.syncScroll();
47410         }
47411     },
47412
47413     /**
47414      * Gets a panel in the header of the grid that can be used for toolbars etc.
47415      * After modifying the contents of this panel a call to grid.autoSize() may be
47416      * required to register any changes in size.
47417      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47418      * @return Roo.Element
47419      */
47420     getHeaderPanel : function(doShow){
47421         if(doShow){
47422             this.headerPanel.show();
47423         }
47424         return this.headerPanel;
47425         },
47426
47427         /**
47428      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47429      * After modifying the contents of this panel a call to grid.autoSize() may be
47430      * required to register any changes in size.
47431      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47432      * @return Roo.Element
47433      */
47434     getFooterPanel : function(doShow){
47435         if(doShow){
47436             this.footerPanel.show();
47437         }
47438         return this.footerPanel;
47439         },
47440
47441         initElements : function(){
47442             var E = Roo.Element;
47443             var el = this.grid.getGridEl().dom.firstChild;
47444             var cs = el.childNodes;
47445
47446             this.el = new E(el);
47447             this.headerPanel = new E(el.firstChild);
47448             this.headerPanel.enableDisplayMode("block");
47449
47450         this.scroller = new E(cs[1]);
47451             this.scrollSizer = new E(this.scroller.dom.firstChild);
47452
47453             this.lockedWrap = new E(cs[2]);
47454             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47455             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47456
47457             this.mainWrap = new E(cs[3]);
47458             this.mainHd = new E(this.mainWrap.dom.firstChild);
47459             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47460
47461             this.footerPanel = new E(cs[4]);
47462             this.footerPanel.enableDisplayMode("block");
47463
47464         this.focusEl = new E(cs[5]);
47465         this.focusEl.swallowEvent("click", true);
47466         this.resizeProxy = new E(cs[6]);
47467
47468             this.headerSelector = String.format(
47469                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47470                this.lockedHd.id, this.mainHd.id
47471             );
47472
47473             this.splitterSelector = String.format(
47474                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47475                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47476             );
47477     },
47478     idToCssName : function(s)
47479     {
47480         return s.replace(/[^a-z0-9]+/ig, '-');
47481     },
47482
47483         getHeaderCell : function(index){
47484             return Roo.DomQuery.select(this.headerSelector)[index];
47485         },
47486
47487         getHeaderCellMeasure : function(index){
47488             return this.getHeaderCell(index).firstChild;
47489         },
47490
47491         getHeaderCellText : function(index){
47492             return this.getHeaderCell(index).firstChild.firstChild;
47493         },
47494
47495         getLockedTable : function(){
47496             return this.lockedBody.dom.firstChild;
47497         },
47498
47499         getBodyTable : function(){
47500             return this.mainBody.dom.firstChild;
47501         },
47502
47503         getLockedRow : function(index){
47504             return this.getLockedTable().rows[index];
47505         },
47506
47507         getRow : function(index){
47508             return this.getBodyTable().rows[index];
47509         },
47510
47511         getRowComposite : function(index){
47512             if(!this.rowEl){
47513                 this.rowEl = new Roo.CompositeElementLite();
47514             }
47515         var els = [], lrow, mrow;
47516         if(lrow = this.getLockedRow(index)){
47517             els.push(lrow);
47518         }
47519         if(mrow = this.getRow(index)){
47520             els.push(mrow);
47521         }
47522         this.rowEl.elements = els;
47523             return this.rowEl;
47524         },
47525
47526         getCell : function(rowIndex, colIndex){
47527             var locked = this.cm.getLockedCount();
47528             var source;
47529             if(colIndex < locked){
47530                 source = this.lockedBody.dom.firstChild;
47531             }else{
47532                 source = this.mainBody.dom.firstChild;
47533                 colIndex -= locked;
47534             }
47535         return source.rows[rowIndex].childNodes[colIndex];
47536         },
47537
47538         getCellText : function(rowIndex, colIndex){
47539             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47540         },
47541
47542         getCellBox : function(cell){
47543             var b = this.fly(cell).getBox();
47544         if(Roo.isOpera){ // opera fails to report the Y
47545             b.y = cell.offsetTop + this.mainBody.getY();
47546         }
47547         return b;
47548     },
47549
47550     getCellIndex : function(cell){
47551         var id = String(cell.className).match(this.cellRE);
47552         if(id){
47553             return parseInt(id[1], 10);
47554         }
47555         return 0;
47556     },
47557
47558     findHeaderIndex : function(n){
47559         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47560         return r ? this.getCellIndex(r) : false;
47561     },
47562
47563     findHeaderCell : function(n){
47564         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47565         return r ? r : false;
47566     },
47567
47568     findRowIndex : function(n){
47569         if(!n){
47570             return false;
47571         }
47572         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47573         return r ? r.rowIndex : false;
47574     },
47575
47576     findCellIndex : function(node){
47577         var stop = this.el.dom;
47578         while(node && node != stop){
47579             if(this.findRE.test(node.className)){
47580                 return this.getCellIndex(node);
47581             }
47582             node = node.parentNode;
47583         }
47584         return false;
47585     },
47586
47587     getColumnId : function(index){
47588             return this.cm.getColumnId(index);
47589         },
47590
47591     getSplitters : function()
47592     {
47593             if(this.splitterSelector){
47594                return Roo.DomQuery.select(this.splitterSelector);
47595             }else{
47596                 return null;
47597             }
47598         },
47599
47600         getSplitter : function(index){
47601             return this.getSplitters()[index];
47602         },
47603
47604     onRowOver : function(e, t){
47605         var row;
47606         if((row = this.findRowIndex(t)) !== false){
47607             this.getRowComposite(row).addClass("x-grid-row-over");
47608         }
47609     },
47610
47611     onRowOut : function(e, t){
47612         var row;
47613         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47614             this.getRowComposite(row).removeClass("x-grid-row-over");
47615         }
47616     },
47617
47618     renderHeaders : function(){
47619             var cm = this.cm;
47620         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47621         var cb = [], lb = [], sb = [], lsb = [], p = {};
47622         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47623             p.cellId = "x-grid-hd-0-" + i;
47624             p.splitId = "x-grid-csplit-0-" + i;
47625             p.id = cm.getColumnId(i);
47626             p.title = cm.getColumnTooltip(i) || "";
47627             p.value = cm.getColumnHeader(i) || "";
47628             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47629             if(!cm.isLocked(i)){
47630                 cb[cb.length] = ct.apply(p);
47631                 sb[sb.length] = st.apply(p);
47632             }else{
47633                 lb[lb.length] = ct.apply(p);
47634                 lsb[lsb.length] = st.apply(p);
47635             }
47636         }
47637         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47638                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47639         },
47640
47641         updateHeaders : function(){
47642         var html = this.renderHeaders();
47643         this.lockedHd.update(html[0]);
47644         this.mainHd.update(html[1]);
47645     },
47646
47647     /**
47648      * Focuses the specified row.
47649      * @param {Number} row The row index
47650      */
47651     focusRow : function(row){
47652         var x = this.scroller.dom.scrollLeft;
47653         this.focusCell(row, 0, false);
47654         this.scroller.dom.scrollLeft = x;
47655     },
47656
47657     /**
47658      * Focuses the specified cell.
47659      * @param {Number} row The row index
47660      * @param {Number} col The column index
47661      * @param {Boolean} hscroll false to disable horizontal scrolling
47662      */
47663     focusCell : function(row, col, hscroll){
47664         var el = this.ensureVisible(row, col, hscroll);
47665         this.focusEl.alignTo(el, "tl-tl");
47666         if(Roo.isGecko){
47667             this.focusEl.focus();
47668         }else{
47669             this.focusEl.focus.defer(1, this.focusEl);
47670         }
47671     },
47672
47673     /**
47674      * Scrolls the specified cell into view
47675      * @param {Number} row The row index
47676      * @param {Number} col The column index
47677      * @param {Boolean} hscroll false to disable horizontal scrolling
47678      */
47679     ensureVisible : function(row, col, hscroll){
47680         if(typeof row != "number"){
47681             row = row.rowIndex;
47682         }
47683         if(row < 0 && row >= this.ds.getCount()){
47684             return;
47685         }
47686         col = (col !== undefined ? col : 0);
47687         var cm = this.grid.colModel;
47688         while(cm.isHidden(col)){
47689             col++;
47690         }
47691
47692         var el = this.getCell(row, col);
47693         if(!el){
47694             return;
47695         }
47696         var c = this.scroller.dom;
47697
47698         var ctop = parseInt(el.offsetTop, 10);
47699         var cleft = parseInt(el.offsetLeft, 10);
47700         var cbot = ctop + el.offsetHeight;
47701         var cright = cleft + el.offsetWidth;
47702
47703         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47704         var stop = parseInt(c.scrollTop, 10);
47705         var sleft = parseInt(c.scrollLeft, 10);
47706         var sbot = stop + ch;
47707         var sright = sleft + c.clientWidth;
47708
47709         if(ctop < stop){
47710                 c.scrollTop = ctop;
47711         }else if(cbot > sbot){
47712             c.scrollTop = cbot-ch;
47713         }
47714
47715         if(hscroll !== false){
47716             if(cleft < sleft){
47717                 c.scrollLeft = cleft;
47718             }else if(cright > sright){
47719                 c.scrollLeft = cright-c.clientWidth;
47720             }
47721         }
47722         return el;
47723     },
47724
47725     updateColumns : function(){
47726         this.grid.stopEditing();
47727         var cm = this.grid.colModel, colIds = this.getColumnIds();
47728         //var totalWidth = cm.getTotalWidth();
47729         var pos = 0;
47730         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47731             //if(cm.isHidden(i)) continue;
47732             var w = cm.getColumnWidth(i);
47733             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47734             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47735         }
47736         this.updateSplitters();
47737     },
47738
47739     generateRules : function(cm){
47740         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47741         Roo.util.CSS.removeStyleSheet(rulesId);
47742         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47743             var cid = cm.getColumnId(i);
47744             var align = '';
47745             if(cm.config[i].align){
47746                 align = 'text-align:'+cm.config[i].align+';';
47747             }
47748             var hidden = '';
47749             if(cm.isHidden(i)){
47750                 hidden = 'display:none;';
47751             }
47752             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47753             ruleBuf.push(
47754                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47755                     this.hdSelector, cid, " {\n", align, width, "}\n",
47756                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47757                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47758         }
47759         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47760     },
47761
47762     updateSplitters : function(){
47763         var cm = this.cm, s = this.getSplitters();
47764         if(s){ // splitters not created yet
47765             var pos = 0, locked = true;
47766             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47767                 if(cm.isHidden(i)) continue;
47768                 var w = cm.getColumnWidth(i); // make sure it's a number
47769                 if(!cm.isLocked(i) && locked){
47770                     pos = 0;
47771                     locked = false;
47772                 }
47773                 pos += w;
47774                 s[i].style.left = (pos-this.splitOffset) + "px";
47775             }
47776         }
47777     },
47778
47779     handleHiddenChange : function(colModel, colIndex, hidden){
47780         if(hidden){
47781             this.hideColumn(colIndex);
47782         }else{
47783             this.unhideColumn(colIndex);
47784         }
47785     },
47786
47787     hideColumn : function(colIndex){
47788         var cid = this.getColumnId(colIndex);
47789         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47790         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47791         if(Roo.isSafari){
47792             this.updateHeaders();
47793         }
47794         this.updateSplitters();
47795         this.layout();
47796     },
47797
47798     unhideColumn : function(colIndex){
47799         var cid = this.getColumnId(colIndex);
47800         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47801         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47802
47803         if(Roo.isSafari){
47804             this.updateHeaders();
47805         }
47806         this.updateSplitters();
47807         this.layout();
47808     },
47809
47810     insertRows : function(dm, firstRow, lastRow, isUpdate){
47811         if(firstRow == 0 && lastRow == dm.getCount()-1){
47812             this.refresh();
47813         }else{
47814             if(!isUpdate){
47815                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47816             }
47817             var s = this.getScrollState();
47818             var markup = this.renderRows(firstRow, lastRow);
47819             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47820             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47821             this.restoreScroll(s);
47822             if(!isUpdate){
47823                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47824                 this.syncRowHeights(firstRow, lastRow);
47825                 this.stripeRows(firstRow);
47826                 this.layout();
47827             }
47828         }
47829     },
47830
47831     bufferRows : function(markup, target, index){
47832         var before = null, trows = target.rows, tbody = target.tBodies[0];
47833         if(index < trows.length){
47834             before = trows[index];
47835         }
47836         var b = document.createElement("div");
47837         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47838         var rows = b.firstChild.rows;
47839         for(var i = 0, len = rows.length; i < len; i++){
47840             if(before){
47841                 tbody.insertBefore(rows[0], before);
47842             }else{
47843                 tbody.appendChild(rows[0]);
47844             }
47845         }
47846         b.innerHTML = "";
47847         b = null;
47848     },
47849
47850     deleteRows : function(dm, firstRow, lastRow){
47851         if(dm.getRowCount()<1){
47852             this.fireEvent("beforerefresh", this);
47853             this.mainBody.update("");
47854             this.lockedBody.update("");
47855             this.fireEvent("refresh", this);
47856         }else{
47857             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47858             var bt = this.getBodyTable();
47859             var tbody = bt.firstChild;
47860             var rows = bt.rows;
47861             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47862                 tbody.removeChild(rows[firstRow]);
47863             }
47864             this.stripeRows(firstRow);
47865             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47866         }
47867     },
47868
47869     updateRows : function(dataSource, firstRow, lastRow){
47870         var s = this.getScrollState();
47871         this.refresh();
47872         this.restoreScroll(s);
47873     },
47874
47875     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47876         if(!noRefresh){
47877            this.refresh();
47878         }
47879         this.updateHeaderSortState();
47880     },
47881
47882     getScrollState : function(){
47883         var sb = this.scroller.dom;
47884         return {left: sb.scrollLeft, top: sb.scrollTop};
47885     },
47886
47887     stripeRows : function(startRow){
47888         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47889             return;
47890         }
47891         startRow = startRow || 0;
47892         var rows = this.getBodyTable().rows;
47893         var lrows = this.getLockedTable().rows;
47894         var cls = ' x-grid-row-alt ';
47895         for(var i = startRow, len = rows.length; i < len; i++){
47896             var row = rows[i], lrow = lrows[i];
47897             var isAlt = ((i+1) % 2 == 0);
47898             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47899             if(isAlt == hasAlt){
47900                 continue;
47901             }
47902             if(isAlt){
47903                 row.className += " x-grid-row-alt";
47904             }else{
47905                 row.className = row.className.replace("x-grid-row-alt", "");
47906             }
47907             if(lrow){
47908                 lrow.className = row.className;
47909             }
47910         }
47911     },
47912
47913     restoreScroll : function(state){
47914         var sb = this.scroller.dom;
47915         sb.scrollLeft = state.left;
47916         sb.scrollTop = state.top;
47917         this.syncScroll();
47918     },
47919
47920     syncScroll : function(){
47921         var sb = this.scroller.dom;
47922         var sh = this.mainHd.dom;
47923         var bs = this.mainBody.dom;
47924         var lv = this.lockedBody.dom;
47925         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47926         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47927     },
47928
47929     handleScroll : function(e){
47930         this.syncScroll();
47931         var sb = this.scroller.dom;
47932         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47933         e.stopEvent();
47934     },
47935
47936     handleWheel : function(e){
47937         var d = e.getWheelDelta();
47938         this.scroller.dom.scrollTop -= d*22;
47939         // set this here to prevent jumpy scrolling on large tables
47940         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47941         e.stopEvent();
47942     },
47943
47944     renderRows : function(startRow, endRow){
47945         // pull in all the crap needed to render rows
47946         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47947         var colCount = cm.getColumnCount();
47948
47949         if(ds.getCount() < 1){
47950             return ["", ""];
47951         }
47952
47953         // build a map for all the columns
47954         var cs = [];
47955         for(var i = 0; i < colCount; i++){
47956             var name = cm.getDataIndex(i);
47957             cs[i] = {
47958                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47959                 renderer : cm.getRenderer(i),
47960                 id : cm.getColumnId(i),
47961                 locked : cm.isLocked(i)
47962             };
47963         }
47964
47965         startRow = startRow || 0;
47966         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47967
47968         // records to render
47969         var rs = ds.getRange(startRow, endRow);
47970
47971         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47972     },
47973
47974     // As much as I hate to duplicate code, this was branched because FireFox really hates
47975     // [].join("") on strings. The performance difference was substantial enough to
47976     // branch this function
47977     doRender : Roo.isGecko ?
47978             function(cs, rs, ds, startRow, colCount, stripe){
47979                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47980                 // buffers
47981                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47982                 
47983                 var hasListener = this.grid.hasListener('rowclass');
47984                 var rowcfg = {};
47985                 for(var j = 0, len = rs.length; j < len; j++){
47986                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47987                     for(var i = 0; i < colCount; i++){
47988                         c = cs[i];
47989                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47990                         p.id = c.id;
47991                         p.css = p.attr = "";
47992                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47993                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47994                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47995                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47996                         }
47997                         var markup = ct.apply(p);
47998                         if(!c.locked){
47999                             cb+= markup;
48000                         }else{
48001                             lcb+= markup;
48002                         }
48003                     }
48004                     var alt = [];
48005                     if(stripe && ((rowIndex+1) % 2 == 0)){
48006                         alt.push("x-grid-row-alt")
48007                     }
48008                     if(r.dirty){
48009                         alt.push(  " x-grid-dirty-row");
48010                     }
48011                     rp.cells = lcb;
48012                     if(this.getRowClass){
48013                         alt.push(this.getRowClass(r, rowIndex));
48014                     }
48015                     if (hasListener) {
48016                         rowcfg = {
48017                              
48018                             record: r,
48019                             rowIndex : rowIndex,
48020                             rowClass : ''
48021                         }
48022                         this.grid.fireEvent('rowclass', this, rowcfg);
48023                         alt.push(rowcfg.rowClass);
48024                     }
48025                     rp.alt = alt.join(" ");
48026                     lbuf+= rt.apply(rp);
48027                     rp.cells = cb;
48028                     buf+=  rt.apply(rp);
48029                 }
48030                 return [lbuf, buf];
48031             } :
48032             function(cs, rs, ds, startRow, colCount, stripe){
48033                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48034                 // buffers
48035                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48036                 var hasListener = this.grid.hasListener('rowclass');
48037                 var rowcfg = {};
48038                 for(var j = 0, len = rs.length; j < len; j++){
48039                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48040                     for(var i = 0; i < colCount; i++){
48041                         c = cs[i];
48042                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48043                         p.id = c.id;
48044                         p.css = p.attr = "";
48045                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48046                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48047                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48048                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48049                         }
48050                         var markup = ct.apply(p);
48051                         if(!c.locked){
48052                             cb[cb.length] = markup;
48053                         }else{
48054                             lcb[lcb.length] = markup;
48055                         }
48056                     }
48057                     var alt = [];
48058                     if(stripe && ((rowIndex+1) % 2 == 0)){
48059                         alt.push( "x-grid-row-alt");
48060                     }
48061                     if(r.dirty){
48062                         alt.push(" x-grid-dirty-row");
48063                     }
48064                     rp.cells = lcb;
48065                     if(this.getRowClass){
48066                         alt.push( this.getRowClass(r, rowIndex));
48067                     }
48068                     if (hasListener) {
48069                         rowcfg = {
48070                              
48071                             record: r,
48072                             rowIndex : rowIndex,
48073                             rowClass : ''
48074                         }
48075                         this.grid.fireEvent('rowclass', this, rowcfg);
48076                         alt.push(rowcfg.rowClass);
48077                     }
48078                     rp.alt = alt.join(" ");
48079                     rp.cells = lcb.join("");
48080                     lbuf[lbuf.length] = rt.apply(rp);
48081                     rp.cells = cb.join("");
48082                     buf[buf.length] =  rt.apply(rp);
48083                 }
48084                 return [lbuf.join(""), buf.join("")];
48085             },
48086
48087     renderBody : function(){
48088         var markup = this.renderRows();
48089         var bt = this.templates.body;
48090         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48091     },
48092
48093     /**
48094      * Refreshes the grid
48095      * @param {Boolean} headersToo
48096      */
48097     refresh : function(headersToo){
48098         this.fireEvent("beforerefresh", this);
48099         this.grid.stopEditing();
48100         var result = this.renderBody();
48101         this.lockedBody.update(result[0]);
48102         this.mainBody.update(result[1]);
48103         if(headersToo === true){
48104             this.updateHeaders();
48105             this.updateColumns();
48106             this.updateSplitters();
48107             this.updateHeaderSortState();
48108         }
48109         this.syncRowHeights();
48110         this.layout();
48111         this.fireEvent("refresh", this);
48112     },
48113
48114     handleColumnMove : function(cm, oldIndex, newIndex){
48115         this.indexMap = null;
48116         var s = this.getScrollState();
48117         this.refresh(true);
48118         this.restoreScroll(s);
48119         this.afterMove(newIndex);
48120     },
48121
48122     afterMove : function(colIndex){
48123         if(this.enableMoveAnim && Roo.enableFx){
48124             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48125         }
48126     },
48127
48128     updateCell : function(dm, rowIndex, dataIndex){
48129         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48130         if(typeof colIndex == "undefined"){ // not present in grid
48131             return;
48132         }
48133         var cm = this.grid.colModel;
48134         var cell = this.getCell(rowIndex, colIndex);
48135         var cellText = this.getCellText(rowIndex, colIndex);
48136
48137         var p = {
48138             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48139             id : cm.getColumnId(colIndex),
48140             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48141         };
48142         var renderer = cm.getRenderer(colIndex);
48143         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48144         if(typeof val == "undefined" || val === "") val = "&#160;";
48145         cellText.innerHTML = val;
48146         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48147         this.syncRowHeights(rowIndex, rowIndex);
48148     },
48149
48150     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48151         var maxWidth = 0;
48152         if(this.grid.autoSizeHeaders){
48153             var h = this.getHeaderCellMeasure(colIndex);
48154             maxWidth = Math.max(maxWidth, h.scrollWidth);
48155         }
48156         var tb, index;
48157         if(this.cm.isLocked(colIndex)){
48158             tb = this.getLockedTable();
48159             index = colIndex;
48160         }else{
48161             tb = this.getBodyTable();
48162             index = colIndex - this.cm.getLockedCount();
48163         }
48164         if(tb && tb.rows){
48165             var rows = tb.rows;
48166             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48167             for(var i = 0; i < stopIndex; i++){
48168                 var cell = rows[i].childNodes[index].firstChild;
48169                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48170             }
48171         }
48172         return maxWidth + /*margin for error in IE*/ 5;
48173     },
48174     /**
48175      * Autofit a column to its content.
48176      * @param {Number} colIndex
48177      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48178      */
48179      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48180          if(this.cm.isHidden(colIndex)){
48181              return; // can't calc a hidden column
48182          }
48183         if(forceMinSize){
48184             var cid = this.cm.getColumnId(colIndex);
48185             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48186            if(this.grid.autoSizeHeaders){
48187                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48188            }
48189         }
48190         var newWidth = this.calcColumnWidth(colIndex);
48191         this.cm.setColumnWidth(colIndex,
48192             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48193         if(!suppressEvent){
48194             this.grid.fireEvent("columnresize", colIndex, newWidth);
48195         }
48196     },
48197
48198     /**
48199      * Autofits all columns to their content and then expands to fit any extra space in the grid
48200      */
48201      autoSizeColumns : function(){
48202         var cm = this.grid.colModel;
48203         var colCount = cm.getColumnCount();
48204         for(var i = 0; i < colCount; i++){
48205             this.autoSizeColumn(i, true, true);
48206         }
48207         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48208             this.fitColumns();
48209         }else{
48210             this.updateColumns();
48211             this.layout();
48212         }
48213     },
48214
48215     /**
48216      * Autofits all columns to the grid's width proportionate with their current size
48217      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48218      */
48219     fitColumns : function(reserveScrollSpace){
48220         var cm = this.grid.colModel;
48221         var colCount = cm.getColumnCount();
48222         var cols = [];
48223         var width = 0;
48224         var i, w;
48225         for (i = 0; i < colCount; i++){
48226             if(!cm.isHidden(i) && !cm.isFixed(i)){
48227                 w = cm.getColumnWidth(i);
48228                 cols.push(i);
48229                 cols.push(w);
48230                 width += w;
48231             }
48232         }
48233         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48234         if(reserveScrollSpace){
48235             avail -= 17;
48236         }
48237         var frac = (avail - cm.getTotalWidth())/width;
48238         while (cols.length){
48239             w = cols.pop();
48240             i = cols.pop();
48241             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48242         }
48243         this.updateColumns();
48244         this.layout();
48245     },
48246
48247     onRowSelect : function(rowIndex){
48248         var row = this.getRowComposite(rowIndex);
48249         row.addClass("x-grid-row-selected");
48250     },
48251
48252     onRowDeselect : function(rowIndex){
48253         var row = this.getRowComposite(rowIndex);
48254         row.removeClass("x-grid-row-selected");
48255     },
48256
48257     onCellSelect : function(row, col){
48258         var cell = this.getCell(row, col);
48259         if(cell){
48260             Roo.fly(cell).addClass("x-grid-cell-selected");
48261         }
48262     },
48263
48264     onCellDeselect : function(row, col){
48265         var cell = this.getCell(row, col);
48266         if(cell){
48267             Roo.fly(cell).removeClass("x-grid-cell-selected");
48268         }
48269     },
48270
48271     updateHeaderSortState : function(){
48272         var state = this.ds.getSortState();
48273         if(!state){
48274             return;
48275         }
48276         this.sortState = state;
48277         var sortColumn = this.cm.findColumnIndex(state.field);
48278         if(sortColumn != -1){
48279             var sortDir = state.direction;
48280             var sc = this.sortClasses;
48281             var hds = this.el.select(this.headerSelector).removeClass(sc);
48282             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48283         }
48284     },
48285
48286     handleHeaderClick : function(g, index){
48287         if(this.headersDisabled){
48288             return;
48289         }
48290         var dm = g.dataSource, cm = g.colModel;
48291             if(!cm.isSortable(index)){
48292             return;
48293         }
48294             g.stopEditing();
48295         dm.sort(cm.getDataIndex(index));
48296     },
48297
48298
48299     destroy : function(){
48300         if(this.colMenu){
48301             this.colMenu.removeAll();
48302             Roo.menu.MenuMgr.unregister(this.colMenu);
48303             this.colMenu.getEl().remove();
48304             delete this.colMenu;
48305         }
48306         if(this.hmenu){
48307             this.hmenu.removeAll();
48308             Roo.menu.MenuMgr.unregister(this.hmenu);
48309             this.hmenu.getEl().remove();
48310             delete this.hmenu;
48311         }
48312         if(this.grid.enableColumnMove){
48313             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48314             if(dds){
48315                 for(var dd in dds){
48316                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48317                         var elid = dds[dd].dragElId;
48318                         dds[dd].unreg();
48319                         Roo.get(elid).remove();
48320                     } else if(dds[dd].config.isTarget){
48321                         dds[dd].proxyTop.remove();
48322                         dds[dd].proxyBottom.remove();
48323                         dds[dd].unreg();
48324                     }
48325                     if(Roo.dd.DDM.locationCache[dd]){
48326                         delete Roo.dd.DDM.locationCache[dd];
48327                     }
48328                 }
48329                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48330             }
48331         }
48332         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48333         this.bind(null, null);
48334         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48335     },
48336
48337     handleLockChange : function(){
48338         this.refresh(true);
48339     },
48340
48341     onDenyColumnLock : function(){
48342
48343     },
48344
48345     onDenyColumnHide : function(){
48346
48347     },
48348
48349     handleHdMenuClick : function(item){
48350         var index = this.hdCtxIndex;
48351         var cm = this.cm, ds = this.ds;
48352         switch(item.id){
48353             case "asc":
48354                 ds.sort(cm.getDataIndex(index), "ASC");
48355                 break;
48356             case "desc":
48357                 ds.sort(cm.getDataIndex(index), "DESC");
48358                 break;
48359             case "lock":
48360                 var lc = cm.getLockedCount();
48361                 if(cm.getColumnCount(true) <= lc+1){
48362                     this.onDenyColumnLock();
48363                     return;
48364                 }
48365                 if(lc != index){
48366                     cm.setLocked(index, true, true);
48367                     cm.moveColumn(index, lc);
48368                     this.grid.fireEvent("columnmove", index, lc);
48369                 }else{
48370                     cm.setLocked(index, true);
48371                 }
48372             break;
48373             case "unlock":
48374                 var lc = cm.getLockedCount();
48375                 if((lc-1) != index){
48376                     cm.setLocked(index, false, true);
48377                     cm.moveColumn(index, lc-1);
48378                     this.grid.fireEvent("columnmove", index, lc-1);
48379                 }else{
48380                     cm.setLocked(index, false);
48381                 }
48382             break;
48383             default:
48384                 index = cm.getIndexById(item.id.substr(4));
48385                 if(index != -1){
48386                     if(item.checked && cm.getColumnCount(true) <= 1){
48387                         this.onDenyColumnHide();
48388                         return false;
48389                     }
48390                     cm.setHidden(index, item.checked);
48391                 }
48392         }
48393         return true;
48394     },
48395
48396     beforeColMenuShow : function(){
48397         var cm = this.cm,  colCount = cm.getColumnCount();
48398         this.colMenu.removeAll();
48399         for(var i = 0; i < colCount; i++){
48400             this.colMenu.add(new Roo.menu.CheckItem({
48401                 id: "col-"+cm.getColumnId(i),
48402                 text: cm.getColumnHeader(i),
48403                 checked: !cm.isHidden(i),
48404                 hideOnClick:false
48405             }));
48406         }
48407     },
48408
48409     handleHdCtx : function(g, index, e){
48410         e.stopEvent();
48411         var hd = this.getHeaderCell(index);
48412         this.hdCtxIndex = index;
48413         var ms = this.hmenu.items, cm = this.cm;
48414         ms.get("asc").setDisabled(!cm.isSortable(index));
48415         ms.get("desc").setDisabled(!cm.isSortable(index));
48416         if(this.grid.enableColLock !== false){
48417             ms.get("lock").setDisabled(cm.isLocked(index));
48418             ms.get("unlock").setDisabled(!cm.isLocked(index));
48419         }
48420         this.hmenu.show(hd, "tl-bl");
48421     },
48422
48423     handleHdOver : function(e){
48424         var hd = this.findHeaderCell(e.getTarget());
48425         if(hd && !this.headersDisabled){
48426             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48427                this.fly(hd).addClass("x-grid-hd-over");
48428             }
48429         }
48430     },
48431
48432     handleHdOut : function(e){
48433         var hd = this.findHeaderCell(e.getTarget());
48434         if(hd){
48435             this.fly(hd).removeClass("x-grid-hd-over");
48436         }
48437     },
48438
48439     handleSplitDblClick : function(e, t){
48440         var i = this.getCellIndex(t);
48441         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48442             this.autoSizeColumn(i, true);
48443             this.layout();
48444         }
48445     },
48446
48447     render : function(){
48448
48449         var cm = this.cm;
48450         var colCount = cm.getColumnCount();
48451
48452         if(this.grid.monitorWindowResize === true){
48453             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48454         }
48455         var header = this.renderHeaders();
48456         var body = this.templates.body.apply({rows:""});
48457         var html = this.templates.master.apply({
48458             lockedBody: body,
48459             body: body,
48460             lockedHeader: header[0],
48461             header: header[1]
48462         });
48463
48464         //this.updateColumns();
48465
48466         this.grid.getGridEl().dom.innerHTML = html;
48467
48468         this.initElements();
48469         
48470         // a kludge to fix the random scolling effect in webkit
48471         this.el.on("scroll", function() {
48472             this.el.dom.scrollTop=0; // hopefully not recursive..
48473         },this);
48474
48475         this.scroller.on("scroll", this.handleScroll, this);
48476         this.lockedBody.on("mousewheel", this.handleWheel, this);
48477         this.mainBody.on("mousewheel", this.handleWheel, this);
48478
48479         this.mainHd.on("mouseover", this.handleHdOver, this);
48480         this.mainHd.on("mouseout", this.handleHdOut, this);
48481         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48482                 {delegate: "."+this.splitClass});
48483
48484         this.lockedHd.on("mouseover", this.handleHdOver, this);
48485         this.lockedHd.on("mouseout", this.handleHdOut, this);
48486         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48487                 {delegate: "."+this.splitClass});
48488
48489         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48490             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48491         }
48492
48493         this.updateSplitters();
48494
48495         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48496             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48497             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48498         }
48499
48500         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48501             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48502             this.hmenu.add(
48503                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48504                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48505             );
48506             if(this.grid.enableColLock !== false){
48507                 this.hmenu.add('-',
48508                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48509                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48510                 );
48511             }
48512             if(this.grid.enableColumnHide !== false){
48513
48514                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48515                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48516                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48517
48518                 this.hmenu.add('-',
48519                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48520                 );
48521             }
48522             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48523
48524             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48525         }
48526
48527         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48528             this.dd = new Roo.grid.GridDragZone(this.grid, {
48529                 ddGroup : this.grid.ddGroup || 'GridDD'
48530             });
48531         }
48532
48533         /*
48534         for(var i = 0; i < colCount; i++){
48535             if(cm.isHidden(i)){
48536                 this.hideColumn(i);
48537             }
48538             if(cm.config[i].align){
48539                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48540                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48541             }
48542         }*/
48543         
48544         this.updateHeaderSortState();
48545
48546         this.beforeInitialResize();
48547         this.layout(true);
48548
48549         // two part rendering gives faster view to the user
48550         this.renderPhase2.defer(1, this);
48551     },
48552
48553     renderPhase2 : function(){
48554         // render the rows now
48555         this.refresh();
48556         if(this.grid.autoSizeColumns){
48557             this.autoSizeColumns();
48558         }
48559     },
48560
48561     beforeInitialResize : function(){
48562
48563     },
48564
48565     onColumnSplitterMoved : function(i, w){
48566         this.userResized = true;
48567         var cm = this.grid.colModel;
48568         cm.setColumnWidth(i, w, true);
48569         var cid = cm.getColumnId(i);
48570         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48571         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48572         this.updateSplitters();
48573         this.layout();
48574         this.grid.fireEvent("columnresize", i, w);
48575     },
48576
48577     syncRowHeights : function(startIndex, endIndex){
48578         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48579             startIndex = startIndex || 0;
48580             var mrows = this.getBodyTable().rows;
48581             var lrows = this.getLockedTable().rows;
48582             var len = mrows.length-1;
48583             endIndex = Math.min(endIndex || len, len);
48584             for(var i = startIndex; i <= endIndex; i++){
48585                 var m = mrows[i], l = lrows[i];
48586                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48587                 m.style.height = l.style.height = h + "px";
48588             }
48589         }
48590     },
48591
48592     layout : function(initialRender, is2ndPass){
48593         var g = this.grid;
48594         var auto = g.autoHeight;
48595         var scrollOffset = 16;
48596         var c = g.getGridEl(), cm = this.cm,
48597                 expandCol = g.autoExpandColumn,
48598                 gv = this;
48599         //c.beginMeasure();
48600
48601         if(!c.dom.offsetWidth){ // display:none?
48602             if(initialRender){
48603                 this.lockedWrap.show();
48604                 this.mainWrap.show();
48605             }
48606             return;
48607         }
48608
48609         var hasLock = this.cm.isLocked(0);
48610
48611         var tbh = this.headerPanel.getHeight();
48612         var bbh = this.footerPanel.getHeight();
48613
48614         if(auto){
48615             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48616             var newHeight = ch + c.getBorderWidth("tb");
48617             if(g.maxHeight){
48618                 newHeight = Math.min(g.maxHeight, newHeight);
48619             }
48620             c.setHeight(newHeight);
48621         }
48622
48623         if(g.autoWidth){
48624             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48625         }
48626
48627         var s = this.scroller;
48628
48629         var csize = c.getSize(true);
48630
48631         this.el.setSize(csize.width, csize.height);
48632
48633         this.headerPanel.setWidth(csize.width);
48634         this.footerPanel.setWidth(csize.width);
48635
48636         var hdHeight = this.mainHd.getHeight();
48637         var vw = csize.width;
48638         var vh = csize.height - (tbh + bbh);
48639
48640         s.setSize(vw, vh);
48641
48642         var bt = this.getBodyTable();
48643         var ltWidth = hasLock ?
48644                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48645
48646         var scrollHeight = bt.offsetHeight;
48647         var scrollWidth = ltWidth + bt.offsetWidth;
48648         var vscroll = false, hscroll = false;
48649
48650         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48651
48652         var lw = this.lockedWrap, mw = this.mainWrap;
48653         var lb = this.lockedBody, mb = this.mainBody;
48654
48655         setTimeout(function(){
48656             var t = s.dom.offsetTop;
48657             var w = s.dom.clientWidth,
48658                 h = s.dom.clientHeight;
48659
48660             lw.setTop(t);
48661             lw.setSize(ltWidth, h);
48662
48663             mw.setLeftTop(ltWidth, t);
48664             mw.setSize(w-ltWidth, h);
48665
48666             lb.setHeight(h-hdHeight);
48667             mb.setHeight(h-hdHeight);
48668
48669             if(is2ndPass !== true && !gv.userResized && expandCol){
48670                 // high speed resize without full column calculation
48671                 
48672                 var ci = cm.getIndexById(expandCol);
48673                 if (ci < 0) {
48674                     ci = cm.findColumnIndex(expandCol);
48675                 }
48676                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48677                 var expandId = cm.getColumnId(ci);
48678                 var  tw = cm.getTotalWidth(false);
48679                 var currentWidth = cm.getColumnWidth(ci);
48680                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48681                 if(currentWidth != cw){
48682                     cm.setColumnWidth(ci, cw, true);
48683                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48684                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48685                     gv.updateSplitters();
48686                     gv.layout(false, true);
48687                 }
48688             }
48689
48690             if(initialRender){
48691                 lw.show();
48692                 mw.show();
48693             }
48694             //c.endMeasure();
48695         }, 10);
48696     },
48697
48698     onWindowResize : function(){
48699         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48700             return;
48701         }
48702         this.layout();
48703     },
48704
48705     appendFooter : function(parentEl){
48706         return null;
48707     },
48708
48709     sortAscText : "Sort Ascending",
48710     sortDescText : "Sort Descending",
48711     lockText : "Lock Column",
48712     unlockText : "Unlock Column",
48713     columnsText : "Columns"
48714 });
48715
48716
48717 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48718     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48719     this.proxy.el.addClass('x-grid3-col-dd');
48720 };
48721
48722 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48723     handleMouseDown : function(e){
48724
48725     },
48726
48727     callHandleMouseDown : function(e){
48728         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48729     }
48730 });
48731 /*
48732  * Based on:
48733  * Ext JS Library 1.1.1
48734  * Copyright(c) 2006-2007, Ext JS, LLC.
48735  *
48736  * Originally Released Under LGPL - original licence link has changed is not relivant.
48737  *
48738  * Fork - LGPL
48739  * <script type="text/javascript">
48740  */
48741  
48742 // private
48743 // This is a support class used internally by the Grid components
48744 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48745     this.grid = grid;
48746     this.view = grid.getView();
48747     this.proxy = this.view.resizeProxy;
48748     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48749         "gridSplitters" + this.grid.getGridEl().id, {
48750         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48751     });
48752     this.setHandleElId(Roo.id(hd));
48753     this.setOuterHandleElId(Roo.id(hd2));
48754     this.scroll = false;
48755 };
48756 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48757     fly: Roo.Element.fly,
48758
48759     b4StartDrag : function(x, y){
48760         this.view.headersDisabled = true;
48761         this.proxy.setHeight(this.view.mainWrap.getHeight());
48762         var w = this.cm.getColumnWidth(this.cellIndex);
48763         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48764         this.resetConstraints();
48765         this.setXConstraint(minw, 1000);
48766         this.setYConstraint(0, 0);
48767         this.minX = x - minw;
48768         this.maxX = x + 1000;
48769         this.startPos = x;
48770         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48771     },
48772
48773
48774     handleMouseDown : function(e){
48775         ev = Roo.EventObject.setEvent(e);
48776         var t = this.fly(ev.getTarget());
48777         if(t.hasClass("x-grid-split")){
48778             this.cellIndex = this.view.getCellIndex(t.dom);
48779             this.split = t.dom;
48780             this.cm = this.grid.colModel;
48781             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48782                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48783             }
48784         }
48785     },
48786
48787     endDrag : function(e){
48788         this.view.headersDisabled = false;
48789         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48790         var diff = endX - this.startPos;
48791         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48792     },
48793
48794     autoOffset : function(){
48795         this.setDelta(0,0);
48796     }
48797 });/*
48798  * Based on:
48799  * Ext JS Library 1.1.1
48800  * Copyright(c) 2006-2007, Ext JS, LLC.
48801  *
48802  * Originally Released Under LGPL - original licence link has changed is not relivant.
48803  *
48804  * Fork - LGPL
48805  * <script type="text/javascript">
48806  */
48807  
48808 // private
48809 // This is a support class used internally by the Grid components
48810 Roo.grid.GridDragZone = function(grid, config){
48811     this.view = grid.getView();
48812     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48813     if(this.view.lockedBody){
48814         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48815         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48816     }
48817     this.scroll = false;
48818     this.grid = grid;
48819     this.ddel = document.createElement('div');
48820     this.ddel.className = 'x-grid-dd-wrap';
48821 };
48822
48823 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48824     ddGroup : "GridDD",
48825
48826     getDragData : function(e){
48827         var t = Roo.lib.Event.getTarget(e);
48828         var rowIndex = this.view.findRowIndex(t);
48829         if(rowIndex !== false){
48830             var sm = this.grid.selModel;
48831             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48832               //  sm.mouseDown(e, t);
48833             //}
48834             if (e.hasModifier()){
48835                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48836             }
48837             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48838         }
48839         return false;
48840     },
48841
48842     onInitDrag : function(e){
48843         var data = this.dragData;
48844         this.ddel.innerHTML = this.grid.getDragDropText();
48845         this.proxy.update(this.ddel);
48846         // fire start drag?
48847     },
48848
48849     afterRepair : function(){
48850         this.dragging = false;
48851     },
48852
48853     getRepairXY : function(e, data){
48854         return false;
48855     },
48856
48857     onEndDrag : function(data, e){
48858         // fire end drag?
48859     },
48860
48861     onValidDrop : function(dd, e, id){
48862         // fire drag drop?
48863         this.hideProxy();
48864     },
48865
48866     beforeInvalidDrop : function(e, id){
48867
48868     }
48869 });/*
48870  * Based on:
48871  * Ext JS Library 1.1.1
48872  * Copyright(c) 2006-2007, Ext JS, LLC.
48873  *
48874  * Originally Released Under LGPL - original licence link has changed is not relivant.
48875  *
48876  * Fork - LGPL
48877  * <script type="text/javascript">
48878  */
48879  
48880
48881 /**
48882  * @class Roo.grid.ColumnModel
48883  * @extends Roo.util.Observable
48884  * This is the default implementation of a ColumnModel used by the Grid. It defines
48885  * the columns in the grid.
48886  * <br>Usage:<br>
48887  <pre><code>
48888  var colModel = new Roo.grid.ColumnModel([
48889         {header: "Ticker", width: 60, sortable: true, locked: true},
48890         {header: "Company Name", width: 150, sortable: true},
48891         {header: "Market Cap.", width: 100, sortable: true},
48892         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48893         {header: "Employees", width: 100, sortable: true, resizable: false}
48894  ]);
48895  </code></pre>
48896  * <p>
48897  
48898  * The config options listed for this class are options which may appear in each
48899  * individual column definition.
48900  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48901  * @constructor
48902  * @param {Object} config An Array of column config objects. See this class's
48903  * config objects for details.
48904 */
48905 Roo.grid.ColumnModel = function(config){
48906         /**
48907      * The config passed into the constructor
48908      */
48909     this.config = config;
48910     this.lookup = {};
48911
48912     // if no id, create one
48913     // if the column does not have a dataIndex mapping,
48914     // map it to the order it is in the config
48915     for(var i = 0, len = config.length; i < len; i++){
48916         var c = config[i];
48917         if(typeof c.dataIndex == "undefined"){
48918             c.dataIndex = i;
48919         }
48920         if(typeof c.renderer == "string"){
48921             c.renderer = Roo.util.Format[c.renderer];
48922         }
48923         if(typeof c.id == "undefined"){
48924             c.id = Roo.id();
48925         }
48926         if(c.editor && c.editor.xtype){
48927             c.editor  = Roo.factory(c.editor, Roo.grid);
48928         }
48929         if(c.editor && c.editor.isFormField){
48930             c.editor = new Roo.grid.GridEditor(c.editor);
48931         }
48932         this.lookup[c.id] = c;
48933     }
48934
48935     /**
48936      * The width of columns which have no width specified (defaults to 100)
48937      * @type Number
48938      */
48939     this.defaultWidth = 100;
48940
48941     /**
48942      * Default sortable of columns which have no sortable specified (defaults to false)
48943      * @type Boolean
48944      */
48945     this.defaultSortable = false;
48946
48947     this.addEvents({
48948         /**
48949              * @event widthchange
48950              * Fires when the width of a column changes.
48951              * @param {ColumnModel} this
48952              * @param {Number} columnIndex The column index
48953              * @param {Number} newWidth The new width
48954              */
48955             "widthchange": true,
48956         /**
48957              * @event headerchange
48958              * Fires when the text of a header changes.
48959              * @param {ColumnModel} this
48960              * @param {Number} columnIndex The column index
48961              * @param {Number} newText The new header text
48962              */
48963             "headerchange": true,
48964         /**
48965              * @event hiddenchange
48966              * Fires when a column is hidden or "unhidden".
48967              * @param {ColumnModel} this
48968              * @param {Number} columnIndex The column index
48969              * @param {Boolean} hidden true if hidden, false otherwise
48970              */
48971             "hiddenchange": true,
48972             /**
48973          * @event columnmoved
48974          * Fires when a column is moved.
48975          * @param {ColumnModel} this
48976          * @param {Number} oldIndex
48977          * @param {Number} newIndex
48978          */
48979         "columnmoved" : true,
48980         /**
48981          * @event columlockchange
48982          * Fires when a column's locked state is changed
48983          * @param {ColumnModel} this
48984          * @param {Number} colIndex
48985          * @param {Boolean} locked true if locked
48986          */
48987         "columnlockchange" : true
48988     });
48989     Roo.grid.ColumnModel.superclass.constructor.call(this);
48990 };
48991 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48992     /**
48993      * @cfg {String} header The header text to display in the Grid view.
48994      */
48995     /**
48996      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48997      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48998      * specified, the column's index is used as an index into the Record's data Array.
48999      */
49000     /**
49001      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49002      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49003      */
49004     /**
49005      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49006      * Defaults to the value of the {@link #defaultSortable} property.
49007      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49008      */
49009     /**
49010      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49011      */
49012     /**
49013      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49014      */
49015     /**
49016      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49017      */
49018     /**
49019      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49020      */
49021     /**
49022      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49023      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49024      * default renderer uses the raw data value.
49025      */
49026        /**
49027      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49028      */
49029     /**
49030      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49031      */
49032
49033     /**
49034      * Returns the id of the column at the specified index.
49035      * @param {Number} index The column index
49036      * @return {String} the id
49037      */
49038     getColumnId : function(index){
49039         return this.config[index].id;
49040     },
49041
49042     /**
49043      * Returns the column for a specified id.
49044      * @param {String} id The column id
49045      * @return {Object} the column
49046      */
49047     getColumnById : function(id){
49048         return this.lookup[id];
49049     },
49050
49051     
49052     /**
49053      * Returns the column for a specified dataIndex.
49054      * @param {String} dataIndex The column dataIndex
49055      * @return {Object|Boolean} the column or false if not found
49056      */
49057     getColumnByDataIndex: function(dataIndex){
49058         var index = this.findColumnIndex(dataIndex);
49059         return index > -1 ? this.config[index] : false;
49060     },
49061     
49062     /**
49063      * Returns the index for a specified column id.
49064      * @param {String} id The column id
49065      * @return {Number} the index, or -1 if not found
49066      */
49067     getIndexById : function(id){
49068         for(var i = 0, len = this.config.length; i < len; i++){
49069             if(this.config[i].id == id){
49070                 return i;
49071             }
49072         }
49073         return -1;
49074     },
49075     
49076     /**
49077      * Returns the index for a specified column dataIndex.
49078      * @param {String} dataIndex The column dataIndex
49079      * @return {Number} the index, or -1 if not found
49080      */
49081     
49082     findColumnIndex : function(dataIndex){
49083         for(var i = 0, len = this.config.length; i < len; i++){
49084             if(this.config[i].dataIndex == dataIndex){
49085                 return i;
49086             }
49087         }
49088         return -1;
49089     },
49090     
49091     
49092     moveColumn : function(oldIndex, newIndex){
49093         var c = this.config[oldIndex];
49094         this.config.splice(oldIndex, 1);
49095         this.config.splice(newIndex, 0, c);
49096         this.dataMap = null;
49097         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49098     },
49099
49100     isLocked : function(colIndex){
49101         return this.config[colIndex].locked === true;
49102     },
49103
49104     setLocked : function(colIndex, value, suppressEvent){
49105         if(this.isLocked(colIndex) == value){
49106             return;
49107         }
49108         this.config[colIndex].locked = value;
49109         if(!suppressEvent){
49110             this.fireEvent("columnlockchange", this, colIndex, value);
49111         }
49112     },
49113
49114     getTotalLockedWidth : function(){
49115         var totalWidth = 0;
49116         for(var i = 0; i < this.config.length; i++){
49117             if(this.isLocked(i) && !this.isHidden(i)){
49118                 this.totalWidth += this.getColumnWidth(i);
49119             }
49120         }
49121         return totalWidth;
49122     },
49123
49124     getLockedCount : function(){
49125         for(var i = 0, len = this.config.length; i < len; i++){
49126             if(!this.isLocked(i)){
49127                 return i;
49128             }
49129         }
49130     },
49131
49132     /**
49133      * Returns the number of columns.
49134      * @return {Number}
49135      */
49136     getColumnCount : function(visibleOnly){
49137         if(visibleOnly === true){
49138             var c = 0;
49139             for(var i = 0, len = this.config.length; i < len; i++){
49140                 if(!this.isHidden(i)){
49141                     c++;
49142                 }
49143             }
49144             return c;
49145         }
49146         return this.config.length;
49147     },
49148
49149     /**
49150      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49151      * @param {Function} fn
49152      * @param {Object} scope (optional)
49153      * @return {Array} result
49154      */
49155     getColumnsBy : function(fn, scope){
49156         var r = [];
49157         for(var i = 0, len = this.config.length; i < len; i++){
49158             var c = this.config[i];
49159             if(fn.call(scope||this, c, i) === true){
49160                 r[r.length] = c;
49161             }
49162         }
49163         return r;
49164     },
49165
49166     /**
49167      * Returns true if the specified column is sortable.
49168      * @param {Number} col The column index
49169      * @return {Boolean}
49170      */
49171     isSortable : function(col){
49172         if(typeof this.config[col].sortable == "undefined"){
49173             return this.defaultSortable;
49174         }
49175         return this.config[col].sortable;
49176     },
49177
49178     /**
49179      * Returns the rendering (formatting) function defined for the column.
49180      * @param {Number} col The column index.
49181      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49182      */
49183     getRenderer : function(col){
49184         if(!this.config[col].renderer){
49185             return Roo.grid.ColumnModel.defaultRenderer;
49186         }
49187         return this.config[col].renderer;
49188     },
49189
49190     /**
49191      * Sets the rendering (formatting) function for a column.
49192      * @param {Number} col The column index
49193      * @param {Function} fn The function to use to process the cell's raw data
49194      * to return HTML markup for the grid view. The render function is called with
49195      * the following parameters:<ul>
49196      * <li>Data value.</li>
49197      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49198      * <li>css A CSS style string to apply to the table cell.</li>
49199      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49200      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49201      * <li>Row index</li>
49202      * <li>Column index</li>
49203      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49204      */
49205     setRenderer : function(col, fn){
49206         this.config[col].renderer = fn;
49207     },
49208
49209     /**
49210      * Returns the width for the specified column.
49211      * @param {Number} col The column index
49212      * @return {Number}
49213      */
49214     getColumnWidth : function(col){
49215         return this.config[col].width * 1 || this.defaultWidth;
49216     },
49217
49218     /**
49219      * Sets the width for a column.
49220      * @param {Number} col The column index
49221      * @param {Number} width The new width
49222      */
49223     setColumnWidth : function(col, width, suppressEvent){
49224         this.config[col].width = width;
49225         this.totalWidth = null;
49226         if(!suppressEvent){
49227              this.fireEvent("widthchange", this, col, width);
49228         }
49229     },
49230
49231     /**
49232      * Returns the total width of all columns.
49233      * @param {Boolean} includeHidden True to include hidden column widths
49234      * @return {Number}
49235      */
49236     getTotalWidth : function(includeHidden){
49237         if(!this.totalWidth){
49238             this.totalWidth = 0;
49239             for(var i = 0, len = this.config.length; i < len; i++){
49240                 if(includeHidden || !this.isHidden(i)){
49241                     this.totalWidth += this.getColumnWidth(i);
49242                 }
49243             }
49244         }
49245         return this.totalWidth;
49246     },
49247
49248     /**
49249      * Returns the header for the specified column.
49250      * @param {Number} col The column index
49251      * @return {String}
49252      */
49253     getColumnHeader : function(col){
49254         return this.config[col].header;
49255     },
49256
49257     /**
49258      * Sets the header for a column.
49259      * @param {Number} col The column index
49260      * @param {String} header The new header
49261      */
49262     setColumnHeader : function(col, header){
49263         this.config[col].header = header;
49264         this.fireEvent("headerchange", this, col, header);
49265     },
49266
49267     /**
49268      * Returns the tooltip for the specified column.
49269      * @param {Number} col The column index
49270      * @return {String}
49271      */
49272     getColumnTooltip : function(col){
49273             return this.config[col].tooltip;
49274     },
49275     /**
49276      * Sets the tooltip for a column.
49277      * @param {Number} col The column index
49278      * @param {String} tooltip The new tooltip
49279      */
49280     setColumnTooltip : function(col, tooltip){
49281             this.config[col].tooltip = tooltip;
49282     },
49283
49284     /**
49285      * Returns the dataIndex for the specified column.
49286      * @param {Number} col The column index
49287      * @return {Number}
49288      */
49289     getDataIndex : function(col){
49290         return this.config[col].dataIndex;
49291     },
49292
49293     /**
49294      * Sets the dataIndex for a column.
49295      * @param {Number} col The column index
49296      * @param {Number} dataIndex The new dataIndex
49297      */
49298     setDataIndex : function(col, dataIndex){
49299         this.config[col].dataIndex = dataIndex;
49300     },
49301
49302     
49303     
49304     /**
49305      * Returns true if the cell is editable.
49306      * @param {Number} colIndex The column index
49307      * @param {Number} rowIndex The row index
49308      * @return {Boolean}
49309      */
49310     isCellEditable : function(colIndex, rowIndex){
49311         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49312     },
49313
49314     /**
49315      * Returns the editor defined for the cell/column.
49316      * return false or null to disable editing.
49317      * @param {Number} colIndex The column index
49318      * @param {Number} rowIndex The row index
49319      * @return {Object}
49320      */
49321     getCellEditor : function(colIndex, rowIndex){
49322         return this.config[colIndex].editor;
49323     },
49324
49325     /**
49326      * Sets if a column is editable.
49327      * @param {Number} col The column index
49328      * @param {Boolean} editable True if the column is editable
49329      */
49330     setEditable : function(col, editable){
49331         this.config[col].editable = editable;
49332     },
49333
49334
49335     /**
49336      * Returns true if the column is hidden.
49337      * @param {Number} colIndex The column index
49338      * @return {Boolean}
49339      */
49340     isHidden : function(colIndex){
49341         return this.config[colIndex].hidden;
49342     },
49343
49344
49345     /**
49346      * Returns true if the column width cannot be changed
49347      */
49348     isFixed : function(colIndex){
49349         return this.config[colIndex].fixed;
49350     },
49351
49352     /**
49353      * Returns true if the column can be resized
49354      * @return {Boolean}
49355      */
49356     isResizable : function(colIndex){
49357         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49358     },
49359     /**
49360      * Sets if a column is hidden.
49361      * @param {Number} colIndex The column index
49362      * @param {Boolean} hidden True if the column is hidden
49363      */
49364     setHidden : function(colIndex, hidden){
49365         this.config[colIndex].hidden = hidden;
49366         this.totalWidth = null;
49367         this.fireEvent("hiddenchange", this, colIndex, hidden);
49368     },
49369
49370     /**
49371      * Sets the editor for a column.
49372      * @param {Number} col The column index
49373      * @param {Object} editor The editor object
49374      */
49375     setEditor : function(col, editor){
49376         this.config[col].editor = editor;
49377     }
49378 });
49379
49380 Roo.grid.ColumnModel.defaultRenderer = function(value){
49381         if(typeof value == "string" && value.length < 1){
49382             return "&#160;";
49383         }
49384         return value;
49385 };
49386
49387 // Alias for backwards compatibility
49388 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49389 /*
49390  * Based on:
49391  * Ext JS Library 1.1.1
49392  * Copyright(c) 2006-2007, Ext JS, LLC.
49393  *
49394  * Originally Released Under LGPL - original licence link has changed is not relivant.
49395  *
49396  * Fork - LGPL
49397  * <script type="text/javascript">
49398  */
49399
49400 /**
49401  * @class Roo.grid.AbstractSelectionModel
49402  * @extends Roo.util.Observable
49403  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49404  * implemented by descendant classes.  This class should not be directly instantiated.
49405  * @constructor
49406  */
49407 Roo.grid.AbstractSelectionModel = function(){
49408     this.locked = false;
49409     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49410 };
49411
49412 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49413     /** @ignore Called by the grid automatically. Do not call directly. */
49414     init : function(grid){
49415         this.grid = grid;
49416         this.initEvents();
49417     },
49418
49419     /**
49420      * Locks the selections.
49421      */
49422     lock : function(){
49423         this.locked = true;
49424     },
49425
49426     /**
49427      * Unlocks the selections.
49428      */
49429     unlock : function(){
49430         this.locked = false;
49431     },
49432
49433     /**
49434      * Returns true if the selections are locked.
49435      * @return {Boolean}
49436      */
49437     isLocked : function(){
49438         return this.locked;
49439     }
49440 });/*
49441  * Based on:
49442  * Ext JS Library 1.1.1
49443  * Copyright(c) 2006-2007, Ext JS, LLC.
49444  *
49445  * Originally Released Under LGPL - original licence link has changed is not relivant.
49446  *
49447  * Fork - LGPL
49448  * <script type="text/javascript">
49449  */
49450 /**
49451  * @extends Roo.grid.AbstractSelectionModel
49452  * @class Roo.grid.RowSelectionModel
49453  * The default SelectionModel used by {@link Roo.grid.Grid}.
49454  * It supports multiple selections and keyboard selection/navigation. 
49455  * @constructor
49456  * @param {Object} config
49457  */
49458 Roo.grid.RowSelectionModel = function(config){
49459     Roo.apply(this, config);
49460     this.selections = new Roo.util.MixedCollection(false, function(o){
49461         return o.id;
49462     });
49463
49464     this.last = false;
49465     this.lastActive = false;
49466
49467     this.addEvents({
49468         /**
49469              * @event selectionchange
49470              * Fires when the selection changes
49471              * @param {SelectionModel} this
49472              */
49473             "selectionchange" : true,
49474         /**
49475              * @event afterselectionchange
49476              * Fires after the selection changes (eg. by key press or clicking)
49477              * @param {SelectionModel} this
49478              */
49479             "afterselectionchange" : true,
49480         /**
49481              * @event beforerowselect
49482              * Fires when a row is selected being selected, return false to cancel.
49483              * @param {SelectionModel} this
49484              * @param {Number} rowIndex The selected index
49485              * @param {Boolean} keepExisting False if other selections will be cleared
49486              */
49487             "beforerowselect" : true,
49488         /**
49489              * @event rowselect
49490              * Fires when a row is selected.
49491              * @param {SelectionModel} this
49492              * @param {Number} rowIndex The selected index
49493              * @param {Roo.data.Record} r The record
49494              */
49495             "rowselect" : true,
49496         /**
49497              * @event rowdeselect
49498              * Fires when a row is deselected.
49499              * @param {SelectionModel} this
49500              * @param {Number} rowIndex The selected index
49501              */
49502         "rowdeselect" : true
49503     });
49504     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49505     this.locked = false;
49506 };
49507
49508 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49509     /**
49510      * @cfg {Boolean} singleSelect
49511      * True to allow selection of only one row at a time (defaults to false)
49512      */
49513     singleSelect : false,
49514
49515     // private
49516     initEvents : function(){
49517
49518         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49519             this.grid.on("mousedown", this.handleMouseDown, this);
49520         }else{ // allow click to work like normal
49521             this.grid.on("rowclick", this.handleDragableRowClick, this);
49522         }
49523
49524         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49525             "up" : function(e){
49526                 if(!e.shiftKey){
49527                     this.selectPrevious(e.shiftKey);
49528                 }else if(this.last !== false && this.lastActive !== false){
49529                     var last = this.last;
49530                     this.selectRange(this.last,  this.lastActive-1);
49531                     this.grid.getView().focusRow(this.lastActive);
49532                     if(last !== false){
49533                         this.last = last;
49534                     }
49535                 }else{
49536                     this.selectFirstRow();
49537                 }
49538                 this.fireEvent("afterselectionchange", this);
49539             },
49540             "down" : function(e){
49541                 if(!e.shiftKey){
49542                     this.selectNext(e.shiftKey);
49543                 }else if(this.last !== false && this.lastActive !== false){
49544                     var last = this.last;
49545                     this.selectRange(this.last,  this.lastActive+1);
49546                     this.grid.getView().focusRow(this.lastActive);
49547                     if(last !== false){
49548                         this.last = last;
49549                     }
49550                 }else{
49551                     this.selectFirstRow();
49552                 }
49553                 this.fireEvent("afterselectionchange", this);
49554             },
49555             scope: this
49556         });
49557
49558         var view = this.grid.view;
49559         view.on("refresh", this.onRefresh, this);
49560         view.on("rowupdated", this.onRowUpdated, this);
49561         view.on("rowremoved", this.onRemove, this);
49562     },
49563
49564     // private
49565     onRefresh : function(){
49566         var ds = this.grid.dataSource, i, v = this.grid.view;
49567         var s = this.selections;
49568         s.each(function(r){
49569             if((i = ds.indexOfId(r.id)) != -1){
49570                 v.onRowSelect(i);
49571             }else{
49572                 s.remove(r);
49573             }
49574         });
49575     },
49576
49577     // private
49578     onRemove : function(v, index, r){
49579         this.selections.remove(r);
49580     },
49581
49582     // private
49583     onRowUpdated : function(v, index, r){
49584         if(this.isSelected(r)){
49585             v.onRowSelect(index);
49586         }
49587     },
49588
49589     /**
49590      * Select records.
49591      * @param {Array} records The records to select
49592      * @param {Boolean} keepExisting (optional) True to keep existing selections
49593      */
49594     selectRecords : function(records, keepExisting){
49595         if(!keepExisting){
49596             this.clearSelections();
49597         }
49598         var ds = this.grid.dataSource;
49599         for(var i = 0, len = records.length; i < len; i++){
49600             this.selectRow(ds.indexOf(records[i]), true);
49601         }
49602     },
49603
49604     /**
49605      * Gets the number of selected rows.
49606      * @return {Number}
49607      */
49608     getCount : function(){
49609         return this.selections.length;
49610     },
49611
49612     /**
49613      * Selects the first row in the grid.
49614      */
49615     selectFirstRow : function(){
49616         this.selectRow(0);
49617     },
49618
49619     /**
49620      * Select the last row.
49621      * @param {Boolean} keepExisting (optional) True to keep existing selections
49622      */
49623     selectLastRow : function(keepExisting){
49624         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49625     },
49626
49627     /**
49628      * Selects the row immediately following the last selected row.
49629      * @param {Boolean} keepExisting (optional) True to keep existing selections
49630      */
49631     selectNext : function(keepExisting){
49632         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49633             this.selectRow(this.last+1, keepExisting);
49634             this.grid.getView().focusRow(this.last);
49635         }
49636     },
49637
49638     /**
49639      * Selects the row that precedes the last selected row.
49640      * @param {Boolean} keepExisting (optional) True to keep existing selections
49641      */
49642     selectPrevious : function(keepExisting){
49643         if(this.last){
49644             this.selectRow(this.last-1, keepExisting);
49645             this.grid.getView().focusRow(this.last);
49646         }
49647     },
49648
49649     /**
49650      * Returns the selected records
49651      * @return {Array} Array of selected records
49652      */
49653     getSelections : function(){
49654         return [].concat(this.selections.items);
49655     },
49656
49657     /**
49658      * Returns the first selected record.
49659      * @return {Record}
49660      */
49661     getSelected : function(){
49662         return this.selections.itemAt(0);
49663     },
49664
49665
49666     /**
49667      * Clears all selections.
49668      */
49669     clearSelections : function(fast){
49670         if(this.locked) return;
49671         if(fast !== true){
49672             var ds = this.grid.dataSource;
49673             var s = this.selections;
49674             s.each(function(r){
49675                 this.deselectRow(ds.indexOfId(r.id));
49676             }, this);
49677             s.clear();
49678         }else{
49679             this.selections.clear();
49680         }
49681         this.last = false;
49682     },
49683
49684
49685     /**
49686      * Selects all rows.
49687      */
49688     selectAll : function(){
49689         if(this.locked) return;
49690         this.selections.clear();
49691         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49692             this.selectRow(i, true);
49693         }
49694     },
49695
49696     /**
49697      * Returns True if there is a selection.
49698      * @return {Boolean}
49699      */
49700     hasSelection : function(){
49701         return this.selections.length > 0;
49702     },
49703
49704     /**
49705      * Returns True if the specified row is selected.
49706      * @param {Number/Record} record The record or index of the record to check
49707      * @return {Boolean}
49708      */
49709     isSelected : function(index){
49710         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49711         return (r && this.selections.key(r.id) ? true : false);
49712     },
49713
49714     /**
49715      * Returns True if the specified record id is selected.
49716      * @param {String} id The id of record to check
49717      * @return {Boolean}
49718      */
49719     isIdSelected : function(id){
49720         return (this.selections.key(id) ? true : false);
49721     },
49722
49723     // private
49724     handleMouseDown : function(e, t){
49725         var view = this.grid.getView(), rowIndex;
49726         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49727             return;
49728         };
49729         if(e.shiftKey && this.last !== false){
49730             var last = this.last;
49731             this.selectRange(last, rowIndex, e.ctrlKey);
49732             this.last = last; // reset the last
49733             view.focusRow(rowIndex);
49734         }else{
49735             var isSelected = this.isSelected(rowIndex);
49736             if(e.button !== 0 && isSelected){
49737                 view.focusRow(rowIndex);
49738             }else if(e.ctrlKey && isSelected){
49739                 this.deselectRow(rowIndex);
49740             }else if(!isSelected){
49741                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49742                 view.focusRow(rowIndex);
49743             }
49744         }
49745         this.fireEvent("afterselectionchange", this);
49746     },
49747     // private
49748     handleDragableRowClick :  function(grid, rowIndex, e) 
49749     {
49750         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49751             this.selectRow(rowIndex, false);
49752             grid.view.focusRow(rowIndex);
49753              this.fireEvent("afterselectionchange", this);
49754         }
49755     },
49756     
49757     /**
49758      * Selects multiple rows.
49759      * @param {Array} rows Array of the indexes of the row to select
49760      * @param {Boolean} keepExisting (optional) True to keep existing selections
49761      */
49762     selectRows : function(rows, keepExisting){
49763         if(!keepExisting){
49764             this.clearSelections();
49765         }
49766         for(var i = 0, len = rows.length; i < len; i++){
49767             this.selectRow(rows[i], true);
49768         }
49769     },
49770
49771     /**
49772      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49773      * @param {Number} startRow The index of the first row in the range
49774      * @param {Number} endRow The index of the last row in the range
49775      * @param {Boolean} keepExisting (optional) True to retain existing selections
49776      */
49777     selectRange : function(startRow, endRow, keepExisting){
49778         if(this.locked) return;
49779         if(!keepExisting){
49780             this.clearSelections();
49781         }
49782         if(startRow <= endRow){
49783             for(var i = startRow; i <= endRow; i++){
49784                 this.selectRow(i, true);
49785             }
49786         }else{
49787             for(var i = startRow; i >= endRow; i--){
49788                 this.selectRow(i, true);
49789             }
49790         }
49791     },
49792
49793     /**
49794      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49795      * @param {Number} startRow The index of the first row in the range
49796      * @param {Number} endRow The index of the last row in the range
49797      */
49798     deselectRange : function(startRow, endRow, preventViewNotify){
49799         if(this.locked) return;
49800         for(var i = startRow; i <= endRow; i++){
49801             this.deselectRow(i, preventViewNotify);
49802         }
49803     },
49804
49805     /**
49806      * Selects a row.
49807      * @param {Number} row The index of the row to select
49808      * @param {Boolean} keepExisting (optional) True to keep existing selections
49809      */
49810     selectRow : function(index, keepExisting, preventViewNotify){
49811         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49812         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49813             if(!keepExisting || this.singleSelect){
49814                 this.clearSelections();
49815             }
49816             var r = this.grid.dataSource.getAt(index);
49817             this.selections.add(r);
49818             this.last = this.lastActive = index;
49819             if(!preventViewNotify){
49820                 this.grid.getView().onRowSelect(index);
49821             }
49822             this.fireEvent("rowselect", this, index, r);
49823             this.fireEvent("selectionchange", this);
49824         }
49825     },
49826
49827     /**
49828      * Deselects a row.
49829      * @param {Number} row The index of the row to deselect
49830      */
49831     deselectRow : function(index, preventViewNotify){
49832         if(this.locked) return;
49833         if(this.last == index){
49834             this.last = false;
49835         }
49836         if(this.lastActive == index){
49837             this.lastActive = false;
49838         }
49839         var r = this.grid.dataSource.getAt(index);
49840         this.selections.remove(r);
49841         if(!preventViewNotify){
49842             this.grid.getView().onRowDeselect(index);
49843         }
49844         this.fireEvent("rowdeselect", this, index);
49845         this.fireEvent("selectionchange", this);
49846     },
49847
49848     // private
49849     restoreLast : function(){
49850         if(this._last){
49851             this.last = this._last;
49852         }
49853     },
49854
49855     // private
49856     acceptsNav : function(row, col, cm){
49857         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49858     },
49859
49860     // private
49861     onEditorKey : function(field, e){
49862         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49863         if(k == e.TAB){
49864             e.stopEvent();
49865             ed.completeEdit();
49866             if(e.shiftKey){
49867                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49868             }else{
49869                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49870             }
49871         }else if(k == e.ENTER && !e.ctrlKey){
49872             e.stopEvent();
49873             ed.completeEdit();
49874             if(e.shiftKey){
49875                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49876             }else{
49877                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49878             }
49879         }else if(k == e.ESC){
49880             ed.cancelEdit();
49881         }
49882         if(newCell){
49883             g.startEditing(newCell[0], newCell[1]);
49884         }
49885     }
49886 });/*
49887  * Based on:
49888  * Ext JS Library 1.1.1
49889  * Copyright(c) 2006-2007, Ext JS, LLC.
49890  *
49891  * Originally Released Under LGPL - original licence link has changed is not relivant.
49892  *
49893  * Fork - LGPL
49894  * <script type="text/javascript">
49895  */
49896 /**
49897  * @class Roo.grid.CellSelectionModel
49898  * @extends Roo.grid.AbstractSelectionModel
49899  * This class provides the basic implementation for cell selection in a grid.
49900  * @constructor
49901  * @param {Object} config The object containing the configuration of this model.
49902  */
49903 Roo.grid.CellSelectionModel = function(config){
49904     Roo.apply(this, config);
49905
49906     this.selection = null;
49907
49908     this.addEvents({
49909         /**
49910              * @event beforerowselect
49911              * Fires before a cell is selected.
49912              * @param {SelectionModel} this
49913              * @param {Number} rowIndex The selected row index
49914              * @param {Number} colIndex The selected cell index
49915              */
49916             "beforecellselect" : true,
49917         /**
49918              * @event cellselect
49919              * Fires when a cell is selected.
49920              * @param {SelectionModel} this
49921              * @param {Number} rowIndex The selected row index
49922              * @param {Number} colIndex The selected cell index
49923              */
49924             "cellselect" : true,
49925         /**
49926              * @event selectionchange
49927              * Fires when the active selection changes.
49928              * @param {SelectionModel} this
49929              * @param {Object} selection null for no selection or an object (o) with two properties
49930                 <ul>
49931                 <li>o.record: the record object for the row the selection is in</li>
49932                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49933                 </ul>
49934              */
49935             "selectionchange" : true
49936     });
49937     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49938 };
49939
49940 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49941
49942     /** @ignore */
49943     initEvents : function(){
49944         this.grid.on("mousedown", this.handleMouseDown, this);
49945         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49946         var view = this.grid.view;
49947         view.on("refresh", this.onViewChange, this);
49948         view.on("rowupdated", this.onRowUpdated, this);
49949         view.on("beforerowremoved", this.clearSelections, this);
49950         view.on("beforerowsinserted", this.clearSelections, this);
49951         if(this.grid.isEditor){
49952             this.grid.on("beforeedit", this.beforeEdit,  this);
49953         }
49954     },
49955
49956         //private
49957     beforeEdit : function(e){
49958         this.select(e.row, e.column, false, true, e.record);
49959     },
49960
49961         //private
49962     onRowUpdated : function(v, index, r){
49963         if(this.selection && this.selection.record == r){
49964             v.onCellSelect(index, this.selection.cell[1]);
49965         }
49966     },
49967
49968         //private
49969     onViewChange : function(){
49970         this.clearSelections(true);
49971     },
49972
49973         /**
49974          * Returns the currently selected cell,.
49975          * @return {Array} The selected cell (row, column) or null if none selected.
49976          */
49977     getSelectedCell : function(){
49978         return this.selection ? this.selection.cell : null;
49979     },
49980
49981     /**
49982      * Clears all selections.
49983      * @param {Boolean} true to prevent the gridview from being notified about the change.
49984      */
49985     clearSelections : function(preventNotify){
49986         var s = this.selection;
49987         if(s){
49988             if(preventNotify !== true){
49989                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49990             }
49991             this.selection = null;
49992             this.fireEvent("selectionchange", this, null);
49993         }
49994     },
49995
49996     /**
49997      * Returns true if there is a selection.
49998      * @return {Boolean}
49999      */
50000     hasSelection : function(){
50001         return this.selection ? true : false;
50002     },
50003
50004     /** @ignore */
50005     handleMouseDown : function(e, t){
50006         var v = this.grid.getView();
50007         if(this.isLocked()){
50008             return;
50009         };
50010         var row = v.findRowIndex(t);
50011         var cell = v.findCellIndex(t);
50012         if(row !== false && cell !== false){
50013             this.select(row, cell);
50014         }
50015     },
50016
50017     /**
50018      * Selects a cell.
50019      * @param {Number} rowIndex
50020      * @param {Number} collIndex
50021      */
50022     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50023         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50024             this.clearSelections();
50025             r = r || this.grid.dataSource.getAt(rowIndex);
50026             this.selection = {
50027                 record : r,
50028                 cell : [rowIndex, colIndex]
50029             };
50030             if(!preventViewNotify){
50031                 var v = this.grid.getView();
50032                 v.onCellSelect(rowIndex, colIndex);
50033                 if(preventFocus !== true){
50034                     v.focusCell(rowIndex, colIndex);
50035                 }
50036             }
50037             this.fireEvent("cellselect", this, rowIndex, colIndex);
50038             this.fireEvent("selectionchange", this, this.selection);
50039         }
50040     },
50041
50042         //private
50043     isSelectable : function(rowIndex, colIndex, cm){
50044         return !cm.isHidden(colIndex);
50045     },
50046
50047     /** @ignore */
50048     handleKeyDown : function(e){
50049         Roo.log('Cell Sel Model handleKeyDown');
50050         if(!e.isNavKeyPress()){
50051             return;
50052         }
50053         var g = this.grid, s = this.selection;
50054         if(!s){
50055             e.stopEvent();
50056             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50057             if(cell){
50058                 this.select(cell[0], cell[1]);
50059             }
50060             return;
50061         }
50062         var sm = this;
50063         var walk = function(row, col, step){
50064             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50065         };
50066         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50067         var newCell;
50068
50069         switch(k){
50070             case e.TAB:
50071                 // handled by onEditorKey
50072                 if (g.isEditor && g.editing) {
50073                     return;
50074                 }
50075                 if(e.shiftKey){
50076                      newCell = walk(r, c-1, -1);
50077                 }else{
50078                      newCell = walk(r, c+1, 1);
50079                 }
50080              break;
50081              case e.DOWN:
50082                  newCell = walk(r+1, c, 1);
50083              break;
50084              case e.UP:
50085                  newCell = walk(r-1, c, -1);
50086              break;
50087              case e.RIGHT:
50088                  newCell = walk(r, c+1, 1);
50089              break;
50090              case e.LEFT:
50091                  newCell = walk(r, c-1, -1);
50092              break;
50093              case e.ENTER:
50094                  if(g.isEditor && !g.editing){
50095                     g.startEditing(r, c);
50096                     e.stopEvent();
50097                     return;
50098                 }
50099              break;
50100         };
50101         if(newCell){
50102             this.select(newCell[0], newCell[1]);
50103             e.stopEvent();
50104         }
50105     },
50106
50107     acceptsNav : function(row, col, cm){
50108         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50109     },
50110
50111     onEditorKey : function(field, e){
50112         
50113         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50114         ///Roo.log('onEditorKey' + k);
50115         
50116         if(k == e.TAB){
50117             if(e.shiftKey){
50118                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50119             }else{
50120                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50121             }
50122             e.stopEvent();
50123         }else if(k == e.ENTER && !e.ctrlKey){
50124             ed.completeEdit();
50125             e.stopEvent();
50126             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50127         }else if(k == e.ESC){
50128             ed.cancelEdit();
50129         }
50130         
50131         
50132         if(newCell){
50133             //Roo.log('next cell after edit');
50134             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50135         }
50136     }
50137 });/*
50138  * Based on:
50139  * Ext JS Library 1.1.1
50140  * Copyright(c) 2006-2007, Ext JS, LLC.
50141  *
50142  * Originally Released Under LGPL - original licence link has changed is not relivant.
50143  *
50144  * Fork - LGPL
50145  * <script type="text/javascript">
50146  */
50147  
50148 /**
50149  * @class Roo.grid.EditorGrid
50150  * @extends Roo.grid.Grid
50151  * Class for creating and editable grid.
50152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50153  * The container MUST have some type of size defined for the grid to fill. The container will be 
50154  * automatically set to position relative if it isn't already.
50155  * @param {Object} dataSource The data model to bind to
50156  * @param {Object} colModel The column model with info about this grid's columns
50157  */
50158 Roo.grid.EditorGrid = function(container, config){
50159     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50160     this.getGridEl().addClass("xedit-grid");
50161
50162     if(!this.selModel){
50163         this.selModel = new Roo.grid.CellSelectionModel();
50164     }
50165
50166     this.activeEditor = null;
50167
50168         this.addEvents({
50169             /**
50170              * @event beforeedit
50171              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50172              * <ul style="padding:5px;padding-left:16px;">
50173              * <li>grid - This grid</li>
50174              * <li>record - The record being edited</li>
50175              * <li>field - The field name being edited</li>
50176              * <li>value - The value for the field being edited.</li>
50177              * <li>row - The grid row index</li>
50178              * <li>column - The grid column index</li>
50179              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50180              * </ul>
50181              * @param {Object} e An edit event (see above for description)
50182              */
50183             "beforeedit" : true,
50184             /**
50185              * @event afteredit
50186              * Fires after a cell is edited. <br />
50187              * <ul style="padding:5px;padding-left:16px;">
50188              * <li>grid - This grid</li>
50189              * <li>record - The record being edited</li>
50190              * <li>field - The field name being edited</li>
50191              * <li>value - The value being set</li>
50192              * <li>originalValue - The original value for the field, before the edit.</li>
50193              * <li>row - The grid row index</li>
50194              * <li>column - The grid column index</li>
50195              * </ul>
50196              * @param {Object} e An edit event (see above for description)
50197              */
50198             "afteredit" : true,
50199             /**
50200              * @event validateedit
50201              * Fires after a cell is edited, but before the value is set in the record. 
50202          * You can use this to modify the value being set in the field, Return false
50203              * to cancel the change. The edit event object has the following properties <br />
50204              * <ul style="padding:5px;padding-left:16px;">
50205          * <li>editor - This editor</li>
50206              * <li>grid - This grid</li>
50207              * <li>record - The record being edited</li>
50208              * <li>field - The field name being edited</li>
50209              * <li>value - The value being set</li>
50210              * <li>originalValue - The original value for the field, before the edit.</li>
50211              * <li>row - The grid row index</li>
50212              * <li>column - The grid column index</li>
50213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50214              * </ul>
50215              * @param {Object} e An edit event (see above for description)
50216              */
50217             "validateedit" : true
50218         });
50219     this.on("bodyscroll", this.stopEditing,  this);
50220     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50221 };
50222
50223 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50224     /**
50225      * @cfg {Number} clicksToEdit
50226      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50227      */
50228     clicksToEdit: 2,
50229
50230     // private
50231     isEditor : true,
50232     // private
50233     trackMouseOver: false, // causes very odd FF errors
50234
50235     onCellDblClick : function(g, row, col){
50236         this.startEditing(row, col);
50237     },
50238
50239     onEditComplete : function(ed, value, startValue){
50240         this.editing = false;
50241         this.activeEditor = null;
50242         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50243         var r = ed.record;
50244         var field = this.colModel.getDataIndex(ed.col);
50245         var e = {
50246             grid: this,
50247             record: r,
50248             field: field,
50249             originalValue: startValue,
50250             value: value,
50251             row: ed.row,
50252             column: ed.col,
50253             cancel:false,
50254             editor: ed
50255         };
50256         if(String(value) !== String(startValue)){
50257             
50258             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50259                 r.set(field, e.value);
50260                 // if we are dealing with a combo box..
50261                 // then we also set the 'name' colum to be the displayField
50262                 if (ed.field.displayField && ed.field.name) {
50263                     r.set(ed.field.name, ed.field.el.dom.value);
50264                 }
50265                 
50266                 delete e.cancel; //?? why!!!
50267                 this.fireEvent("afteredit", e);
50268             }
50269         } else {
50270             this.fireEvent("afteredit", e); // always fire it!
50271         }
50272         this.view.focusCell(ed.row, ed.col);
50273     },
50274
50275     /**
50276      * Starts editing the specified for the specified row/column
50277      * @param {Number} rowIndex
50278      * @param {Number} colIndex
50279      */
50280     startEditing : function(row, col){
50281         this.stopEditing();
50282         if(this.colModel.isCellEditable(col, row)){
50283             this.view.ensureVisible(row, col, true);
50284             var r = this.dataSource.getAt(row);
50285             var field = this.colModel.getDataIndex(col);
50286             var e = {
50287                 grid: this,
50288                 record: r,
50289                 field: field,
50290                 value: r.data[field],
50291                 row: row,
50292                 column: col,
50293                 cancel:false
50294             };
50295             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50296                 this.editing = true;
50297                 var ed = this.colModel.getCellEditor(col, row);
50298                 
50299                 if (!ed) {
50300                     return;
50301                 }
50302                 if(!ed.rendered){
50303                     ed.render(ed.parentEl || document.body);
50304                 }
50305                 ed.field.reset();
50306                 (function(){ // complex but required for focus issues in safari, ie and opera
50307                     ed.row = row;
50308                     ed.col = col;
50309                     ed.record = r;
50310                     ed.on("complete", this.onEditComplete, this, {single: true});
50311                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50312                     this.activeEditor = ed;
50313                     var v = r.data[field];
50314                     ed.startEdit(this.view.getCell(row, col), v);
50315                     // combo's with 'displayField and name set
50316                     if (ed.field.displayField && ed.field.name) {
50317                         ed.field.el.dom.value = r.data[ed.field.name];
50318                     }
50319                     
50320                     
50321                 }).defer(50, this);
50322             }
50323         }
50324     },
50325         
50326     /**
50327      * Stops any active editing
50328      */
50329     stopEditing : function(){
50330         if(this.activeEditor){
50331             this.activeEditor.completeEdit();
50332         }
50333         this.activeEditor = null;
50334     }
50335 });/*
50336  * Based on:
50337  * Ext JS Library 1.1.1
50338  * Copyright(c) 2006-2007, Ext JS, LLC.
50339  *
50340  * Originally Released Under LGPL - original licence link has changed is not relivant.
50341  *
50342  * Fork - LGPL
50343  * <script type="text/javascript">
50344  */
50345
50346 // private - not really -- you end up using it !
50347 // This is a support class used internally by the Grid components
50348
50349 /**
50350  * @class Roo.grid.GridEditor
50351  * @extends Roo.Editor
50352  * Class for creating and editable grid elements.
50353  * @param {Object} config any settings (must include field)
50354  */
50355 Roo.grid.GridEditor = function(field, config){
50356     if (!config && field.field) {
50357         config = field;
50358         field = Roo.factory(config.field, Roo.form);
50359     }
50360     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50361     field.monitorTab = false;
50362 };
50363
50364 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50365     
50366     /**
50367      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50368      */
50369     
50370     alignment: "tl-tl",
50371     autoSize: "width",
50372     hideEl : false,
50373     cls: "x-small-editor x-grid-editor",
50374     shim:false,
50375     shadow:"frame"
50376 });/*
50377  * Based on:
50378  * Ext JS Library 1.1.1
50379  * Copyright(c) 2006-2007, Ext JS, LLC.
50380  *
50381  * Originally Released Under LGPL - original licence link has changed is not relivant.
50382  *
50383  * Fork - LGPL
50384  * <script type="text/javascript">
50385  */
50386   
50387
50388   
50389 Roo.grid.PropertyRecord = Roo.data.Record.create([
50390     {name:'name',type:'string'},  'value'
50391 ]);
50392
50393
50394 Roo.grid.PropertyStore = function(grid, source){
50395     this.grid = grid;
50396     this.store = new Roo.data.Store({
50397         recordType : Roo.grid.PropertyRecord
50398     });
50399     this.store.on('update', this.onUpdate,  this);
50400     if(source){
50401         this.setSource(source);
50402     }
50403     Roo.grid.PropertyStore.superclass.constructor.call(this);
50404 };
50405
50406
50407
50408 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50409     setSource : function(o){
50410         this.source = o;
50411         this.store.removeAll();
50412         var data = [];
50413         for(var k in o){
50414             if(this.isEditableValue(o[k])){
50415                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50416             }
50417         }
50418         this.store.loadRecords({records: data}, {}, true);
50419     },
50420
50421     onUpdate : function(ds, record, type){
50422         if(type == Roo.data.Record.EDIT){
50423             var v = record.data['value'];
50424             var oldValue = record.modified['value'];
50425             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50426                 this.source[record.id] = v;
50427                 record.commit();
50428                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50429             }else{
50430                 record.reject();
50431             }
50432         }
50433     },
50434
50435     getProperty : function(row){
50436        return this.store.getAt(row);
50437     },
50438
50439     isEditableValue: function(val){
50440         if(val && val instanceof Date){
50441             return true;
50442         }else if(typeof val == 'object' || typeof val == 'function'){
50443             return false;
50444         }
50445         return true;
50446     },
50447
50448     setValue : function(prop, value){
50449         this.source[prop] = value;
50450         this.store.getById(prop).set('value', value);
50451     },
50452
50453     getSource : function(){
50454         return this.source;
50455     }
50456 });
50457
50458 Roo.grid.PropertyColumnModel = function(grid, store){
50459     this.grid = grid;
50460     var g = Roo.grid;
50461     g.PropertyColumnModel.superclass.constructor.call(this, [
50462         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50463         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50464     ]);
50465     this.store = store;
50466     this.bselect = Roo.DomHelper.append(document.body, {
50467         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50468             {tag: 'option', value: 'true', html: 'true'},
50469             {tag: 'option', value: 'false', html: 'false'}
50470         ]
50471     });
50472     Roo.id(this.bselect);
50473     var f = Roo.form;
50474     this.editors = {
50475         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50476         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50477         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50478         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50479         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50480     };
50481     this.renderCellDelegate = this.renderCell.createDelegate(this);
50482     this.renderPropDelegate = this.renderProp.createDelegate(this);
50483 };
50484
50485 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50486     
50487     
50488     nameText : 'Name',
50489     valueText : 'Value',
50490     
50491     dateFormat : 'm/j/Y',
50492     
50493     
50494     renderDate : function(dateVal){
50495         return dateVal.dateFormat(this.dateFormat);
50496     },
50497
50498     renderBool : function(bVal){
50499         return bVal ? 'true' : 'false';
50500     },
50501
50502     isCellEditable : function(colIndex, rowIndex){
50503         return colIndex == 1;
50504     },
50505
50506     getRenderer : function(col){
50507         return col == 1 ?
50508             this.renderCellDelegate : this.renderPropDelegate;
50509     },
50510
50511     renderProp : function(v){
50512         return this.getPropertyName(v);
50513     },
50514
50515     renderCell : function(val){
50516         var rv = val;
50517         if(val instanceof Date){
50518             rv = this.renderDate(val);
50519         }else if(typeof val == 'boolean'){
50520             rv = this.renderBool(val);
50521         }
50522         return Roo.util.Format.htmlEncode(rv);
50523     },
50524
50525     getPropertyName : function(name){
50526         var pn = this.grid.propertyNames;
50527         return pn && pn[name] ? pn[name] : name;
50528     },
50529
50530     getCellEditor : function(colIndex, rowIndex){
50531         var p = this.store.getProperty(rowIndex);
50532         var n = p.data['name'], val = p.data['value'];
50533         
50534         if(typeof(this.grid.customEditors[n]) == 'string'){
50535             return this.editors[this.grid.customEditors[n]];
50536         }
50537         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50538             return this.grid.customEditors[n];
50539         }
50540         if(val instanceof Date){
50541             return this.editors['date'];
50542         }else if(typeof val == 'number'){
50543             return this.editors['number'];
50544         }else if(typeof val == 'boolean'){
50545             return this.editors['boolean'];
50546         }else{
50547             return this.editors['string'];
50548         }
50549     }
50550 });
50551
50552 /**
50553  * @class Roo.grid.PropertyGrid
50554  * @extends Roo.grid.EditorGrid
50555  * This class represents the  interface of a component based property grid control.
50556  * <br><br>Usage:<pre><code>
50557  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50558       
50559  });
50560  // set any options
50561  grid.render();
50562  * </code></pre>
50563   
50564  * @constructor
50565  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50566  * The container MUST have some type of size defined for the grid to fill. The container will be
50567  * automatically set to position relative if it isn't already.
50568  * @param {Object} config A config object that sets properties on this grid.
50569  */
50570 Roo.grid.PropertyGrid = function(container, config){
50571     config = config || {};
50572     var store = new Roo.grid.PropertyStore(this);
50573     this.store = store;
50574     var cm = new Roo.grid.PropertyColumnModel(this, store);
50575     store.store.sort('name', 'ASC');
50576     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50577         ds: store.store,
50578         cm: cm,
50579         enableColLock:false,
50580         enableColumnMove:false,
50581         stripeRows:false,
50582         trackMouseOver: false,
50583         clicksToEdit:1
50584     }, config));
50585     this.getGridEl().addClass('x-props-grid');
50586     this.lastEditRow = null;
50587     this.on('columnresize', this.onColumnResize, this);
50588     this.addEvents({
50589          /**
50590              * @event beforepropertychange
50591              * Fires before a property changes (return false to stop?)
50592              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50593              * @param {String} id Record Id
50594              * @param {String} newval New Value
50595          * @param {String} oldval Old Value
50596              */
50597         "beforepropertychange": true,
50598         /**
50599              * @event propertychange
50600              * Fires after a property changes
50601              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50602              * @param {String} id Record Id
50603              * @param {String} newval New Value
50604          * @param {String} oldval Old Value
50605              */
50606         "propertychange": true
50607     });
50608     this.customEditors = this.customEditors || {};
50609 };
50610 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50611     
50612      /**
50613      * @cfg {Object} customEditors map of colnames=> custom editors.
50614      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50615      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50616      * false disables editing of the field.
50617          */
50618     
50619       /**
50620      * @cfg {Object} propertyNames map of property Names to their displayed value
50621          */
50622     
50623     render : function(){
50624         Roo.grid.PropertyGrid.superclass.render.call(this);
50625         this.autoSize.defer(100, this);
50626     },
50627
50628     autoSize : function(){
50629         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50630         if(this.view){
50631             this.view.fitColumns();
50632         }
50633     },
50634
50635     onColumnResize : function(){
50636         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50637         this.autoSize();
50638     },
50639     /**
50640      * Sets the data for the Grid
50641      * accepts a Key => Value object of all the elements avaiable.
50642      * @param {Object} data  to appear in grid.
50643      */
50644     setSource : function(source){
50645         this.store.setSource(source);
50646         //this.autoSize();
50647     },
50648     /**
50649      * Gets all the data from the grid.
50650      * @return {Object} data  data stored in grid
50651      */
50652     getSource : function(){
50653         return this.store.getSource();
50654     }
50655 });/*
50656  * Based on:
50657  * Ext JS Library 1.1.1
50658  * Copyright(c) 2006-2007, Ext JS, LLC.
50659  *
50660  * Originally Released Under LGPL - original licence link has changed is not relivant.
50661  *
50662  * Fork - LGPL
50663  * <script type="text/javascript">
50664  */
50665  
50666 /**
50667  * @class Roo.LoadMask
50668  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50669  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50670  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50671  * element's UpdateManager load indicator and will be destroyed after the initial load.
50672  * @constructor
50673  * Create a new LoadMask
50674  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50675  * @param {Object} config The config object
50676  */
50677 Roo.LoadMask = function(el, config){
50678     this.el = Roo.get(el);
50679     Roo.apply(this, config);
50680     if(this.store){
50681         this.store.on('beforeload', this.onBeforeLoad, this);
50682         this.store.on('load', this.onLoad, this);
50683         this.store.on('loadexception', this.onLoad, this);
50684         this.removeMask = false;
50685     }else{
50686         var um = this.el.getUpdateManager();
50687         um.showLoadIndicator = false; // disable the default indicator
50688         um.on('beforeupdate', this.onBeforeLoad, this);
50689         um.on('update', this.onLoad, this);
50690         um.on('failure', this.onLoad, this);
50691         this.removeMask = true;
50692     }
50693 };
50694
50695 Roo.LoadMask.prototype = {
50696     /**
50697      * @cfg {Boolean} removeMask
50698      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50699      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50700      */
50701     /**
50702      * @cfg {String} msg
50703      * The text to display in a centered loading message box (defaults to 'Loading...')
50704      */
50705     msg : 'Loading...',
50706     /**
50707      * @cfg {String} msgCls
50708      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50709      */
50710     msgCls : 'x-mask-loading',
50711
50712     /**
50713      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50714      * @type Boolean
50715      */
50716     disabled: false,
50717
50718     /**
50719      * Disables the mask to prevent it from being displayed
50720      */
50721     disable : function(){
50722        this.disabled = true;
50723     },
50724
50725     /**
50726      * Enables the mask so that it can be displayed
50727      */
50728     enable : function(){
50729         this.disabled = false;
50730     },
50731
50732     // private
50733     onLoad : function(){
50734         this.el.unmask(this.removeMask);
50735     },
50736
50737     // private
50738     onBeforeLoad : function(){
50739         if(!this.disabled){
50740             this.el.mask(this.msg, this.msgCls);
50741         }
50742     },
50743
50744     // private
50745     destroy : function(){
50746         if(this.store){
50747             this.store.un('beforeload', this.onBeforeLoad, this);
50748             this.store.un('load', this.onLoad, this);
50749             this.store.un('loadexception', this.onLoad, this);
50750         }else{
50751             var um = this.el.getUpdateManager();
50752             um.un('beforeupdate', this.onBeforeLoad, this);
50753             um.un('update', this.onLoad, this);
50754             um.un('failure', this.onLoad, this);
50755         }
50756     }
50757 };/*
50758  * Based on:
50759  * Ext JS Library 1.1.1
50760  * Copyright(c) 2006-2007, Ext JS, LLC.
50761  *
50762  * Originally Released Under LGPL - original licence link has changed is not relivant.
50763  *
50764  * Fork - LGPL
50765  * <script type="text/javascript">
50766  */
50767 Roo.XTemplate = function(){
50768     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50769     var s = this.html;
50770
50771     s = ['<tpl>', s, '</tpl>'].join('');
50772
50773     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50774
50775     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50776     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50777     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50778     var m, id = 0;
50779     var tpls = [];
50780
50781     while(m = s.match(re)){
50782        var m2 = m[0].match(nameRe);
50783        var m3 = m[0].match(ifRe);
50784        var m4 = m[0].match(execRe);
50785        var exp = null, fn = null, exec = null;
50786        var name = m2 && m2[1] ? m2[1] : '';
50787        if(m3){
50788            exp = m3 && m3[1] ? m3[1] : null;
50789            if(exp){
50790                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50791            }
50792        }
50793        if(m4){
50794            exp = m4 && m4[1] ? m4[1] : null;
50795            if(exp){
50796                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50797            }
50798        }
50799        if(name){
50800            switch(name){
50801                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50802                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50803                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50804            }
50805        }
50806        tpls.push({
50807             id: id,
50808             target: name,
50809             exec: exec,
50810             test: fn,
50811             body: m[1]||''
50812         });
50813        s = s.replace(m[0], '{xtpl'+ id + '}');
50814        ++id;
50815     }
50816     for(var i = tpls.length-1; i >= 0; --i){
50817         this.compileTpl(tpls[i]);
50818     }
50819     this.master = tpls[tpls.length-1];
50820     this.tpls = tpls;
50821 };
50822 Roo.extend(Roo.XTemplate, Roo.Template, {
50823
50824     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50825
50826     applySubTemplate : function(id, values, parent){
50827         var t = this.tpls[id];
50828         if(t.test && !t.test.call(this, values, parent)){
50829             return '';
50830         }
50831         if(t.exec && t.exec.call(this, values, parent)){
50832             return '';
50833         }
50834         var vs = t.target ? t.target.call(this, values, parent) : values;
50835         parent = t.target ? values : parent;
50836         if(t.target && vs instanceof Array){
50837             var buf = [];
50838             for(var i = 0, len = vs.length; i < len; i++){
50839                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50840             }
50841             return buf.join('');
50842         }
50843         return t.compiled.call(this, vs, parent);
50844     },
50845
50846     compileTpl : function(tpl){
50847         var fm = Roo.util.Format;
50848         var useF = this.disableFormats !== true;
50849         var sep = Roo.isGecko ? "+" : ",";
50850         var fn = function(m, name, format, args){
50851             if(name.substr(0, 4) == 'xtpl'){
50852                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50853             }
50854             var v;
50855             if(name.indexOf('.') != -1){
50856                 v = name;
50857             }else{
50858                 v = "values['" + name + "']";
50859             }
50860             if(format && useF){
50861                 args = args ? ',' + args : "";
50862                 if(format.substr(0, 5) != "this."){
50863                     format = "fm." + format + '(';
50864                 }else{
50865                     format = 'this.call("'+ format.substr(5) + '", ';
50866                     args = ", values";
50867                 }
50868             }else{
50869                 args= ''; format = "("+v+" === undefined ? '' : ";
50870             }
50871             return "'"+ sep + format + v + args + ")"+sep+"'";
50872         };
50873         var body;
50874         // branched to use + in gecko and [].join() in others
50875         if(Roo.isGecko){
50876             body = "tpl.compiled = function(values, parent){ return '" +
50877                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50878                     "';};";
50879         }else{
50880             body = ["tpl.compiled = function(values, parent){ return ['"];
50881             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50882             body.push("'].join('');};");
50883             body = body.join('');
50884         }
50885         /** eval:var:zzzzzzz */
50886         eval(body);
50887         return this;
50888     },
50889
50890     applyTemplate : function(values){
50891         return this.master.compiled.call(this, values, {});
50892         var s = this.subs;
50893     },
50894
50895     apply : function(){
50896         return this.applyTemplate.apply(this, arguments);
50897     },
50898
50899     compile : function(){return this;}
50900 });
50901
50902 Roo.XTemplate.from = function(el){
50903     el = Roo.getDom(el);
50904     return new Roo.XTemplate(el.value || el.innerHTML);
50905 };/*
50906  * Original code for Roojs - LGPL
50907  * <script type="text/javascript">
50908  */
50909  
50910 /**
50911  * @class Roo.XComponent
50912  * A delayed Element creator...
50913  * 
50914  * Mypart.xyx = new Roo.XComponent({
50915
50916     parent : 'Mypart.xyz', // empty == document.element.!!
50917     order : '001',
50918     name : 'xxxx'
50919     region : 'xxxx'
50920     disabled : function() {} 
50921      
50922     tree : function() { // return an tree of xtype declared components
50923         var MODULE = this;
50924         return 
50925         {
50926             xtype : 'NestedLayoutPanel',
50927             // technicall
50928         }
50929      ]
50930  *})
50931  * @extends Roo.util.Observable
50932  * @constructor
50933  * @param cfg {Object} configuration of component
50934  * 
50935  */
50936 Roo.XComponent = function(cfg) {
50937     Roo.apply(this, cfg);
50938     this.addEvents({ 
50939         /**
50940              * @event built
50941              * Fires when this the componnt is built
50942              * @param {Roo.XComponent} c the component
50943              */
50944         'built' : true,
50945         /**
50946              * @event buildcomplete
50947              * Fires on the top level element when all elements have been built
50948              * @param {Roo.XComponent} c the top level component.
50949          */
50950         'buildcomplete' : true
50951         
50952     });
50953     
50954     Roo.XComponent.register(this);
50955     this.modules = false;
50956     this.el = false; // where the layout goes..
50957     
50958     
50959 }
50960 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50961     /**
50962      * @property el
50963      * The created element (with Roo.factory())
50964      * @type {Roo.Layout}
50965      */
50966     el  : false,
50967     
50968     /**
50969      * @property el
50970      * for BC  - use el in new code
50971      * @type {Roo.Layout}
50972      */
50973     panel : false,
50974     
50975     /**
50976      * @property layout
50977      * for BC  - use el in new code
50978      * @type {Roo.Layout}
50979      */
50980     layout : false,
50981     
50982      /**
50983      * @cfg {Function|boolean} disabled
50984      * If this module is disabled by some rule, return true from the funtion
50985      */
50986     disabled : false,
50987     
50988     /**
50989      * @cfg {String} parent 
50990      * Name of parent element which it get xtype added to..
50991      */
50992     parent: false,
50993     
50994     /**
50995      * @cfg {String} order
50996      * Used to set the order in which elements are created (usefull for multiple tabs)
50997      */
50998     
50999     order : false,
51000     /**
51001      * @cfg {String} name
51002      * String to display while loading.
51003      */
51004     name : false,
51005     /**
51006      * @cfg {Array} items
51007      * A single item array - the first element is the root of the tree..
51008      * It's done this way to stay compatible with the Xtype system...
51009      */
51010     items : false
51011      
51012      
51013     
51014 });
51015
51016 Roo.apply(Roo.XComponent, {
51017     
51018     /**
51019      * @property  buildCompleted
51020      * True when the builder has completed building the interface.
51021      * @type Boolean
51022      */
51023     buildCompleted : false,
51024      
51025     /**
51026      * @property  topModule
51027      * the upper most module - uses document.element as it's constructor.
51028      * @type Object
51029      */
51030      
51031     topModule  : false,
51032       
51033     /**
51034      * @property  modules
51035      * array of modules to be created by registration system.
51036      * @type Roo.XComponent
51037      */
51038     
51039     modules : [],
51040       
51041     
51042     /**
51043      * Register components to be built later.
51044      *
51045      * This solves the following issues
51046      * - Building is not done on page load, but after an authentication process has occured.
51047      * - Interface elements are registered on page load
51048      * - Parent Interface elements may not be loaded before child, so this handles that..
51049      * 
51050      *
51051      * example:
51052      * 
51053      * MyApp.register({
51054           order : '000001',
51055           module : 'Pman.Tab.projectMgr',
51056           region : 'center',
51057           parent : 'Pman.layout',
51058           disabled : false,  // or use a function..
51059         })
51060      
51061      * * @param {Object} details about module
51062      */
51063     register : function(obj) {
51064         this.modules.push(obj);
51065          
51066     },
51067     /**
51068      * convert a string to an object..
51069      * 
51070      */
51071     
51072     toObject : function(str)
51073     {
51074         if (!str || typeof(str) == 'object') {
51075             return str;
51076         }
51077         var ar = str.split('.');
51078         var rt, o;
51079         rt = ar.shift();
51080             /** eval:var:o */
51081         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51082         if (o === false) {
51083             throw "Module not found : " + str;
51084         }
51085         Roo.each(ar, function(e) {
51086             if (typeof(o[e]) == 'undefined') {
51087                 throw "Module not found : " + str;
51088             }
51089             o = o[e];
51090         });
51091         return o;
51092         
51093     },
51094     
51095     
51096     /**
51097      * move modules into their correct place in the tree..
51098      * 
51099      */
51100     preBuild : function ()
51101     {
51102         
51103         Roo.each(this.modules , function (obj)
51104         {
51105             obj.parent = this.toObject(obj.parent);
51106             
51107             if (!obj.parent) {
51108                 this.topModule = obj;
51109                 return;
51110             }
51111             
51112             if (!obj.parent.modules) {
51113                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51114                     function(o) { return o.order + '' }
51115                 );
51116             }
51117             
51118             obj.parent.modules.add(obj);
51119         }, this);
51120     },
51121     
51122      /**
51123      * make a list of modules to build.
51124      * @return {Array} list of modules. 
51125      */ 
51126     
51127     buildOrder : function()
51128     {
51129         var _this = this;
51130         var cmp = function(a,b) {   
51131             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51132         };
51133         
51134         if (!this.topModule || !this.topModule.modules) {
51135             throw "No top level modules to build";
51136         }
51137        
51138         // make a flat list in order of modules to build.
51139         var mods = [ this.topModule ];
51140         
51141         
51142         // add modules to their parents..
51143         var addMod = function(m) {
51144            // Roo.debug && Roo.log(m.modKey);
51145             
51146             mods.push(m);
51147             if (m.modules) {
51148                 m.modules.keySort('ASC',  cmp );
51149                 m.modules.each(addMod);
51150             }
51151             // not sure if this is used any more..
51152             if (m.finalize) {
51153                 m.finalize.name = m.name + " (clean up) ";
51154                 mods.push(m.finalize);
51155             }
51156             
51157         }
51158         this.topModule.modules.keySort('ASC',  cmp );
51159         this.topModule.modules.each(addMod);
51160         return mods;
51161     },
51162     
51163      /**
51164      * Build the registered modules.
51165      * @param {Object} parent element.
51166      * @param {Function} optional method to call after module has been added.
51167      * 
51168      */ 
51169    
51170     build : function() 
51171     {
51172         
51173         this.preBuild();
51174         var mods = this.buildOrder();
51175       
51176         //this.allmods = mods;
51177         //Roo.debug && Roo.log(mods);
51178         //return;
51179         if (!mods.length) { // should not happen
51180             throw "NO modules!!!";
51181         }
51182         
51183         
51184         
51185         // flash it up as modal - so we store the mask!?
51186         Roo.MessageBox.show({ title: 'loading' });
51187         Roo.MessageBox.show({
51188            title: "Please wait...",
51189            msg: "Building Interface...",
51190            width:450,
51191            progress:true,
51192            closable:false,
51193            modal: false
51194           
51195         });
51196         var total = mods.length;
51197         
51198         var _this = this;
51199         var progressRun = function() {
51200             if (!mods.length) {
51201                 Roo.debug && Roo.log('hide?');
51202                 Roo.MessageBox.hide();
51203                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51204                 return;    
51205             }
51206             
51207             var m = mods.shift();
51208             Roo.debug && Roo.log(m);
51209             if (typeof(m) == 'function') { // not sure if this is supported any more..
51210                 m.call(this);
51211                 return progressRun.defer(10, _this);
51212             } 
51213             
51214             Roo.MessageBox.updateProgress(
51215                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51216                     " of " + total + 
51217                     (m.name ? (' - ' + m.name) : '')
51218                     );
51219             
51220          
51221             
51222             var disabled = (typeof(m.disabled) == 'function') ?
51223                 m.disabled.call(m.module.disabled) : m.disabled;    
51224             
51225             
51226             if (disabled) {
51227                 return progressRun(); // we do not update the display!
51228             }
51229             
51230             if (!m.parent) {
51231                 // it's a top level one..
51232                 var layoutbase = new Ext.BorderLayout(document.body, {
51233                
51234                     center: {
51235                          titlebar: false,
51236                          autoScroll:false,
51237                          closeOnTab: true,
51238                          tabPosition: 'top',
51239                          //resizeTabs: true,
51240                          alwaysShowTabs: true,
51241                          minTabWidth: 140
51242                     }
51243                 });
51244                 var tree = m.tree();
51245                 tree.region = 'center';
51246                 m.el = layoutbase.addxtype(tree);
51247                 m.panel = m.el;
51248                 m.layout = m.panel.layout;    
51249                 return progressRun.defer(10, _this);
51250             }
51251             
51252             var tree = m.tree();
51253             tree.region = tree.region || m.region;
51254             m.el = m.parent.el.addxtype(tree);
51255             m.fireEvent('built', m);
51256             m.panel = m.el;
51257             m.layout = m.panel.layout;    
51258             progressRun.defer(10, _this); 
51259             
51260         }
51261         progressRun.defer(1, _this);
51262      
51263         
51264         
51265     }
51266      
51267    
51268     
51269     
51270 });
51271  //<script type="text/javascript">
51272
51273
51274 /**
51275  * @class Roo.Login
51276  * @extends Roo.LayoutDialog
51277  * A generic Login Dialog..... - only one needed in theory!?!?
51278  *
51279  * Fires XComponent builder on success...
51280  * 
51281  * Sends 
51282  *    username,password, lang = for login actions.
51283  *    check = 1 for periodic checking that sesion is valid.
51284  *    passwordRequest = email request password
51285  *    logout = 1 = to logout
51286  * 
51287  * Affects: (this id="????" elements)
51288  *   loading  (removed) (used to indicate application is loading)
51289  *   loading-mask (hides) (used to hide application when it's building loading)
51290  *   
51291  * 
51292  * Usage: 
51293  *    
51294  * 
51295  * Myapp.login = Roo.Login({
51296      url: xxxx,
51297    
51298      realm : 'Myapp', 
51299      
51300      
51301      method : 'POST',
51302      
51303      
51304      * 
51305  })
51306  * 
51307  * 
51308  * 
51309  **/
51310  
51311 Roo.Login = function(cfg)
51312 {
51313     this.addEvents({
51314         'refreshed' : true
51315     });
51316     
51317     Roo.apply(this,cfg);
51318     
51319     Roo.onReady(function() {
51320         this.onLoad();
51321     }, this);
51322     // call parent..
51323     
51324    
51325     Roo.Login.superclass.constructor.call(this, this);
51326     //this.addxtype(this.items[0]);
51327     
51328     
51329 }
51330
51331
51332 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51333     
51334     /**
51335      * @cfg {String} method
51336      * Method used to query for login details.
51337      */
51338     
51339     method : 'POST',
51340     /**
51341      * @cfg {String} url
51342      * URL to query login data. - eg. baseURL + '/Login.php'
51343      */
51344     url : '',
51345     
51346     /**
51347      * @property user
51348      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51349      * @type {Object} 
51350      */
51351     user : false,
51352     /**
51353      * @property checkFails
51354      * Number of times we have attempted to get authentication check, and failed.
51355      * @type {Number} 
51356      */
51357     checkFails : 0,
51358       /**
51359      * @property intervalID
51360      * The window interval that does the constant login checking.
51361      * @type {Number} 
51362      */
51363     intervalID : 0,
51364     
51365     
51366     onLoad : function() // called on page load...
51367     {
51368         // load 
51369          
51370         if (Roo.get('loading')) { // clear any loading indicator..
51371             Roo.get('loading').remove();
51372         }
51373         
51374         //this.switchLang('en'); // set the language to english..
51375        
51376         this.check({
51377             success:  function(response, opts)  {  // check successfull...
51378             
51379                 var res = this.processResponse(response);
51380                 this.checkFails =0;
51381                 if (!res.success) { // error!
51382                     this.checkFails = 5;
51383                     //console.log('call failure');
51384                     return this.failure(response,opts);
51385                 }
51386                 
51387                 if (!res.data.id) { // id=0 == login failure.
51388                     return this.show();
51389                 }
51390                 
51391                               
51392                         //console.log(success);
51393                 this.fillAuth(res.data);   
51394                 this.checkFails =0;
51395                 Roo.XComponent.build();
51396             },
51397             failure : this.show
51398         });
51399         
51400     }, 
51401     
51402     
51403     check: function(cfg) // called every so often to refresh cookie etc..
51404     {
51405         if (cfg.again) { // could be undefined..
51406             this.checkFails++;
51407         } else {
51408             this.checkFails = 0;
51409         }
51410         var _this = this;
51411         if (this.sending) {
51412             if ( this.checkFails > 4) {
51413                 Roo.MessageBox.alert("Error",  
51414                     "Error getting authentication status. - try reloading, or wait a while", function() {
51415                         _this.sending = false;
51416                     }); 
51417                 return;
51418             }
51419             cfg.again = true;
51420             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51421             return;
51422         }
51423         this.sending = true;
51424         
51425         Roo.Ajax.request({  
51426             url: this.url,
51427             params: {
51428                 getAuthUser: true
51429             },  
51430             method: this.method,
51431             success:  cfg.success || this.success,
51432             failure : cfg.failure || this.failure,
51433             scope : this,
51434             callCfg : cfg
51435               
51436         });  
51437     }, 
51438     
51439     
51440     logout: function()
51441     {
51442         window.onbeforeunload = function() { }; // false does not work for IE..
51443         this.user = false;
51444         var _this = this;
51445         
51446         Roo.Ajax.request({  
51447             url: this.url,
51448             params: {
51449                 logout: 1
51450             },  
51451             method: 'GET',
51452             failure : function() {
51453                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51454                     document.location = document.location.toString() + '?ts=' + Math.random();
51455                 });
51456                 
51457             },
51458             success : function() {
51459                 _this.user = false;
51460                 this.checkFails =0;
51461                 // fixme..
51462                 document.location = document.location.toString() + '?ts=' + Math.random();
51463             }
51464               
51465               
51466         }); 
51467     },
51468     
51469     processResponse : function (response)
51470     {
51471         var res = '';
51472         try {
51473             res = Roo.decode(response.responseText);
51474             // oops...
51475             if (typeof(res) != 'object') {
51476                 res = { success : false, errorMsg : res, errors : true };
51477             }
51478             if (typeof(res.success) == 'undefined') {
51479                 res.success = false;
51480             }
51481             
51482         } catch(e) {
51483             res = { success : false,  errorMsg : response.responseText, errors : true };
51484         }
51485         return res;
51486     },
51487     
51488     success : function(response, opts)  // check successfull...
51489     {  
51490         this.sending = false;
51491         var res = this.processResponse(response);
51492         if (!res.success) {
51493             return this.failure(response, opts);
51494         }
51495         if (!res.data || !res.data.id) {
51496             return this.failure(response,opts);
51497         }
51498         //console.log(res);
51499         this.fillAuth(res.data);
51500         
51501         this.checkFails =0;
51502         
51503     },
51504     
51505     
51506     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51507     {
51508         this.authUser = -1;
51509         this.sending = false;
51510         var res = this.processResponse(response);
51511         //console.log(res);
51512         if ( this.checkFails > 2) {
51513         
51514             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51515                 "Error getting authentication status. - try reloading"); 
51516             return;
51517         }
51518         opts.callCfg.again = true;
51519         this.check.defer(1000, this, [ opts.callCfg ]);
51520         return;  
51521     },
51522     
51523     
51524     
51525     fillAuth: function(au) {
51526         this.startAuthCheck();
51527         this.authUserId = au.id;
51528         this.authUser = au;
51529         this.lastChecked = new Date();
51530         this.fireEvent('refreshed', au);
51531         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51532         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51533         au.lang = au.lang || 'en';
51534         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51535         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51536         this.switchLang(au.lang );
51537         
51538      
51539         // open system... - -on setyp..
51540         if (this.authUserId  < 0) {
51541             Roo.MessageBox.alert("Warning", 
51542                 "This is an open system - please set up a admin user with a password.");  
51543         }
51544          
51545         //Pman.onload(); // which should do nothing if it's a re-auth result...
51546         
51547              
51548     },
51549     
51550     startAuthCheck : function() // starter for timeout checking..
51551     {
51552         if (this.intervalID) { // timer already in place...
51553             return false;
51554         }
51555         var _this = this;
51556         this.intervalID =  window.setInterval(function() {
51557               _this.check(false);
51558             }, 120000); // every 120 secs = 2mins..
51559         
51560         
51561     },
51562          
51563     
51564     switchLang : function (lang) 
51565     {
51566         _T = typeof(_T) == 'undefined' ? false : _T;
51567           if (!_T || !lang.length) {
51568             return;
51569         }
51570         
51571         if (!_T && lang != 'en') {
51572             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51573             return;
51574         }
51575         
51576         if (typeof(_T.en) == 'undefined') {
51577             _T.en = {};
51578             Roo.apply(_T.en, _T);
51579         }
51580         
51581         if (typeof(_T[lang]) == 'undefined') {
51582             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51583             return;
51584         }
51585         
51586         
51587         Roo.apply(_T, _T[lang]);
51588         // just need to set the text values for everything...
51589         var _this = this;
51590         /* this will not work ...
51591         if (this.form) { 
51592             
51593                
51594             function formLabel(name, val) {
51595                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51596             }
51597             
51598             formLabel('password', "Password"+':');
51599             formLabel('username', "Email Address"+':');
51600             formLabel('lang', "Language"+':');
51601             this.dialog.setTitle("Login");
51602             this.dialog.buttons[0].setText("Forgot Password");
51603             this.dialog.buttons[1].setText("Login");
51604         }
51605         */
51606         
51607         
51608     },
51609     
51610     
51611     title: "Login",
51612     modal: true,
51613     width:  350,
51614     //height: 230,
51615     height: 180,
51616     shadow: true,
51617     minWidth:200,
51618     minHeight:180,
51619     //proxyDrag: true,
51620     closable: false,
51621     draggable: false,
51622     collapsible: false,
51623     resizable: false,
51624     center: {  // needed??
51625         autoScroll:false,
51626         titlebar: false,
51627        // tabPosition: 'top',
51628         hideTabs: true,
51629         closeOnTab: true,
51630         alwaysShowTabs: false
51631     } ,
51632     listeners : {
51633         
51634         show  : function(dlg)
51635         {
51636             //console.log(this);
51637             this.form = this.layout.getRegion('center').activePanel.form;
51638             this.form.dialog = dlg;
51639             this.buttons[0].form = this.form;
51640             this.buttons[0].dialog = dlg;
51641             this.buttons[1].form = this.form;
51642             this.buttons[1].dialog = dlg;
51643            
51644            //this.resizeToLogo.defer(1000,this);
51645             // this is all related to resizing for logos..
51646             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51647            //// if (!sz) {
51648              //   this.resizeToLogo.defer(1000,this);
51649              //   return;
51650            // }
51651             //var w = Ext.lib.Dom.getViewWidth() - 100;
51652             //var h = Ext.lib.Dom.getViewHeight() - 100;
51653             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51654             //this.center();
51655             if (this.disabled) {
51656                 this.hide();
51657                 return;
51658             }
51659             
51660             if (this.user.id < 0) { // used for inital setup situations.
51661                 return;
51662             }
51663             
51664             if (this.intervalID) {
51665                 // remove the timer
51666                 window.clearInterval(this.intervalID);
51667                 this.intervalID = false;
51668             }
51669             
51670             
51671             if (Roo.get('loading')) {
51672                 Roo.get('loading').remove();
51673             }
51674             if (Roo.get('loading-mask')) {
51675                 Roo.get('loading-mask').hide();
51676             }
51677             
51678             //incomming._node = tnode;
51679             this.form.reset();
51680             //this.dialog.modal = !modal;
51681             //this.dialog.show();
51682             this.el.unmask(); 
51683             
51684             
51685             this.form.setValues({
51686                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51687                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51688             });
51689             
51690             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51691             if (this.form.findField('username').getValue().length > 0 ){
51692                 this.form.findField('password').focus();
51693             } else {
51694                this.form.findField('username').focus();
51695             }
51696     
51697         }
51698     },
51699     items : [
51700          {
51701        
51702             xtype : 'ContentPanel',
51703             xns : Roo,
51704             region: 'center',
51705             fitToFrame : true,
51706             
51707             items : [
51708     
51709                 {
51710                
51711                     xtype : 'Form',
51712                     xns : Roo.form,
51713                     labelWidth: 100,
51714                     style : 'margin: 10px;',
51715                     
51716                     listeners : {
51717                         actionfailed : function(f, act) {
51718                             // form can return { errors: .... }
51719                                 
51720                             //act.result.errors // invalid form element list...
51721                             //act.result.errorMsg// invalid form element list...
51722                             
51723                             this.dialog.el.unmask();
51724                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51725                                         "Login failed - communication error - try again.");
51726                                       
51727                         },
51728                         actioncomplete: function(re, act) {
51729                              
51730                             Roo.state.Manager.set(
51731                                 this.dialog.realm + '.username',  
51732                                     this.findField('username').getValue()
51733                             );
51734                             Roo.state.Manager.set(
51735                                 this.dialog.realm + '.lang',  
51736                                 this.findField('lang').getValue() 
51737                             );
51738                             
51739                             this.dialog.fillAuth(act.result.data);
51740                               
51741                             this.dialog.hide();
51742                             
51743                             if (Roo.get('loading-mask')) {
51744                                 Roo.get('loading-mask').show();
51745                             }
51746                             Roo.XComponent.build();
51747                             
51748                              
51749                             
51750                         }
51751                     },
51752                     items : [
51753                         {
51754                             xtype : 'TextField',
51755                             xns : Roo.form,
51756                             fieldLabel: "Email Address",
51757                             name: 'username',
51758                             width:200,
51759                             autoCreate : {tag: "input", type: "text", size: "20"}
51760                         },
51761                         {
51762                             xtype : 'TextField',
51763                             xns : Roo.form,
51764                             fieldLabel: "Password",
51765                             inputType: 'password',
51766                             name: 'password',
51767                             width:200,
51768                             autoCreate : {tag: "input", type: "text", size: "20"},
51769                             listeners : {
51770                                 specialkey : function(e,ev) {
51771                                     if (ev.keyCode == 13) {
51772                                         this.form.dialog.el.mask("Logging in");
51773                                         this.form.doAction('submit', {
51774                                             url: this.form.dialog.url,
51775                                             method: this.form.dialog.method
51776                                         });
51777                                     }
51778                                 }
51779                             }  
51780                         },
51781                         {
51782                             xtype : 'ComboBox',
51783                             xns : Roo.form,
51784                             fieldLabel: "Language",
51785                             name : 'langdisp',
51786                             store: {
51787                                 xtype : 'SimpleStore',
51788                                 fields: ['lang', 'ldisp'],
51789                                 data : [
51790                                     [ 'en', 'English' ],
51791                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51792                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51793                                 ]
51794                             },
51795                             
51796                             valueField : 'lang',
51797                             hiddenName:  'lang',
51798                             width: 200,
51799                             displayField:'ldisp',
51800                             typeAhead: false,
51801                             editable: false,
51802                             mode: 'local',
51803                             triggerAction: 'all',
51804                             emptyText:'Select a Language...',
51805                             selectOnFocus:true,
51806                             listeners : {
51807                                 select :  function(cb, rec, ix) {
51808                                     this.form.switchLang(rec.data.lang);
51809                                 }
51810                             }
51811                         
51812                         }
51813                     ]
51814                 }
51815                   
51816                 
51817             ]
51818         }
51819     ],
51820     buttons : [
51821         {
51822             xtype : 'Button',
51823             xns : 'Roo',
51824             text : "Forgot Password",
51825             listeners : {
51826                 click : function() {
51827                     //console.log(this);
51828                     var n = this.form.findField('username').getValue();
51829                     if (!n.length) {
51830                         Roo.MessageBox.alert("Error", "Fill in your email address");
51831                         return;
51832                     }
51833                     Roo.Ajax.request({
51834                         url: this.dialog.url,
51835                         params: {
51836                             passwordRequest: n
51837                         },
51838                         method: this.dialog.method,
51839                         success:  function(response, opts)  {  // check successfull...
51840                         
51841                             var res = this.dialog.processResponse(response);
51842                             if (!res.success) { // error!
51843                                Roo.MessageBox.alert("Error" ,
51844                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51845                                return;
51846                             }
51847                             Roo.MessageBox.alert("Notice" ,
51848                                 "Please check you email for the Password Reset message");
51849                         },
51850                         failure : function() {
51851                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51852                         }
51853                         
51854                     });
51855                 }
51856             }
51857         },
51858         {
51859             xtype : 'Button',
51860             xns : 'Roo',
51861             text : "Login",
51862             listeners : {
51863                 
51864                 click : function () {
51865                         
51866                     this.dialog.el.mask("Logging in");
51867                     this.form.doAction('submit', {
51868                             url: this.dialog.url,
51869                             method: this.dialog.method
51870                     });
51871                 }
51872             }
51873         }
51874     ]
51875   
51876   
51877 })
51878  
51879
51880
51881